前言
了解this之前我們看看這篇文章:
[譯]讓我們一起解決“this”難題第一部分
真的看完之后無數(shù)次都嘖嘖稱奇葛碧,老外的文章看得太爽了吧萎河。這里我只截取部分我認為重要的知識來記錄我對this的理解金砍,部分表述有出入忘苛。
以下對this的理解浦徊,默認你已經(jīng)了解一些JavaScript的背景知識媒楼,講到global、window雳刺、this劫灶、prototype等等時,你知道它們是什么意思掖桦。雖然文章中默認所使用global和window本昏,在這里是同一回事,可以互換滞详。實際上我查了資料凛俱,其實是不一樣的紊馏,簡要說明一下:(不過不要過于糾結(jié))
global: 全局對象global是在全局上下文中的變量對象料饥,是一個不依賴宿主環(huán)境的對象,(注:常見的宿主環(huán)境:瀏覽器和nodejs)一切全局里存在的變量和函數(shù)都是它的屬性和方法朱监。global 不能直接被訪問而是間接訪問岸啡,訪問其屬性。如Math赫编、String等等都可以看作是全局對象 global的屬性巡蘸,對global的屬性的訪問往往省略掉前綴,比如Math.abs(1)其實就是[[global]].Math.abs(1)
window:在‘usestrict’
模式下擂送,我們更強調(diào)window的全局對象的作用(除此之外悦荒,則常用global作為全局對象)。window對象是相對于Web瀏覽器而言的嘹吨,它并不是ECMAScript規(guī)定的內(nèi)置對象搬味,它是瀏覽器的WebAPI,是存在于瀏覽器之中的蟀拷,也就是離開瀏覽器這個宿主環(huán)境的話就不存在此對象了碰纬。所以,Global對象是包含于window對象這種情況的開始
一问芬、this的第1條規(guī)則:默認綁定
默認綁定規(guī)則規(guī)定悦析,當一個函數(shù)執(zhí)行獨立調(diào)用時,例如只是funcName();
此衅,這時函數(shù)的“this”被指向 global 對象强戴。這里要注意的是亭螟,嚴格模式下,全局對象是不會默認綁定的酌泰,因此如果你在開啟了嚴格模式媒佣,那么控制臺輸出的就是 undefined,而不再是全局變量陵刹。
先看下面這個代碼:
//例子1
function foo() {
function bar() {
console.log(this);
}
bar();
}
foo();
foo 先被調(diào)用默伍,然后又調(diào)用bar,bar將“this”打印到控制臺中衰琐。這里的技巧是看看函數(shù)是如何被調(diào)用的:foo和bar都被單獨調(diào)用也糊,因此,他們內(nèi)部的“this”都是指向 global 對象羡宙。但是由于 bar 是唯一執(zhí)行打印的函數(shù)狸剃,所以我們看到 global 對象在控制臺中輸出了一次。
再看一下面的代碼:
//例子2
var a = 1;
function foo() {
console.log(this.a);
}
foo();
//1
輸出結(jié)果是 undefined狗热?是 1钞馁?還是什么?
如果你已經(jīng)很好地理解了之前講解的內(nèi)容匿刮,那么你應該知道控制臺輸出的是“1”僧凰。為什么?首先熟丸,默認綁定作用于函數(shù) foo训措。因此 foo 中的“this”指向 global 對象,并且 a 被聲明為 global 變量光羞,這就意味著 a 是 global 對象的屬性(也稱之為全局對象污染)绩鸣,因此this.a
和var a
就是同一個東西。
二纱兑、this的第2條規(guī)則:隱式綁定呀闻。
隱式綁定規(guī)則規(guī)定,當一個函數(shù)被作為一個對象方法被調(diào)用時潜慎,那么它內(nèi)部的“this”應該指向這個對象捡多。
先看下面這個代碼:
//例子3
var obj = {
a: 1,
foo: function() {
console.log(this);
}
};
obj.foo();
如果函數(shù)調(diào)用前面有多個對象(obj1.obj2.func()
),那么函數(shù)之前的最后一個對象(obj3)會被綁定勘纯。
需要注意的一點是局服,函數(shù)調(diào)用必須有效,那也就是說當你調(diào)用obj.func()
時驳遵,必須確保 func 是對象 obj 的屬性淫奔。
因此,在上面的例子中調(diào)用obj.foo()
時堤结,“this”就指向 obj唆迁,因此 obj 被打印輸出在控制臺中鸭丛。
三、進階:默認綁定和隱性綁定兩種規(guī)則結(jié)合
【重要】
我們必須知道唐责,this在調(diào)用一個函數(shù)函數(shù)前鳞溉,并沒有綁定在這個函數(shù),
判斷運用了那種綁定規(guī)則鼠哥,要密切注意這個函數(shù)是如何是否被單獨調(diào)用熟菲。
如果單獨調(diào)用的話,就是默認綁定朴恳,函數(shù)的“this”都指向全局對象global抄罕;如果是對象屬性的調(diào)用,那么就是隱性綁定于颖。
1呆贿、先看以下代碼:
//例子4
function logThis() {
console.log(this);
}
var myObject = {
a: 1,
logThis: logThis
};
logThis();
myObject.logThis();
跟在 myObject
后面的這個全局調(diào)用logThis()
單獨被調(diào)用的時候,通過 console.log(this)
打印的是global對象森渐;而myObject.logThis()
做入,此時logThis()
就是作為函數(shù)的屬性進行調(diào)用的, 打印是 myObject 對象同衣。
2竟块、一個有趣的現(xiàn)象:
console.log(logThis === myObject.logThis); // true
為什么不呢?它們當然是相同的函數(shù)乳怎,但是你可以看到 如何調(diào)用_logThis_
會讓其中的“this”發(fā)生改變彩郊。當logThis被單獨調(diào)用時前弯,使用默認綁定規(guī)則蚪缀,但是當 logThis 作為前面的對象屬性被調(diào)用時,使用隱式綁定規(guī)則恕出。
四询枚、繼續(xù)探討this的運用
1、先看這個例子:
//例子5
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo();
//undefined
就像例子2 的 var a
一樣浙巫,bar也是全局對象的屬性金蜀。因為foo被單獨調(diào)用了,它內(nèi)部的“this”
就是全局對象(默認綁定)的畴。因此 foo 內(nèi)部的 this.bar
就是 bar渊抄。控制臺中輸出什么丧裁?如果你猜的沒錯护桦,“undefined”
會被打印出來。
注意 bar 是如何被調(diào)用的煎娇?看起來二庵,隱式綁定在這里發(fā)揮作用贪染。隱式綁定意味著 bar 中的“this”
是其前面的對象引用。bar前面的對象引用是全局對象催享,在 foo 里面是全局對象杭隙,對不對?因此在 bar 中嘗試訪問 this.a
等同于訪問 [global object].a
因妙。而全局對象沒有屬性a痰憎,沒有什么意外的話,控制臺會輸出 undefined
攀涵。
2信殊、再看一下下面例子:
//例子6
var obj = {
a: 1,
foo: function(fn) {
console.log(this);
fn();
}
};
obj.foo(function() {
console.log(this);
});
//{a: 1, foo: ?}
//window
是不是瞬間看不懂這個代碼是什么呢?不要著急汁果,把它簡化一下吧涡拘,把
obj.foo(function() { console.log(this); });
簡化成 obj.foo();
這樣看來,函數(shù)foo是接受一個回調(diào)函數(shù)作為參數(shù)据德。我們所做的就是在調(diào)用foo的時候在參數(shù)里面放了一個函數(shù)鳄乏。
obj.foo( function() { console.log(this); } );
foo 是 如何 被調(diào)用的?它是一個單獨調(diào)用嗎棘利?當然不是橱野,因此第一個輸出到控制臺的是對象 obj 。我們傳入的回調(diào)函數(shù)是什么善玫?在 foo 內(nèi)部水援,回調(diào)函數(shù)變?yōu)?fn 。那么 fn 是 如何被調(diào)用茅郎?fn中的“this”是指向全局對象蜗元,因此第二個被輸出到控制臺的是全局對象。
3系冗、看一個和構(gòu)造函數(shù)相關(guān)的例子:
//例子7
var arr = [1, 2, 3, 4];
Array.prototype.myCustomFunc = function() {
console.log(this);
};
arr.myCustomFunc();
//[1, 2, 3, 4]
是不是又傻眼了奕扣?如果對于原型不是很熟悉,就把它當做一個普通的對象掌敬,然后一個執(zhí)行this的函數(shù)賦值給它惯豆,然后調(diào)用myCustomFunc時,就是調(diào)用這個函數(shù)對象奔害,此刻發(fā)揮了隱性綁定的作用(我們始終不要忘記兩種綁定規(guī)則的判定標準)楷兽,此時函數(shù)內(nèi)部的this就是指向myCustomFunc這個對象,arr調(diào)用之后华临,輸出結(jié)果就是:[1, 2, 3, 4]
4芯杀、再看一下下面的例子:
//例子8
var obj = {
a: 2,
foo: function() {
console.log(this);
}
};
obj.foo();
var bar = obj.foo;
bar();
不要被這里面的花哨代碼所分心,只需注意函數(shù)是如何被調(diào)用的,就可以弄明白“this”的含義瘪匿。你現(xiàn)在一定已經(jīng)掌握這個技巧了吧跛梗。首先obj.foo()
被調(diào)用,因為 foo 前面有一個對象引用棋弥,所以首先輸出的是對象obj核偿。bar當然是被獨立調(diào)用的,因此下一個輸出是全局變量顽染。這里要注意的是漾岳,bar和foo是對同一個函數(shù)的引用,唯一區(qū)別是它們被調(diào)用的方式不同粉寞。
做到這里我已經(jīng)夠理解this(其實是時間不夠)尼荆。其實還有幾個例子,我全看完了唧垦,自己做了之后全對捅儒,希望看到這篇的你也能學會this的指向。