this 的問題

與其他語言相比,函數(shù)的this關(guān)鍵字在JavaScript中的表現(xiàn)略有不同洒放,此外弛车,在嚴格模式非嚴格模式之間也會有一些差別齐媒。

在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了this的值纷跛。this不能在執(zhí)行期間被賦值喻括,并且在每次函數(shù)被調(diào)用時this的值也可能會不同。ES5引入了bind方法來設(shè)置函數(shù)的this值贫奠,而不用考慮函數(shù)如何被調(diào)用的唬血,ES2015引入了支持this詞法解析的箭頭函數(shù)(它在閉合的執(zhí)行上下文內(nèi)設(shè)置this的值)。

語法

this

全局上下文

無論是否在嚴格模式下唤崭,在全局執(zhí)行上下文中(在任何函數(shù)體外部)this都指代全局對象刁品。

// 在瀏覽器中, window 對象同時也是全局對象:

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ù)上下文

在函數(shù)內(nèi)部,this的值取決于函數(shù)被調(diào)用的方式浩姥。

1. 直接調(diào)用

因為下面的代碼不是在嚴格模式下執(zhí)行挑随,且this的值不是通過調(diào)用設(shè)置的,所以this的值默認指向全局對象勒叠。

function f1(){

return this;

}

//在瀏覽器中:

f1() === window;? //在瀏覽器中兜挨,全局對象是window

//在Node中:

f1() === global;

然而,在嚴格模式下眯分,this將保持他進入執(zhí)行上下文時的值拌汇,所以下面的this將會默認為undefined。

function f2(){

"use strict"; // 這里是嚴格模式

return this;

}

f2() === undefined; // true

所以弊决,在嚴格模式下噪舀,如果this未在執(zhí)行的上下文中定義,那它將會默認為undefined飘诗。

在第二個例子中与倡,this的確應(yīng)該是undefined,因為f2是被直接調(diào)用的昆稿,而不是作為對象的屬性/方法調(diào)用的(比如window.f2())纺座。有一些瀏覽器最初在支持嚴格模式時沒有正確實現(xiàn)這個功能,于是它們錯誤地返回了window對象溉潭。

2. call和apply方法

如果要想把this的值從一個context傳到另一個净响,就要用call,或者apply方法喳瓣。

譯者注:call()和apply()方法屬于間接調(diào)用(indirect invocation)馋贤。

// 一個對象可以作為call和apply的第一個參數(shù),并且this會被綁定到這個對象畏陕。

var obj = {a: 'Custom'};

// 這個屬性是在global對象定義的配乓。

var a = 'Global';

function whatsThis(arg) {

return this.a;? // this的值取決于函數(shù)的調(diào)用方式

}

whatsThis();? ? ? ? ? // 直接調(diào)用,? ? ? 返回'Global'

whatsThis.call(obj);? // 通過call調(diào)用,? 返回'Custom'

whatsThis.apply(obj); // 通過apply調(diào)用 扰付,返回'Custom'

當一個函數(shù)的函數(shù)體中使用了this關(guān)鍵字時,通過call()方法和apply()方法調(diào)用仁讨,this的值可以綁定到一個指定的對象上羽莺。call()和apply()的所有函數(shù)都繼承自Function.prototype。

function add(c, d) {

return this.a + this.b + c + d;

}

var o = {a: 1, b: 3};

// 第一個參數(shù)是作為‘this’使用的對象

// 后續(xù)參數(shù)作為參數(shù)傳遞給函數(shù)調(diào)用

add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

// 第一個參數(shù)也是作為‘this’使用的對象

// 第二個參數(shù)是一個數(shù)組洞豁,數(shù)組里的元素用作函數(shù)調(diào)用中的參數(shù)

add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

使用call和apply函數(shù)的時候要注意盐固,如果傳遞的this值不是一個對象,JavaScript將會嘗試使用內(nèi)部ToObject操作將其轉(zhuǎn)換為對象丈挟。因此刁卜,如果傳遞的值是一個原始值比如?7 或 'foo' ,那么就會使用相關(guān)構(gòu)造函數(shù)將它轉(zhuǎn)換為對象曙咽,所以原始值7通過new Number(7)被轉(zhuǎn)換為對象蛔趴,而字符串'foo'使用new?String('foo')轉(zhuǎn)化為對象,例如:

