Skip to content

《你不知道的JavaScript(上卷)》(Scope & Closures & This & Object Prototypes)

第一部分Scope
第一章 作用域是什么
作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。赋值操作符会导致LHS查询。“=”操作符或调用函数是传入参数的操作都会导致关联作用域的赋值操作。
不成功的RHS引用会导致抛出ReferenceError异常。不成功的LHS引用会导致自动隐式地创建一个全局变量(非严格模式),该变量使用LHS引用的目标作为标识符,或者抛出ReferenceError异常(严格模式)。
第二章 词法作用域
词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能够知道全部标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对他们进行查找。
JavaScript中有两个机制可以“欺骗”词法作用域:eval和with。前者可以对一段包含一段或多段声明的“代码”字符串进行演算,并借此在运行时修改已经存在的词法作用域。后者本质上通过将一个对象的引用当做作用域来处理,将对象的属性当做作用域的标识符来处理,从而在运行时创建一个新的词法作用域。
这两个机制的副作用是引擎无法在编译时对作用域查找进行优化。
第三章 函数作用域和块作用域
IIFE(Immediately Invoked Function Expression)
函数是JavaScript中最常见的作用域单元。本质上,声明在一个函数内部的变量或函数会在所处的作用域中“隐藏”起来,这是有意为之的良好软件的设计原则。
第四章 提升
我们习惯将var a=2;看做一个声明,而实际上JavaScript引擎并不这样认为,它将var a和a=2当做两个单独的声明,第一个是编译阶段的任务,第二个是执行阶段的任务。
第五章 作用域闭包
一个非常重要但又难以掌握,近乎神话的概念:闭包。
对于那些有一点JavaScript使用经验但从未真正理解闭包概念的人来说,理解闭包可以看做是某种意义上的重生。
闭包:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
闭包就好像从JavaScript中分离出来的一个充满神秘色彩的未开化的世界,只有最勇敢的人才能够到达那里。但实际上它只是一个标准,显然就是关于如何在函数作为值按需传递的词法环境中书写代码的。
附录
try/catch实现块作用域
{try{throw undefined;}catch(a){console.log(a);}}
IIFE和try/catch并不是完全等价的,因为如果将一段代码的任意部分拿出来用函数进行包裹,会改变这段代码的含义,其中的this、return、break和continue都会发生变化。IIFE并不是一个普适的解决方案。

第二部分this
第一章 关于this
任何足够先进的技术都和魔法无异。————Arthur C.Clarke
在一个函数内部可以通过自己的函数名引用自身。argument.callee是一种传统的但是现在已经被弃用和批判的用法。
this不是指向函数本身,this不是指向函数的作用域。this是在运行时绑定的,并不是在编写时绑定的。this的上下文取决于函数调用时的各种条件,只取决于函数的调用方式。
第二章 this全面解析
影响this绑定的情况
不带任何修饰函数调用的默认绑定,调用者的隐式绑定,apply和call的显示绑定,bind的硬绑定,new的绑定。
上述5种绑定方法,绑定强度由弱到强。
判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。然后按照顺序应用以下4条规则。
由new调用,绑定到新创建的对象。
由call,apply,bind调用,绑定到指定对象。
由上下文对象调用,绑定到那个上下文对象。
默认:严格模式下绑定到undefined,否则绑定到全局对象。
第三章 对象
JavaScript中的对象有字面形式var a = {};和构造形式var a = new Object();
属性描述符
value属性的值
writable属性的值是否可写
enumerable属性是否可枚举(出现在for-in循环)
configurable属性是否可配置(修改描述符)
Object.preventExtensions(obj)禁止添加新属性
Object.seal(obj)禁止添加新属性,也不能配置现有属性。= preventExtensions()+configurable:false
Object.freeze(obj)禁止任意修改 = seal() + writable:false
当你给一个属性定义getter或setter是,这个属性变成访问描述符。对于访问描述符,JavaScript会忽略他们的value和writable特性,取而代之的是关心set和get特性。
第四章 混合对象”类”
类是一种设计模式。许多语言提供了对于面向类软件设计的原生语法。JavaScript也有类似的语法,但是和其他语言中的类完全不同。
类意味着复制。
传统的类被实例化时,它的行为会被复制到实例中。类被继承时,行为也会复制到子类中。
多态(在继承链的不同层次名称相同但功能不同的函数)看起来似乎是从子类引用父类,但本质上引用的其实是复制的结果。
JavaScript并不会像类那样自动创建对象的副本。
第五章 原型
当你试图引用对象的属性时会触发[[Get]]操作,然后沿着原型链搜索。
Object.create = function(o){
function F(){};F.prototype = o; return new F();
}
使用new调用函数时会把新对象的.prototype属性关联到“其他对象”。带new的函数调用通常被成为“构造函数调用”尽管他们实际上和传统面相类语言中的类构造函数不一样。虽然这些JavaScript机制和传统面相类语言中的“类初始化”和“类继承”很相似,但是JavaScript中的机智有一个核心区别,那就是不会进行复制,对象之间是通过内部的[[prototype]]链关联的。
第六章 行为委托
在软件架构中你可以选择是否使用类和继承设计模式。大多数开发者理所当然的认为类是唯一的代码组织方式,但在JavaScript中更强大的设计模式,行为委托。
行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。JavaScript的[[prototype]]机制本质上就是行为委托机制。
对象关联是一种编码风格,它倡导的是直接创建和关联对象,不把它们抽象成类。对象关联可以用基于[[prototype]]的行为委托非常自然地实现。

Published inReading

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注