理解JavaScript中的this關鍵字

原文:Understanding the "this" keyword in JavaScript

許多人被JavaScript中的this關鍵字給困擾住了,我想混亂的根源來自人們理所當然地認為JavaScript中的this應該像Java中的this或Python中的self一樣工作。盡管JavaScript的this有時有類似的效果,但它跟Java或其他語言中的this是完全不同的。盡管有點難理解,但它的原理并不神秘。事實上,this遵循一些簡單的小規則,這篇文章就對這些規則作出了解釋。

但首先,我想給出一些專業術語和一些JavaScript運行時環境的相關信息,希望能幫助你構建一個心智模型來更好的理解this

執行上下文

每一行JavaScript代碼都運行在一個“執行上下文”中。JavaScript運行時環境用一個棧維持著這些上下文,棧頂的一個就是當前正在運行的執行上下文。

有三類可執行代碼:全局代碼(global code)函數代碼(function code)eval代碼(eval code)。大概的說,全局代碼是應用程序最頂層的代碼,不被包含在任何方法中,函數代碼是在函數(function)體中的代碼,eval代碼是被eval解釋的全局代碼。

this對象每次控制進入一個新的執行上下文時會被重新確定指向,直到控制切換到一個不同的上下文。this的值取決于兩件事:被執行的代碼的類型(global,function,eval)和調用代碼的對象。

全局對象

所有的JavaScript運行時都有一個唯一的全局對象。他的屬性包括內置的對象如MathString,以及其他由主環境變量定義的屬性。

在瀏覽器中,全局對象是window對象。在Node.js中,它就叫作“global object”。(我假定你將在瀏覽器中運行代碼,然而,我所說的一切也同樣適用于Node.js。)

確定this的值

第一條規則很簡單:this指向全局對象在所有全局代碼中。由于所有的程序都是由執行全局代碼開始的,并且this在給定的執行上下文中會被修正,所以,默認的this指全局對象。

當控制進入一個新的執行上下文時發生了什么呢?只有三種this的值發生改變的情況:方法調用,new一個函數對象,函數被callapply調用。我將逐一解釋。

方法調用

當我們通過點(例obj.foo())或者方括號(例obj["foo"])把一個方法作為一個對象的屬性來調用時,this將指向方法體的父對象:

var counter = {
  val: 0,
  increment: function () {
    this.val += 1;
  }
};
counter.increment();
console.log(counter.val); // 1
counter['increment']();
console.log(counter.val); // 2

這是第二條規則:函數被當作父對象的屬性來調用時,在函數代碼中的this指向函數的父對象。

注意,如果我們直接調用相同的方法,即,不作為父對象的屬性,this將不會指向counter對象:

var inc = counter.increment;
inc();
console.log(counter.val); // 2
console.log(val); // NaN

inc被調用時,這里的this不會改變,所以它還是指向全局對象。

inc執行

this.val += 1;

它等效于執行:

window.val += 1;

window.val是undefined,且undefined1得到NaN

new運算符

任何JavaScript函數都可以通過new運算符當成構造函數使用。new運算符創建一個新對象并且設置函數中的this指向調用函數的新對象。舉個栗子:

function F (v) {
  this.val = v;
}
var f = new F("Woohoo!");
console.log(f.val); // Woohoo!
console.log(val); // ReferenceError

這就是我們的第三條規則:在用new運算符調用的函數代碼中的this指向新創建的對象。

注意F沒有任何特殊。如果不通過new調用,this將指向全局對象:

var f = F("Oops!");
console.log(f.val); // undefined
console.log(val); // Oops!

在這個例子中,F("Oops!")是一個通常調用,this沒有指向新的對象,因為沒有用new創建新的對象,this繼續指向全局對象。

Call & Apply

所有JavaScript函數都有兩個方法,callapply,讓你能夠調用函數并且清楚的設置this指向的對象。apply函數有兩個參數:一個是this指向的對象,一個(可選)傳進函數的參數的數組:

var add = function (x, y) {
      this.val = x + y;
    },
    obj = {
      val: 0
    };
add.apply(obj, [2, 8]);
console.log(obj.val); // 10

call方法和apply方法是完全一樣的,只不過要逐個的傳遞參數,而不是用一個數組:

add.call(obj, 2, 8);
console.log(obj.val); // 10

這是第四條規則:當使用callapply調用函數時,函數代碼中的this被設置為callapply中的第一個參數。

總結

  • 默認的,this指向全局對象
  • 當一個函數被作為一個父對象的屬性調用時,函數中的this指向父對象
  • 當一個函數被new運算符調用時,函數中的this指向新創建的對象
  • 當使用callapply調用函數時,函數代碼中的this被設置為callapply中的第一個參數。如果第一個參數是null或不是個對象,this指向全局對象。

如果你理解并接受了上面的4條規則,你就能明白this指的是什么了。

補充:eval打破上面所有規則

Remember when I said that code evaluated inside eval is its own type of executable code? Well, that’s true, and it means that the rules for determining what this refers to inside of eval code are a little more complex.

As a first pass, you might think that this directly inside eval refers to the same object as it does in eval’s caller’s context. For example:

var obj = {
  val: 0,
  func: function() { 
    eval("console.log(this.val)");
  }
};
obj.func(); // 0

That works likely as you expect it to. However, there are many cases with eval where this will probably not work as you expect:

eval.call({val: 0}, "console.log(this.val)"); // depends on browser

The output of the above code depends on your browser. If your JavaScript runtime implements ECMAScript 5, this will refer to the global object and the above should print undefined, because it’s an “indirect” call of eval. That’s what the latest versions of Chrome and Firefox do. Safari 5.1 actually throws an error (“The ‘this’ value passed to eval must be the global object from which eval originated”), which is kosher according to ECMAScript 3.

If you want know how this works with eval, you should read Juriy Zaytsev’s excellent, “Global eval. What are the options?” You’ll learn more about eval, execution contexts, and direct vs. indirect calls than you probably ever wanted to know.

In general, you should just avoid using eval, in which case the simple rules about this given previously will always hold true.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容

  • 導語 不得不說,作為一名初級的前端開發者,this關鍵字這個問題對于我來說一直是一個痛點,什么是this?什么是函...
    Nicole_tiny閱讀 537評論 0 4
  • 1. this之謎 在JavaScript中,this是當前執行函數的上下文。因為JavaScript有4種不同的...
    百里少龍閱讀 1,019評論 0 3
  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,262評論 0 4
  • 前段時間算了一個命,覺得心拔涼拔涼的,甚至非常后悔,為啥花錢給自己找不痛快? 輾轉反思,寤寐思服。 虛無縹緲的命運...
    奢奢閱讀 613評論 0 1
  • 文/孤鳥差魚 黑夜 忘了白天 愛的聲嘶力竭
    孤鳥差魚閱讀 216評論 0 1