博客 / 詳情

返回

js沒有原型鏈的對象

一次偶然操作,處理http接口返回的數據時,判斷這個對象是否有某個key,用了hasOwnProperty

const { data } = await getDataFromAjax()
console.log(data.hasOwnProperty('key1'))

結果報錯,data.hasOwnProperty undefined

這個錯誤解決不是重點,用inObject.hasOwn都可以。
重點是無原型對象,這個平常很少涉及的地方。

無原型對象的表現

const pureObj = Object.create(null);
pureObj.key = "value";
console.log(pureObj.toString); // undefined
console.log(Object.getPrototypeOf(pureObj));// null

用瀏覽器控制枱查看展開,是沒有那一連串眼花繚亂的[[Prototype]]一直到原型鏈根部null的。

所以包括toString,valueOf和hasOwnProperty等繼承自Object的方法屬性全部不可用

以下是瀏覽器JavaScript環境中生成無原型對象的場景及操作注意事項的整理:


⚙️ 一、生成無原型對象的主要方式

  1. Object.create(null)

    • 原理:顯式創建原型鏈指向 null 的對象,不繼承 Object.prototype 的任何方法(如 toStringhasOwnProperty)。
    • 特點

      • Object.getPrototypeOf(obj) === null
      • obj instanceof Object === false
      • 適用於純淨鍵值存儲,避免原型污染。
  2. 顯式修改原型鏈

    • Object.setPrototypeOf(obj, null)
      將現有對象的原型設為 null,效果類似 Object.create(null),但性能較差(可能觸發引擎優化失效),不推薦高頻使用。
  3. 特殊場景下的無原型對象

    • JSON解析
      若JSON字符串包含 __proto__: null(如 '{"__proto__": null, "key": "value"}'),則 JSON.parse() 生成的對象無原型鏈。

      開頭出現的問題就是因為除了node外的大部分http服務(java/go)返回的都是純數據json,即JSON.parse返回對象沒有原型鏈繼承

    • Proxy代理
      通過 getPrototypeOf 陷阱返回 null,可創建無原型的代理對象。
  4. 對象字面量聲明

    • 使用 { __proto__: null } 可直接創建無原型對象(ES6+),但 __proto__ 是非標準屬性,建議優先用 Object.create(null)

⚠️ 二、無原型對象的操作注意事項

1. 方法調用限制

  • 問題:無法調用 Object.prototype 上的方法(如 obj.toString()obj.hasOwnProperty())。
  • 解決方案

    • 顯式借用方法:

      Object.prototype.hasOwnProperty.call(obj, "key"); // ✅
    • 避免直接調用 obj.toString()(會拋出 TypeError)。

2. 類型轉換問題

  • 隱式轉換失敗

    • 字符串拼接("" + obj)或算術運算(如 obj * 1)會因缺少 valueOf/toString 而報錯。
  • 解決方案

    • 顯式定義 toString 方法:

      obj.toString = () => "[Custom Object]";
      console.log("" + obj); // "[Custom Object]"
    • 避免依賴默認的類型轉換邏輯。

3. 對象比較

  • 引用比較

    • ===== 比較兩個無原型對象時,始終返回 false(比較的是內存地址)。
  • 內容比較

    • 需使用深度比較函數或 JSON.stringify()

      const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2); // 注意屬性順序影響結果
    • 深度比較函數需遞歸遍歷屬性,並處理無原型對象的特殊方法調用。

4. 遍歷與擴展

  • 遍歷更安全

    • for...in 循環不會遍歷原型鏈屬性(因無原型),無需 hasOwnProperty 過濾。
    • 推薦使用 Object.keys()Object.entries() 等靜態方法。
  • 擴展運算符問題

    • { ...pureObj } 會生成繼承 Object.prototype 的新對象,丟失無原型特性。

5. JSON序列化

  • 序列化正常JSON.stringify() 可正常處理無原型對象的屬性。
  • 反序列化陷阱

    • JSON.parse(JSON.stringify(obj)) 默認生成帶原型的對象(除非輸入字符串含 __proto__: null)。

6. 第三方庫兼容性

  • 潛在問題:某些庫依賴對象的原型方法(如 obj.constructorobj.toString())。
  • 解決方案

    • 手動恢復基礎原型鏈(謹慎使用):

      const safeObj = Object.assign(Object.create(Object.prototype), pureObj);
    • 或確保庫支持無原型對象。

📊 三、無原型對象 vs 普通對象

特性 無原型對象 (Object.create(null)) 普通對象 ({})
原型鏈 null Object.prototype
繼承方法 無 (toString 等) 有 (toString, hasOwnProperty 等)
instanceof Object false true
遍歷屬性 僅自身屬性(無需過濾) 需用 hasOwnProperty 過濾原型屬性
擴展運算符 生成帶原型的對象 保留原型

可以看到,無原型對象在純數據處理方面的優勢,節省內存,無需考慮原型污染。
不過涉及原型方法使用,比如最常見的js隱式轉換就要注意了。

user avatar sunhengzhe 頭像 79px 頭像 user_p5fejtxs 頭像 lidalei 頭像 dashan_5c230d1ae1f9e 頭像 xiaohuoche 頭像 harryfyodor 頭像 alogy 頭像 xiaodaigua_ray 頭像 delai 頭像 liyuan_5aa3282cf3353 頭像 wuwhs 頭像
23 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.