JavaScript 物件基礎

在本文中,我們將探討 JavaScript 物件的基本語法,並回顧本課程中已經遇到的一些 JavaScript 特性,重申您已經處理過的許多特性都是物件。

預備知識 瞭解 HTMLCSS 基礎,熟悉前面課程中介紹的 JavaScript 基礎。
學習成果
  • 您需要了解在 JavaScript 中,大多數事物都是物件,而且您可能每次接觸 JavaScript 時都使用了物件。
  • 基本語法:物件字面量、屬性和方法,在物件中巢狀物件和陣列。
  • 使用建構函式建立新物件。
  • 物件作用域和 this
  • 訪問屬性和方法 — 方括號和點語法。

物件基礎

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

首先,在本地複製我們的 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: function () 寫成 bio()。像這樣

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
const person = {
  // …
  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!");

總結

現在您應該對如何在 JavaScript 中使用物件有了一個很好的理解——包括建立您自己的簡單物件。您還應該意識到物件作為儲存相關資料和功能的結構非常有用——如果您嘗試將我們 person 物件中的所有屬性和方法作為獨立的變數和函式進行跟蹤,那將是低效且令人沮喪的,而且我們還會面臨與其他同名變數和函式衝突的風險。物件讓我們能夠將資訊安全地鎖定在自己的包中,免受傷害。

在下一篇文章中,我們將為您提供一些測試,您可以使用它們來檢查您對所有這些資訊的理解和掌握程度。