Javascript|Getter 與 Setter 完整解析

更新日期: 2024 年 4 月 10 日

基本概念

在開始談論 setter 和 getter 之前,先來瞭解一下 JavaScript 物件裡面的兩種特別的屬性,一種叫做「數據屬性」(Data properties),另一種叫做「訪問器屬性」(Accessor properties)。

這兩種屬性讓我們在處理物件和數據時可以有更多的靈活性和選擇。

數據屬性

數據屬性直接關聯到物件的值。當我們談論在物件上設置和獲取值時,我們通常指的是操作數據屬性。

舉例來說,當我們這樣定義一個物件時:

let person = {
  name: '徐培鈞'
}

我們創建了一個名為 name 的數據屬性,它包含了字符串值 “John”。

訪問器屬性

訪問器屬性則不直接包含一個值,而是包含一對 getter 和 setter 函數。

當訪問一個訪問器屬性時,會調用其 getter 函數來返回值;當對其賦值時,會調用其 setter 函數處理這個值。

let person = {

    // 這是一個數據屬性
    name: '徐培鈞',

    // 這是一個訪問器屬性
    get name() {
        return this._name;
    }

}

什麼是 Getter

Getter 是一種讓我們可以「取得」物件屬性值的方法。

當我們從物件獲取某個屬性的值時,實際上是在執行 getter 函數。這允許我們在返回屬性值之前進行額外的處理或計算。

例如,如果我們想要在獲取某個人的全名時自動將名和姓合併,我們可以這樣定義 getter:

let person = {

    firstName: "",
    lastName: "培鈞",
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }

}

console.log(person.fullName);

// 回傳:
// 徐培鈞

什麼是 Setter

Setter 允許我們在「賦值」物件屬性值時,執行一些自定義操作。

這意味著當我們嘗試設置某個屬性的值時,實際上是在調用 setter 函數。

比如,如果我們希望能夠一次性重新設定某人的姓氏 + 名字,並透過空格作為判斷標準,我們可以使用 setter 進行以下設定:

let person = {

    firstName: "",
    lastName: "培鈞",
    set fullName(name) {
        [this.firstName, this.lastName] = name.split(" ");
    }
};

person.fullName = "王 小名";

console.log(person.firstName); 

// 回傳:
// 王

console.log(person.lastName); 

// 回傳:
// 小名

當你看到這段代碼時,可能會好奇為什麼設定 person.fullName = “王 小名”; 能夠使 setter 函數運作,看起來好像我們沒有直接呼叫方法一樣。

其實,當我們這樣賦值的時候,背後其實是用了一種特別的設計,叫做「訪問器屬性」。

這裡的 fullName 不是一個普通的屬性,它是個特殊的存在,有點像是個隱形的門,當你試圖給它賦值(也就是把名字放進去)的時候,它會自動觸發背後的一段代碼(這就是我們的 set fullName 那段)。

因為當你進行 person.fullName = “王 小名”; 這個動作時,你其實是在告訴程式:「嘿,我要設定 fullName 了」。

程式看到這個動作後,就會說:「好的,我知道你要設定 fullName,你給我的值是什麼?」

然後你給它的 “王 小名” 就自動被當作參數送進去了。

通常我們呼叫一個方法時,確實是像 methodName(value) 這樣子。

但在 JavaScript 裡,設計了一種訪問器特殊的方式讓我們操作物件的屬性,就像在操作普通屬性一樣簡單。

當你設定一個有 setter 的屬性時,就像是在用這種特別簡單的方式呼叫方法,只是看起來你只是在賦值而已。

這讓代碼讀起來更自然,也讓我們可以在設定屬性值時做一些額外的操作,比如我們的範例裡面把全名分開成姓和名。

所以,這種看起來像是直接賦值的動作其實背後有很多事情在發生,這就是 JavaScript 讓我們用更直觀的方式來控制物件屬性的巧妙之處。

Setter 和 Getter 的優點

在 JavaScript 裡面,setter 和 getter 不只是幫我們存取物件裡頭的東西那麼簡單;它們還能讓我們的代碼更加整齊、好讀、好維護,而且還能保護我們的資料不被亂改。

以下是使用它們的一些關鍵優點:

當然,讓我們透過一些簡單的示範代碼來解釋 setter 和 getter 的四大優點。

封裝性

封裝性,想像你有個寶箱,裡面放著一些秘密的東西。

你不希望任何人都能隨便打開這個箱子看到或改動裡面的東西,所以你給寶箱上了把鎖。

現在,如果有人想要放點什麼進去或從裡面取點什麼出來,他們必須要通過你給的鑰匙(也就是一些特定的方法或手段)才行。

在程式裡的「物件」也是類似的概念。一個物件會有一些內部的資料(像是寶箱裡的秘密東西),這些資料我們不希望被外界隨便訪問或修改。

所以,我們會用封裝性這個特性「上鎖」,隱藏這些內部資料。

然後,我們提供一些特定的方法(也就是「鑰匙」),讓外界在需要的時候可以通過這些方法來「開鎖」,也就是安全地讀取或更新這些資料,而不是直接去碰那些隱藏起來的內部狀態。

這麼做的好處是可以確保物件的資料安全,避免被不當操作,也讓物件的使用更加安全、清晰和方便。

class Person {

  constructor(name) {
    this._name = name; // 使用 _ 表示這是一個私有屬性
  }

  // 提供 getter 方法來訪問名字
  get name() {
    return this._name;
  }

  // 提供 setter 方法來安全設置名字
  set name(value) {
    this._name = value.trim(); // 移除名字兩側的空格
  }
}

let person = new Person(" John ");
console.log(person.name); // John,自動移除了空格

延伸閱讀:類別(class)是什麼?|JavaScript 初學者筆記(4)

可讀性和可維護性

通過明確的方法(getter 和 setter)來訪問屬性,代碼變得更易於讀取和維護。

class Circle {

  constructor(radius) {
    this._radius = radius;
  }

  // 使用 getter 來提供半徑
  get radius() {
    return this._radius;
  }

  // 使用 setter 來設置半徑,同時確保半徑永遠是正數
  set radius(value) {
    this._radius = Math.abs(value);
  }

  // 額外的方法來顯示圓的面積
  get area() {
    return Math.PI * this._radius * this._radius;
  }
}

let circle = new Circle(-5);
console.log(circle.radius); // 5,自動轉為正數
console.log(circle.area); // 顯示圓的面積

驗證和控制

在設置屬性值時可以添加額外的驗證邏輯,確保數據的正確性。

class User {

  constructor(email) {
    this._email = email;
  }

  get email() {
    return this._email;
  }

  set email(value) {
    if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      this._email = value;
    } else {
      console.log("Invalid email address.");
    }
  }
}

let user = new User("example@example.com");

console.log(user.email); 

// 回傳:
// example@example.com

user.email = "wrong-email"; 

// 回傳:
// Invalid email address.

延伸閱讀:深入探索 JavaScript 中的 RegExp:創建動態規則運算式

實現計算屬性

計算屬性允許你在訪問屬性時動態計算值。

class Rectangle {

    constructor(width, height) {
        this._width = width;
        this._height = height;
    }

    get area() {
        return this._width * this._height;
    }
}

let rectangle = new Rectangle(5, 10);
console.log(rectangle.area); // 50,動態計算面積

結論

本文介紹了 JavaScript 中 setter 和 getter 的概念,以及它們與數據屬性和訪問器屬性的關係。

這些特性為開發者提供了強大的工具來增強代碼的封裝性、靈活性和安全性,同時也使代碼變得更加可讀和易於維護。

Similar Posts