js watch 实现


实现 JavaScript 的 watch 功能
在 JavaScript 中实现类似 Vue 的 watch 功能,可以通过以下几种方式:
使用 Proxy 实现数据监听
function createWatcher(target, callback) {
return new Proxy(target, {
set(obj, prop, value) {
const oldValue = obj[prop];
obj[prop] = value;
callback(prop, oldValue, value);
return true;
}
});
}
const data = { count: 0 };
const watchedData = createWatcher(data, (key, oldValue, newValue) => {
console.log(`属性 ${key} 从 ${oldValue} 变为 ${newValue}`);
});
watchedData.count = 1; // 输出: 属性 count 从 0 变为 1
使用 Object.defineProperty 实现
function defineReactive(obj, key, val, callback) {
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
const oldVal = val;
val = newVal;
callback(key, oldVal, newVal);
}
});
}
const data = {};
defineReactive(data, 'message', 'hello', (key, oldVal, newVal) => {
console.log(`${key} changed from ${oldVal} to ${newVal}`);
});
data.message = 'world'; // 输出: message changed from hello to world
使用 ES6 的 Reflect 和 Proxy
class Watcher {
constructor(data, callback) {
this.data = data;
this.callback = callback;
return this.proxy();
}
proxy() {
return new Proxy(this.data, {
set: (target, key, value) => {
const oldValue = target[key];
Reflect.set(target, key, value);
this.callback(key, oldValue, value);
return true;
}
});
}
}
const obj = { name: 'Alice' };
const watchedObj = new Watcher(obj, (key, oldVal, newVal) => {
console.log(`属性 ${key} 变化: ${oldVal} -> ${newVal}`);
});
watchedObj.name = 'Bob'; // 输出: 属性 name 变化: Alice -> Bob
实现深度监听
function deepWatch(obj, callback, path = []) {
return new Proxy(obj, {
get(target, key) {
const value = Reflect.get(target, key);
if (typeof value === 'object' && value !== null) {
return deepWatch(value, callback, [...path, key]);
}
return value;
},
set(target, key, value) {
const oldValue = target[key];
const result = Reflect.set(target, key, value);
if (result) {
const fullPath = [...path, key].join('.');
callback(fullPath, oldValue, value);
}
return result;
}
});
}
const nestedObj = { user: { name: 'John' } };
const watchedNested = deepWatch(nestedObj, (path, oldVal, newVal) => {
console.log(`${path} changed from ${oldVal} to ${newVal}`);
});
watchedNested.user.name = 'Doe'; // 输出: user.name changed from John to Doe
注意事项
- Proxy 无法监听数组的某些方法(如 push/pop),需要额外处理
- Object.defineProperty 对数组支持有限,且无法监听新增属性
- 深度监听可能影响性能,应根据实际需求决定监听层级
- 现代前端框架(如 Vue 3)已使用 Proxy 替代 Object.defineProperty
以上方法可以根据具体需求选择使用,Proxy 方案更现代且功能更强大,但需要考虑浏览器兼容性。






