this指向
與其他語言相比,函數(shù)的this關(guān)鍵字在 JavaScript 中的表現(xiàn)略有不同,此外,在嚴(yán)格模式和非嚴(yán)格模式之間也會有一些差別。
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了this的值。this是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中this等于window,而當(dāng)函數(shù)被作為某個(gè)對象方法調(diào)用時(shí),this等于那個(gè)對象。ES5引入了bind方法來設(shè)置函數(shù)的this值,而不用考慮函數(shù)如何被調(diào)用的。
全局環(huán)境
無論是否在嚴(yán)格模式下,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)this 都指向全局對象。
// 在瀏覽器中, window 對象同時(shí)也是全局對象:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b) // "MDN"
console.log(b) // "MDN"
函數(shù)(運(yùn)行內(nèi))環(huán)境
在函數(shù)內(nèi)部,this的值取決于函數(shù)被調(diào)用的方式。
普通函數(shù)內(nèi)部的this分兩種情況,嚴(yán)格模式和非嚴(yán)格模式。
- 非嚴(yán)格模式下,this 默認(rèn)指向全局對象window
function f1(){
return this;
}
//在瀏覽器中:
f1() === window; //在瀏覽器中,全局對象是window
//在Node中:
f1() === global;
然而,在嚴(yán)格模式下,this將會默認(rèn)為undefined。
function f2(){
"use strict"; // 這里是嚴(yán)格模式
return this;
}
f2() === undefined; // true
作為對象的方法
當(dāng)函數(shù)作為對象里的方法被調(diào)用時(shí),它們的this是調(diào)用該函數(shù)的對象。
- 函數(shù)的定義位置不影響其this指向,this指向只和調(diào)用函數(shù)的對象有關(guān)。
- 多層嵌套的對象,內(nèi)部方法的this指向離被調(diào)用函數(shù)最近的對象(window也是對象,其內(nèi)部對象調(diào)用方法的this指向內(nèi)部對象, 而非window)。
下面的例子中,當(dāng)o.f()被調(diào)用時(shí),函數(shù)內(nèi)的this將綁定到o對象。
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
請注意,這樣的行為,根本不受函數(shù)定義方式或位置的影響。在前面的例子中,我們在定義對象o的同時(shí),將函數(shù)內(nèi)聯(lián)定義為成員 f 。但是,我們也可以先定義函數(shù),然后再將其附屬到o.f。這樣做會導(dǎo)致相同的行為:
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
同樣,this的綁定只受最靠近的成員引用的影響。在下面的這個(gè)例子中,我們把一個(gè)方法g當(dāng)作對象o.b的函數(shù)調(diào)用。在這次執(zhí)行期間,函數(shù)中的this將指向o.b。事實(shí)證明,這與他是對象 o的成員沒有多大關(guān)系,最靠近的引用才是最重要的。
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42
原型鏈中的this
對于在對象原型鏈上某處定義的方法,同樣的概念也適用。如果該方法存在于一個(gè)對象的原型鏈上,那么this指向的是調(diào)用這個(gè)方法的對象,就像該方法在對象上一樣。
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
可以看出, 在p中沒有屬性f,當(dāng)執(zhí)行p.f()時(shí),會查找p的原型鏈,找到 f 函數(shù)并執(zhí)行,但這與函數(shù)內(nèi)部this指向?qū)ο?p 沒有任何關(guān)系,只需記住誰調(diào)用指向誰。
以上對于函數(shù)作為getter & setter 調(diào)用時(shí)同樣適用。
作為構(gòu)造函數(shù)
當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)(使用new關(guān)鍵字),它的this被綁定到正在構(gòu)造的新對象。
雖然構(gòu)造器返回的默認(rèn)值是this所指的那個(gè)對象,但它仍可以手動返回其他的對象(如果返回值不是一個(gè)對象,則返回this對象)。
/*
* 構(gòu)造函數(shù)這樣工作:
*
* function MyConstructor(){
* // 函數(shù)實(shí)體寫在這里
* // 根據(jù)需要在this上創(chuàng)建屬性,然后賦值給它們,比如:
* this.fum = "nom";
* // 等等...
*
* // 如果函數(shù)具有返回對象的return語句,
* // 則該對象將是 new 表達(dá)式的結(jié)果。
* // 否則,表達(dá)式的結(jié)果是當(dāng)前綁定到 this 的對象。
* //(即通??吹降某R娗闆r)。
* }
*/
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // logs 38
在剛剛的例子中(C2),因?yàn)樵谡{(diào)用構(gòu)造函數(shù)的過程中,手動的設(shè)置了返回對象,與this綁定的默認(rèn)對象被丟棄了。(這基本上使得語句 “this.a = 37;”成了“僵尸”代碼,實(shí)際上并不是真正的“僵尸”,這條語句執(zhí)行了,但是對于外部沒有任何影響,因此完全可以忽略它)。
作為一個(gè)DOM事件處理函數(shù)
當(dāng)函數(shù)被用作事件處理函數(shù)時(shí),它的this指向觸發(fā)事件的元素(一些瀏覽器在使用非addEventListener的函數(shù)動態(tài)添加監(jiān)聽函數(shù)時(shí)不遵守這個(gè)約定)。
// 被調(diào)用時(shí),將關(guān)聯(lián)的元素變成藍(lán)色
function bluify(e){
this.style.backgroundColor = '#A5D9F3';
}
// 獲取文檔中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 將bluify作為元素的點(diǎn)擊監(jiān)聽函數(shù),當(dāng)元素被點(diǎn)擊時(shí),就會變成藍(lán)色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
作為一個(gè)內(nèi)聯(lián)事件處理函數(shù)
- 當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時(shí),它的this指向監(jiān)聽器所在的DOM元素
- 當(dāng)代碼被包括在函數(shù)內(nèi)部執(zhí)行時(shí),其this指向等同于 ****函數(shù)直接調(diào)用****的情況,即在非嚴(yán)格模式指向全局對象window, 在嚴(yán)格模式指向undefined
<button onclick="console.log(this)"></button> // button
<button onclick="(function() { console.log(this) })()"></button> // window
<button onclick="(function() {'use strict'; console.log(this) })()"></button> // undefined
setTimeout & setInterval
對于延時(shí)函數(shù)內(nèi)部的回調(diào)函數(shù)的this指向全局對象window(當(dāng)然我們可以通過bind方法改變其內(nèi)部函數(shù)的this指向)
//默認(rèn)情況下代碼
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 3000);
}
var p = new Person();//3秒后返回 window 對象
//通過bind綁定
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 3000);
}
var p = new Person();//3秒后返回構(gòu)造函數(shù)新生成的對象 Person{...}
箭頭函數(shù)中的 this
箭頭函數(shù)的this定義:箭頭函數(shù)的this是在定義函數(shù)時(shí)綁定的,不是在執(zhí)行過程中綁定的。簡單的說,函數(shù)在定義時(shí),this就繼承了定義函數(shù)的對象。
一句話,箭頭函數(shù)內(nèi)的this就是箭頭函數(shù)外的那個(gè)this為什么?因?yàn)榧^函數(shù)沒有自己的this。
所以,這會很好的解決匿名函數(shù)和setTimeout和setInterval的this指向問題。我們不用再去給其用that變量存儲this。
// demo1
var obj = {
func: function () {
setTimeout(function () {
console.log(this) // window
}, 100)
// console.log(this)
},
say:function () {
setTimeout(() => {
console.log(this) // obj
}, 1000)
}
}
obj.func()
obj.say()
// demo2
var a = 0
var obj = {
a: 1,
b1: this.a,
b2: () => this.a,
};
console.log(obj.b1); // 0
console.log(obj.b2()); // 0
根據(jù)結(jié)果可以看到obj對象中的屬性b1的值來自全局變量a,而不是obj對象的屬性a,也就是說b1: this.a,中的this指向window。而b2: () => this.a,中的this同樣指向window,返回全局變量a的值。
// demo3
var a = 0;
function fn(){
var a = 2;
console.log(this.a); // 0
const subf = ()=>this.a;
console.log(subf()); // 0
}
fn()
/*
箭頭函數(shù)中有this就當(dāng)做這個(gè)箭頭函數(shù)不存在
fn函數(shù) -> window.fn()
*/
// demo4
var a = 0
var obj2 = {
a: 3,
b1: function(){ return this.a;},
b2: function(){ return ()=>this.a},
};
console.log(obj2.b1()); // 3
console.log(obj2.b2()()); // 3
/*
箭頭函數(shù)中有this就當(dāng)做這個(gè)箭頭函數(shù)不存在
this.a 指向的事當(dāng)前的對象,因此結(jié)果都是:3
*/
// demo6
var obj3 = {
a: 4,
b1: function(){ return this.a;},
b2: function(){ return ()=> () => () => this.a},
};
console.log(obj4.b1()); // 4
console.log(obj4.b2()()()()); // 4
/*
這里箭頭函數(shù)返回箭頭函數(shù)再返回箭頭函數(shù),最后一個(gè)箭頭函數(shù)返回this.a。這個(gè)this是誰?就是最后箭頭函數(shù)外面的外面的外面的那個(gè)this,也就是obj3.b1中的那個(gè)this,也就是obj3.a。:)
實(shí)情是這樣:箭頭函數(shù)沒有自己的this,所以不管嵌套多少層,都不影響this是誰。
*/
// demo7
var a = 0
function f0() {
var a = 5;
setTimeout(function() {
var b1 = this.a;
console.log(b1); // 0
}, 100);
setTimeout(function() {
var b2 = ()=> this.a;
console.log(b2()); // 0
}, 200);
}
f0(); // 0 0
/*
setTimeout中的function運(yùn)行于全局環(huán)境下,所以第一個(gè)就是:0
第二個(gè)setTimeout中的函數(shù)是箭頭函數(shù),箭頭函數(shù)里面的this和setTimeout所處環(huán)境一直,因此指向的也是window下的a結(jié)果就是0
*/
更多干貨請點(diǎn)擊 - this指向完整版
