iT邦幫忙

2022 iThome 鐵人賽

DAY 15
2

前言

這篇要介紹的是用 Object.create() 這個函式。


語法

Object.create(proto[, propertiesObject])

帶入的兩個參數分別為:

  1. proto,傳入的該物件會變成新物件的 prototype 物件,如果不是 null 或是物件,會拋出錯誤。
  2. propertiesObject,是一個物件,裡面放要加到新增物件的屬性或是可以修改新增物件的屬性值

帶入兩個參數後,會從指定的 prototype 物件建立一個新物件,並加上第二個參數的屬性,那這樣產生新物件可以做什麼?我們繼續看下去吧!


從範例認識 Object.create()

讀者可以先閱讀以下程式碼:

function Shape(x, y) {
  this.x = x;
  this.y = y;
}

Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
};

function Rectangle(...args) {
  Shape.apply(this, args);
}

const properties = {
  name: {
    value: "長方形",
    enumerable: true,
    writable: true,
  },
  getPos: {
    value: function () {
      console.log(`Current Position: ${this.x}, ${this.y}.`);
    },
  },
};

// Rectangle 繼承 Shape 物件
Rectangle.prototype = Object.create(Shape.prototype, properties);
Rectangle.prototype.constructor = Rectangle;

const rect = new Rectangle(0, 0);

閱讀完程式碼後,我們把一些函式的相關資訊都印出來看看:

Rectangle 的原型物件變成了 Shape 物件加上 Object.create() 第二個參數屬性後的物件,其它屬性則和一般函式沒什麼不一樣。

Shape 函式也沒什麼不一樣。

繼續觀察 Rectangle 的原型物件,它的 __proto__ 為 Shape 函式的原型物件,但它的建構子指向 Rectangle。

而 new 關鍵字用來建立實體,像範例中的 react 物件。

我們將上面的內容畫成圖片來看:

可以看到 Object.create() 產生的物件在左下角(有最長黑色說明文字的那個),它的兩個屬性指向可以解釋以下兩行程式碼:

Rectangle.prototype = Object.create(Shape.prototype, properties);
Rectangle.prototype.constructor = Rectangle;

第一行就是透過 Object.create() 產生物件,它的 __proto__ 是指向 Shape.prototype,所以這樣 Rectangle 函式產生的物件,也能透過原型鏈去取到 Shape 的函式。

第二行則是將 Object.create() 產生的物件的建構子,指向繼承的函式,所以可以看到左下角的物件 constructor 指向 Rectangle 函式。

透過這些分析,我們瞭解了 Object.create() 在程式背後實踐繼承的原理,也瞭解了為什麼透過上面兩行程式就可以讓 Rectangle 函式能繼承到 Shape 函式的一些屬性和方法。


其他範例 2025/06/06 更新

來看些特別的例子,如果 Object.create() 的第一個參數是傳入 Object.prototype 的話,obj 變數就像是宣告了空物件一樣,也會有物件的一些屬性方法。

但如果傳入 null,就不會有物件的任何屬性方法,因此透過這個方法建立的物件,不會因為命名衝突的情況去意外覆蓋掉原本原型鏈上的屬性,特別適合用來當作純資料的容器。

const obj = Object.create(Object.prototype);
// 等同於: const obj = {};

const empty = Object.create(null);
// 背後的原型鏈為 obj --> null

// 使用 {} 建立的物件,可能會影響原本的屬性,以下範例中,toString 原本是一個函式,被改掉了
const map = {};
console.log(map["toString"]); // ƒ toString() { [native code] }
map["toString"] = 123;
console.log(map["toString"]); // 123

// 使用 Object.create(null),不會影響
const map2 = Object.create(null);
console.log(map2["toString"]); // undefined
map2["toString"] = 123;
console.log(map2["toString"]); // 123


練習題

文章的最後來個小練習題吧,這題是來自於 臉書的一篇貼文,若前面的內容有了解的話,這篇答案就很輕易知道囉~

const employee = {
  company: 'levelhunt'
};

const empl = Object.create(employee);
delete empl.company;
console.log(empl.company); // ?

練習解答

雖然刪除了 empl 物件本身的屬性,但它的 prototype 實際上不會影響,所以還是會印出 'levelhunt'。


參考資料 & 推薦閱讀

Object.create()

JavaScript 核心觀念(51)- 繼承與原型鍊 - 使用 Object.create 建立多層繼承


上一篇
Day14-ES6 Class 繼承
下一篇
Day16-instanceof 介紹
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言