Object.defineproperty 核心代码 自定义实现

1// 触发更新视图
2function updateView() {
3  console.log('视图更新');
4}
5
6// 重新定义数组原型
7const oldArrayProperty = Array.prototype;
8// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
9const arrProto = Object.create(oldArrayProperty);
10['push', 'pop', 'shift', 'unshift', 'splice'].forEach((methodName) => {
11  arrProto[methodName] = function () {
12    updateView(); // 触发视图更新
13    oldArrayProperty[methodName].call(this, ...arguments);
14    // Array.prototype.push.call(this, ...arguments)
15  };
16});
17
18// 重新定义属性,监听起来
19function defineReactive(target, key, value) {
20  // 深度监听
21  observer(value);
22
23  // 核心 API
24  Object.defineProperty(target, key, {
25    get() {
26      return value;
27    },
28    set(newValue) {
29      if (newValue !== value) {
30        // 深度监听
31        observer(newValue);
32
33        // 设置新值
34        // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
35        value = newValue;
36
37        // 触发更新视图
38        updateView();
39      }
40    },
41  });
42}
43
44// 监听对象属性
45function observer(target) {
46  if (typeof target !== 'object' || target === null) {
47    // 不是对象或数组
48    return target;
49  }
50
51  // 污染全局的 Array 原型
52  // Array.prototype.push = function () {
53  //     updateView()
54  //     ...
55  // }
56
57  if (Array.isArray(target)) {
58    target.__proto__ = arrProto;
59  }
60
61  // 重新定义各个属性(for in 也可以遍历数组)
62  for (let key in target) {
63    defineReactive(target, key, target[key]);
64  }
65}
66
67// 准备数据
68const data = {
69  name: 'zhangsan',
70  age: 20,
71  info: {
72    address: '北京', // 需要深度监听
73  },
74  nums: [10, 20, 30],
75};
76
77// 监听数据
78observer(data);
79
80// 测试
81// data.name = 'lisi'
82// data.age = 21
83// // console.log('age', data.age)
84// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
85// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
86// data.info.address = '上海' // 深度监听
87data.nums.push(4); // 监听数组