與其他語言相比忌愚,函數(shù)的 this 關(guān)鍵字在JavaScript中的行為略有不同吊档。它在嚴(yán)格模式和非嚴(yán)格模式之間也有一些區(qū)別判沟。
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了this的值恶导。this不能在執(zhí)行期間被賦值浆竭,在每次函數(shù)被調(diào)用時this的值也可能會不同。ES5引入了bind方法來設(shè)置函數(shù)的this值,而不用考慮函數(shù)如何被調(diào)用的邦泄。
全局上下文
- 在全局運行上下文中(在任何函數(shù)體外部)删窒,this指代全局對象,無論是否在嚴(yán)格模式下顺囊。
console.log(this.document === document); // true
// 在瀏覽器中易稠,全局對象為 window 對象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
函數(shù)上下文
- 在函數(shù)內(nèi)部,this的值取決于函數(shù)是如何調(diào)用的包蓝。
1.直接調(diào)用
如果不是在嚴(yán)格模式下執(zhí)行驶社,并且this的值不會在函數(shù)執(zhí)行時被設(shè)置,此時的this的值會默認(rèn)設(shè)置為全局對象测萎。
function f1(){
return this;
}
this === window; // true
然而亡电,在嚴(yán)格模式下,this將保持他進入執(zhí)行環(huán)境時的值硅瞧,所以直接調(diào)用的this將會默認(rèn)為undefined或者報錯
function f2(){
"use strict"; // 這里是嚴(yán)格模式
return this;
}
this === undefined; // true
2.對象方法中的 this
當(dāng)以對象里的方法的方式調(diào)用函數(shù)時份乒,它們的 this 是調(diào)用該函數(shù)的對象.
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37
or
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
3.原型鏈中的 this
相同的概念在定義在原型鏈中的方法也是一致的。如果該方法存在于一個對象的原型鏈上腕唧,那么this指向的是調(diào)用這個方法的對象或辖,表現(xiàn)得好像是這個方法就存在于這個對象上一樣。
var o = {
f : function(){
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
4.getter 與 setter 中的 this
作為getter或setter函數(shù)都會綁定 this 到從設(shè)置屬性或得到屬性的那個對象枣接。
function modulus(){
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase(){
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {
get: modulus, enumerable:true, configurable:true});
console.log(o.phase, o.modulus); // logs -0.78 1.4142
5.構(gòu)造函數(shù)中的 this
當(dāng)一個函數(shù)被作為一個構(gòu)造函數(shù)來使用(使用new關(guān)鍵字)颂暇,它的this與即將被創(chuàng)建的新對象綁定。
var Obj = function (p) {
this.p = p;
};
Obj.prototype.m = function() {
return this.p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
o.m() // "Hello World!"
call 和 apply但惶,bind 方法
- ECMAScript 5 引入了 Function.prototype.bind
調(diào)用f.bind(someObject)會創(chuàng)建一個與f具有相同函數(shù)體和作用域的函數(shù)耳鸯,但是在這個新函數(shù)中,this將永久地被綁定到了bind的第一個參數(shù)膀曾,無論這個函數(shù)是如何被調(diào)用的县爬。
bind比call方法和apply方法更進一步的是,除了綁定this以外添谊,還可以綁定原函數(shù)的參數(shù)财喳。
function.prototype.bind()
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
bind比call方法和apply方法更進一步的是,除了綁定this以外斩狱,還可以綁定原函數(shù)的參數(shù)耳高。
2.當(dāng)一個函數(shù)的函數(shù)體中使用了this關(guān)鍵字時,通過所有函數(shù)都從Function對象的原型中繼承的call()方法和apply()方法調(diào)用時喊废,它的值可以綁定到一個指定的對象上祝高。
call方法的參數(shù),應(yīng)該是一個對象污筷。如果參數(shù)為空、null和undefined,則默認(rèn)傳入全局對象瓣蛀。
var n = 123;
var obj = { n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
call方法還可以接受多個參數(shù)
func.call(thisValue, arg1, arg2, ...)
call的第一個參數(shù)就是this所要指向的那個對象陆蟆,后面的參數(shù)則是函數(shù)調(diào)用時所需的參數(shù)。
3.function.prototype.apply()
apply方法的作用與call方法類似惋增,也是改變this指向叠殷,然后再調(diào)用該函數(shù)。唯一的區(qū)別就是诈皿,它接收一個數(shù)組作為函數(shù)執(zhí)行時的參數(shù)林束,使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
apply方法的第一個參數(shù)也是this所要指向的那個對象稽亏,如果設(shè)為null或undefined壶冒,則等同于指定全局對象。第二個參數(shù)則是一個數(shù)組截歉,該數(shù)組的所有成員依次作為參數(shù)胖腾,傳入原函數(shù)。原函數(shù)的參數(shù)瘪松,在call方法中必須一個個添加咸作,但是在apply方法中,必須以數(shù)組形式添加宵睦。
function f(x,y){
console.log(x+y);
}
f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2
DOM事件處理函數(shù)中的 this
- 當(dāng)函數(shù)被用作事件處理函數(shù)時记罚,它的this指向觸發(fā)事件的元素(一些瀏覽器在使用非addEventListener的函數(shù)動態(tài)添加監(jiān)聽函數(shù)時不遵守這個約定)。
// 被調(diào)用時壳嚎,將關(guān)聯(lián)的元素變成藍色
function bluify(e){
console.log(this === e.currentTarget); // 總是 true
// 當(dāng) currentTarget 和 target 是同一個對象是為 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// 獲取文檔中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 將bluify作為元素的點擊監(jiān)聽函數(shù)毫胜,當(dāng)元素被點擊時,就會變成藍色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
內(nèi)聯(lián)事件處理函數(shù)中的 this
- 當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時诬辈,它的this指向監(jiān)聽器所在的DOM元素
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
上面的alert會顯示button
其實酵使,幾句話就可以概括,實踐中記住這幾點就可以了焙糟,偷了幾句學(xué)霸的總結(jié):
this在函數(shù)執(zhí)行時才能確定口渔,JavaScript 中的 this 可以顯式的確定,比如通過call apply bind 以及尚未納入標(biāo)準(zhǔn)的函數(shù)綁定運算符::穿撮。
確認(rèn)this具體是什么有三個辦法:
console.log(this)
source code, look for .call
API documentation
面試的時候缺脉,不能 log,沒有文檔悦穿,源碼又沒有用 call 之類的顯式的確定攻礼,就需要自己手動的轉(zhuǎn)換成fn.call(...)調(diào)用的方式來確定 this
PS: 需要注意的是箭頭函數(shù)函數(shù)體內(nèi)的this,就是定義時所在的對象栗柒,而不是使用時所在的對象礁扮。