Object.create(null) 和 {} 的區別

tomastong發表於2018-12-07

這兩天在看vue的原始碼,發現作者定義對映字典的時候,喜歡用Object.create(null),而不是直接定義一個物件字面量,那麼兩者有什麼區別呢,又存在什麼業務場景呢

   let m = Object.create(null);
   let n = {};
   
   // 猜測下,m和n有什麼區別呢
   console.log(m);
   console.log(n);
複製程式碼

Object.create(null) 和 {} 的區別

就是一個純粹的空物件

Object.create(null) 和 {} 的區別

繼承了物件原型的空物件,也就是說存在n.toString();

Object.create(proto, [propertiesObject]) 方法

  • proto 新建物件的原型物件
  • propertiesObject 可選,新增到新建物件的屬性(不是原型鏈的屬性),預設可列舉,引數對應Object.defineProperties的第二個引數

{} 相當於 Object.create(Object.ptototype), 現在你明白他們之間的區別了吧

for...in可以遍歷n的可列舉屬性(功能同Object.keys())

這裡要謝謝@IZumi提醒(試了一下發現沒有列印toString\get\set方法屬性,後來才發現混淆了兩個概念,就是__proto__和prototype)

修正之後如下:

   function A() {
       console.log('我是函式A')
   };
   // 定義原型物件
   let protoObj = {};
   protoObj = Object.defineProperty(protoObj, 'a', {
       enumerable: false,   // 不可列舉
       value: 'a屬性'
   });
   protoObj.b = 'b屬性';
   
   A.prototype = protoObj;
   let a = new A();
   //  這裡的let...in 和Object.keys()類似,都能訪問可列舉屬性
   for (let key in a) {
       console.log(key, a[key]);  // b  b屬性 
   }
複製程式碼

接下來判斷下上文截圖上提到的n.__proto__中的toString()方法是否列舉

    // getOwnPropertyNames方法列印所有屬性,可列舉的和不可列舉的
    let res1 = Object.getOwnPropertyNames(n.__proto__);
    console.log(res1);
    // 結果如下
    // ["constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "toLocaleString"]
    
    // 上文我們知道Object.keys()只能訪問可列舉屬性
    let res2 = Object.keys(n.__proto__);
    console.log(res2);
    // []
複製程式碼

結果顯而易見,繼承Object.prototype而來的父方法,是客觀存在、可呼叫的,不可列舉、不可遍歷的

另外這個__proto__屬性, 是一個物件的隱式原型,指向構造該物件的建構函式的原型

   console.log(a.__proto__ === A.prototype)  // true
複製程式碼

這個以後再做深入討論。

ps. 剛開始用md語法寫文件,用的還不是很熟練,以後有更多跟有內涵的文章給大家分享,加油加油!

相關文章