> 文章列表 > vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

其实 我们之前对数组的一个监听 还并不是很完美
我们打开案例 打开 output.js
更改代码如下

import { observe } from "./dataResp"
const output = () => {var obj = {data: {data: {map: {dom: {isgin: true}},arg: 13},name: "小猫猫"},bool: [1,2,3,4]};observe(obj);obj.bool.push(7);document.getElementById("text").innerHTML = obj.data.name;
}export default output

运行结果如下
vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

很明显 我们数组并捕获不到 push方法 除了 push 很多数组方法都捕获不到

首先 我们来看几个方法
分别是
vue2数据响应式原理(5) 通过重写函数实现数组响应式监听
这几个方法执行 说明 当前数组被改写了内容

那么 我们可以去改写这七个方法 我们在案例 src目录下创建 Arrays.js 然后 大家先要知道 这些方法被定义在哪里?
Array.prototype
Array是数组类型的一个类对象 这些方法就定义在Array的原型链上
所以 大家才能通过 数组.这些方法 去调用他们 而我们要做的就是 重写这些方法 要做的就是 调用原来的方法 然后 在后面再去做自己需要做的响应式的事情

我们先在src下创建一个def.js
然后将之前写在dataResp.js中的def放进def.js来

export const def = function(obj,key,value,enumerable) {Object.defineProperty(obj,key,{value,enumerable,//true  设为可写writable: true,//true  设为可被删除configurable: true});
}

然后 将dataResp.js中的def方法干掉 然后 dataResp.js引入一下def.js的 def方法

import { def } from './def.js';

这个是我之前没想清楚 抱歉

然后 我们在Arrays.js中编写代码如下

import { def } from './def.js';const arrayPrototype = Array.prototype;export const arrayMethods = Object.create(arrayPrototype);const redefineArrayMethod = ['push','pop','shift','unshift','splice','sort','reverse'
]redefineArrayMethod.forEach(item =>{const backupFunction = arrayPrototype[item];def(arrayMethods,item,function(){backupFunction.apply(this, arguments);console.log('数组执行了',item,'操作,值被修改为',this);},false);
})

这里 我们先定义了redefineArrayMethod变量 他的值是一个数组 对应下面每一个字符串下标 都是这些改变数组结构的函数关键字

然后定义 arrayPrototype 存一下Array的 原型链 prototype 因为 这些函数就在上面

然后 创建arrayMethods 记录创建数组 并将arrayPrototype

最后 循环redefineArrayMethod 对应每一次下标循环 拿到都是一个数组方法关键字 第一次拿到’push’ 第二次’pop’

然后 我们的响应式代码就可以写在这里 首先 我们先

const backupFunction = arrayPrototype[item];

备份一下原来的方法 因为 你别重写之后 方法原有的逻辑没有了啊 是不是

你别说 push我监听一下 写完 push都加不进去东西了 那响应个啥 这BUG了啊

然后 我们调用def

第一个参数是 arrayMethods 就是被创建的对象 第二个 这里传的是 当前循环的内容 就是方法名数组下标 看着是第几次循环 第一次 push 第二次 pop 第三次 shift 依次类推

第四个参数 不用说 这东西你们肯定不希望他参与遍历吧 果断false
至于第三个 是他的值 我们要改写这个函数啊 就写了这个函数新的内容

backupFunction.apply(this, arguments);

先把我们备份的之前的函数内容执行了 就是 比如 push 我们先用执行参数帮他把push应该有的增加下标的逻辑执行了 this不用说 肯定要的 当前对象 然后arguments代表我们当前函数拿到的所有参数 全部给了backupFunction.apply当参数 就是保证参数不要少了

然后下面的代码 我们就捕获被改变的这个事情

然后 我们改写 dataResp.js中的Observer类代码如下

class Observer{constructor(value) {//相当于  给拿到的对象  其中的__ob__绑定 值为thsi,在类中用this 表示取实例本身给__ob__赋值  最后一个enumerable为false 表示属性不参与for遍历def(value,'__ob__',this,false);if(Array.isArray(value)){Object.setPrototypeOf(value, arrayMethods);}this.walk(value);}walk(value) {for(let key in value){defineReactive(value,key);}}
}

就是 监听到 如果是数字类型 调用一下 Object.setPrototypeOf 监听

然后 我们再次运行代码
vue2数据响应式原理(5) 通过重写函数实现数组响应式监听
可以看到 我们重写的方法 就起效了 push被成功捕获 并输出了语句
vue2数据响应式原理(5) 通过重写函数实现数组响应式监听