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); // 监听数组