在监听对象变化的基础上只修改了observe.js,新增了array.js
通过重写数组内的会改变数组本身的方法进行监听数组的变化
//observe.js
import { newArrayProto } from './array'
class Observer {
constructor (data) {
console.log(data,this)
// Object.defineProperty只能劫持已经存在的属性(所以vue里定义了$set,$delete)
Object.defineProperty(data,'__ob__',{
value:this,
enumerable:false//将__ob__变为不可枚举(循环的时候无法获取,避免死循环)
})
// data.__ob__ = this
//判断是否是数组
if (Array.isArray(data)) {
// 重写数组部分方法(保留数组原有的特性)
data._proto_ = newArrayProto
this.observeArray(data)
} else {
this.walk(data)
}
}
walk (data) {
//重新定义属性
Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
}
observeArray (data) {
data.forEach(item => observe(item))
}
}
export function defineReactive (target, key, value) {//闭包
observe(value) //对所有的对象进行属性劫持
Object.defineProperty(target, key, {
get () {//取值的时候会执行get
return value
},
set (newV) {//修改的时候会执行set
if (newV === value) return
observe(newV)//防止新值为对象,所以对新值进行再次代理
value = newV
},
})
}
export function observe (data) {
// 对data进行劫持
if (typeof data !== 'object' || data == null) {
return;//只对对象进行劫持
}
if(data.__ob__ instanceof Observer){
return
}
// 如果一个对象被劫持过了,就不需要再劫持了(判断一个对象是否被接吃过,可以增添一个实例,用实例来判断)
return new Observer(data)
}
//array.js重写数组中的部分方法
let oldArrayProto = Array.prototype
export let newArrayProto = Object.create(oldArrayProto)
//所有能修改原数组的方法
let methods = [
'push',//向数组的末尾添加一个或更多元素,并返回新的长度。
'pop',//删除数组的最后一个元素并返回删除的元素。
'shift',// 删除并返回数组的第一个元素。
'unshift',//向数组的开头添加一个或更多元素,并返回新的长度。
'reverse',//反转数组的元素顺序。
'sort',//对数组进行排序(可以穿进去一个函数)
'splice',//splice(n,m)删除数组的第n个到第m-1个(不包含m个)从数组中添加或删除元素。
]
methods.forEach(method => {
newArrayProto[method] = function (...args) {//重写数组方法
const result = oldArrayProto[method].call(this, ...args)//内部调用原来的方法,函数的劫持,切片编程
//需要对新增的数据再次进行劫持
let ob = this.__ob__
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
default:
break
}
console.log(inserted) //新增的内容
if (inserted) {
// 对新增的内容再次进行劫持
ob.observeArray(inserted)
}
return result
}
})