Object.defineProperty
vue2.x 以及之前的版本使用 Object.defineProperty 實現數據的雙向綁定,簡單實現下
let obj = { name: '李四', address: '上海浦東新區', flags: { book: { page: 153, name: 'JS' }, hobby: ['足球', '游戲', '音樂'] } } function observer(obj) { if (typeof obj == 'object') { for (let key in obj) { defineReactive(obj, key, obj[key]) } } } function defineReactive(obj, key, value) { Object.defineProperty(obj, key, { get() { console.log('獲取:' + key) return value }, set(val) { observer(val) console.log(key + "-數據改變了") value = val } }) } observer(obj)
問題1.刪除或者增加對象屬性無法監聽到
問題2.數組的變化無法監聽到
問題3. 由於是使用遞歸遍歷對象,使用 Object.defineProperty 劫持對象的屬性,如果遍歷的對象層級比較深,花的時間比較久,甚至有性能的問題
proxy
對象用於定義基本操作的自定義行為
簡單來說就是,可以在對目標對象設置一層攔截。無論對目標對象進行什么操作,都要經過這層攔截
let obj = { name: '李四', address: '上海浦東新區', flags: { book: { page: 153, name: 'JS' }, hobby: ['足球', '游戲', '音樂'] } } function observerProxy(obj) { const handler = { get(target, key, receiver) { console.log('獲取:' + key) // 如果是對象,就遞歸添加 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) // 如果是對象,就遞歸添加 proxy 攔截 return Reflect.set(target, key, value, receiver) } } return new Proxy(obj, handler) } let newObj = observerProxy(obj)
1.Object.defineProperty 攔截的是對象的屬性,會改變原對象。proxy 是攔截整個對象,通過 new 生成一個新對象,不會改變原對象。
2.proxy 的攔截方式,除了上面的 get 和 set ,還有 11 種。選擇的方式很多 Proxy,也可以監聽一些 Object.defineProperty 監聽不到的操作,比如監聽數組,監聽對象屬性的新增,刪除等。