再說(shuō) this 指向問(wèn)題

要知道 this 指向問(wèn)題,需要知道函數(shù)調(diào)用棧,函數(shù)調(diào)用的位置。
先來(lái)看看以下這個(gè)例子:
function baz() {
// 當(dāng)前調(diào)用棧:baz
// 因此,當(dāng)前調(diào)用位置是全局作用域
console.log("baz")
bar(); // bar 的調(diào)用位置
}
function bar() {
// 當(dāng)前調(diào)用棧是 baz -> bar
// 因此,當(dāng)前調(diào)用位置在 baz
console.log("bar")
foo(); // foo 調(diào)用位置
}
function foo() {
// 當(dāng)前調(diào)用棧是 baz -> bar -> foo
// 因此,當(dāng)前調(diào)用位置在 bar 中
console.log("foo")
}
baz() // baz 的調(diào)用位置
綁定規(guī)則
獨(dú)立函數(shù)調(diào)用
function foo() {
console.log(this.a)
}
var a = 10
foo() // 10
在本例子中 this 默認(rèn)指向了 window 全局對(duì)象
為什么呢?
因?yàn)椋瘮?shù)調(diào)用時(shí)不帶其他修飾的函數(shù)調(diào)用,使用默認(rèn)綁定 this 到全局 window 對(duì)象上。
隱式綁定
非嚴(yán)格模式下,函數(shù)調(diào)用 this 指向調(diào)用者。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
隱式丟失
把 this 綁定到全局對(duì)象或者 undefined 上,取決于是否是嚴(yán)格模式。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函數(shù)別名!
var a = "oops, global"; // a 是全局對(duì)象的屬性
bar(); // "oops, global" 調(diào)用的位置是在全局對(duì)象上的,所以 this 指向了 window
一種更微妙、更常見(jiàn)并且更出乎意料的情況發(fā)生在傳入回調(diào)函數(shù)時(shí):
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn 其實(shí)引用的是 foo
fn(); // <-- 調(diào)用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局對(duì)象的屬性
doFoo( obj.foo ); // "oops, global" 實(shí)際上 this 也是指向 window 的
將函數(shù)傳入內(nèi)置函數(shù)呢:
function foo() {
console.log( this.a );
}
var obj = {
a:2,
foo:foo
}
var a = "oops, global"; // a 是全局對(duì)象上的屬性
setTimeout(obj.foo, 100) // "oops, global" 也是指向了 window
顯式綁定
使用 call,apply,bind 方式來(lái)綁定
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
上面這種方式就好像下面這樣定義,只是換了一種方式
function foo(){
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo() // 2
如果你傳入了一個(gè)原始值(字符串類(lèi)型、布爾類(lèi)型或者數(shù)字類(lèi)型)來(lái)當(dāng)作 this 的綁定對(duì)象,
這個(gè)原始值會(huì)被轉(zhuǎn)換成它的對(duì)象形式(也就是 new String(..)、new Boolean(..) 或者 new Number(..))。這通常被稱(chēng)為“裝箱”。
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( 2 ); // undefined
硬綁定
function foo() {
console.log( this.a )
}
var obj = {
a:2
}
var bar = function() {
foo.call(obj)
}
bar() // 2
setTimeout(bar, 100) // 2
硬綁定的典型應(yīng)用場(chǎng)景就是創(chuàng)建一個(gè)包裹函數(shù),負(fù)責(zé)接收參數(shù)并返回值:
function foo(something) {
console.log(this.a, something)
return this.a + something
}
var obj = {
a: 2
}
var bar = function() {
return foo.apply(obj, arguments)
}
var b = bar(3) // 2 3
console.log(b) // 5
另一種使用方法是創(chuàng)建一個(gè)可以重復(fù)使用的輔助函數(shù):
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
// 簡(jiǎn)單的輔助綁定函數(shù)
function bind(fn, obj) {
return function() {
return fn.apply( obj, arguments );
};
}
var obj = {
a:2
};
var bar = bind( foo, obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
es6 提供的 bind 方法
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a: 2
}
var bar = foo.bind(obj)
var b = bar(3) // 2 3
console.log(b) // 5
循環(huán)中的 this 綁定
function foo(el) {
console.log(el, this.id)
}
var obj = {
id: "awesome"
}
var arr = [1,2,3]
arr.forEach(foo, obj);
// 1 "awesome"
// 2 "awesome"
// 3 "awesome"
順便來(lái)看看 forEach, map 的核心區(qū)分:
function foo(el) {
console.log(el, this.id)
}
var obj = {
id: "awesome"
}
var arr = [1,2,3]
var res = arr.map(foo, obj);
console.log("map===>", res)
var eachRes = arr.forEach(foo, obj)
console.log("each===>", eachRes)
// 1 "awesome"
// 2 "awesome"
// 3 "awesome"
// map===> (3) [undefined, undefined, undefined] // 返回?cái)?shù)組
// 1 "awesome"
// 2 "awesome"
// 3 "awesome"
// each===> undefined // 返回undefined
new 操作
使用 new 來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí),會(huì)自動(dòng)執(zhí)行下面的操作。
創(chuàng)建(或者說(shuō)構(gòu)造)一個(gè)全新的對(duì)象。 這個(gè)新對(duì)象會(huì)被執(zhí)行 [[Prototype]] 連接。 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的 this。 如果函數(shù)沒(méi)有返回其他對(duì)象,那么 new 表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
以下是我根據(jù)上面這個(gè)思路實(shí)現(xiàn)的代碼
function newOP(fn) {
var obj = Object.create(null)
var fnProto = Object.create(fn.__proto__)
obj.__proto__ = fnProto
var res = fn.apply(obj, arguments)
return res ? res : obj
}
this詞法
箭頭函數(shù)在創(chuàng)建的過(guò)程就綁定了 this 的指向問(wèn)題,我們可以考慮以下這個(gè)問(wèn)題:
function foo() {
// 返回一個(gè)箭頭函數(shù)
return (a) => {
//this 繼承自 foo()
console.log( this.a );
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
}
var bar = foo.call(obj1)
bar.call(obj2); // 2 而不是 3
箭頭函數(shù)最常用于回調(diào)函數(shù)中,例如事件處理器或者定時(shí)器:
function foo() {
setTimeout(() => {
// 這里的 this 在詞法上繼承自 foo()
console.log( this.a );
},100);
}
var obj = {
a:2
};
foo.call( obj ); // 2