function bar() {

console.log(Object.prototype.toString.call(this));

}

//原始值 7 被隱式轉(zhuǎn)換為對象

bar.call(7); // [object Number]

3. bind?方法

ECMAScript 5 引入了Function.prototype.bind例朱。調(diào)用f.bind(某個對象)會創(chuàng)建一個與f具有相同函數(shù)體和作用域的函數(shù)孝情,但是在這個新函數(shù)中,this將永久地被綁定到了bind的第一個參數(shù)洒嗤,無論這個函數(shù)是如何被調(diào)用的箫荡。

function f(){

return this.a;

}

//this被固定到了傳入的對象上

var g = f.bind({a:"azerty"});

console.log(g()); // azerty

var h = g.bind({a:'yoo'}); //bind只生效一次!

console.log(h()); // azerty

var o = {a:37, f:f, g:g, h:h};

console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty

4. 箭頭函數(shù)

箭頭函數(shù)中渔隶,this是根據(jù)當前的詞法作用域來決定的羔挡,就是說,箭頭函數(shù)會繼承外層函數(shù)調(diào)用的this綁定(無論this綁定到什么)间唉。在全局作用域中绞灼,它會綁定到全局對象上:

var globalObject = this;

var foo = (() => this);

console.log(foo() === globalObject); // true

注意:如果將thisArg傳遞給call、bind呈野、或者apply镀赌,它將被忽略(譯者注:thisArg即傳入三個函數(shù)中的第一個參數(shù))。不過你仍然可以為調(diào)用添加參數(shù)际跪,不過第一個參數(shù)應(yīng)該設(shè)置為null商佛。

// 接著上面的代碼

// 作為對象的一個方法調(diào)用

var obj = {foo: foo};

console.log(obj.foo() === globalObject); // true

// 嘗試使用call來設(shè)定this

console.log(foo.call(obj) === globalObject); // true

// 嘗試使用bind來設(shè)定this

foo = foo.bind(obj);

console.log(foo() === globalObject); // true

無論如何,foo的this被設(shè)置為它被創(chuàng)建時的上下文(在上面的例子中姆打,就是global對象)良姆。這同樣適用于在其他函數(shù)中創(chuàng)建的箭頭函數(shù):這些箭頭函數(shù)的this被設(shè)置為外層執(zhí)行上下文。

// 創(chuàng)建一個含有bar方法的obj對象幔戏,bar返回一個函數(shù)玛追,這個函數(shù)返回它自己的this,

// 這個返回的函數(shù)是以箭頭函數(shù)創(chuàng)建的,所以它的this被永久綁定到了它外層函數(shù)的this痊剖。

// bar的值可以在調(diào)用中設(shè)置韩玩,它反過來又設(shè)置返回函數(shù)的值。

var obj = {bar: function() {

var x = (() => this);

return x;

}

};

// 作為obj對象的一個方法來調(diào)用bar陆馁,把它的this綁定到obj找颓。

// x所指向的匿名函數(shù)賦值給fn。

var fn = obj.bar();

// 直接調(diào)用fn而不設(shè)置this叮贩,通常(即不使用箭頭函數(shù)的情況)默認為全局對象击狮,若在嚴格模式則為undefined

console.log(fn() === obj); // true

// 但是注意,如果你只是引用obj的方法益老,而沒有調(diào)用它(this是在函數(shù)調(diào)用過程中設(shè)置的)

var fn2 = obj.bar;

// 那么調(diào)用箭頭函數(shù)后彪蓬,this指向window,因為它從 bar 繼承了this捺萌。

console.log(fn2()() == window); // true

在上面的例子中档冬,一個賦值給了obj.bar的函數(shù)(稱它為匿名函數(shù)A) ,返回了另一個箭頭函數(shù)(稱它為匿名函數(shù)B)桃纯。因此捣郊,函數(shù)B被調(diào)用時,它的this被永久地設(shè)置為obj.bar(匿名函數(shù)A)的this慈参。

而且當這個返回的函數(shù)B被調(diào)用時呛牲,它的this將始終為最初設(shè)定的值。

