Object.create淺析

SaltAir發表於2018-12-06

Object.created是ES5出生的,官方給的介紹是 Object.create()方法建立一個新物件,使用現有的物件來提供新建立的物件的__proto__ 對於__proto__的解釋請戳 這裡 簡單理解,Object.created是一個繼承方法,**返回一個新物件,帶著指定的原型物件和屬性。**例如:

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// "My name is Matthew. Am I human? true"
複製程式碼

語法

官方特地介紹了語法:

Object.create(proto, [propertiesObject])

引數: proto :新建立物件的原型物件。 propertiesObject :可選。如果沒有指定為 undefined,則是要新增到新建立物件的可列舉屬性(即其自身定義的屬性,而不是其原型鏈上的列舉屬性)物件的屬性描述符以及相應的屬性名稱。這些屬性對應Object.defineProperties()的第二個引數。如果propertiesObject引數不是 null 或一個物件,則丟擲一個 TypeError 異常。

此處對Object.defineProperties()不多加贅述,介紹一個概念和用法不應該引入其他難以理解的部分和概念,Object.defineProperties()後續我可能會追加一篇文章專門介紹

常見用法

我們平時常用的就是使用一個引數的情況,可以實現簡單的繼承,例如上面的例子,包含一個引數,直接將某一個物件當作原型物件,使新的物件去繼承。

包含第二個引數的用法

包含第二個引數的用法可能在大多數場景下用的比較少,尤其是業務程式碼中會比較少的用到。但是深入探究第二個引數的用法之後,可以給我們帶來很多便利: 舉例

  1. 建立一個原型為null的空物件
let o
o = Object.create(null)
//得到的結果是這個物件No properties
複製程式碼
  1. 建立一個空物件的原理
o = {};
// 以字面量方式建立的空物件就相當於:
o = Object.create(Object.prototype);
複製程式碼
  1. 多個引數
o = Object.create(Object.prototype, {
  // foo會成為所建立物件的資料屬性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar會成為所建立物件的訪問器屬性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});
複製程式碼

結果
4. 建立一個以另一個空物件為原型,且擁有一個屬性的物件

o = Object.create({}, { p: { value: 42 } })

// 省略了的屬性特性預設為false,所以屬性p是不可寫,不可列舉,不可配置的:
o.p = 24
o.p
//42

o.q = 12
for (var prop in o) {
   console.log(prop)
}
//"q"

delete o.p
//false
複製程式碼
  1. 建立一個可寫的,可列舉的,可配置的屬性
o2 = Object.create({}, {
  p: {
    value: 42, 
    writable: true,
    enumerable: true,
    configurable: true 
  } 
});
複製程式碼

現在做有關繼承相關的操作,大家更習慣用ES6的`Class xx extends xxx`來實現繼承,在ES6出現之前,用的大多是call,apply,bind和Object.create;但是MDN上明確說了Class的語法:

ECMAScript 2015 中引入的 JavaScript 類實質上是 JavaScript 現有的基於原型的繼承的語法糖。類語法不會為JavaScript引入新的物件導向的繼承模型。

所以最終我們寫的程式碼底層仍然是基於原型來進行繼承的。雖然ES6裡面涉及到的關鍵字,例如:super、constructor、extends、static和java中類裡面涉及到的關鍵字相同,但是實際上是完全不同的,不能用看待java中類的眼光去看待JavaScript中的'Class',但是針對Class的用法和基於prototype實現的繼承或宣告一個'類'在底層其實是相同的。MDN文件後面並沒有polyfill相關的程式碼,我個人的想法是在一定程度上,polyfill是沒有必要的存在,這兩種寫法涉及到的底層是相同的,只不過區別在於使用的瀏覽器是否能正確識別Class一系列的關鍵字而已。 如果有興趣還可以去試一下用typeof 操作符看一下class宣告的“類”的具體型別

class.png

本來是想說一下Object.create的用法和我們平時沒注意到的點……結果扯到了ES6中的class上~ 下個周打算寫一下symbol的使用?