由于数组无法处理,所以要单独处理数组
知识点
复制Array.prototype里的push,pop,shift,unshift,splice,sort,reverse方法, 重写此方法 利用Object.setPrototypeOf
setPrototypeOf()
Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null
create()
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
流程
- 入参-对象
- observer-是否数组
- 将-对象-原型指向,新的数组增强方法,再通过observer类的observeArray方法,对数组逐一进行observe
- observeArray循环需要改写的7个方法
- 备份原方法,通过工具函数定义新方法
- 定义新方法
- 恢复原有方法功能
- 将类似数组(参数) 转换为数组
- 取出ob
- 处理3个冲洗插入值的方法(push,unshift,splice)将方法插入的值,再次执行observeArray,以防依旧有数组
源码
// 入口
import observe from './observe'
const obj = {
a: {
b: {
m:5
},
c: 5
},
n: 4,
g: [33,22,55]
}
observe(obj)
obj.a.c = 10
obj.g.push([33,44])
console.log(obj.g, '--------pushed-------')
// array.js
import { def } from "./utils"
const arrayPrototype = Array.prototype
// 以Array.prototype 为原型创建对象
export const arrayMethods = Object.create(arrayPrototype)
// 要被改写的7个方法,vue只有此7个列表方法数据会更新 单独改一个值不会出发数据更新
const methodsNeedTochange = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
console.log(methodsNeedTochange, '------------')
methodsNeedTochange.forEach(methodName => {
// 备份原有方法
const original = arrayPrototype[methodName];
// 定义新方法
def(arrayMethods, methodName, function () {
// 恢复原来的功能,才能使用 原有方法
const result = original.apply(this, arguments);
// 把类数组对象变为数组
const args = [...arguments];
// 把这个数组身上的__ob__取出来,__ob__已经被添加了,为什么已经被添加了?因为数组肯定不是最高层,比如obj.g属性是数组,obj不能是数组,第一次遍历obj这个对象的第一层的时候,已经给g属性(就是这个数组)添加了__ob__属性。
const ob = this.__ob__;
// 有三种方法push\unshift\splice能够插入新项,现在要把插入的新项也要变为observe的
let inserted = [];
switch (methodName) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
// splice格式是splice(下标, 数量, 插入的新项)
inserted = args.slice(2);
break;
}
// 判断有没有要插入的新项,让新项也变为响应的
if (inserted) {
// 因为是整个数组已经 实例化过了 拥有了walk,和observeArray 才可以调用
ob.observeArray(inserted);
}
// pop等方法 要返回值
return result;
}, false)
})
// observer.js
import { def } from './utils'
import defineReactive from './defineReactive'
import { arrayMethods } from './array'
import observe from './observe'
export default class Observer {
constructor(value) {
// 给实例增加了一个不可枚举的__ob__
def(value, '__ob__', this, false)
console.log('Observer构造器', value)
// Observer类的目的是:将一个正常的object转换为每个层级的属性都是响应式(可以被侦测的)的object
if (Array.isArray(value)) {
// 将数组的原型,指向arrayMethods
Object.setPrototypeOf(value, arrayMethods)
// 数组逐项observe,非数组,子元素已经在defineReactive,中处理了子项目,而数组,则需单独处理
this.observeArray(value);
} else {
this.walk(value)
}
}
// 遍历
walk(value) {
for (let i in value) {
defineReactive(value, i)
}
}
observeArray(arr) {
for (let i = 0, l = arr.length; i < l; i++) {
// 逐个进行observe
observe(arr[i])
}
}
}
// observe.js
import Observer from './observer'
export default function observe(value) {
// 如果不是对象 返回
if (typeof value != 'object') return
// 定义ob
let ob;
if ('__ob__' in value) {
ob = value.__ob__;
} else {
ob = new Observer(value)
}
return ob
}
// utils.js
export function def(obj, key, value, enumerable) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable,
writable: true,
value
});
}
// defineReactive.js
import observe from './observe'
export default function defineReactive(obj, key, val) {
if (arguments.length == 2) {
val = obj[key]
}
// 子元素observe,形成多文件互相调用成为递归
let childOb = observe(val)
console.log('-------------defineReactive', obj, key)
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
// getter
get() {
console.log('-----------------访问属性', key, ':', val)
return val
},
// setter
set(newData) {
if (val === newData) {
return false
}
console.log('-----------------设置属性', key, ':', val)
val = newData
// 当设置了新值,这个值页要被observe,防止新值又是个对象
childOb = observe(newData)
}
});
}