js实现watch功能
使用Proxy实现数据监听
Proxy是ES6引入的新特性,可以用于拦截和自定义对象的基本操作。通过Proxy可以方便地实现数据监听功能。
function watch(obj, callback) {
return new Proxy(obj, {
get(target, prop) {
return target[prop]
},
set(target, prop, value) {
const oldValue = target[prop]
target[prop] = value
callback(prop, oldValue, value)
return true
}
})
}
// 使用示例
const obj = { a: 1 }
const watchedObj = watch(obj, (prop, oldVal, newVal) => {
console.log(`属性${prop}从${oldVal}变为${newVal}`)
})
watchedObj.a = 2 // 输出: 属性a从1变为2
使用Object.defineProperty实现
对于不支持Proxy的环境,可以使用Object.defineProperty来实现类似功能。

function watch(obj, callback) {
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
const oldVal = value
value = newVal
callback(key, oldVal, newVal)
}
})
})
return obj
}
// 使用示例
const obj = { a: 1 }
watch(obj, (prop, oldVal, newVal) => {
console.log(`属性${prop}从${oldVal}变为${newVal}`)
})
obj.a = 2 // 输出: 属性a从1变为2
深度监听实现
上述方法只能监听对象第一层属性变化,下面实现深度监听。

function deepWatch(obj, callback, path = []) {
if (typeof obj !== 'object' || obj === null) return obj
const handler = {
get(target, prop) {
const value = target[prop]
return deepWatch(value, callback, [...path, prop])
},
set(target, prop, value) {
const oldValue = target[prop]
target[prop] = deepWatch(value, callback, [...path, prop])
callback([...path, prop], oldValue, target[prop])
return true
}
}
return new Proxy(obj, handler)
}
// 使用示例
const obj = { a: { b: 1 } }
const watchedObj = deepWatch(obj, (path, oldVal, newVal) => {
console.log(`属性${path.join('.')}从${oldVal}变为${newVal}`)
})
watchedObj.a.b = 2 // 输出: 属性a.b从1变为2
监听数组变化
监听数组需要特殊处理,因为Proxy对数组的push/pop等方法也需要拦截。
function watchArray(arr, callback) {
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
arrayMethods[method] = function(...args) {
const result = original.apply(this, args)
callback(method, args, this)
return result
}
})
return new Proxy(arr, {
get(target, prop) {
if (prop in arrayMethods) {
return arrayMethods[prop]
}
return target[prop]
},
set(target, prop, value) {
const oldValue = target[prop]
target[prop] = value
callback('set', prop, oldValue, value)
return true
}
})
}
// 使用示例
const arr = [1, 2, 3]
const watchedArr = watchArray(arr, (method, args, arr) => {
console.log(`调用了${method}方法,参数为${args},当前数组为${arr}`)
})
watchedArr.push(4) // 输出: 调用了push方法,参数为4,当前数组为1,2,3,4
性能优化建议
对于大型对象或频繁操作的对象,监听可能会影响性能。可以考虑以下优化:
- 使用节流(throttle)或防抖(debounce)技术减少回调触发频率
- 只监听需要的属性而非整个对象
- 在不需要监听时取消监听
- 使用WeakMap存储监听状态,避免内存泄漏






