物件通常用來表示真實世界的實體,例如使用者、訂單等
let user = {
name: "John",
age: 30
};
而在真實世界中,使用者可以執行動作:從購物車中選擇商品、登入、登出等。
在 JavaScript 中,動作以屬性中的函式表示。
方法範例
首先,我們來教導 使用者
打招呼
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
在這裡,我們只使用函式表達式來建立一個函式,並將其指定給物件的屬性 user.sayHi
。
然後我們可以將其呼叫為 user.sayHi()
。使用者現在可以說話了!
屬於物件的函式稱為其方法。
因此,這裡我們有物件 user
的方法 sayHi
。
當然,我們可以使用預先宣告的函式作為方法,如下所示
let user = {
// ...
};
// first, declare
function sayHi() {
alert("Hello!");
}
// then add as a method
user.sayHi = sayHi;
user.sayHi(); // Hello!
當我們使用物件來表示實體來撰寫程式碼時,這稱為物件導向程式設計,簡稱「OOP」。
OOP 是一項龐大的事物,本身就是一門有趣的科學。如何選擇正確的實體?如何組織它們之間的互動?那是架構,而且有許多關於該主題的精彩書籍,例如 E. Gamma、R. Helm、R. Johnson、J. Vissides 所著的「設計模式:可重複使用物件導向軟體的要素」,或 G. Booch 所著的「物件導向分析與設計與應用」等。
方法簡寫
在物件文字中,方法有更簡短的語法
// these objects do the same
user = {
sayHi: function() {
alert("Hello");
}
};
// method shorthand looks better, right?
user = {
sayHi() { // same as "sayHi: function(){...}"
alert("Hello");
}
};
如示範所示,我們可以省略 "function"
,只寫 sayHi()
。
說實話,這些符號並非完全相同。與物件繼承相關的細微差異(稍後會介紹),但目前並不重要。在幾乎所有情況下,較簡短的語法較為優先。
方法中的「this」
物件方法通常需要存取儲存在物件中的資訊才能執行其工作。
例如,user.sayHi()
內的程式碼可能需要 user
的名稱。
為了存取物件,方法可以使用 this
關鍵字。
this
的值是「點之前」的物件,用於呼叫方法的物件。
例如
let user = {
name: "John",
age: 30,
sayHi() {
// "this" is the "current object"
alert(this.name);
}
};
user.sayHi(); // John
在執行 user.sayHi()
期間,this
的值將為 user
。
技術上,也可以不透過 this
存取物件,而是透過外部變數來參照物件
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "user" instead of "this"
}
};
…但這種程式碼不可靠。如果我們決定將 user
複製到另一個變數,例如 admin = user
,並用其他內容覆寫 user
,那麼它將存取錯誤的物件。
以下示範
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // leads to an error
}
};
let admin = user;
user = null; // overwrite to make things obvious
admin.sayHi(); // TypeError: Cannot read property 'name' of null
如果我們在 alert
內部使用 this.name
而不是 user.name
,那麼程式碼將會執行。
「this」未繫結
在 JavaScript 中,關鍵字 this
的行為與大多數其他程式語言不同。它可以在任何函式中使用,即使它不是物件的方法。
以下範例沒有語法錯誤
function sayHi() {
alert( this.name );
}
this
的值是在執行期間評估的,具體取決於上下文。
例如,這裡將同一個函式指定給兩個不同的物件,且在呼叫時有不同的「this」
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// use the same function in two objects
user.f = sayHi;
admin.f = sayHi;
// these calls have different this
// "this" inside the function is the object "before the dot"
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (dot or square brackets access the method – doesn't matter)
規則很簡單:如果呼叫 obj.f()
,則在呼叫 f
時,this
為 obj
。因此在上方的範例中,this
為 user
或 admin
。
this == undefined
我們甚至可以在完全沒有物件的情況下呼叫函式
function sayHi() {
alert(this);
}
sayHi(); // undefined
在這種情況下,this
在嚴格模式中為 undefined
。如果我們嘗試存取 this.name
,將會產生錯誤。
在非嚴格模式中,此情況下 this
的值將為全域物件(瀏覽器中的 window
,我們將在本章節稍後 全域物件 中說明)。這是一種歷史行為,"use strict"
已修正此行為。
通常此類呼叫為程式設計錯誤。如果函式內有 this
,則表示預期在物件內容中呼叫它。
this
的後果如果您來自另一種程式設計語言,則您可能習慣「繫結 this
」的概念,其中在物件中定義的方法總是讓 this
參照該物件。
在 JavaScript 中,this
為「自由」的,其值會在呼叫時評估,且不取決於方法宣告的位置,而是取決於「點之前」的物件為何。
在執行階段評估 this
的概念有優點也有缺點。一方面,函式可以重複使用於不同的物件。另一方面,更大的彈性會產生更多錯誤的可能性。
我們的立場並非判斷此語言設計決策的好壞。我們將了解如何使用它,如何獲得好處並避免問題。
箭頭函式沒有「this」
箭頭函式很特別:它們沒有「自己的」this
。如果我們從此類函式參照 this
,則會從外部「一般」函式取得。
例如,這裡的 arrow()
使用來自外部 user.sayHi()
方法的 this
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
這是箭頭函式的特殊功能,當我們實際上不想要有獨立的 this
,而是想要從外部內容取得 this
時,這項功能會很有用。稍後在本章節 重新檢視箭頭函式 中,我們將更深入探討箭頭函式。
摘要
- 儲存在物件屬性中的函式稱為「方法」。
- 方法允許物件「作用」類似於
object.doSomething()
。 - 方法可以將物件參照為
this
。
this
的值在執行階段定義。
- 宣告函式時,它可以使用
this
,但this
在函式呼叫之前沒有值。 - 函式可以在物件之間複製。
- 在「方法」語法中呼叫函式時:
object.method()
,呼叫期間this
的值為object
。
請注意,箭頭函式很特別:它們沒有 this
。在箭頭函式內存取 this
時,會從外部取得。
留言
<code>
標籤,若要插入多行,請將它們包覆在<pre>
標籤中,若要插入超過 10 行,請使用沙盒 (plnkr、jsbin、codepen…)