在vue中,數(shù)據(jù)凍結(jié)“Object.freeze()”方法用于凍結(jié)對(duì)象,禁止對(duì)于該對(duì)象的屬性進(jìn)行修改(由于數(shù)組本質(zhì)也是對(duì)象,因此該方法可以對(duì)數(shù)組使用)。對(duì)象凍結(jié)后,不能刪除已有屬性,不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫(xiě)性,以及不能修改已有屬性的值;此外,凍結(jié)一個(gè)對(duì)象后該對(duì)象的原型也不能被修改。
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
在 Vue 的文檔中介紹數(shù)據(jù)綁定和響應(yīng)時(shí),特意標(biāo)注了對(duì)于經(jīng)過(guò) Object.freeze() 方法的對(duì)象無(wú)法進(jìn)行更新響應(yīng)。因此,特意去查了 Object.freeze() 方法的具體含義。
含義
Object.freeze() 方法用于凍結(jié)對(duì)象,禁止對(duì)于該對(duì)象的屬性進(jìn)行修改(由于數(shù)組本質(zhì)也是對(duì)象
,因此該方法可以對(duì)數(shù)組使用)。在 Mozilla MDN 中是如下介紹的:
可以?xún)鼋Y(jié)一個(gè)對(duì)象。一個(gè)被凍結(jié)的對(duì)象再也不能被修改;凍結(jié)了一個(gè)對(duì)象則不能向這個(gè)對(duì)象添加新的屬性,
不能刪除已有屬性,不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫(xiě)性,以及不能修改已有屬性的值。此外,凍結(jié)一個(gè)對(duì)象后該對(duì)象的原型也不能被修改
該方法的返回值是其參數(shù)本身。
需要注意的是以下兩點(diǎn)
1、Object.freeze() 和 const 變量聲明不同,也不承擔(dān) const 的功能。
const和Object.freeze()完全不同
- const的行為像 let。它們唯一的區(qū)別是, const定義了一個(gè)無(wú)法重新分配的變量。 通過(guò) const聲明的變量是具有塊級(jí)作用域的,而不是像 var聲明的變量具有函數(shù)作用域。
- Object.freeze()接受一個(gè)對(duì)象作為參數(shù),并返回一個(gè)相同的不可變的對(duì)象。這就意味著我們不能添加,刪除或更改對(duì)象的任何屬性。
- const和Object.freeze()并不同,const是防止變量重新分配,而Object.freeze()是使對(duì)象具有不可變性。
以下代碼是正確的:
2、Object.freeze() 是“淺凍結(jié)”,以下代碼是生效的:
實(shí)例
常規(guī)用法
明顯看到,a 的 prop 屬性未被改變,即使重新賦值了。
延伸
"深凍結(jié)"
要完全凍結(jié)具有嵌套屬性的對(duì)象,您可以編寫(xiě)自己的庫(kù)或使用已有的庫(kù)來(lái)凍結(jié)對(duì)象,如Deepfreeze或immutable-js
// 深凍結(jié)函數(shù). function deepFreeze(obj) { // 取回定義在obj上的屬性名 var propNames = Object.getOwnPropertyNames(obj); // 在凍結(jié)自身之前凍結(jié)屬性 propNames.forEach(function(name) { var prop = obj[name]; // 如果prop是個(gè)對(duì)象,凍結(jié)它 if (typeof prop == 'object' && prop !== null) deepFreeze(prop); }); // 凍結(jié)自身(no-op if already frozen) return Object.freeze(obj); }
其實(shí)就是個(gè)簡(jiǎn)單的遞歸方法。但是涉及到一個(gè)很重要,但是在寫(xiě)業(yè)務(wù)邏輯的時(shí)候很少用的知識(shí)點(diǎn) Object.getOwnPropertyNames(obj)
。我們都知道在 JS 的 Object 中存在原型鏈屬性,通過(guò)這個(gè)方法可以獲取所有的非原型鏈屬性。
利用Object.freeze()
提升性能
除了組件上的優(yōu)化,我們還可以對(duì)vue的依賴(lài)改造入手。初始化時(shí),vue會(huì)對(duì)data做getter、setter改造,在現(xiàn)代瀏覽器里,這個(gè)過(guò)程實(shí)際上挺快的,但仍然有優(yōu)化空間。
Object.freeze()
可以?xún)鼋Y(jié)一個(gè)對(duì)象,凍結(jié)之后不能向這個(gè)對(duì)象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫(xiě)性。該方法返回被凍結(jié)的對(duì)象。
當(dāng)你把一個(gè)普通的 JavaScript 對(duì)象傳給 Vue 實(shí)例的 data
選項(xiàng),Vue 將遍歷此對(duì)象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter,這些 getter/setter 對(duì)用戶(hù)來(lái)說(shuō)是不可見(jiàn)的,但是在內(nèi)部它們讓 Vue 追蹤依賴(lài),在屬性被訪問(wèn)和修改時(shí)通知變化。
但 Vue 在遇到像 Object.freeze()
這樣被設(shè)置為不可配置之后的對(duì)象屬性時(shí),不會(huì)為對(duì)象加上 setter getter 等數(shù)據(jù)劫持的方法。參考 Vue 源碼
Vue observer 源碼
性能提升效果對(duì)比
在基于 Vue 的一個(gè) big table benchmark 里,可以看到在渲染一個(gè)一個(gè) 1000 x 10 的表格的時(shí)候,開(kāi)啟Object.freeze()
前后重新渲染的對(duì)比。
big table benchmark
開(kāi)啟優(yōu)化之前
開(kāi)啟優(yōu)化之后
在這個(gè)例子里,使用了 Object.freeze()
比不使用快了 4 倍
為什么Object.freeze()
的性能會(huì)更好
不使用Object.freeze()
的CPU開(kāi)銷(xiāo)
使用 Object.freeze()
的CPU開(kāi)銷(xiāo)
對(duì)比可以看出,使用了 Object.freeze()
之后,減少了 observer 的開(kāi)銷(xiāo)。
Object.freeze()
應(yīng)用場(chǎng)景
由于 Object.freeze()
會(huì)把對(duì)象凍結(jié),所以比較適合展示類(lèi)的場(chǎng)景,如果你的數(shù)據(jù)屬性需要改變,可以重新替換成一個(gè)新的 Object.freeze()
的對(duì)象。
Javascript對(duì)象解凍
修改 React props React生成的對(duì)象是不能修改props的, 但實(shí)踐中遇到需要修改props的情況. 如果直接修改, js代碼將報(bào)錯(cuò), 原因是props對(duì)象被凍結(jié)了, 可以用Object.isFrozen()來(lái)檢測(cè), 其結(jié)果是true. 說(shuō)明該對(duì)象的屬性是只讀的.
那么, 有方法將props對(duì)象解凍, 從而進(jìn)行修改嗎?
事實(shí)上, 在javascript中, 對(duì)象凍結(jié)后, 沒(méi)有辦法再解凍, 只能通過(guò)克隆一個(gè)具有相同屬性的新對(duì)象, 通過(guò)修改新對(duì)象的屬性來(lái)達(dá)到目的.
可以這樣:
ES6: Object.assign({}, frozenObject); lodash: _.assign({}, frozenObject);
來(lái)看實(shí)際代碼:
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 }
鎖定對(duì)象的方法
- Object.preventExtensions()
no new properties or methods can be added to the project 對(duì)象不可擴(kuò)展, 即不可以新增屬性或方法, 但可以修改/刪除
- Object.seal()
same as prevent extension, plus prevents existing properties and methods from being deleted 在上面的基礎(chǔ)上,對(duì)象屬性不可刪除, 但可以修改
- Object.freeze()
same as seal, plus prevent existing properties and methods from being modified 在上面的基礎(chǔ)上,對(duì)象所有屬性只讀, 不可修改
以上三個(gè)方法分別可用Object.isExtensible(), Object.isSealed(), Object.isFrozen()來(lái)檢測(cè)
Object.freeze( ) 阻止Vue無(wú)法實(shí)現(xiàn) 響應(yīng)式系統(tǒng)
當(dāng)一個(gè) Vue 實(shí)例被創(chuàng)建時(shí),它向 Vue 的響應(yīng)式系統(tǒng)中加入了其 data 對(duì)象中能找到的所有的屬性。當(dāng)這些屬性的值發(fā)生改變時(shí),視圖將會(huì)產(chǎn)生“響應(yīng)”,即匹配更新為新的值。但是如果使用 Object.freeze(),這會(huì)阻止修改現(xiàn)有的屬性,也意味著響應(yīng)系統(tǒng)無(wú)法再追蹤變化。
具體使用辦法舉例:
<template> <div> <p>freeze后會(huì)改變嗎 {{obj.foo}} </p> <!-- 兩個(gè)都不能修改??為什么?第二個(gè)理論上應(yīng)該是可以修改的--> <button @click="change">點(diǎn)我確認(rèn)</button> </div> </template> <script> var obj = { foo: '不會(huì)變' } Object.freeze(obj) export default { name: 'index', data () { return { obj: obj } }, methods: { change () { this.obj.foo = '改變' } } } </script>
運(yùn)行后:
從報(bào)錯(cuò)可以看出只讀屬性foo不能進(jìn)行修改,Object.freeze()凍結(jié)的是值,你仍然可以將變量的引用替換掉,將上述代碼更改為:
<button @click="change">點(diǎn)我確認(rèn)</button> change () { this.obj = { foo: '會(huì)改變' } }
Object.freeze()是ES5新增的特性,可以?xún)鼋Y(jié)一個(gè)對(duì)象,凍結(jié)指的是不能向這個(gè)對(duì)象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫(xiě)性。防止對(duì)象被修改。 如果你有一個(gè)巨大的數(shù)組或Object,并且確信數(shù)據(jù)不會(huì)修改,使用Object.freeze()可以讓性能大幅提升。
實(shí)踐心得和技巧
Object.freeze()是ES5新增的特性,可以?xún)鼋Y(jié)一個(gè)對(duì)象,防止對(duì)象被修改。
vue 1.0.18+對(duì)其提供了支持,對(duì)于data或vuex里使用freeze凍結(jié)了的對(duì)象,vue不會(huì)做getter和setter的轉(zhuǎn)換。
如果你有一個(gè)巨大的數(shù)組或Object,并且確信數(shù)據(jù)不會(huì)修改,使用Object.freeze()可以讓性能大幅提升。在我的實(shí)際開(kāi)發(fā)中,這種提升大約有5~10倍,倍數(shù)隨著數(shù)據(jù)量遞增。
并且,Object.freeze()凍結(jié)的是值,你仍然可以將變量的引用替換掉。舉個(gè)例子:
<p v-for="item in list">{{ item.value }}</p>
new Vue({ data: { // vue不會(huì)對(duì)list里的object做getter、setter綁定 list: Object.freeze([ { value: 1 }, { value: 2 } ]) }, created () { // 界面不會(huì)有響應(yīng) this.list[0].value = 100; // 下面兩種做法,界面都會(huì)響應(yīng) this.list = [ { value: 100 }, { value: 200 } ]; this.list = Object.freeze([ { value: 100 }, { value: 200 } ]); } })
vue的文檔沒(méi)有寫(xiě)上這個(gè)特性,但這是個(gè)非常實(shí)用的做法,對(duì)于純展示的大數(shù)據(jù),都可以使用Object.freeze提升性能。
(學(xué)習(xí)視頻分享:vuejs入門(mén)教程、編程基礎(chǔ)視頻)