我们知道,改变数组的方法有很多,举个例子比如说push方法吧。push存在Array.prototype上的,如果我们能
能拦截到原型上的push方法,是不是就可以做一些事情呢?
Object.defineProperty
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。存取描述符是由getter-setter函数对描述的属性,也就是我们用来给对象做响应式绑定的。Object.defineProperty-MDN
虽然我们无法使用Object.defineProperty将数组进行响应式的处理,也就是getter-setter,但是还有其他的功能可以供我们使用。就是数据描述符,数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
因此我们只要把原型上的方法,进行value的重新赋值。
如下代码,在重新赋值的过程中,我们可以获取到方法名和所有参数。
function def (obj, key) {
Object.defineProperty(obj, key, {
writable: true,
enumerable: true,
configurable: true,
value: function(...args) {
console.log('key', key);
console.log('args', args);
}
});
}// 重写的数组方法
let obj = {
push() {}
}
// 数组方法的绑定
def(obj, 'push');
obj.push([1, 2], 7, 'hello!');
// 控制台输出 key push
// 控制台输出 args [Array(2), 7, "hello!"]
通过如上代码我们就可以知道,用户使用了数组上原型的方法以及参数我们都可以拦截到,这个拦截的过程就可以做一些变化的通知。
Vue监听Array三步曲
接下来,就看看Vue是如何实现的吧~
第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。
第二步:对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。
第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型。
我们将代码进行下改造,拦截的过程中还是要将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变,然后我们再去做视图的更新等操作。
const arrayProto = Array.prototype // 获取Array的原型function def (obj, key) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
value: function(...args) {
console.log(key); // 控制台输出 push
console.log(args); // 控制台输出 [Array(2), 7, "hello!"]










