您當前的位置:首頁 > 舞蹈

馴服JavaScript的“this”!

作者:由 Rachel 發表于 舞蹈時間:2017-02-13

JavaScript中的this關鍵字往往讓菜鳥和大神都感到困惑。本文目的就是帶你馴服它。我們將瞭解如何在每一個場景中正確地使用this。文章翻譯自JavaScriptisSexy。

在JavaScript中,其實this就類似於我們常常說的代詞“這個”,我們使用

this

關鍵字作為快捷方式,指示物件:

var person = {

firstName: “Penelope”,

lastName: “Barrymore”,

fullName: function () {

// 注意,我們使用“this”就像我們在之前例句用“he”:

console。log(this。firstName + “ ” + this。lastName);

​//我們也可以寫this:​

console。log(person。firstName + “ ” + person。lastName);

}

}

如果我們使用person。firstName和person。lastName,如在最後一個例子中,我們的程式碼變得模糊。考慮可能有另一個全域性變數,名稱為“person”。然後,對person。firstName的引用可能嘗試從

person

全域性變數訪問firstName屬性,這可能導致到難以除錯的錯誤。因此,我們使用“this”關鍵字不僅用於美學(優雅),而且用於精度; 它的使用使我們的程式碼更明確,正如代詞“他”使我們的句子更清楚。

就像代詞“he”用於指代先行詞(先行詞是代詞所指的名詞),

this

關鍵詞類似地用於指代函式(在

this

使用的地方)所繫結的物件。

this

關鍵字不僅指的是物件,但它也包含了物件的值。就像代詞一樣,

this

可以被認為是一個快捷方式來引用

上下文

中的物件(“先行物件”)。

JavaScript的

this

關鍵字基礎

JavaScript中使用

this

var person = {

firstName :“Penelope”,

lastName :“Barrymore”,

// 因為“this”關鍵字使用下面的方法showFullName裡面,而showFullName方法定義person物件上,“this”將有person物件的值,因為person物件將呼叫showFullName()

showFullName:function () {

console。log (this。firstName + “ ” + this。lastName);

}​

}

person。showFullName (); // Penelope Barrymore

關於this的基本的jQuery的例子:

// 一段很普通的jQuery程式碼:

$ (“button”)。click (function (event) {

// $(this)將有按鈕$(“button”)物件的值,因為按鈕物件呼叫了click方法:

console。log ($ (this)。prop (“name”));

//$(this)是JavaScript中的this關鍵字的jQuery語法

});

JavaScript中this的最大的問題

如果你理解這個JavaScript中this的原理,你就能清除明白“this”關鍵字:this不能儲存為變數直到物件呼叫this定義的函式,咱們就叫this定義的函式為“this Function”。

即使this出現在本身指向的被定義的物件,它不能被儲存為變數直到物件呼叫“this Function,this才被賦值。this在大多數情況下具有呼叫物件的值。但是,也有一些情況下,它沒有呼叫物件的值。

在全域性範圍使用

this

在全域性範圍中,當代碼在瀏覽器中執行時,所有全域性變數和函式都在

window

物件上定義。因此,當我們使用

this

在一個全域性函式,它指的是(並具有的值)的全域性

window

物件,它是整個JavaScript應用或網頁的主要容器。

從而:

var firstName = “Peter”,

lastName = “Ally”;

function showFullName () {

//“this”在這個函數里面將有window物件的值,

因為showFullName()函式和變數firstName和lastName一樣被定義在全域性中

console。log (this。firstName + “ ” + this。lastName);

}

var person = {

firstName:“Penelope”,

lastName:“Barrymore”,

showFullName:function () {

// showFullName函式會被person物件呼叫,所以this只想person物件

console。log (this。firstName + “ ” + this。lastName);

}

}

showFullName (); // Peter Ally

// window 是所有全域性變數和函式上定義的物件:

window。showFullName (); // Peter Ally

// showFullName函數里的this定義在person物件上,仍然指向person物件:

person。showFullName (); // Penelope Barrymore

this被誤解的場景有哪些?

(1)當我們使用this借用一個方法;(2)當把一個使用this的方法賦值為一個變數;(3)使用this的回撥函式作為引數傳遞,(4)當this被用在一個閉包的內部 - - 內部函式。我們在以下例子中說明這些場景。

ps:在繼續之前,我們說一個很重要的問題先,context 上下文:

