您好,登錄后才能下訂單哦!
隨著 vue3.x
的消息越來越多,proxy
的討論也。相對(duì)于 Object.defineProperty
,proxy
有什么區(qū)別,有什么優(yōu)勢(shì),以及可以應(yīng)用在什么地方。該文章就簡(jiǎn)單的介紹下
講 proxy
之前,先回顧下 Object.defineProperty
。大家都知道,vue2.x
以及之前的版本是使用 Object.defineProperty
實(shí)現(xiàn)數(shù)據(jù)的雙向綁定的,至于是怎樣綁定的呢?下面簡(jiǎn)單實(shí)現(xiàn)一下
function observer(obj) { if (typeof obj === 'object') { for (let key in obj) { defineReactive(obj, key, obj[key]) } } } function defineReactive(obj, key, value) { //針對(duì)value是對(duì)象,遞歸檢測(cè) observer(value) //劫持對(duì)象的key Object.defineProperty(obj, key, { get() { console.log('獲取:' + key) return value }, set(val) { //針對(duì)所設(shè)置的val是對(duì)象 observer(val) console.log(key + "-數(shù)據(jù)改變了") value = val } }) } let obj = { name: '守候', flag: { book: { name: 'js', page: 325 }, interest: ['火鍋', '旅游'], } } observer(obj)
在瀏覽器的 console
執(zhí)行一下,似乎能正常運(yùn)行
但是實(shí)際上,Object.defineProperty
問題有以下幾個(gè)
問題1.刪除或者增加對(duì)象屬性無法監(jiān)聽到
比如增加一個(gè)屬性 gender
,由于在執(zhí)行 observer(obj)
的時(shí)候,沒有這個(gè)屬性,所以這個(gè)無法監(jiān)聽到。刪除的屬性也是無法監(jiān)聽到
增加屬性的時(shí)候,
vue
需要使用$set
進(jìn)行操作,$set
的內(nèi)部也是使用Object.defineProperty
進(jìn)行操作
問題2.數(shù)組的變化無法監(jiān)聽到
由上圖得知,雖然數(shù)組屬性實(shí)際上是修改成功了,但是不能被監(jiān)聽到
問題3.
由于是使用遞歸遍歷對(duì)象,使用 Object.defineProperty
劫持對(duì)象的屬性,如果遍歷的對(duì)象層級(jí)比較深,花的時(shí)間比較久,甚至有性能的問題
對(duì)于 proxy
,在 mdn 上的描述是: 對(duì)象用于定義基本操作的自定義行為(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)
簡(jiǎn)單來說就是,可以在對(duì)目標(biāo)對(duì)象設(shè)置一層攔截。無論對(duì)目標(biāo)對(duì)象進(jìn)行什么操作,都要經(jīng)過這層攔截
聽上去似乎,proxy
比 Object.defineProperty
要好用,并且簡(jiǎn)單很多,實(shí)際上就是如此。下面用 proxy 對(duì)上面的代碼進(jìn)行改寫試下
function observerProxy(obj) { let handler = { get(target, key, receiver) { console.log('獲?。?#39; + key) // 如果是對(duì)象,就遞歸添加 proxy 攔截 if (typeof target[key] === 'object' && target[key] !== null) { return new Proxy(target[key], handler) } return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { console.log(key + "-數(shù)據(jù)改變了") return Reflect.set(target, key, value, receiver) } } return new Proxy(obj, handler) } let obj = { name: '守候', flag: { book: { name: 'js', page: 325 }, interest: ['火鍋', '旅游'], } } let objTest = observerProxy(obj)
也是一樣的效果
而且,能做到 Object.defineProperty
做不到的事情,比如增加一個(gè)屬性 gender
,能夠監(jiān)聽到
操作數(shù)組,也能監(jiān)聽到
最后敲一下黑板,簡(jiǎn)單總結(jié)一下兩者的區(qū)別
1.Object.defineProperty
攔截的是對(duì)象的屬性,會(huì)改變?cè)瓕?duì)象。proxy
是攔截整個(gè)對(duì)象,通過 new 生成一個(gè)新對(duì)象,不會(huì)改變?cè)瓕?duì)象。
2.proxy
的攔截方式,除了上面的 get 和 set ,還有 11 種。選擇的方式很多 Proxy,也可以監(jiān)聽一些 Object.defineProperty
監(jiān)聽不到的操作,比如監(jiān)聽數(shù)組,監(jiān)聽對(duì)象屬性的新增,刪除等。
關(guān)于 proxy
的使用場(chǎng)景,受限于篇幅,這里就簡(jiǎn)單列舉幾個(gè),更多的可以移步我的 github 筆記或者 mdn。
看到這里,兩者的區(qū)別,和 proxy
的優(yōu)勢(shì)已經(jīng)知道個(gè)大概了。但是在開發(fā)上,有哪些場(chǎng)景可以使用到 proxy
呢,下面列舉個(gè)可能會(huì)遇上的情況
在使用 splice(-1)
,slice(-1)
等 API 的時(shí)候,當(dāng)輸入負(fù)數(shù)的時(shí)候,會(huì)定位到數(shù)組的最后一項(xiàng),但是在普通數(shù)組上,并不能使用負(fù)數(shù)。 [1,2,3][-1]
這個(gè)代碼并不能輸出 3 。要讓上面的代碼輸出 3 , 也可以使用 proxy
實(shí)現(xiàn)。
在對(duì)表單的值進(jìn)行改動(dòng)的時(shí)候,可以在 set
里面進(jìn)行攔截,判斷值是否合法
let ecValidate = { set(target, key, value, receiver) { if (key === 'age') { //如果值小于0,或者不是正整數(shù) if (value < 0 || !Number.isInteger(value)) { throw new TypeError('請(qǐng)輸入正確的年齡'); } } return Reflect.set(target, key, value, receiver) } } let obj = new Proxy({ age: 18 }, ecValidate) obj.age = 16obj.age = '少年'
比如有一個(gè)需求,保證用戶輸入正確身份證號(hào)碼之后,把出生年月,籍貫,性別都添加進(jìn)用戶信息里面
眾所周知,身份證號(hào)碼第一和第二位代表所在?。ㄗ灾螀^(qū),直轄市,特別行政區(qū)),第三和第四位代表所在市(地級(jí)市、自治州、盟及國家直轄市所屬市轄區(qū)和縣的匯總碼)。第七至第十四位是出生年月日。低17位代表性別,男單女雙。
const PROVINCE_NUMBER = { 44 : '廣東省', 46 : '海南省' } const CITY_NUMBER = { 4401 : '廣州市', 4601 : '??谑?#39; } let ecCardNumber = { set(target, key, value, receiver) { if (key === 'cardNumber') { Reflect.set(target, 'hometown', PROVINCE_NUMBER[value.substr(0, 2)] + CITY_NUMBER[value.substr(0, 4)], receiver) Reflect.set(target, 'date', value.substr(6, 8), receiver) Reflect.set(target, 'gender', value.substr( - 2, 1) % 2 === 1 ? '男': '女', receiver) } return Reflect.set(target, key, value, receiver) } } let obj = new Proxy({ cardNumber: '' }, ecCardNumber)
比如有一個(gè)需求,需要傳時(shí)間戳給到后端,但是前端拿到的是一個(gè)時(shí)間字符串,這個(gè)也可以用 proxy
進(jìn)行攔截,當(dāng)?shù)玫綍r(shí)間字符串之后,可以自動(dòng)加上時(shí)間戳。
let ecArrayProxy = { get(target, key, receiver) { let _index = key < 0 ? target.length + Number(key) : key return Reflect.get(target, _index, receiver) } } let arr = new Proxy([1, 2, 3], ecArrayProxy)
以上就是JS Proxy 的優(yōu)勢(shì)以及使用場(chǎng)景的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注億速云其它相關(guān)文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。