### 綁定規則
#### 默認綁定
```
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之后