javascript成神之路(4):深入理解this關鍵字,是的就是this

cacao111發表於2018-07-11

摘要:如果你真的理解了this是什麼,那麼你的web幾乎所向披靡(文章後面有驚喜,不要錯過)

很多程式設計師會這麼認為,this關鍵字與物件導向程式開發緊密相關,其完全指向由構造器新建立的物件。在ECMAScript規範中也是這樣實現的,但正如我們將看到那樣,在ECMAScript中,this並不限於只用來指向新建立的物件。還有很多新功能特性

一、定義

this代表當前物件,說明this是在某種特定的情況下才是成立的,它是執行上下文的一個屬性。

activeExecutionContext = { AEC: {…}, this: thisVal };

這裡AEC是我們討論的變數物件。this與上下文中可執行程式碼的型別有直接關係,this值在進入上下文時確定,並且在上下文執行期間永久不變。

二、全域性程式碼中的this

在全域性程式碼中,this始終是全域性物件本身,這樣就有可能間接的引用到它了。

// 顯示定義全域性物件的屬性 this.ab = 10; // global.ab = 10 alert(ab); // 10 // 通過賦值給一個無標示符隱式 bb = 20; alert(this.bb); // 20 // 也是通過變數宣告隱式宣告的 // 因為全域性上下文的變數物件是全域性物件自身 var cc = 30; alert(this.cc); // 30

三、函式程式碼中的this

在這時候this值的首要特點是它不是靜態的繫結到一個函式。this是進入上下文時確定,在一個函式程式碼中,這個值在每一次完全不同,但是在程式碼執行時的this值是不變的,也就是說,因為它不是一個變數,就不可能為其分配一個新值。

var F = {y: 10}; var Bar = { y: 20, test: function () { alert(this === Bar); // true alert(this.y); // 20 this = F; // 錯誤,任何時候不能改變this的值 alert(this.y); // 如果不出錯的話,應該是10,而不是20 } }; // 在進入上下文的時候 // this被當成Bar物件 B.test(); // true, 20 F.test = Bar.test; // 不過,這裡this依然不會是foo // 儘管呼叫的是相同的function F.test(); // false, 10

在我們通常的函式呼叫中,this是由啟用上下文程式碼的呼叫者來提供的,即呼叫函式的父上下文(parent context )。this取決於呼叫函式的方式,正是呼叫函式的方式影響了呼叫的上下文中的this值,沒有別的什麼可以看到,即使是正常的全域性函式也會被呼叫方式的不同形式啟用,這些不同的呼叫方式導致了不同的this值。例如:

function F() { alert(this); } F(); // global alert(F === F.prototype.constructor); // true // 但是同一個function的不同的呼叫表示式,this是不同的 F.prototype.constructor(); // F.prototype

那麼,呼叫函式的方式如何影響this值?請看下面

四、引用型別

引用型別的值只有兩種情況: 1、當我們處理一個標示符時,2、或者一個屬性訪問器

在一個函式上下文中,this由呼叫者提供,由呼叫函式的方式來決定。如果呼叫括號()的左邊是引用型別的值,this將設為引用型別值的base物件(base object),在其他情況下(與引用型別不同的任何其它屬性),這個值為null。不過,實際不存在this的值為null的情況,因為當this的值為null的時候,其值會被隱式轉換為全域性物件,例如:

function F() { return this; } F(); // global

我們看到在呼叫括號的左邊是一個引用型別值(因為F是一個標示符)

var fooReference = { base: global, propertyName: `foo` };

相應地,this也設定為引用型別的base物件。即全域性物件

var F = { B: function () { return this; } }; F.B(); // F

我們再次擁有一個引用型別,其base是F物件,在函式B啟用時用作this。

var FB = { base: F, propertyName: `B` };

五、作為構造器呼叫的函式中的this

還有一個與this值相關的情況是在函式的上下文中,這是一個建構函式的呼叫。

unction A() { alert(this); // “a”物件下建立一個新屬性 this.x = 10; } var a = new A(); alert(a.x); // 10

new運算子呼叫“A”函式的內部的[[Construct]] 方法,接著,在物件建立後,呼叫內部的[[Call]] 方法。 所有相同的函式“A”都將this的值設定為新建立的物件。

六、函式呼叫中手動設定this

在函式原型中定義的兩個方法允許去手動設定函式呼叫的this值。它們是.apply和.call方法。他們用接受的第一個引數作為this值,this 在呼叫的作用域中使用。這兩個方法的區別很小,對於.apply,第二個引數必須是陣列(或者是類似陣列的物件,如arguments,反過來,.call能接受任何引數。兩個方法必須的引數是第一個——this。

var dog = { say() { console.log(this) } } var cat = {} dog.say.call(cat)

其中this通過call被繫結到了cat上

七、箭頭函式

它的this指向與普通函式有很大的不同。箭頭函式內部的 this 是詞法作用域,由上下文確定。簡單說就是箭頭函式中的 this 只和定義它時候的作用域的 this 有關,而與在哪裡以及如何呼叫它無關,同時它的 this 指向是不可改變的。

var obj = { x: 10, foo: function() { var fn = () => { return () => { return () => { console.log(this); //Object {x: 10} console.log(this.x); //10 } } } fn()()(); } } obj.foo();

對於箭頭函式還需要注意一點,就是它的this確定後不會改變。使用call、apply、bind也無法改變它的this,因此使用它們傳入的第一個引數無效。但是後面新增的引數值還是有效的。

原文釋出時間:2018年01月10日

作者:技術金三胖 

本文來源:開源中國  如需轉載請聯絡原作者


相關文章