当前位置:首页 > 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来实现类似功能。

js实现watch功能

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

深度监听实现

上述方法只能监听对象第一层属性变化,下面实现深度监听。

js实现watch功能

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存储监听状态,避免内存泄漏

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

相关文章

vue实现按钮实现点击功能

vue实现按钮实现点击功能

Vue 按钮点击功能实现 在 Vue 中实现按钮点击功能可以通过多种方式完成,以下是常见的实现方法: 方法一:使用 v-on 指令 通过 v-on:click 或简写 @click 绑定事件:…

vue实现分页功能

vue实现分页功能

Vue 分页功能实现 在 Vue 中实现分页功能通常需要结合后端 API 或前端数据分页逻辑。以下是两种常见的实现方式: 前端分页实现 适用于数据量较小的情况,直接在 Vue 组件中处理分页逻辑。…

js实现全屏

js实现全屏

实现全屏的基本方法 使用JavaScript实现全屏功能主要依赖Element.requestFullscreen()方法。现代浏览器均支持此API,但不同浏览器可能需要添加前缀。 // 触发全屏…

js实现图片预览

js实现图片预览

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

vue 实现 功能

vue 实现 功能

Vue.js 是一个流行的前端框架,用于构建用户界面和单页应用程序。以下是一些常见的 Vue 功能实现方法: 数据绑定 Vue 的核心特性之一是数据绑定,可以通过 v-model 指令实现双向数据绑…

js实现论坛

js实现论坛

实现论坛的基本功能 使用JavaScript实现一个论坛需要结合前端和后端技术。前端可以使用React、Vue或Angular等框架,后端可以选择Node.js配合Express或Koa框架。数据库可…