context在JavaScript中類似的句子的主語:“約翰是贏錢的那個人。”該句的主語是約翰,我們可以說,句子的

context

是約翰。

同樣,在JavaScript程式碼中:

var person = {​

firstName :“Penelope”,

lastName :“Barrymore”,

showFullName:function () { //上下文:

console。log (this。firstName + “ ” + this。lastName);​

}

}​

​// 當我們在person物件上呼叫showFullName ()方法時,上下文是person物件

​//​ 在showFullName()裡面的this有person物件的值

person。showFullName (); // Penelope Barrymore

​// 如果我們用一個不同的物件呼叫showFullName方法:

​var anotherPerson = {

firstName :“Rohit”,

lastName :“Khan”​

};

​//我們可以用apply方法去設定this值,這樣在呼叫this函式是,this可以獲取任何物件的值

person。showFullName。apply (anotherPerson); // Rohit Khan

注意的是,我們可以透過使用另一個物件呼叫

this Function

來改變上下文; 那麼這個新物件在

上下文context 中

以下是this關鍵字變得棘手的幾種情況和對應解決方案:

修復回撥函式方法中的this

當我們傳遞一個方法(使用

this

)作為一個引數用作一個回撥函式時,事情有點毛。例如:

// 當頁面上一個按鈕被點選,有一個簡單的包涵clickHandler方法的物件

var user = {

data:[​

{name:“T。 Woods”, age:37},​

{name:“P。 Mickelson”, age:43}

],

clickHandler:function (event) {

var randomNum = ((Math。random () * 2 | 0) + 1) - 1; // 隨機數 0~1​​

// 從陣列物件裡面打印出來一個隨機的名字和年齡

console。log (this。data[randomNum]。name+ “ ”

+this。data[randomNum]。age);

}

}

// 按鈕被包裹一個jQuery $包裝裡面,所以它現在是一個jQuery物件,而輸出將是undefined ,因為在按鈕物件上沒有資料屬性:

$ (“button”)。click (user。clickHandler); //無法讀取未定義的屬性“0”

在上面的程式碼中,由於按鈕($(“button”))是一個物件本身,我們將

user。clickHandler

方法傳遞給它的click()方法作為回撥,我們知道

在我們的user

。 clickHandler

方法將不再引用

user

物件。

將引用執行user。clickHandler方法的物件,因為

是在user。clickHandler方法內定義的。呼叫user。clickHandler的物件是button物件 - user。clickHandler將在button物件的click方法中執行。

注意,即使我們使用使用者 。clickHandler(因為clickHandler是在user上定義的方法)呼叫clickHandler()方法,clickHandler()方法本身將以按鈕物件作為context上下文執行“this”現在是指。所以

這裡

現在指的是按鈕($(“button”))物件。

在這一點上,應該顯而易見的是,當上下文改變時,當我們對除了最初定義的物件之外的其他物件執行方法時,

this

關鍵字不再引用最初定義“this”的原始物件,而是現在指的是在this定義的地方呼叫的方法的物件。

解決方案,當一個方法作為回撥函式傳遞時修復

這個問題

因為我們真的想要this。data引用

user

物件的data屬性,我們可以使用Bind(),Apply()或Call()方法專門設定。

我們必須將 clickHandler方法繫結到使用者物件,如下所示:

$(“button”)。click(user。clickHandler。bind(user)); // P。 Mickelson 43

修復閉包裡的

this

另一個例項,當

被誤解是當我們使用內部方法(閉包)。重要的是要注意,閉包不能透過使用

this

關鍵字訪問外部函式的

這個

變數,因為

這個

變數只能由函式本身訪問,而不是內部函式。例如:

var user = {

tournament:“The Masters”,

data :[

{name:“T。 Woods”, age:37},

{name:“P。 Mickelson”, age:43}

],

clickHandler:function () {

// 這裡使用this。data是可以的, ​因為this指向user物件,並且在user物件上data是一個屬性。

this。data。forEach (function (person) {

// 匿名函式,this不指向user了

// 內部函式訪問不了外部函式的this

console。log (“What is This referring to? ” + this); //[object Window]

console。log (person。name + “ is playing at ” + this。tournament);

// T。 Woods is playing at undefined

// P。 Mickelson is playing at undefined

})

}

​}

user。clickHandler(); // “this”指向? [Window物件]

裡面的匿名函式不能訪問外部函式的

這個

