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 监听不到的操作,比如监听数组,监听对象属性的新增,删除等。