JavaScript 物件基礎

在本文中,我們將探討基本的 JavaScript 物件語法,並回顧本課程中我們之前已經看到的一些 JavaScript 功能,重申一個事實,即你已經使用過的許多功能都是物件。

先決條件 對 HTML 和 CSS 的基本瞭解,熟悉 JavaScript 基礎知識 (參見 第一步構建塊)。
目標 瞭解在 JavaScript 中使用物件的 基礎知識:建立物件、訪問和修改物件屬性以及使用建構函式。

物件基礎

物件是相關資料和/或功能的集合。它們通常包含多個變數和函式 (在物件內部稱為屬性和方法)。讓我們透過一個例子來理解它們的樣子。

首先,在本地建立一個 oojs.html 檔案的副本。這個檔案包含很少的內容——一個 <script> 元素,供我們編寫原始碼。我們將以此為基礎來探索基本的 物件語法。在處理此示例時,你應該開啟你的 開發者工具 JavaScript 控制檯,並準備輸入一些命令。

與 JavaScript 中的許多事物一樣,建立物件通常從定義和初始化變數開始。嘗試在你檔案中的 JavaScript 程式碼下方輸入以下行,然後儲存並重新整理

js
const person = {};

現在開啟瀏覽器的 JavaScript 控制檯,在其中輸入 person 並按 Enter/Return。你應該會得到類似於以下幾行之一的結果

[object Object]
Object { }
{ }

恭喜,你剛剛建立了你的第一個物件。任務完成!但這只是一個空物件,所以我們不能真正對它做太多事情。讓我們更新檔案中的 JavaScript 物件,使其看起來像這樣

js
const person = {
  name: ["Bob", "Smith"],
  age: 32,
  bio: function () {
    console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
  },
  introduceSelf: function () {
    console.log(`Hi! I'm ${this.name[0]}.`);
  },
};

儲存並重新整理後,嘗試在瀏覽器開發者工具的 JavaScript 控制檯中輸入以下內容

js
person.name;
person.name[0];
person.age;
person.bio();
// "Bob Smith is 32 years old."
person.introduceSelf();
// "Hi! I'm Bob."

你現在已經在你的物件中包含了一些資料和功能,現在可以使用一些簡單易懂的語法來訪問它們了!

那麼這裡究竟發生了什麼?好吧,物件由多個成員組成,每個成員都有一個名稱 (例如,上面的 nameage),以及一個值 (例如,['Bob', 'Smith']32)。每個名稱/值對必須用逗號隔開,每個名稱和值用冒號隔開。語法始終遵循以下模式

js
const objectName = {
  member1Name: member1Value,
  member2Name: member2Value,
  member3Name: member3Value,
};

物件成員的值幾乎可以是任何東西——在我們的 person 物件中,我們有一個數字、一個數組和兩個函式。前兩個專案是資料項,被稱為物件的 屬性。最後兩個專案是允許物件對該資料執行某些操作的函式,被稱為物件的 方法

當物件的成員是函式時,有一個更簡單的語法。我們可以寫 bio(),而不是 bio: function ()。就像這樣

js
const person = {
  name: ["Bob", "Smith"],
  age: 32,
  bio() {
    console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
  },
  introduceSelf() {
    console.log(`Hi! I'm ${this.name[0]}.`);
  },
};

從現在開始,我們將使用這種更短的語法。

像這樣的物件被稱為 物件字面量——我們已經按照我們建立它的方式直接寫出了物件的內容。這與從類例項化的物件不同,我們將在後面討論。

當你想以某種方式傳遞一系列結構化的、相關的資料項時,例如將請求傳送到伺服器以將其放入資料庫,使用物件字面量建立物件非常普遍。傳送單個物件比單獨傳送多個專案效率高得多,並且當你想要透過名稱標識單個專案時,它比陣列更容易處理。

點表示法

在上面,你使用 點表示法訪問了物件的屬性和方法。物件名稱 (person) 充當 名稱空間——必須先輸入它才能訪問物件內部的任何內容。接下來,你寫一個點,然後寫你想訪問的專案——這可以是簡單屬性的名稱、陣列屬性的專案,或者對物件方法的呼叫,例如

js
person.age;
person.bio();

物件作為物件屬性

物件屬性本身可以是一個物件。例如,嘗試將 name 成員從

js
const person = {
  name: ["Bob", "Smith"],
};

改為

js
const person = {
  name: {
    first: "Bob",
    last: "Smith",
  },
  // …
};

要訪問這些專案,你只需要使用另一個點將額外的步驟連結到末尾。在 JS 控制檯中嘗試以下操作

