先導(dǎo)知識:
call, apply, bind
兩個概念:
調(diào)用點(call-site):代碼中函數(shù)被調(diào)用的地方而不是函數(shù)被定義的地方孝偎。
調(diào)用棧(call-stack):代碼執(zhí)行時到達(dá)當(dāng)前位置的函數(shù)棧访敌。我們care的是當(dāng)前函數(shù)環(huán)境的調(diào)用位置。
舉個栗子:
function a(){
//調(diào)用棧為'a'
//調(diào)用點在全局作用域中
console.log('a');
//b的調(diào)用點
b();
}
function b(){
//調(diào)用棧為'a' ->'b'
//調(diào)用點在a中
console.log('b');
//c的調(diào)用點
c();
}
function c(){
//調(diào)用棧為'a' ->'b'->'c'
//調(diào)用點在b中
console.log('c');
}
//a的調(diào)用點
a();
四種綁定:
默認(rèn)綁定(Default Binding)
被直接(plain, un-decorated)調(diào)用的函數(shù)在非嚴(yán)格(non-strict)模式下綁定的是全局對象衣盾;在嚴(yán)格(strict)模式下undefined寺旺,報錯爷抓。
栗子:
- 非嚴(yán)格模式直接調(diào)用函數(shù)
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
- 嚴(yán)格模式直接調(diào)用函數(shù)(只和函數(shù)內(nèi)部是不是嚴(yán)格模式有關(guān),而和調(diào)用點是不是嚴(yán)格模式無關(guān))
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: `this` is `undefined`
隱式綁定(Implicit Binding)
調(diào)用點函數(shù)引用是否有一個環(huán)境對象(如果有多級引用阻塑,則取離函數(shù)最近的對象)
栗子:
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
番栗子:
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
//此處調(diào)用bar依然是對foo函數(shù)的直接引用蓝撇,所以是默認(rèn)綁定
bar(); // "oops, global"
如果不想像調(diào)用對象上的屬性函數(shù)這樣來綁定this,請看下面的顯示綁定
顯示綁定(Explicit Binding)
使用call方法和bind方法
栗子:foo.call(obj)陈莽,此時foo函數(shù)中的this引用的就是obj
- 強(qiáng)綁定(Hard Binding)
栗子:
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// bar是一個將this強(qiáng)綁定為obj的函數(shù)渤昌,他已經(jīng)不是foo了
//所以bar中的this不能被重載了
bar.call( window ); // 2
個人覺得You don't know JS作者將強(qiáng)綁定歸入顯示綁定而不是劃等號應(yīng)該是因為顯示綁定中可能就是直接以foo.call(obj)形式調(diào)用一次,而強(qiáng)綁定則將這個強(qiáng)綁定的函數(shù)保留多次調(diào)用走搁。如有其它建議独柑,歡迎討論。
- API調(diào)用環(huán)境(API call context)
許多庫函數(shù)都可以接受一個環(huán)境參數(shù)
栗子:
function foo(el) {
console.log( el, this.id );
}
var obj = {
id: "awesome"
};
// 每次調(diào)用foo函數(shù)都把obj作為this引用
[1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
new綁定(new Binding):
在JS中私植,構(gòu)造器(constructor)僅僅是使用new操作符調(diào)用的函數(shù)而已忌栅。它不附屬于類,沒有實例化一個類曲稼,甚至不是特殊類型的函數(shù)索绪。它只是因為new的用法而被誤解的普通函數(shù)。
在JS中沒有構(gòu)造函數(shù)贫悄,只有對函數(shù)的構(gòu)造調(diào)用求摇。當(dāng)一個函數(shù)被new操作符調(diào)用時扶平,發(fā)生了下列操作
- 生成一個新對象
- 新對象被[[Prototype]]-linked
- 新對象被綁定到這次函數(shù)調(diào)用的this
- 除非函數(shù)返回其他對象屈糊,否則默認(rèn)就返回這個新創(chuàng)建的對象的了
栗子:
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );//this綁定到了新創(chuàng)建的對象谍失,即bar
console.log( bar.a ); // 2
綁定例外(Binding Exceptions)
詞法this(Lexical this)
ES6的箭頭函數(shù)