在上面的代碼示例中驮配,函數(shù)B的 this被設(shè)定為函數(shù)A的this娘扩,也就是obj,所以即使以某種默認方式調(diào)用它(比如默認讓它指向全局對象或者undefined壮锻,或者在前面示例中的任何其他方法)琐旁,它仍然會指向obj.

5. 作為對象的一個方法

當以對象里的方法的方式調(diào)用函數(shù)時,它們的this是調(diào)用該函數(shù)的對象.

下面的例子中猜绣,當o.f()被調(diào)用時灰殴,函數(shù)內(nèi)的this將綁定到o對象。

var o = {

prop: 37,

f: function() {

return this.prop;

}

};

console.log(o.f()); // logs 37

請注意掰邢,這樣的行為牺陶,根本不受函數(shù)定義方式或位置的影響。在前面的例子中辣之,我們在定義對象o的同時掰伸,將成員f定義了一個匿名函數(shù)。但是怀估,我們也可以首先定義函數(shù)狮鸭,然后再將其附屬到o.f合搅。這樣做會導(dǎo)致相同的行為:

var o = {prop: 37};

function independent() {

return this.prop;

}

o.f = independent;

console.log(o.f()); // logs 37

這說明this的值只與 函數(shù)從o的成員f中調(diào)用的方式 有關(guān)系。

類似的歧蕉,this的綁定只受最靠近的成員引用的影響灾部。在下面的這個例子中,我們把一個方法g當作對象o.b的函數(shù)調(diào)用惯退。在這次執(zhí)行期間赌髓,函數(shù)中的this將指向o.b。事實上蒸痹,這與對象本身的成員沒有多大關(guān)系,最靠近的引用才是最重要的呛哟。

o.b = {

g: independent,

prop: 42

};

console.log(o.b.g()); // logs 42

6. 原型鏈中的this

相同的概念在定義在原型鏈中的方法也是一致的叠荠。如果該方法存在于一個對象的原型鏈上,那么this指向的是調(diào)用這個方法的對象扫责,就好像該方法本來就存在于這個對象上榛鼎。

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屬性鳖孤,它的f屬性繼承自它的原型者娱。但是這對于最終在o中找到f屬性的查找過程來說沒有關(guān)系;查找過程首先從p.f的引用開始苏揣,所以函數(shù)中的this指向p黄鳍。也就是說,因為f是作為p的方法調(diào)用的平匈,所以它的this指向了p框沟。這是JavaScript的原型繼承中的一個有趣的特性。

7. getter 與 setter 中的 this

再次增炭,相同的概念也適用時的函數(shù)作為一個getter或者?一個setter調(diào)用忍燥。用作getter或setter的函數(shù)都會把this綁定到正在設(shè)置或獲取屬性的對象。

function sum() {

return this.a + this.b + this.c;

}

var o = {

a: 1,

b: 2,

c: 3,

get average() {

return (this.a + this.b + this.c) / 3;

}

};

Object.defineProperty(o, 'sum', {

get: sum, enumerable: true, configurable: true});

console.log(o.average, o.sum); // logs 2, 6

8. 作為一個構(gòu)造函數(shù)

當一個函數(shù)用作構(gòu)造函數(shù)時(使用new關(guān)鍵字)隙姿,它的this被綁定到正在構(gòu)造的新對象梅垄。

注意:雖然構(gòu)造器返回的默認值是this所指的那個對象,但它仍可以手動返回其他的對象(如果返回值不是一個對象输玷,則返回this對象)队丝。

/*

* 構(gòu)造函數(shù)這樣工作:

*

* function MyConstructor(){

*? // 函數(shù)實體寫在這里

*? // 根據(jù)需要在this上創(chuàng)建屬性,然后賦值給它們欲鹏,比如:

*? this.fum = "nom";

*? // 等等...

*

*? // 如果函數(shù)具有返回對象的return語句炭玫,則該對象將是 new 表達式的結(jié)果。

*? // 否則貌虾,表達式的結(jié)果是當前綁定到 this 的對象吞加。

*? //(即通常看到的常見情況)。

* }

*/

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)衔憨,因為在調(diào)用構(gòu)造函數(shù)的過程中叶圃,手動的設(shè)置了返回對象,與this綁定的默認對象被丟棄了践图。(這基本上使得語句“this.a = 37;”成了“僵尸”代碼掺冠,實際上并不是真正的“僵尸”,這條語句執(zhí)行了码党,但是對于外部沒有任何影響德崭,因此完全可以忽略它)。

