当前位置:首页 > 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 可以拦截对象的多种操作,适合监听嵌套对象或数组的变化。

js实现watch功能

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

监听数组变化

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

js实现watch功能

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 等库。

// 使用 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实现跳转

使用 window.location 跳转 通过修改 window.location.href 或直接使用 window.location 实现页面跳转,适用于普通跳转或带参数的 URL。 // 方…

js实现计算器

js实现计算器

实现基础计算器功能 使用JavaScript创建一个基础计算器需要处理数字输入、运算符逻辑和结果显示。以下是一个简单的实现示例: let currentInput = '0'; let previo…

js实现图片放大缩小

js实现图片放大缩小

实现图片放大缩小的基础方法 使用CSS的transform: scale()属性可以快速实现图片的放大缩小效果。通过JavaScript动态修改该属性值: const img = document.…

js实现抽奖

js实现抽奖

实现抽奖功能的基本思路 抽奖功能的核心是随机选择奖项并展示结果。可以通过数组存储奖项,利用随机数生成索引,最后通过动画增强用户体验。 准备奖项数据 定义一个数组存储奖项信息,每个奖项可以包含名称、图…

uniapp实现选项卡功能

uniapp实现选项卡功能

实现选项卡功能的基本方法 在uniapp中实现选项卡功能通常需要使用uni-segmented-control组件或自定义样式结合swiper组件。以下是两种常见实现方式: 使用uni-se…

vue实现答题功能

vue实现答题功能

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