数据劫持|数据代理
數(shù)據(jù)劫持:
指的是在訪問(wèn)或者修改對(duì)象的某個(gè)屬性時(shí),通過(guò)一段代碼攔截這個(gè)行為,進(jìn)行額外的操作或者修改返回結(jié)果。
比較典型的是 Object.defineProperty() 和 ES2016 中新增的 Proxy 對(duì)象。數(shù)據(jù)劫持最著名的應(yīng)用當(dāng)屬雙向綁定,這也是一個(gè)已經(jīng)被討論爛了的面試必考題。例如 Vue 2.x 使用的是 Object.defineProperty()(Vue 在 3.x 版本之后改用 Proxy 進(jìn)行實(shí)現(xiàn))。
一、Object.defineProperty
Vue 的雙向綁定已經(jīng)升級(jí)為前端面試的必考題,原理我就不再重復(fù)了,網(wǎng)上一大片。簡(jiǎn)單來(lái)說(shuō)就是利用 Object.defineProperty(),并且把內(nèi)部解耦為 Observer, Dep, 并使用 Watcher 相連。
Object.defineProperty() 的問(wèn)題主要有三個(gè):
1、不能監(jiān)聽(tīng)數(shù)組的變化
let arr = [1,2,3] let obj = {} Object.defineProperty(obj, 'arr', {get () {console.log('get arr')return arr},set (newVal) {console.log('set', newVal)arr = newVal} }) obj.arr.push(4) // 只會(huì)打印 get arr, 不會(huì)打印 set obj.arr = [1,2,3,4] // 這個(gè)能正常 set數(shù)組的以下幾個(gè)方法不會(huì)觸發(fā) set:
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
Vue 把這些方法定義為變異方法 (mutation method),指的是會(huì)修改原來(lái)數(shù)組的方法。與之對(duì)應(yīng)則是非變異方法 (non-mutating method),例如 filter, concat, slice 等,它們都不會(huì)修改原始數(shù)組,而會(huì)返回一個(gè)新的數(shù)組。Vue 官網(wǎng)有相關(guān)文檔講述這個(gè)問(wèn)題。
2、必須遍歷對(duì)象的每個(gè)屬性
使用 Object.defineProperty() 多數(shù)要配合 Object.keys() 和遍歷,于是多了一層嵌套。如:
3、必須深層遍歷嵌套的對(duì)象
所謂的嵌套對(duì)象,是指類似
如果是這一類嵌套對(duì)象,那就必須逐層遍歷,直到把每個(gè)對(duì)象的每個(gè)屬性都調(diào)用 Object.defineProperty() 為止。 Vue 的源碼中就能找到這樣的邏輯 (叫做 walk 方法)。
4、造成的問(wèn)題
造成所謂的響應(yīng)式數(shù)據(jù),DOM無(wú)法進(jìn)行更新。
解決方案:
Vue 無(wú)法檢測(cè)到對(duì)象屬性的添加或刪除。由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)屬性執(zhí)行 getter/setter 轉(zhuǎn)化,所以屬性必須在 data 對(duì)象上存在才能讓 Vue 將它轉(zhuǎn)換為響應(yīng)式的。例如:
var vm = new Vue({data:{a:1} })// `vm.a` 是響應(yīng)式的vm.b = 2 // `vm.b` 是非響應(yīng)式的對(duì)于已經(jīng)創(chuàng)建的實(shí)例,Vue 不允許動(dòng)態(tài)添加根級(jí)別的響應(yīng)式屬性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套對(duì)象添加響應(yīng)式屬性。例如,對(duì)于:
Vue.set(vm.someObject, 'b', 2)您還可以使用 vm.$set 實(shí)例方法,這也是全局 Vue.set 方法的別名:
this.$set(this.someObject,'b',2)有時(shí)你可能需要為已有對(duì)象賦值多個(gè)新屬性,比如使用 Object.assign() 或 _.extend()。但是,這樣添加到對(duì)象上的新屬性不會(huì)觸發(fā)更新。在這種情況下,你應(yīng)該用原對(duì)象與要混合進(jìn)去的對(duì)象的屬性一起創(chuàng)建一個(gè)新的對(duì)象。
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })` this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })也有一些數(shù)組相關(guān)的注意事項(xiàng),之前已經(jīng)在列表渲染中講過(guò)。
二、Proxy
在數(shù)據(jù)劫持這個(gè)問(wèn)題上,Proxy 可以被認(rèn)為是 Object.defineProperty() 的升級(jí)版。外界對(duì)某個(gè)對(duì)象的訪問(wèn),都必須經(jīng)過(guò)這層攔截。因此它是針對(duì) 整個(gè)對(duì)象,而不是 對(duì)象的某個(gè)屬性,所以也就不需要對(duì) keys 進(jìn)行遍歷。這解決了上述 Object.defineProperty() 的第二個(gè)問(wèn)題。
let obj = {name: 'Eason',age: 30 } let handler = {get (target, key, receiver) {console.log('get', key)return Reflect.get(target, key, receiver)},set (target, key, value, receiver) {console.log('set', key, value)return Reflect.set(target, key, value, receiver)} } let proxy = new Proxy(obj, handler) proxy.name = 'Zoe' // set name Zoe proxy.age = 18 // set age 18一道面試題
什么樣的 a 可以滿足 (a === 1 && a === 2 && a === 3) === true 呢?(注意是 3 個(gè) =,也就是嚴(yán)格相等)???
Object.defineProperty(window, 'a', {get () {current++return current} }) console.log(a === 1 && a === 2 && a === 3) // true總結(jié)
- 上一篇: dnf战令80到88要多少经验 地下城与
- 下一篇: js超出文字个数展示省略号