9. 作為一個DOM事件處理函數(shù)

當函數(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

// 當 currentTarget 和 target 是同一個對象是為 true

console.log(this === e.target);

this.style.backgroundColor = '#A5D9F3';

}

// 獲取文檔中的所有元素的列表

var elements = document.getElementsByTagName('*');

// 將bluify作為元素的點擊監(jiān)聽函數(shù)兽狭,當元素被點擊時憾股,就會變成藍色

for(var i=0 ; i

elements[i].addEventListener('click', bluify, false);

}

10. 作為一個內(nèi)聯(lián)事件處理函數(shù)

當代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時,它的this指向監(jiān)聽器所在的DOM元素:

Show this

上面的alert會顯示button箕慧。注意只有外層代碼中的this是這樣設(shè)置的:

Show inner this

在這種情況下服球,沒有設(shè)置內(nèi)部函數(shù)的this,所以它指向global/window對象(即非嚴格模式下調(diào)用的函數(shù)未設(shè)置 this?時指向的默認對象)颠焦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斩熊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子伐庭,更是在濱河造成了極大的恐慌座享,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件似忧,死亡現(xiàn)場離奇詭異渣叛,居然都是意外死亡,警方通過查閱死者的電腦和手機盯捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門淳衙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饺著,你說我怎么就攤上這事箫攀。” “怎么了幼衰?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵靴跛,是天一觀的道長。 經(jīng)常有香客問我渡嚣,道長梢睛,這世上最難降的妖魔是什么肥印? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮绝葡,結(jié)果婚禮上深碱,老公的妹妹穿的比我還像新娘。我一直安慰自己藏畅,他們只是感情好敷硅,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愉阎,像睡著了一般绞蹦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榜旦,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天幽七,我揣著相機與錄音,去河邊找鬼章办。 笑死锉走,一個胖子當著我的面吹牛滨彻,可吹牛的內(nèi)容都是我干的藕届。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亭饵,長吁一口氣:“原來是場噩夢啊……” “哼休偶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辜羊,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤踏兜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后八秃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碱妆,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年昔驱,在試婚紗的時候發(fā)現(xiàn)自己被綠了疹尾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡骤肛,死狀恐怖纳本,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腋颠,我是刑警寧澤繁成,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站淑玫,受9級特大地震影響巾腕,放射性物質(zhì)發(fā)生泄漏面睛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一祠墅、第九天 我趴在偏房一處隱蔽的房頂上張望侮穿。 院中可真熱鬧,春花似錦毁嗦、人聲如沸亲茅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽克锣。三九已至,卻和暖如春腔长,著一層夾襖步出監(jiān)牢的瞬間袭祟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工捞附, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留巾乳,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓鸟召,卻偏偏與公主長得像胆绊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子欧募,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 單例模式 適用場景:可能會在場景中使用到對象压状,但只有一個實例,加載時并不主動創(chuàng)建跟继,需要時才創(chuàng)建 最常見的單例模式种冬,...
    Obeing閱讀 2,065評論 1 10
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情舔糖,實現(xiàn)同樣的效果;這時候需要使用工廠模式娱两。簡單...
    舟漁行舟閱讀 7,750評論 2 17
  • 如何控制alert中的換行?\n alert(“p\np”); 請編寫一個JavaScript函數(shù) parseQu...
    heyunqiang99閱讀 1,084評論 0 6
  • 相關(guān)知識點 數(shù)據(jù)類型金吗、運算十兢、對象、function辽聊、繼承纪挎、閉包、作用域跟匆、原型鏈异袄、事件、RegExp玛臂、JSON烤蜕、Aj...
    sandisen閱讀 11,379評論 7 175
  • 現(xiàn)在是周一的晚上10點半讽营,周一周四是我們小組加班培訓(xùn)的時候虎忌,每到這個時候回來都覺得比較累,不想再碼代碼了橱鹏。趁著這個...
    wenju_song閱讀 172評論 2 3