,所以它繫結到全域性視窗物件,當

嚴格

模式不被使用時。

在匿名函式中維護

這個

方法的解決方案:

為了解決

這個

在傳遞給

forEach

方法的匿名函式中使用

這個

問題,我們在JavaScript中使用一個常見的做法,並在我們輸入forEach 方法之前將

this

儲存起來:

var user = {

tournament:“The Masters”,

data :[

{name:“T。 Woods”, age:37},

{name:“P。 Mickelson”, age:43}

],

clickHandler:function (event) {

//為了捕獲當this指向user物件的值,我們用另一個變數來儲存this,稍後可以用

var theUserObj = this;

this。data。forEach (function (person) {

//不用this。tournament, 我們現在用 theUserObj。tournament

console。log (person。name + “ is playing at ” +

theUserObj。tournament);

})

}

}

user。clickHandler();

// T。 Woods is playing at The Masters

// P。 Mickelson is playing at The Masters

值得注意的是,許多JavaScript開發人員喜歡命名that來儲存this,使用“that”這個詞對我來說非常尷尬,所以我試著將變數命名為描述這個物件“this”的引用,因此我在前面的程式碼中使用了

var theUserObj = this

var that = this; // JavaScript的使用者通常的做法是使用此程式碼。

修復當方法賦值為一個變數時的this

在值逃脫了我們的想象,並繫結到另一個物件,如果我們指定使用的方法

一個變數。讓我們看看如何:

var data = [//這個data資料變數是一個全域性變數

{name:“Samantha”, age:12},

{name:“Alexis”, age:14}

];

var user = {//這個data資料變數是user物件上的屬性

data:[

{name:“T。 Woods”, age:37},

{name:“P。 Mickelson”, age:43}

],

showData:function (event) {

var randomNum = ((Math。random () * 2 | 0) + 1) - 1; //0~1

console。log (this。data[randomNum]。name + “ ” +

this。data[randomNum]。age);//從資料陣列中隨機取出一個:

}

}

//分配user。showData一個變數

var showUserData = user。showData;

//當我們執行showUserData函式, 列印的值是出自全域性data資料變數,不是user裡的

showUserData (); // Samantha 12 (出自全域性data資料變數)

當方法分配給變數時,可以透過使用bind方法專門設定

this

值來解決此問題:

// 繫結showData方法給user物件,就能獲取到user物件的值

var showUserData = user。showData。bind (user);

showUserData (); // P。 Mickelson 43

修復借用方法時

this

讓我們檢查

this

在借用方法的上下文context中的相關性:

// 有兩個物件,其中一個有avg()方法,另一個沒有,我們要借用avg()方法。

var gameController = {

scores:[20, 34, 55, 46, 77],

avgScore:null,​

players :[ {name:“Tommy”, playerID:987, age:23},

{name:“Pau”, playerID:87, age:33} ]

}

var appController = {

scores:[900, 845, 809, 950],​

avgScore:null,

avg:function () {

var sumOfScores = this。scores。reduce(

function(prev,cur,index, array) {

return prev + cur;

});

this。avgScore = sumOfScores / this。scores。length;

}

}

//如果我們執行下面的程式碼,gameController。avgScore屬性將被從appController物件的scores陣列的值設定

// 不執行這段程式碼,因為它只是為了說明; 我們希望appController。avgScore保持空

gameController。avgScore = appController。avg();

avg方法的“this”關鍵字不會引用gameController物件,它將引用appController物件,因為它在appController上被呼叫。

要確保appController。avg () 方法的this指向gameController,可以用

apply ()

方法。

//注意,我們使用的是apply()方法,所以第二個引數必須是陣列。

appController。avg。apply (gameController, gameController。scores);

//該avgScore屬性被成功設定gameController物件上,即使我們從AppController的物件借用了avg()方法

console。log (gameController。avgScore); // 46。4

// appController。avgScore仍然為null; 它沒有更新,只有gameController。avgScore進行了更新

console。log (appController。avgScore); // null

gameController

物件借用的AppController的 avg()方法。appController。avg()方法中的“this”值將被設定為gameController物件,因為我們將gameController物件作為第一個引數傳遞給apply()方法。apply方法中的第一個引數總是明確設定“this”的值。

好,就到這,怎麼樣?你馴服this了嗎?PS:情人節就要到了,你的玫瑰花準備好了嗎?

標簽: 物件  user  person  var  方法