JS是编译型语言编译发生在代码执行前几微秒,简单来说就是js在执行前要进行编译,编译过程发生在代码执行前几微妙,甚至更短。编译的步骤词法分析以var a = 2 为例,词法分析会将其分成三个有意义的代码块即词法单元。语法分析将词法单元组合生成代表了程序语法的结构的树,即抽象语法书(AST)。代码生成将AST生成可执行的代码。即将AST转化成一组机器指令。LHS RHS如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。词法作用域决定于你在写代码时的块级作用域优化依赖于词法的静态分析eval with 会创建新的作用域在词法分析阶段,无法知道eval with会对作用域做怎样的修改,此时引擎不再对作用域进行任何优化函数作用域函数声明 函数表达式区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。let隐式的生成块级作用域不存在变量提升提升原因变量(包括函数在内)的所有声明都会优先执行,只有声明本身会提升,而赋值或其他运行逻辑会留在原位置过程这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。 声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。闭包定义当函数能够记住或访问所在的词法作用域,及时是被作用域外调用,就产生了闭包模块现代模块机制未来的模块机制关于this绑定时间点是在函数运行时绑定的,而非定义时。它的上下文取决于函数调用时的各种条件,和在哪里定义的没有关系,只取决于函数的调用方式。绑定过程当函数被调用时,会创建一个执行上下文,在这个上下文里包含了函数在哪里没调用(调用栈),调用函数的方法,参数等。this作为执行上下文的一个属性,可以在函数执行的过程中用到。绑定类型默认绑定即绑定到全局,严格模式下回绑定到undefined。function foo() { console.log( this.a );}var a = 2;(function(){ “use strict”; foo(); // 2})()隐式绑定即绑定到最顶层(或最近调用对象)上function fun() { console.log(this.a)}var obj2 = { a: 3, fun: fun,}var obj1 = { a: 2, obj2: obj2,}obj1.obj2.fun() // 3显式绑定即用call或apply手动进行绑定bind方法实现new绑定(构造函数)不存在其实在js中不存在构造函数,我们所说的构造函数其实就是普通的函数,它只是用new被“构造调用”而已。new发生了什么?创建(或者说构造)一个全新的对象。这个新对象会被执行[[原型]]连接。这个新对象会绑定到函数调用的this。如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。箭头函数 =>对象内置对象基本类型在需要的时候(比如说获取长度),会被引擎默认转成内置对象,从而进行方法的调用。基础类型并不是继承自内置对象 var strPrimitive = “I am a string”; typeof strPrimitive; // “string” strPrimitive instanceof String; // false var strObject = new String( “I am a string” ); typeof strObject; // “object” strObject instanceof String; // true Object.prototype.toString.call( strObject ); // [object String] nulltypeof null === Object; 原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”拷贝浅拷贝Object.assign({}, obj)深拷贝JSON.stringify属性描述符getOwnPropertyDescriptor(myObj, ‘a’)definePropertyObject.defineProperty(myObj, ‘a’, { value: 2, writable: true, configurable: true, enumerable: true })Getter 、Settervar obj = { get a() { return this.a }, set a(val) { this.a = val * 5 }}obj.a = 10console.log(obj.a) // 50var obj2 = {}Object.defineProperty(obj2, ‘a’, { get: function() { return this.a }, set: function(val) { this.a = val * 2 }})obj2.a = 15console.log(obj2.a) // 30存在性in’a’ in obj1 会检查obj及其原型链上是否有’a’hasOwnProperty不会检查原型链,如果需要可以Object.prototype.hasOwnProperty.call(myObj, ‘a’)原型(prototype)constructor返回实例对象O的构造函数(的引用)。任何一个prototype对象都有一个constructor属性,指向它的构造函数,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性例如function Test() { this.name = ’test’}var test = new Test()console.log(test.constructor === Test) // true类constructor构造函数 constructor 是用于创建和初始化类中创建的一个对象的一种特殊方法.class Polygon { constructor() { this.name = “Polygon”; }}class Square extends Polygon { constructor() { super(); }}class Rectangle {}Object.setPrototypeOf(Square.prototype, Rectangle.prototype);console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //falseconsole.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //truelet newInstance = new Square();console.log(newInstance.name); //Polygonproto实例对象__proto__指向生成改对象的构造函数的原型例如|function Test() { this.name = ’test’}Test.prototype = { color: ‘red’}var test = new Test()console.log(test.proto === Test.prototype) // trueconsole.log(test.proto)Object.createvar foo = {something: function() { console.log( “Tell me something good…” );}};var bar = Object.create( foo ); bar.something(); // Tell me something good…Object.create(..) 会创建一个新对象(bar)并把它关联到我们指定的对象(foo)这样 我们就可以充分发挥 [[Prototype]] 机制的威力(委托)并且避免不必要的麻烦(比如使 用 new 的构造函数调用会生成 .prototype 和 .constructor 引用)。继承原型继承缺点实例的属性都会指向同一个引用实现function Parent() { this.names = [1, 2, 3]}function Child() { }Child.prototype = new Parent()var child1 = new Child()var child2 = new Child()child1.names.push(4)console.log(child1.names) // [1,2, 3,4]console.log(child2.names) // [1,2, 3,4]借用构造函数实现function Parent() { this.names = [1, 2, 3] this.getName = function () { console.log(this.name) }}function Child() { Parent.call(this)}var child1 = new Child()var child2 = new Child()child1.names.push(4)console.log(child1.names)console.log(child2.names)缺点每个子实例都会实例化方法一次,内存爆炸组合继承(最常用)实现function Parent() { this.names = [1, 2, 3]}Parent.prototype.getName = function () { console.log(this.names)}function Child() { Parent.call(this)}Child.prototype = new Parent()var child1 = new Child()var child2 = new Child()child1.names.push(4)child1.getName()child2.getName()缺点子类实例上有一份父类的属性,二者重复造成内存浪费父类的构造函数被调用了两次寄生式组合继承实现function Parent() { this.names = [1, 2, 3]}Parent.prototype.getName = function () { console.log(this.names)}function Child() { Parent.call(this)}Child.prototype = Object.create(Parent.prototype)var child1 = new Child()var child2 = new Child()child1.names.push(4)child1.getName()child2.getName()优点属性不会再原型链上重复行为委托js中的继承其实就是在对象间建立关联关系,而行为委托就是建立这种关联关系的纽带。(“原型”)面向对象风格function Foo(who) { this.me = who;}Foo.prototype.identify = function () { return “I am” + this.me;};function Bar(who) { Foo.call(this,who);}Bar.prototype = Object.create(Foo.prototype);Bar.prototype.speak = function () { alert(“Hello,” + this.identify() + ‘.’);};var b1 = new Bar(“b1”);var b2 = new Bar(“b2”);b1.speak();b2.speak();对象关联风格Foo = { init:function (who) { this.me = who; }, identify:function () { return “I am” + this.name }};Bar = Object.create(Foo);Bar.speak = function () { alert(“hello,” + this.identify() + ‘.’);};var b3 = Object.create(Bar);b3.init(“b3”);var b4 = Object.create(Bar);b4.init(“b4”);b1.speak();b2.speak();