Object.defineProperty
因此,可以通过设置属性的 set
方法来实现监听数据变化的效果。
const obj = {
a: 1,
b: [1, 2],
};
for (let key in obj) {
let _val = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log('get ', key);
return _val;
},
set: (value) => {
console.log(`set ${key}: `, value);
_val = value;
},
});
}
obj.a = 2;
console.log(obj.a);
obj.b = [3, 4];
console.log(obj.b);
// set a 2
// get a
// 2
// set b [3, 4]
// get b
// [3, 4]
这样一看没什么问题, 但是当通过数组方法去改变值时,会发现并没有被 set
监听到。
obj.b.push(5);
console.log(obj.b);
// [1, 2, 5] b的值正确没问题,push进去了,但是没有set b
因为我们是调用了数组方法,而不是通过 obj.b = []
重新赋值,所以解法也简单:重写数组方法,让我们的数组去继承这些方法。
const obj = {
a: 1,
b: [1, 2],
};
const aim = []; // 定义一个工具人数组,下面重写其方法,最后让其他数组的去继承其方法
const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
// 重写工具人的方法
arrayMethods.forEach(item => {
Object.defineProperty(aim, item, {
value() {
console.log(`call ${item}`);
[][item].apply(this, arguments);
},
})
});
obj.b.__proto__ = aim; // 让我们的数据继承工具人的方法
obj.b.push(3);
console.log(obj.b);
// call push
// [1, 2, 3]
简单整合后如下
const obj = {
a: 1,
b: [1, 2],
};
const aim = []; // 定义一个数组,下面重写其方法,最后让其他数组的去继承其方法
const arrayMethods = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse',
];
arrayMethods.forEach(item => {
Object.defineProperty(aim, item, {
value() {
console.log(`call ${item}`);
[][item].apply(this, arguments);
},
})
});
for (let key in obj) {
if (Array.isArray(obj[key])) {
obj[key].__proto__ = aim;
} else {
let _val = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log('get ', key);
return _val;
},
set: (value) => {
console.log(`set ${key}: `, value);
_val = value;
},
});
}
}
obj.a = 2;
console.log(obj.a);
obj.b.push(3);
console.log(obj.b);
Proxy
原理和上面类似,监听属性值变化
const obj = {
a: 1,
b: [1, 2],
};
const objProxy = new Proxy(obj, {
get(target, prop, receiver) {
console.log('get', prop);
return target[prop];
},
set(target, prop, value, receiver) {
console.log('property set: ' + prop + ' = ' + value);
target[prop] = value;
return true;
},
});
objProxy.a = 2;
console.log(objProxy.a);
// property set: a = 2
// get a
// 2
objProxy.b.push(3);
console.log(objProxy.b);
// get b
// get b
// 3
通过上面代码可以看到,属性 a
的监听没问题,但是属性 b
数组的变化还是没有监听到,因为通过这种方式我们监听的是 b
地址值(数组是由一个地址值和地址值指向的堆空间里的数据组成的),我们通过 push
并没有改变地址值,而是改变堆栈中的值,所以没有监听到。解决方法也简单:如果是数组,再次创建一个 Proxy
对象去监听。
const obj = {
a: 1,
b: [1, 2],
};
const fnProxy = (obj) => {
const objProxy = new Proxy(obj, {
get(target, prop, receiver) {
console.log('get', prop);
if (Array.isArray(target[prop])) {
return fnProxy(target[prop]);
}
return target[prop];
},
set(target, prop, value, receiver) {
console.log('property set: ' + prop + ' = ' + value);
target[prop] = value;
return true;
},
});
return objProxy;
};
fnProxy(obj);
const objProxy = fnProxy(obj);
objProxy.b.push(10);
console.log(objProxy.b);
// get b
// get push
// get length
// property set: 2 = 10 这个 ‘2’ 是数组下标
// property set: length = 3
// get b
// [1, 2, 10]
上面的代码中将生成Proxy对象的操作封装了一个方法,如果目标值是数组,就将目标值作为入参再次调用该方法,通过递归的方式完成对数组变化的监听。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!