当前位置:首页 > JavaScript

js实现watch功能

2026-04-07 12:56:54JavaScript

使用 Object.defineProperty 实现基本监听

通过 Object.defineProperty 可以监听对象属性的变化。定义一个 watch 函数,当目标属性发生变化时触发回调。

function watch(obj, prop, callback) {
  let value = obj[prop];
  Object.defineProperty(obj, prop, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        const oldValue = value;
        value = newValue;
        callback(newValue, oldValue);
      }
    },
  });
}

// 示例
const user = { name: 'Alice' };
watch(user, 'name', (newVal, oldVal) => {
  console.log(`Name changed from ${oldVal} to ${newVal}`);
});
user.name = 'Bob'; // 输出: Name changed from Alice to Bob

使用 Proxy 实现深度监听

Proxy 可以拦截对象的多种操作,适合监听嵌套对象或数组的变化。

function deepWatch(obj, callback) {
  return new Proxy(obj, {
    set(target, prop, value) {
      const oldValue = target[prop];
      target[prop] = value;
      callback(prop, oldValue, value);
      return true;
    },
    get(target, prop) {
      const value = target[prop];
      return typeof value === 'object' && value !== null 
        ? deepWatch(value, callback)
        : value;
    },
  });
}

// 示例
const state = deepWatch({ user: { name: 'Alice' } }, (prop, oldVal, newVal) => {
  console.log(`Property ${prop} changed from ${oldVal} to ${newVal}`);
});
state.user.name = 'Bob'; // 输出: Property name changed from Alice to Bob

监听数组变化

通过重写数组原型方法实现对数组操作的监听。

function watchArray(arr, callback) {
  const arrayProto = Array.prototype;
  const arrayMethods = Object.create(arrayProto);
  const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

  methods.forEach(method => {
    arrayMethods[method] = function(...args) {
      const result = arrayProto[method].apply(this, args);
      callback(method, args, this);
      return result;
    };
  });

  arr.__proto__ = arrayMethods;
  return arr;
}

// 示例
const list = watchArray([1, 2], (method, args, arr) => {
  console.log(`Array ${method} called with args: ${args}`);
});
list.push(3); // 输出: Array push called with args: 3

结合 ProxyReflect 实现响应式系统

通过 ProxyReflect 构建更完善的响应式系统,支持依赖收集和触发更新。

const reactiveMap = new WeakMap();
let activeEffect = null;

function track(target, prop) {
  if (activeEffect) {
    let depsMap = reactiveMap.get(target);
    if (!depsMap) {
      depsMap = new Map();
      reactiveMap.set(target, depsMap);
    }
    let dep = depsMap.get(prop);
    if (!dep) {
      dep = new Set();
      depsMap.set(prop, dep);
    }
    dep.add(activeEffect);
  }
}

function trigger(target, prop) {
  const depsMap = reactiveMap.get(target);
  if (depsMap && depsMap.has(prop)) {
    depsMap.get(prop).forEach(effect => effect());
  }
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, prop) {
      track(target, prop);
      return Reflect.get(target, prop);
    },
    set(target, prop, value) {
      Reflect.set(target, prop, value);
      trigger(target, prop);
      return true;
    },
  });
}

function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

// 示例
const state = reactive({ count: 0 });
effect(() => {
  console.log(`Count is: ${state.count}`);
});
state.count++; // 输出: Count is: 1

使用第三方库实现监听

若需要更成熟的解决方案,可直接使用 Vue3reactiveMobX 等库。

js实现watch功能

// 使用 Vue3 的 reactive
import { reactive, watchEffect } from 'vue';

const state = reactive({ count: 0 });
watchEffect(() => {
  console.log(`Count is: ${state.count}`);
});
state.count++; // 输出: Count is: 1

// 使用 MobX
import { observable, autorun } from 'mobx';

const store = observable({ count: 0 });
autorun(() => {
  console.log(`Count is: ${store.count}`);
});
store.count++; // 输出: Count is: 1

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

相关文章

js实现复制功能

js实现复制功能

使用 document.execCommand 方法 这种方法适用于较旧的浏览器,但在现代浏览器中可能被逐步淘汰。通过创建一个临时的 textarea 元素,将文本内容放入其中,然后执行复制命令。…

php登陆功能实现

php登陆功能实现

实现PHP登录功能 数据库准备 创建一个用户表存储登录信息,基本字段包括用户名、密码(需加密)等: CREATE TABLE users ( id INT AUTO_INCREMENT PR…

js实现自举

js实现自举

实现 JavaScript 自举的方法 JavaScript 自举(Bootstrap)通常指通过 JavaScript 动态加载自身或依赖的脚本,实现运行时环境的初始化或扩展。以下是几种常见实现方式…

vue实现答题功能

vue实现答题功能

Vue实现答题功能 数据准备 创建一个包含题目、选项和正确答案的数据结构,通常是一个数组对象。每个题目对象包含问题文本、选项数组和正确答案索引。 const questions = [ {…

vue实现付费功能

vue实现付费功能

Vue 实现付费功能的方法 集成第三方支付平台 选择适合的第三方支付平台(如支付宝、微信支付、Stripe、PayPal等),并按照其文档进行集成。在 Vue 项目中,可以通过引入支付平台的 SDK…

js 实现滚动

js 实现滚动

实现滚动的方法 使用 window.scrollTo() window.scrollTo() 方法可以滚动到文档中的特定位置。可以指定 x 和 y 坐标,或者使用平滑滚动的选项。 // 滚动到指定位…