1. 程式人生 > 實用技巧 >利用Object.freeze() 提升效能

利用Object.freeze() 提升效能

利用Object.freeze() 提升效能

Object.freeze() 方法可以凍結一個物件。一個被凍結的物件再也不能被修改;凍結了一個物件則不能向這個物件新增新的屬性,不能刪除已有屬性,不能修改該物件已有屬性的可列舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個物件後該物件的原型也不能被修改。freeze() 返回和傳入的引數相同的物件。

凍結物件

    var obj = {
      prop: function() {},
      foo: 'bar'
    };

    // 新的屬性會被新增, 已存在的屬性可能
    // 會被修改或移除
    obj.foo = 'baz';
    obj.lumpy = 'woof';
    delete obj.prop;

    // 作為引數傳遞的物件與返回的物件都被凍結
    // 所以不必儲存返回的物件(因為兩個物件全等)
    var o = Object.freeze(obj);

    o === obj; // true
    Object.isFrozen(obj); // === true

    // 現在任何改變都會失效
    obj.foo = 'quux'; // 靜默地不做任何事
    // 靜默地不新增此屬性
    obj.quaxxor = 'the friendly duck';

    console.log(obj) // { foo: "baz", lumpy: "woof" }
    console.log(o) // { foo: "baz", lumpy: "woof" }
    // 在嚴格模式,如此行為將丟擲 TypeErrors
    function fail(){
      'use strict';
      obj.foo = 'sparky'; // Cannot assign to read only property 'foo' of object '#<Object>'
      delete obj.quaxxor; // 返回true,因為quaxxor屬性從來未被新增
      obj.sparky = 'arf'; // Cannot add property sparky, object is not extensible
    }

    fail();

    // 試圖通過 Object.defineProperty 更改屬性
    // 下面兩個語句都會丟擲 Cannot define property ohai, object is not extensible.
    Object.defineProperty(obj, 'ohai', { value: 17 });
    Object.defineProperty(obj, 'foo', { value: 'eit' });

    // 也不能更改原型
    // 下面兩個語句都會丟擲  #<Object> is not extensible.
    Object.setPrototypeOf(obj, { x: 20 })
    obj.__proto__ = { x: 20 }
    

凍結資料

let a = [0];
Object.freeze(a); // 現在陣列不能被修改了.

a[0]=1; // 1
a.push(2); // Cannot add property 1, object is not extensible

console.log(a); // [0]

被凍結的物件是不可變的。但也不總是這樣。下例展示了凍結物件不是常量物件(淺凍結)。

var obj = {
  internal: {}
};

Object.freeze(obj);
obj1.internal.a = 'aValue';

console.log(obj1); // { internal: { a: 'aValue' } }

對於一個常量物件,整個引用圖(直接和間接引用其他物件)只能引用不可變的凍結物件。凍結的物件被認為是不可變的,因為整個物件中的整個物件狀態(對其他物件的值和引用)是固定的。注意,字串,數字和布林總是不可變的,而函式和陣列是物件。

要使物件不可變,需要遞迴凍結每個型別為物件的屬性(深凍結)。當你知道物件在引用圖中不包含任何 環 (迴圈引用)時,將根據你的設計逐個使用該模式,否則將觸發無限迴圈。對 deepFreeze() 的增強將是具有接收路徑(例如Array)引數的內部函式,以便當物件進入不變時,可以遞迴地呼叫 deepFreeze() 。你仍然有凍結不應凍結的物件的風險,例如[window]

// 深凍結函式.
function deepFreeze(obj) {
  // 取回定義在obj上的屬性名
  var propNames = Object.getOwnPropertyNames(obj);
  // 在凍結自身之前凍結屬性
  propNames.forEach(function(name) {
    var prop = obj[name];
    // 如果prop是個物件,凍結它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });
  // 凍結自身(no-op if already frozen)
  return Object.freeze(obj);
}

obj2 = {
  internal: {}
};

deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined

對比 Object.seal()

Object.seal()密封的物件可以改變它們現有的屬性。使用Object.freeze() 凍結的物件中現有屬性是不可變的。

使用

參考vue的原始碼,定義響應式的時候,如果freeze後的資料,不會加上settergetter

地址: https://github.com/vuejs/vue/blob/v2.5.17/src/core/observer/index.js?1535281657346#L134