第1章 关于this
- 为什么要使用this
- 显示传递上下文对象会让代码变得越来越混乱,this提供了一种更优雅的方式隐式地“传递”一个对象引用。
- this并不指向函数本身
- this在任何情况下都不指向函数的词法作用域
- this是在函数被调用时发生的绑定,是函数运行时绑定,不是编写时绑定。
第2章 this全面解析
- 调用栈:为了达到当前执行位置所调用的所有函数
- 调用位置:函数在代码中被调用的位置(而不是声明的位置)
绑定规则
默认绑定
- 不带任何修饰的函数引用进行调用的函数,只能使用默认绑定,无法应用其他规则
- 只有运行在非严格模式下,this的默认绑定才能绑定到全局对象
隐式绑定
- 函数引用拥有上下文对象时,通常是被某个对象拥有或者包含,隐式绑定规则会把函数调用中的this绑定到这个上下文对象
- 对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
- 函数别名、参数传递、调用回调函数的函数都可能会丢失this的隐式绑定
显式绑定
call(...)和apply(...)
方法指定this的绑定对象- 硬绑定:创建一个函数a,在其内部将另一个函数b的this绑定,之后无论如何调用a,都会显式强制绑定。
- 硬绑定的典型应用场景是创建一个包裹函数,传入所有的参数并返回接收到的所有值。
new绑定
- 构造函数只是被new操作符调用的普通函数而已
- 实际上并不存在所谓的构造函数,只是对于函数的构造调用
- new调用函数时,执行的操作:
- 创建(或者说构造)一个全新的对象
- 这个新对象会被执行[[原型]]链接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new的函数调用会自动返回这个新对象
优先级
- new 绑定>显示绑定>隐式绑定>默认绑定
- 之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化的时候就可以值传入其余的参数。
- 部分应用(柯里化的一种):bind(..)的功能之一是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数。
绑定例外
- DMZ对象——它是一个空的非委托的对象
- 创建一个空对象最简单的方法是Object.create(null)。{}会创建Object.prototype,但是Object.create不会创建,所以比{}更空。
- 间接引用:创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。
软绑定
硬绑定会大大降低函数的灵活性,使用应绑定之后就无法使用隐式绑定或者显式绑定来修改this。
软绑定,会对指定的函数进行封装,首先检查调用时的this,如果this绑定到全局对象或者undefined,那就把指定的默认对象obj绑定到this,否则不会修改this。
1
2
3
4
5
6
7
8
9
10
11
12
13if(!Function.prototype.softBind){
Function.prototype.softBind=function(obj){
var fn=this;
var curried=[].slice.call(arguments,1);
var bound=function(){
return fn.apply(
(!this||this===(window||global))?obj:this.curried.concat.apply(curried,arguments)
);
}
bound.prototype=Object.create(fn.prototype);
return bound;
}
}
This词法
- 箭头函数不适用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this,且绑定后无法被修改。
第3章 对象
- 在必要时语言会自动把字符串字面量转换成一个String对象
- object.a:属性访问
- object[“a”]:键访问
- 在对象中,属性名永远都是字符串
- 数组也可以添加命名属性,数组的length值并不会发生变化
- 访问对象属性的时候,返回值为undefined。这个值可能原属性存储的undefined,也可能是因为属性不存在所以返回undefined。解决方法:
- in操作符检查属性是否在对象及其[[prototype]]原型链中。in操作符检查容器内某个属性名是否存在。
- hasOwnProperty(..)只会检查属性是否在对象中,不会检查[[prototype]]原型链中。
- Object.keys(…)会返回一个数组,包含所有可枚举属性
- Object.getOwnPropertyNames(…)会返回一个数组,包含所有属性,无论它们是否可枚举。
遍历
- for…in循环可以用来遍历对象的可枚举属性列表(包括[[Prototype]]链)
- for…of ES6新增遍历数组的语法,直接遍历值而不是数组下标。
- Symbol.iterator获取对象的@@iterator内部属性。
- @@iterator本身不是一个迭代器对象,而是一个返回迭代器对象的函数。
- 数组有内置的@@iterator,但是普通对象没有。可以通过下面的代码给想遍历的对象定义@@iterator.
1 | Object.defineProperty(myObject,Symbol.iterator,{ |