ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] > Vue内部实现了一组观察数组的变异方法,例如:push(),pop(),shift()等。 > Object.definePropert只能把对象属性改为getter/setter,而对于数组的方法就无能为力了,其内部巧妙的使用了数组的属性来实现了数据的双向绑定,下面我们来一步一步的实现一个简单版。 # 给定一个数组 调用相关方法时触发自定义的函数 ## 定义一个需要监听变化的数组 ``` let obarr = [] ``` ## copy一份数组的原型方法,防止污染原生数组方法 ``` const arrayProto = Array.prototype const arrayMethods = Object.create(arrayProto) ``` ## 把arrayMethods对象上的push转换为观察者对象 ``` Object.defineProperty(arrayMethods,'push',{ value:function mutator(){ console.log('obarr.push会走这里') } }) ``` 此时arrayMethods定义了一个push的新属性,那么我们如何把它和 let obarr = \[\] 绑定起来呢,来看看下面的实现? ``` obarr.__proto__ = arrayMethods ``` 使用arrayMethods覆盖obarr的所有方法 <br> 针对于不支持\_\_proto\_\_的浏览器实现如下: ``` let obarr = [] const arrayProto = Array.prototype const arrayMethods = Object.create(arrayProto) Object.defineProperty(arrayMethods,'push',{ value:function mutator(){ console.log('obarr.push会走这里') } }) Object.defineProperty(obarr,'push',{ value:arrayMethods.push }) ``` <br> 上面实现了push方法,其他的方法同理,我们只需要把所有需要实现的方法循环遍历执行即可,升级后代码如下: ``` const arrayProto = Array.prototype const arrayMethods = Object.create(arrayProto) ;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ].forEach(item=>{ Object.defineProperty(arrayMethods,item,{ value:function mutator(){ //缓存原生方法,之后调用 const original = arrayProto[item] let args = Array.from(arguments) original.apply(this,args) }, }) }) function protoAugment (target,src) { target.__proto__ = src } // 调用 let obarr = [] ``` <br> 分析: 1、经过以上的代码可以看出,只会更改我们给定数组(obarr)的相关方法,而不会污染Array的原生方法,因此其他普通数组不受影响。 2、从新赋值数组的\_\_proto\_\_属性为arrayMethods,而arrayMethods我们从新定义了push,pop等相关属性方法,因此当我们使用数组的push,pop等方法时会调用arrayMethods的相关属性方法,达到监听数组变化的能力。 3、对于不支持\_\_proto\_\_属性的浏览器,直接使用Object.defineProperty从新定义相关属性。 4、而Vue的实现方法正如上,更改我们需要监听的Array数组属性值(属性值为函数),在监听函数里执行数组的原生方法,并通知所有注册的观察者进行响应式处理。