利用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
后的數據,不會加上setter
和getter
地址: https://github.com/vuejs/vue/blob/v2.5.17/src/core/observer/index.js?1535281657346#L134