### 绑定规则
#### 默认绑定
```
function foo(){console.log(this.a)}
var a= 2;
foo(); // 2
```
函数foo是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定。在严格模式下默认绑定将会失效,this.a将会变成undefined。
#### 隐式绑定
调用的位置有上下文对象,将会使用隐式绑定:
```
var obj = {a: 3, foo: foo}
obj.foo(); //3
```
foo在被调用时是被obj包含的,this自动指向了该上下文对象。
不正确使用隐式绑定将会导致应用默认绑定:
```
var a = 2;
var obj = {a:3, foo: foo}
var foo1 = obj.foo;
foo1(); //2
//foo在被调用时已经是位于window位置了,obj已经不是它的上下文了。
```
举个更常见、更微妙、更易出错的例子:
```
var a = 2;
function doFoo(fn){ fn() }
var obj = {a: 3, foo: foo }
doFoo(obj.foo); //2
//相当于
doFoo(obj.foo){
var fn = obj.foo;
fn();
}
//或者你这么理解:将obj.foo赋给fn形参是在doFoo中进行的,doFoo在哪?window,所以使用了默认绑定
//setTimeout(obj.foo, 100)结果也是2,原理同上,将obj.foo赋给回调是在timeout中进行的,setTimeout位于window
```
#### 显式绑定call\apply\bind
```
var a = 2;
function foo(){console.log(this.a)}
var obj = {a: 3};
foo.call(obj); //3
```
#### new绑定
与其他语言不同,使用new操作符调用的函数并不属于任何类,也不会实例化一个类,它仅仅是被new操作符调用的普通函数而已。
使用new来调用函数,会执行下面操作:
1. 创建一个新对象
2. 新对象执行[[Proptptype]],新对象的原型指向该函数的原型
3. 新对象绑定到函数调用的this
4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个对象
```
function foo(a){this.a = a};
var bar = new foo(2);
console.log(bar.a); //2
//1. 创建一个{}
//2. {}.__proto__指向foo.prototype
//3. foo.call({})
//4. 由于foo没有返回其他对象,因此new foo将会自动将{}返回,由于foo已经执行了,此时this.a = 2
```
new操作符的关键就在于无论之前以何种方式绑定,使用new后this都会指向新创建的{}上。
#### 优先级
1. 函数是否在new中调用(new绑定)?是的话this(以下均指函数内部的this)指向新创建的对象。`var bar = new foo()`
2. 函数是否通过call、apply、bind绑定?是的话this指向绑定的对象。`var bar = foo.call(obj1)`。注意,call和apply均直接运行函数,而bind将绑定后的函数返回。
3. 函数是否在某个上下文对象中调用?是的话this指向那个上下文对象`var bar = obj1.foo()`
4. 如果以上都不是的话,使用默认绑定,在严格模式下就会绑定到undefined上,否则绑定到全局对象上。
#### 绑定例外
1. 把null、undefined作为this传入到call、apply或bind中,this会使用默认绑定。
```
function foo(){console.log(this.a)}
var a = 2;
foo.call(null); //2
```
为了方便将参数传递给下层函数,使用柯里化是一个很好的选择,将this绑定到window上
```
function foo(a, b){console.log("a:" + a + ",b:" + b)}
foo.apply(null, [2, 3]); // a:2,b:3
//使用柯里化
//bar相当于function foo(b){...}, a已经变成"a"了
var bar = foo.bind(null, 2);
bar(3); //a:2,b:3
//坏处就是this指向了window,多添加了a和b两个全局变量
```
传入null或undefined后会导致this指向window,更优雅的写法是传入一个特殊对象,将this指向它。使用Object.create(null)来创建它。
```
var $ = Object.create(null);
foo.apply($, [2, 3]); //a:2,b:3
var bar = foo.bind($, 2);
bar(3); //a:2,b:3
```
2.间接引用
```
function foo(){console.log(this.a)}
var a = 2;
var o = {a: 3, foo: foo};
var p = {a: 4}
o.foo(); //3
(p.foo = o.foo)(); //2
//赋值表达式的返回值是目标函数的引用,也就是立即执行函数,因此调用位置是foo()而不是p或o。
```
#### 箭头函数
箭头函数不使用四种绑定规则中的任意一种,this的指向完全取决于外层作用域,且一旦绑定,再也无法修改。
```
function foo() {
return (a) => console.log(this.a);
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1);
bar.call(obj2); //2,而不是3
```
- 你不知道的JS上
- 第一部分 第三章 函数作用域和块作用域
- 第一部分 第四章 提升
- 第一部分 第五章 闭包
- 第二部分 第一章 关于this
- 第二部分 第二章 this全面解析
- 第二部分 第三章 对象
- 第二部分 第五章 原型
- 第二部分 第六章 行为委托
- 你不知道的JS中
- 第一部分 第二章 值
- 第一部分 第三章 原生函数
- 第一部分 第四章 强制类型转换
- 第一部分 第五章 语法
- 第二部分 第一章 异步
- 第二部分 第三章 Promise
- 第二部分 第四章 生成器
- 第二部分 第五章 性能
- 你不知道的JS下
- 第一部分 总结
- 第二部分 第二章 语法
- 第二部分 第三章 代码组织
- 第二部分 第四章 Promise
- 第二部分 第五章 集合
- 第二部分 第六章 新增API
- 第二部分 第七章 元编程
- 第二部分 第八章 ES6之后