js
person.name.first;
person.name.last;

如果你這樣做,你還需要遍歷你的方法程式碼,並將所有出現的

js
name[0];
name[1];

改為

js
name.first;
name.last;

否則,你的方法將不再工作。

方括號表示法

方括號表示法提供了一種訪問物件屬性的替代方法。與其使用 點表示法 這樣

js
person.age;
person.name.first;

你可以改用方括號

js
person["age"];
person["name"]["first"];

這看起來非常類似於你如何訪問陣列中的專案,並且它基本上是相同的——與其使用索引號來選擇專案,你使用的是與每個成員的值關聯的名稱。難怪物件有時被稱為 關聯陣列——它們以與陣列將數字對映到值相同的方式將字串對映到值。

點表示法通常比方括號表示法更受歡迎,因為它更簡潔,更易於閱讀。但是,在某些情況下,你必須使用方括號。例如,如果物件屬性名儲存在變數中,那麼你不能使用點表示法來訪問該值,但可以使用方括號表示法來訪問該值。

在下面的示例中,logProperty() 函式可以使用 person[propertyName] 來檢索 propertyName 中命名的屬性的值。

js
const person = {
  name: ["Bob", "Smith"],
  age: 32,
};

function logProperty(propertyName) {
  console.log(person[propertyName]);
}

logProperty("name");
// ["Bob", "Smith"]
logProperty("age");
// 32

設定物件成員

到目前為止,我們只關注檢索 (或 獲取) 物件成員——你還可以透過宣告要設定的成員 (使用點表示法或方括號表示法) 來 設定 (更新) 物件成員的值,就像這樣

js
person.age = 45;
person["name"]["last"] = "Cratchit";

嘗試輸入上面的行,然後再次獲取成員以檢視它們是如何改變的,如下所示

js
person.age;
person["name"]["last"];

設定成員不僅僅侷限於更新現有屬性和方法的值;你還可以建立全新的成員。在 JS 控制檯中嘗試以下操作

js
person["eyes"] = "hazel";
person.farewell = function () {
  console.log("Bye everybody!");
};

你現在可以測試你的新成員了

js
person["eyes"];
person.farewell();
// "Bye everybody!"

方括號表示法的一個有用方面是它不僅可以用來動態設定成員值,還可以用來設定成員名。假設我們希望使用者能夠透過在兩個文字輸入框中輸入成員名和值,將其自定義值型別儲存在他們的人員資料中。我們可以這樣獲取這些值

js
const myDataName = nameInput.value;
const myDataValue = nameValue.value;

然後,我們可以將這個新的成員名和值新增到 person 物件中,就像這樣

js
person[myDataName] = myDataValue;

要測試這一點,嘗試將以下幾行新增到你的程式碼中,緊接在 person 物件的結束大括號下方

js
const myDataName = "height";
const myDataValue = "1.75m";
person[myDataName] = myDataValue;

現在嘗試儲存並重新整理,然後在你的文字輸入框中輸入以下內容

js
person.height;

使用上述方法將屬性新增到物件中,無法使用點表示法,點表示法只能接受文字成員名,不能接受指向名稱的變數值。

什麼是“this”?

你可能已經注意到我們在方法中有一些奇怪的地方。例如,看看這個方法

js
introduceSelf() {
  console.log(`Hi! I'm ${this.name[0]}.`);
}

你可能想知道“this”是什麼。“this” 關鍵字通常指的是正在執行程式碼的當前物件。在物件方法的上下文中,this 指的是呼叫該方法的物件。

讓我們用一對簡化的 person 物件來說明我們的意思

js
const person1 = {
  name: "Chris",
  introduceSelf() {
    console.log(`Hi! I'm ${this.name}.`);
  },
};

const person2 = {
  name: "Deepti",
  introduceSelf() {
    console.log(`Hi! I'm ${this.name}.`);
  },
};

在這種情況下,person1.introduceSelf() 輸出“Hi! I'm Chris.”;person2.introduceSelf() 輸出“Hi! I'm Deepti.”。這是因為,當呼叫方法時,this 指的是呼叫該方法的物件,這使得相同的 方法定義可以用於多個物件。

當你手動寫出物件字面量時,這並沒有什麼用處,因為使用物件的名稱 (person1person2) 會產生完全相同的結果,但當我們開始使用 建構函式 從單個物件定義建立多個物件時,它將變得至關重要,而這正是下一節的主題。

介紹建構函式

