xiecg ’s bolg

你不知道的 JavaScript 上卷笔记

上卷很早就拜读过了,当时没有记录下来,这次也算是复习下。

1:作用域是什么 ?

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询; 如果目的是获取变量的值,就会使用 RHS 查询。

LHSRHS 查询都会在当前执行作用域中开始,如果当前找不到,会向上级作用域继续查找,最后到顶级作用域,无论找到或没找到都将停止。

不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式 地创建一个全局变量 (非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛出 ReferenceError 异常(严格模式下)

2:词法作用域

词法作用域意味着作用域是由书写代码时函数声明的位置来决定的,编译的词法分析阶段就能知道标识符在哪里以及如何声明,从而能够在执行的过程中如何对它们进行查找。

evalwith都能够欺骗词法作用域:

eval 是将一个字符串代码进行演算,并且来修改以及存在的词法作用域。
with是讲一个对象的引用来当做作用域来处理,将对象的属性当做标识符来处理,从而创建一个新的词法作用域(如果对象的属性不存在,会在全局创建一个)

3:函数作用域和块作用域

函数JavaScript中最常见的作用域单元,但这并不是唯一的,块作用域指的是标识符不仅可以属于函数内部,也可以属于某个代码块 {...}

ES3 开始,try/catch 结构在 catch 分句中具有块作用域。

ES6 增加了letconst

4:提升

1
var a = 2;

它将var aa = 2当作两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。
在这个编译阶段就把所有的声明(变量和函数)都被移动到各自作用域的顶端,这个过程叫做提升
声明本身会被提升,而包含函数表达式的赋值在内的赋值操作不会被提升。

5:作用域闭包

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

6:this

this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用(也就是函数的调用方法)

默认绑定:foo();
隐式绑定:obj.foo();
显式绑定:foo.call(..)foo.apply(..)foo.bind(..)
new绑定:new Foo(..)

严格模式上默认绑定的 this会绑定到 undefined

显式绑定也称为硬绑定,绑定之后无法使用隐式绑定或者显式绑定来修改 this

显示绑定比隐式绑定的优先级高。
new绑定比隐式绑定的优先级高。

ES6 中的箭头函数并不会使用四条标准的绑定规则,绑定无法被修改,而是根据当前的词法作用域来决定 this

7:对象

getOwnPropertyDescriptor 属性描述符

writable 可写

enumerable 可枚举
myObject.propertyIsEnumerable('a') 能否可枚举

configurable 可配置

1
2
// 禁止扩展:添加属性失效,非严格模式下,创建一个属性会静默失败。严格模式下,将会抛出`TypeError`错误。
Object.preventExtensions( obj );
1
2
// 密封:这个方法实际上会在一个现有对象上调用 `Object.preventExtensions(..)` 并把所有现有属性标记为 `configurable:false`。
Object.seal( obj );
1
2
// 冻结:这个方法实际上会在一个现有对象上调用 `Object.seal(..)` 并把所有“数据访问”属性标记为 `writable:false`,这样就无法修改它们 的值。
Object.freeze( obj )
1
2
3
4
5
6
7
var myObject = {
a:2
};
("a" in myObject); // true
("b" in myObject); // false
myObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "b" ); // false

in 会检查[[Prototype]]原型链,hasOwnProperty只会检查自身属性。

Object.keys(..) 会返回一个数组,包含所有可枚举属性,Object.getOwnPropertyNames(..)会返回一个数组,包含所有属性,无论它们是否可枚举。

inhasOwnProperty(..) 的区别在于是否查找 [[Prototype]] 链,然而,Object.keys(..)Object.getOwnPropertyNames(..) 都只会查找对象直接包含的属性。

8:原型

如果要访问对象中并不存在的一个属性,[[Get]] 操作就会查找对象内部[[Prototype]] 关联的对象。这个关联关系实际上定义了一条“原型链”,在查找属性时会对它进行遍历。

所有普通对象都有内置的 Object.prototype,指向原型链的顶端(比如说全局作用域),如 果在原型链中找不到指定的属性就会停止。toString()valueOf() 和其他一些通用的功能 都存在于 Object.prototype 对象上,因此语言中所有的对象都可以使用它们。

Object.create(null) 会创建一个拥有空(或者说null) [[Prototype]] 链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以 instanceof操作符(之前解释过)无法进行判断,因此总是会返回 false。 这些特殊的空 [[Prototype]] 对象通常被称作“字典”,它们完全不会受到原 型链的干扰,因此非常适合用来存储数据。

9:行为委托

行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。JavaScript[[Prototype]] 机制本质上就是行为委托机制。也就是说,我们可以选择在 JavaScript 中努 力实现类机制,也可以拥抱更自然的 [[Prototype]] 委托机制。

对象关联(对象之前互相关联)是一种编码风格,它倡导的是直接创建和关联对象,不把它们抽象成类。对象关联可以用基于 [[Prototype]] 的行为委托非常自然地实现。