}
console.log(${key}属性被修改了
);
val = newVal;
}
})
}
我们声明一个Observer类,用于将对象的所有属性都转变为可侦测,并且,我们要给每一个可侦测的对象添加一个__ob__属性,值为value的Observer的实例,这就相当于给每一个value设置了标识,表示这个value是响应式的了。那么当value是一个对象的时候,我们拿到他的所有的key,循环遍历调用Object.defineProperty()的方法,调用get/set方法来侦测,当发现传入的是一个Object的时候,通过递归的方式,再次通过Observer类去实例这个Object。这样我们就实现了对象的侦测。
2、实现数组的侦测(可观察)
上面我们讲了对象的侦测,现在来聊聊数组的侦测。
数组的可观测与对象的可观测有所不同,因为数组不能用Object.defineProperty()去观测。那么数组如何做到可观测呢,其实大同小异,数组是通过拦截器
来做到的,那么这个拦截器是何方神圣?
我们知道,JavaScript中操作数组的原生方法,其实就是那么几个,push/pop/shift/unshift/splice/sort/reverse。这些方法都是通过Array.prototype
来访问,那么我们如果在Array.prototype上重新定义一个方法,去实现原生的方法,如newPush去实现与push同样的作用,是不是很完美。既没有修改原生方法,又可以在调用原生方法之前做了相应的操作。
let arr = [1,2,3]
// arr.push(4) 原生方法调用
Array.prototype.newPush = function(val){
console.log(‘arr被修改了’)
this.push(val)
}
arr.newPush(4)
所以,数组的拦截器就是在数组实例和Array.prototype之间重写了操作数组的方法
。这样在调用处理的方法的时候,就是调用的重写之后的方法,而不是原生方法。来看拦截器的实现:
// 源码位置:/src/core/observer/array.js
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
‘push’,
‘pop’,
‘shift’,
‘unshift’,
‘splice’,
‘sort’,
‘reverse’
]
methodsToPatch.forEach(function (method) {
const original = arrayProto[method]
Object.defineProperty(arrayMethods, method, {
enumerable: false,
configurable: true,
writable: true,
value:function mutator(…args){
const result = original.apply(this, args)
return result
}
})
})
这就是拦截器的实现,当调用数组的方法时,实际调用的是mutator函数
,所以,我们可以在这个函数中做一些操作。但是这么做还不够,只有将拦截器挂载在数组实例和Array.prototype之间才能生效,也就是将数据的__proto__属性设置为拦截器arrayMethods即可
。
augment(value, arrayMethods
, arrayKeys)
// 源码位置:/src/core/observer/index.js
export class Observer {
constructor (value) {
this.value = value
if (Array.isArray(value)) {
const augment = hasProto
-
? protoAugment
- copyAugment
augment(value, arrayMethods, arrayKeys)
} else {
this.walk(value)
}
}
}
// 能力检测:判断__proto__是否可用ÿ