第二部分 this和对象原型
第一章 关于this
- this是在调用时被动态绑定的,其值不是指向自身,也不是指向它的作用域,而是取决于调用方式,事实上,函数被调用时会创建一个活动记录,包含了这个函数执行需要的信息,this就是它的一个属性,所以this的值实际上跟函数的调用位置密切相关
第二章 this全面解析
- 默认绑定:可以理解为在全局环境下调用函数,此时该函数的this在非严格模式下指向全局,而在全局模式下为undefined
- 隐式绑定:可以理解为函数调用时引用有上下文对象,此时this指向最后一层引用的对象,最直接的例子就是obj.oobj.f()、obj.f()与f()指向是不同的,但采用前者调用的方式我们并不能说f被obj拥有或包含,这种说法是不严谨的,f实质上就是一个函数,无论是直接定义在对象中还是作为属性去引用,严格来说展示都不属于这个对象
- 隐式丢失:这种情况常常发生在回调函数中,尽管在传参时可能你具体而正确地传入了一个拥有上下文环境的函数( 如ff(obj.f,1) ),可是编译器只会将回调函数的引用加入调用函数的形参列表中,此时相当于在调用函数中执行了一个无上下文环境的函数,采取的实际上是默认绑定,最显著的例子就是定时器setTimeout中的回调函数
- 显式绑定:JavaScript中函数的原型对象拥有call()与apply()方法,可以显示绑定函数执行时this的指向,但仅仅使用call与apply无法解决绑定丢失的问题
- 硬绑定:通过一些手段,使函数在需要的时候,被特定方式调用时this始终绑定固定的对象
function foo() {
console.log( this.a );
}
var obj = {a:2};
var bar = function() {foo.call( obj )};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的
this bar.call( window ); // 2
- 实际上硬绑定是一项非常有用的模式,在ES5中提供了内置方法bind(),f.bind(xxx)返回一个新函数,将参数设置为this的上下文并调用
- new绑定,由于JavaScript没有类的概念,所以其new机制和其他语言中的不大一样,使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。1. 创建(或者说构造)一个全新的对象。 2. 这个新对象会被执行 [[ 原型 ]] 连接。 3. 这个新对象会绑定到函数调用的 this。 4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
- 上下文:第三方库中的很多函数都提供了一个可选参数,用于指定上下文,确保你回调函数的this
- 箭头函数:箭头函数没有arguments,this指向的是外层作用域,这种表现和ES5中var that=this相似,所以在编写代码时,最好要么都使用箭头函数风格,要么都利用作用域的特性,使用that使代码按照自己的意图执行
第三章 对象
- JavaScript中对象有自定义对象、内置对象、宿主对象
- 人们往往将对象中的函数称为方法,但这并不意味着函数就属于对象,函数只不过是在运行时动态绑定了this
- ES5开始,所有属性都具备属性描述符
- value:保存当前属性的值,修改属性值就是修改value
- writable:决定是否可以修改属性的值
- configurable:决定对象是否可配置,只要其值为真,就可使用Object中的defineProperty()方法修改属性描述符,值得注意的是,一旦把其修改为false,那么将无法复原,(但依旧可以设置writable为false)也不能通过delete删除属性
- enumerable:决定属性是否可枚举,该值为假的属性在for-in循环中无法被枚举
- 前一部分提到JavaScript查找变量有LHS与RHS两种模式,对于obj.a这类写法,明显应该使用RHS查找,但访问对象属性时的流程并不与上一部分所提到的相同,而是使用[[Get]],当所查找属性不存在时,返回undefined,而非像RHS那样抛出ReferenceError,为了区分属性到底是值为undefined还是不存在,可以使用in操作符判断属性名是否存在(而非值)
- 在除ie外最新主流浏览器的实现中,任何一个对象的键值都可以被getter和setter方法所取代,这被称之为“存取器属性”
var obj={
name:'.7\0',
get sex(){
return 'Man'
}
//如果你想要能改变sex的话,还可以添加set sex(val)
}
- 既然有[[Get]]就有[[Put]],[[Put]] 被触发时,实际的行为取决于许多因素,包括对象中是否已经存在这个属性(这是最重要的因素),如果已经存在这个属性,[[Put]] 算法大致会检查下面这些内容:
- 属性是否是访问描述符(参见 3.3.9 节)?如果是并且存在 setter 就调用 setter。
- 属性的数据描述符中 writable 是否是 false?如果是,在非严格模式下静默失败,在严格模式下抛出 TypeError 异常。
- 如果都不是,将该值设置为属性的值。
- ES5中新增forEach()、every()、some()帮助遍历,for-of也可直接应用在数组上,原理是请求一个迭代器对象,然后通过调用迭代器对象的next()方法遍历所有返回值