此处使用defineProperty封装一个方法,该方法将实现对对象的监听效果
function disposehData(obj) {
let reactiveObject = {};
let handlers = {};
Object.keys(obj).forEach(key => {
Object.defineProperty(reactiveObject, key, {
get() {
if (handlers[key] && handlers[key].mode === 'all') {
handlers[key].callbacks.forEach(cb => cb(obj[key], obj[key], 'all'));
}
return obj[key];
},
set(value) {
let oldVal = obj[key];
obj[key] = value;
if (handlers[key] && handlers[key].mode === 'all') {
handlers[key].callbacks.forEach(cb => cb(oldVal, value,'all'));
}
if (handlers[key] && handlers[key].mode === undefined) {
handlers[key].callbacks.forEach(cb => cb(oldVal, value));
}
}
});
});
reactiveObject.watch = function(key, cb, mode) {
if (mode !== undefined && mode !== 'all') {
throw new Error(`Invalid mode: ${mode}. The mode parameter must be 'all'.`);
}
if (!handlers[key]) {
handlers[key] = { callbacks: [] };
}
handlers[key].callbacks.push(cb);
handlers[key].mode = mode;
};
return reactiveObject;
}
调用方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./2.js"></script>
</head>
<body>
<script>
let data = {
name: '你好',
age: 18,
arr:['1','2','3']
};
let reactiveData = disposehData(data);
reactiveData.watch('arr', (oldVal,newVal)=>{
console.log('变量被改变了', oldVal, newVal);
});
reactiveData.arr = 'hello';
</script>
</body>
</html>
值得注意的是,这里实现的是对整个对象的监听,我们需要把要监听的变量方法到一个对象中,这个vue是类似的。之后我们使用封装好的disposehData方法,将对象进行处理,处理后会增加一个watch方法,只要当监听的数据被修改,就会触发该方法。并且可以接收两个参数,oldVal, newVal,分别对应修改前的值和修改后的值
你也可以传入三个参数,第三个参数为可选参数。没有传入第三个参数,则只有在修改时才触发watch监听。如果传入了第三个参数,那第三个参数必须为‘all’,否则报错。如果第三个参数为all,那么在读取和修改属性值时,都会触发对应的 watch 回调函数,并且都会返回三个参数,第三个参数为字符串all(注意:修改时返回的前两个参数值oldVal和newVal,读取时返回的前两个参数值是一样的)。如果仅仅只是想在读取时触发监听,便可以传入第三参数‘all’,再在你自己的代码体中加上if判断即可
let data = {
name: '你好',
age: 18,
arr:['1','2','3']
};
let reactiveData = disposehData(data);
reactiveData.watch('arr', (oldVal,newVal,mode)=>{
console.log('变量被改变了', oldVal, newVal,mode);
},'all');
// console.log(reactiveObj.age);
reactiveObj.arr = 30