当前位置:首页 > JavaScript

js实现watch功能

2026-03-15 19:32:41JavaScript

使用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

性能优化建议

对于大型对象或频繁操作的对象,监听可能会影响性能。可以考虑以下优化:

js实现watch功能

  • 使用节流(throttle)或防抖(debounce)技术减少回调触发频率
  • 只监听需要的属性而非整个对象
  • 在不需要监听时取消监听
  • 使用WeakMap存储监听状态,避免内存泄漏

标签: 功能js
分享给朋友:

相关文章

vue编辑功能怎么实现

vue编辑功能怎么实现

Vue 编辑功能的实现方法 1. 数据绑定与表单处理 使用 v-model 实现双向数据绑定,将表单输入与 Vue 实例的数据属性关联。例如: <template> <inp…

js实现轮播

js实现轮播

实现基础轮播效果 使用HTML结构创建轮播容器和图片元素: <div class="carousel"> <div class="carousel-inner">…

js实现图片预览

js实现图片预览

使用FileReader API实现图片预览 通过FileReader对象读取用户选择的图片文件并显示预览: const input = document.getElementById('image…

php实现评论功能

php实现评论功能

数据库设计 创建评论表(示例结构): CREATE TABLE `comments` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id`…

js实现正交

js实现正交

正交的概念 正交在数学和计算机科学中通常指两个向量垂直或线性无关。在编程中,正交性常被用于设计模块化、低耦合的系统。 向量正交判断 判断两个向量是否正交可以通过点积是否为0来实现: functio…

js实现滚动

js实现滚动

实现滚动效果的方法 在JavaScript中实现滚动效果可以通过多种方式完成,以下是一些常见的方法: 使用window.scrollTo() window.scrollTo()方法可以将页面滚动到指…