Open qiuhongbingo opened 4 years ago
/** * Object.defineProperty 不能监听数组的变化,需要进行数组方法的重写 * Object.defineProperty 必须遍历对象的每个属性,且对于嵌套结构需要深层遍历 * Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的 * Proxy 支持代理数组的变化 * Proxy 的第二个参数除了 set 和 get 以外,可以有 13 种拦截方法,比起 Object.defineProperty() 更加强大,这里不再一一列举 * Proxy 性能将会被底层持续优化,而 Object.defineProperty 已经不再是优化重点 */ let data = { stage: 'Git', course: { title: 'Proxy VS Object.defineProperty', author: ['Bingo'] } } /** * 数组重写思路 */ // const arrExtend = Object.create(Array.prototype) // const arrMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // arrMethods.forEach(method => { // const oldMethod = Array.prototype[method] // const newMethod = function(...args) { // oldMethod.apply(this, args) // console.log(`${method} 方法被执行了`) // } // arrExtend[method] = newMethod // }) // Array.prototype = Object.assign(Array.prototype, arrExtend) /** * 整体实现思路: * 对于数据键值为基本类型的情况,我们使用 Object.defineProperty * 对于键值为对象类型的情况,继续递归调用 observe 方法 * 并通过 Proxy 返回的新对象对 data[key] 重新赋值,这个新值的 getter 和 setter 已经被添加了代理 */ const observe = data => { if (!data || Object.prototype.toString.call(data) !== '[object Object]') return Object.keys(data).forEach(key => { let currentValue = data[key] // 事实上 proxy 也可以对函数类型进行代理。这里只对承载数据类型的 object 进行处理,读者了解即可。 if (typeof currentValue === 'object') { observe(currentValue) data[key] = new Proxy(currentValue, { set(target, property, value, receiver) { // 因为数组的 push 会引起 length 属性的变化,所以 push 之后会触发两次 set 操作,我们只需要保留一次即可,property 为 length 时,忽略 // 第一次:[Array(1), '1', 'Mia', Proxy] // 第二次:[Array(2), 'length', 2, Proxy] if (property !== 'length') console.log(`[Proxy] setting ${key} value now, setting value is`, value) return Reflect.set(target, property, value, receiver) } }) } else { Object.defineProperty(data, key, { enumerable: true, configurable: false, get() { console.log(`[Object.defineProperty] getting ${key} value now, getting value is:`, currentValue) return currentValue }, set(newValue) { currentValue = newValue console.log(`[Object.defineProperty] setting ${key} value now, setting value is`, currentValue) } }) } }) } observe(data) data.stage = 'GitHub' // [Object.defineProperty] setting stage value now, setting value is GitHub data.course.author.push('Mia') // [Proxy] setting author value now, setting value is Mia