當你只需要建立一個物件時,使用物件字面量是可以的,但是如果你必須建立多個物件,就像上一節中那樣,它們就非常不合適。我們必須為我們建立的每個物件編寫相同的程式碼,如果我們想要更改物件的某些屬性——比如新增 height 屬性——那麼我們必須記住更新每個物件。

我們希望有一種方法來定義物件的“形狀”——它可以擁有的方法集和屬性——然後建立任意數量的物件,只需更新屬性值不同的部分。

第一個版本只是一個函式

js
function createPerson(name) {
  const obj = {};
  obj.name = name;
  obj.introduceSelf = function () {
    console.log(`Hi! I'm ${this.name}.`);
  };
  return obj;
}

每次我們呼叫此函式時,它都會建立並返回一個新物件。該物件將有兩個成員

  • 一個屬性 name
  • 一個方法 introduceSelf()

請注意,createPerson() 接收一個引數 name 來設定 name 屬性的值,但是使用此函式建立的所有物件 introduceSelf() 方法的值都將相同。這是建立物件的非常常見的模式。

現在我們可以根據需要建立任意數量的物件,重複使用該定義

js
const salva = createPerson("Salva");
salva.introduceSelf();
// "Hi! I'm Salva."

const frankie = createPerson("Frankie");
frankie.introduceSelf();
// "Hi! I'm Frankie."

這工作正常,但有點冗長:我們必須建立一個空物件,對其進行初始化,然後返回它。更好的方法是使用 建構函式。建構函式只是一個使用 new 關鍵字呼叫的函式。當你呼叫建構函式時,它將

  • 建立一個新物件
  • this 繫結到新物件,以便你可以在建構函式程式碼中引用 this
  • 執行建構函式中的程式碼
  • 返回新物件。

建構函式按照慣例以大寫字母開頭,並以它們建立的物件型別命名。因此,我們可以像這樣重寫我們的示例

js
function Person(name) {
  this.name = name;
  this.introduceSelf = function () {
    console.log(`Hi! I'm ${this.name}.`);
  };
}

要將 Person() 作為建構函式呼叫,我們使用 new

js
const salva = new Person("Salva");
salva.introduceSelf();
// "Hi! I'm Salva."

const frankie = new Person("Frankie");
frankie.introduceSelf();
// "Hi! I'm Frankie."

你一直在使用物件

當你一直在瀏覽這些示例時,你可能一直在想你一直在使用的點表示法非常熟悉。這是因為你一直在整個課程中使用它!每次我們處理使用內建瀏覽器 API 或 JavaScript 物件的示例時,我們都在使用物件,因為這些功能正是使用我們在本文中看到的相同型別的物件結構構建的,只是比我們自己的基本自定義示例更復雜。

因此,當你使用像這樣的字串方法時

js
myString.split(",");

你是在使用 String 物件上可用的方法。每次你在程式碼中建立字串時,該字串都會自動建立為 String 的例項,因此它在其中具有多個通用方法和屬性。

當你使用像這樣的行訪問文件物件模型時

js
const myDiv = document.createElement("div");
const myVideo = document.querySelector("video");

您之前使用的是在 Document 物件上可用的方法。每個載入的網頁都會建立一個 Document 例項,稱為 document,它表示整個頁面的結構、內容和其他功能,例如其 URL。同樣,這意味著它具有在其上可用的多個常用方法和屬性。

幾乎所有其他內建物件或 API 都是如此 — ArrayMath 等等。

請注意,內建物件和 API 並不總是自動建立物件例項。例如,通知 API — 允許現代瀏覽器觸發系統通知 — 要求您使用每個要觸發的通知的建構函式例項化一個新的物件例項。嘗試將以下內容輸入您的 JavaScript 控制檯

js
const myNotification = new Notification("Hello!");

測試你的技能!

您已到達本文的結尾,但您還記得最重要的資訊嗎?您可以在繼續之前找到一些進一步的測試來驗證您是否已保留這些資訊 — 請參見 測試您的技能:物件基礎

總結

恭喜您,您已完成我們的第一篇 JS 物件文章 — 您現在應該對如何在 JavaScript 中使用物件有了很好的瞭解 — 包括建立您自己的簡單物件。您還應該認識到,物件作為儲存相關資料和功能的結構非常有用 — 如果您嘗試將我們 person 物件中的所有屬性和方法作為單獨的變數和函式進行跟蹤,那將非常低效且令人沮喪,而且我們還將面臨與其他具有相同名稱的變數和函式衝突的風險。物件允許我們將資訊安全地鎖定在它們自己的包中,遠離危險。

在下一篇文章中,我們將研究 **原型**,它是 JavaScript 允許物件從其他物件繼承屬性的基本方式。