《你不知道的js(上)》学习笔记

351 阅读5分钟

作用域和闭包

第一章

  1. Js是一门编译语言,编译发生在执行的前几微妙,编译步骤为:1)分词/词法分析;2)解析/语法分析;3)代码生成。
  2. 变量的赋值会执行两个动作: 1)编译器在当前作用域中声明一个变量(之前没有声明过); 2)运行时引擎在作用域中查找该变量,找到就赋值。
  3. RHS:得到某变量的值; LHS:为赋值操作找到目标。
  4. 执行LHS查询时,如果在顶层作用域中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量并返回。(严格模式不行)

第二章

  1. 词法作用域:由你在写代码时将变量和块作用域写在哪里来决定。
  2. eval()可以在运行期修改书写期的词法作用域(遮蔽全局变量)。(严格模式不行)

第三章

  1. 立即执行的闭包:防止函数名污染全局作用域。(匿名函数) == 函数名
    (function() {...})();
    
  2. 如果function出现在声明中的第一位,那么就是一个函数声明,eg:function(){},否则就是一个函数表达式,eg:(function(){})。
  3. 在for循环用var声明i,i会被绑定在外部作用域中。
  4. 使用{}显式创建代码块,在其中使用let,可以创建块级作用域。
  5. let 声明不会在块作用域中进行提升。
for(let i = 0; i < 10; i++){
    console.log(i);
}
等同于
{
    let j;
    for(j = 0; j < 10; j++){
        let i = j;
        console.log(i);
    }
}

第四章

  1. 包括变量和函数在内的所有生命都会在任何代码被执行前首先被处理(编译)。
  2. 函数会首先被提升,然后才是变量。

第五章

  1. 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
  2. 无论闭包函数在哪里被执行,都会持有对原始定义作用域的引用。
  3. ES6模块API是静态的。import/export

附录

  1. 词法作用域是在定义时确定的,而动态作用域是在运行时确定的。

this和对象原型

第一章

  1. 在任何情况下,this都不指向函数的词法作用域。
  2. this是在运行时执行绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

第二章

  1. this绑定:1)默认绑定到全局对象;2)隐式绑定到调用的上一层对象上;3)显示绑定(bind,apply,call);4)new绑定
  2. 使用new来调用函数,或者说发生构造函数调用时,会执行以下操作: (1). 创建一个全新的对象; (2). 这个新对象会执行[[Prototype]]连接; (3). 这个新对象会绑定到函数调用的this; (4). 如果函数没有返回其他对象,函数自动返回新创建的对象。
  3. 创建对象的方法:Object.create(father), Object.create(null)比{}少了Object.prototype这个委托。
  4. 箭头函数会集成外层函数调用的this绑定。

第三章

  1. 对于Object、Array、Function和RegExp来说,无论使用文字形式还是构造形式,他们都是对象。
var myObject = {};
myObject[myObject] = '111';
myObject["[object Object"]; // 111
  1. 函数永远不会属于一个对象,只是引用。
Object.defineProperty(obj, key, {
    value: 1,
    writable: false,
    configurable: false,
    enumberable: false
})

Object.getOwnPropertyDescriptoe(obj, key);
  1. 把configurable修改成false是单向操作,无法撤销,还会禁止删除这个属性。
  2. 结合writable:false和configurable:false就可以创建一个常量属性。
  3. Object.preventExtensions(obj):禁止一个对象添加新属性并且保留已有属性;
  4. Object.seal(obj): 不能添加新属性,不能重新配置或者删除任何现有属性。
  5. Object.freeze(obj):禁止对于对象本身及其任意直接属性的修改。
  6. getter和setter是成对出现的,只定义getter会导致无法赋值。
  7. for..of会寻找内置或者自定义的@@iterator对象并调用它的next()方法来遍历数据值。

第四章

  1. 多态并不表示子类和父类有关联,子类得到的只是父类的一份副本。类的集成其实就是复制。
  2. 显示混入:直接复制对象。对象/函数属性共享。
  3. 显式多态:Vehicle.dirve.call(this);使用call调用其他对象的方法。
  4. 寄生继承:方法定义在prototype,子类继承时先构造父类对象。

第五章

  1. obj.foo = 'bar'; 1)如果在[[prototype]]链上层存在foo并且被标记为只读,则本操作失败;2)如果存在foo并且有setter,会直接调用setter。
  2. obj.a++操作做相当于obj.a = obj.a + 1; // 可能会在obj上创建一个a属性而不是修改[[prototype]]上的a
  3. 继承意味着复制操作,Js并不会复制对象属性。相反,Js会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。
  4. 在Js中对于“构造函数”最准确的解释是:所有带new的函数调用。
  5. 通过内省找出“祖先”(委托关联):a instanceof Foo; / Foo.prototype.isPrototypeOf(a);

第六章

  1. 委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象。Bar = Object.create(Foo)
  2. ES6:
class Father{}
class Son extends Father{}
  1. 对象关联:定义父对象(Father),子对象(Son)使用Object.create(Father)委托父对象(Father)代执行方法。
  2. 对象关联可以使创建和初始化分离。
  3. 在ES6中可以使用对象的字面形式定义对象,然后用Object.setPrototypeOf(Son, Father)来修改[[prototype]]
  4. ES6中的对象定义,如果需要自我引用,最好使用传统的具名函数表达式来定义对应的函数(baz:function bar(){...})。
  5. 行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。

附录

  1. ES6 class的函数在定义时没有进行复制,只是使用基于[[prototype]]的实时委托。 // 修改子类函数会影响父类函数