序
在 Vue 的文件中介紹資料繫結和響應時,特意標註了對於經過 Object.freeze() 方法的物件無法進行更新響應。因此,特意去查了 Object.freeze() 方法的具體含義。
含義
Object.freeze() 方法用於凍結物件,禁止對於該物件的屬性進行修改(由於陣列本質也是物件
,因此該方法可以對陣列使用)。在 Mozilla MDN 中是如下介紹的:
可以凍結一個物件。一個被凍結的物件再也不能被修改;凍結了一個物件則不能向這個物件新增新的屬性,不能刪除已有屬性,不能修改該物件已有屬性的可列舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個物件後該物件的原型也不能被修改
該方法的返回值是其引數本身。
需要注意的是以下兩點
-
Object.freeze() 和 const 變數宣告不同,也不承擔 const 的功能。
const和Object.freeze()完全不同
- const的行為像 let。它們唯一的區別是, const定義了一個無法重新分配的變數。 通過 const宣告的變數是具有塊級作用域的,而不是像 var宣告的變數具有函式作用域。
- Object.freeze()接受一個物件作為引數,並返回一個相同的不可變的物件。這就意味著我們不能新增,刪除或更改物件的任何屬性。
- const和Object.freeze()並不同,const是防止變數重新分配,而Object.freeze()是使物件具有不可變性。
以下程式碼是正確的:
- Object.freeze() 是“淺凍結”,以下程式碼是生效的:
例項
常規用法
明顯看到,a 的 prop 屬性未被改變,即使重新賦值了。
延伸
"深凍結"
要完全凍結具有巢狀屬性的物件,您可以編寫自己的庫或使用已有的庫來凍結物件,如Deepfreeze或immutable-js
// 深凍結函式.
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);
}
複製程式碼
其實就是個簡單的遞迴方法。但是涉及到一個很重要,但是在寫業務邏輯的時候很少用的知識點 Object.getOwnPropertyNames(obj)
。我們都知道在 JS 的 Object 中存在原型鏈屬性,通過這個方法可以獲取所有的非原型鏈屬性。
利用Object.freeze()
提升效能
除了元件上的優化,我們還可以對vue的依賴改造入手。初始化時,vue會對data做getter、setter改造,在現代瀏覽器裡,這個過程實際上挺快的,但仍然有優化空間。
Object.freeze()
可以凍結一個物件,凍結之後不能向這個物件新增新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該物件已有屬性的可列舉性、可配置性、可寫性。該方法返回被凍結的物件。
當你把一個普通的 JavaScript 物件傳給 Vue 例項的 data
選項,Vue 將遍歷此物件所有的屬性,並使用 Object.defineProperty 把這些屬性全部轉為 getter/setter,這些 getter/setter 對使用者來說是不可見的,但是在內部它們讓 Vue 追蹤依賴,在屬性被訪問和修改時通知變化。
但 Vue 在遇到像 Object.freeze()
這樣被設定為不可配置之後的物件屬性時,不會為物件加上 setter getter 等資料劫持的方法。參考 Vue 原始碼
Vue observer 原始碼
效能提升效果對比
在基於 Vue 的一個 big table benchmark 裡,可以看到在渲染一個一個 1000 x 10 的表格的時候,開啟Object.freeze()
前後重新渲染的對比。
big table benchmark
開啟優化之前
開啟優化之後
在這個例子裡,使用了 Object.freeze()
比不使用快了 4 倍
為什麼Object.freeze()
的效能會更好
不使用Object.freeze()
的CPU開銷
使用 Object.freeze()
的CPU開銷
對比可以看出,使用了 Object.freeze()
之後,減少了 observer 的開銷。
Object.freeze()
應用場景
由於 Object.freeze()
會把物件凍結,所以比較適合展示類的場景,如果你的資料屬性需要改變,可以重新替換成一個新的 Object.freeze()
的物件。
Javascript物件解凍
修改 React props React生成的物件是不能修改props的, 但實踐中遇到需要修改props的情況. 如果直接修改, js程式碼將報錯, 原因是props物件被凍結了, 可以用Object.isFrozen()來檢測, 其結果是true. 說明該物件的屬性是隻讀的.
那麼, 有方法將props物件解凍, 從而進行修改嗎?
事實上, 在javascript中, 物件凍結後, 沒有辦法再解凍, 只能通過克隆一個具有相同屬性的新物件, 通過修改新物件的屬性來達到目的.
可以這樣:
ES6: Object.assign({}, frozenObject);
lodash: _.assign({}, frozenObject);
複製程式碼
來看實際程式碼:
function modifyProps(component) {
let condictioin = this.props.condictioin,
newComponent = Object.assign({}, component),
newProps = Object.assign({}, component.props)
if (condictioin) {
if (condictioin.add) newProps.add = true
if (condictioin.del) newProps.del = true
}
newComponent.props = newProps
return newComponent
}
複製程式碼
鎖定物件的方法
- Object.preventExtensions()
no new properties or methods can be added to the project 物件不可擴充套件, 即不可以新增屬性或方法, 但可以修改/刪除
- Object.seal()
same as prevent extension, plus prevents existing properties and methods from being deleted 在上面的基礎上,物件屬性不可刪除, 但可以修改
- Object.freeze()
same as seal, plus prevent existing properties and methods from being modified 在上面的基礎上,物件所有屬性只讀, 不可修改
以上三個方法分別可用Object.isExtensible(), Object.isSealed(), Object.isFrozen()來檢測
Object.freeze( ) 阻止Vue無法實現 響應式系統
當一個 Vue 例項被建立時,它向 Vue 的響應式系統中加入了其 data 物件中能找到的所有的屬性。當這些屬性的值發生改變時,檢視將會產生“響應”,即匹配更新為新的值。但是如果使用 Object.freeze(),這會阻止修改現有的屬性,也意味著響應系統無法再追蹤變化。
具體使用辦法舉例:
<template>
<div>
<p>freeze後會改變嗎
{{obj.foo}}
</p>
<!-- 兩個都不能修改??為什麼?第二個理論上應該是可以修改的-->
<button @click="change">點我確認</button>
</div>
</template>
<script>
var obj = {
foo: '不會變'
}
Object.freeze(obj)
export default {
name: 'index',
data () {
return {
obj: obj
}
},
methods: {
change () {
this.obj.foo = '改變'
}
}
}
</script>
複製程式碼
執行後:
從報錯可以看出只讀屬性foo不能進行修改,Object.freeze()凍結的是值,你仍然可以將變數的引用替換掉,將上述程式碼更改為:
<button @click="change">點我確認</button>
change () {
this.obj = {
foo: '會改變'
}
}
複製程式碼
Object.freeze()是ES5新增的特性,可以凍結一個物件,凍結指的是不能向這個物件新增新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該物件已有屬性的可列舉性、可配置性、可寫性。防止物件被修改。 如果你有一個巨大的陣列或Object,並且確信資料不會修改,使用Object.freeze()可以讓效能大幅提升。
實踐心得和技巧
Object.freeze()是ES5新增的特性,可以凍結一個物件,防止物件被修改。
vue 1.0.18+對其提供了支援,對於data或vuex裡使用freeze凍結了的物件,vue不會做getter和setter的轉換。
如果你有一個巨大的陣列或Object,並且確信資料不會修改,使用Object.freeze()可以讓效能大幅提升。在我的實際開發中,這種提升大約有5~10倍,倍數隨著資料量遞增。
並且,Object.freeze()凍結的是值,你仍然可以將變數的引用替換掉。舉個例子:
<p v-for="item in list">{{ item.value }}</p>
複製程式碼
new Vue({
data: {
// vue不會對list裡的object做getter、setter繫結
list: Object.freeze([
{ value: 1 },
{ value: 2 }
])
},
created () {
// 介面不會有響應
this.list[0].value = 100;
// 下面兩種做法,介面都會響應
this.list = [
{ value: 100 },
{ value: 200 }
];
this.list = Object.freeze([
{ value: 100 },
{ value: 200 }
]);
}
})
複製程式碼
vue的文件沒有寫上這個特性,但這是個非常實用的做法,對於純展示的大資料,都可以使用Object.freeze提升效能。