目录

JS 中监听对象中变量的变化

一、利用 Proxy 实现监听 js 中变量的变化

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var observe1 = (object, onChange) => {
  const handler = {
    get(target, property, receiver) {
      try {
        return new Proxy(target[property], handler);
      } catch (err) {
        return Reflect.get(target, property, receiver);
      }
    },
    set(target, key, value, receiver) {
      onChange(value);
      return Reflect.set(target, key, value, receiver);
    },
  };
  return new Proxy(object, handler);
};

var obj = {
  foo: false,
  a: {
    x: {
      y: 4,
    },
    b: [
      {
        c: false,
      },
    ],
  },
};

var watchedObj = observe1(obj, (val) => {
  console.log(`哈哈哈,监听到值变化为${val}了`);
});

watchedObj.foo = true; //哈哈哈,监听到值变化为true了

watchedObj.a.x.y = 5; ////哈哈哈,监听到值变化为5了

watchedObj.a.b[0].c = true; //哈哈哈,监听到值变化为true了

以上的思路主要是参考自 Github 仓库 on-change,有兴趣可以点进去看看。

Proxy 还不太了解的同学可以先读读阮一峰的这篇文章,或者 MDN 的这篇文章

二、用 Object.defineProperty 实现监听 js 中变量的变化

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function observe2(data, onChange) {
  if (!data || typeof data !== "object") {
    return;
  }
  // 取出所有属性遍历
  Object.keys(data).forEach(function (key) {
    defineReactive(data, key, data[key], onChange);
  });

  function defineReactive(data, key, val, onChange) {
    observe2(val, onChange); // 监听子属性
    Object.defineProperty(data, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      get: function () {
        return val;
      },
      set: function (newVal) {
        onChange(newVal);
        val = newVal;
      },
    });
  }
}

var obj = {
  foo: false,
  a: {
    x: {
      y: 4,
    },
    b: [
      {
        c: false,
      },
    ],
  },
};

observe2(obj, (val) => {
  console.log(`哈哈哈,监听到值变化为${val}了`);
});

obj.foo = true; //哈哈哈,监听到值变化为true了

obj.a.x.y = 5; ////哈哈哈,监听到值变化为5了

obj.a.b[0].c = true; //哈哈哈,监听到值变化为true了

这样写的话也能实现第一种用 Proxy 实现的功能,不过两者也有差异:

1、如果写一个新的元素,则第一种方法能监听到,但是第二种方法监听不到 2、还有一个问题就是修改数组的length,以及数组的push等变异方法是无法触发 setter 的,也就是这两种情况第二种方法也监听不到

试试如下验证代码:

1
2
3
4
5
6
7
watchedObj.a.b.length = 4;
watchedObj.a.x.z = "tt";
watchedObj.a.b.push("2");

obj.a.b.length = 4;
obj.a.x.z = "tt";
obj.a.b.push("2");