应用场景:
使用 Object.defineProperty(obj, props, descriptor)
实现观察者模式, 其也是 vue 双向绑定 的核心, 示例如下(当改变 obj 中的 value 的时候, 自动调用相应相关函数):
var obj = {data: { list: [] },}Object.defineProperty(obj, 'list', {get() {return this.data['list']},set(val) {console.log('值被更改了')this.data['list'] = val}})
Proxy/Reflect 是 ES6 引入的新特性, 也可以使用其完成观察者模式, 示例如下(效果同上):
var obj = {value: 0}var proxy = new Proxy(obj, {set: function(target, key, value, receiver) { // {value: 0} "value" 1 Proxy {value: 0}console.log('调用相应函数')Reflect.set(target, key, value, receiver)}})proxy.value = 1 // 调用相应函数
下面来实现 sinon 框架的 spy 函数:
const sinon = {analyze: {},spy: function(obj, fnName) {const that = thisconst oldFn = Object.getOwnPropertyDescriptor(obj, fnName).valueObject.defineProperty(obj, fnName, {value: function() {oldFn()if (that.analyze[fnName]) {that.analyze[fnName].count = ++that.analyze[fnName].count} else {that.analyze[fnName] = {}that.analyze[fnName].count = 1}console.log(`${fnName} 被调用了 ${that.analyze[fnName].count} 次`)}})}}const obj = {someFn: function() {console.log('my name is someFn')}}sinon.spy(obj, 'someFn')obj.someFn()// my name is someFn// someFn 被调用了 1 次obj.someFn()// my name is someFn// someFn 被调用了 2 次
vue
在 3.0 版本上使用 Proxy
重构的原因首先罗列 Object.defineProperty()
的缺点:
Object.defineProperty()
不会监测到数组引用不变的操作(比如 push/pop
等);Object.defineProperty()
只能监测到对象的属性的改变, 即如果有深度嵌套的对象则需要再次给之绑定 Object.defineProperty()
;关于 Proxy
的优点
defineProperty
是对属性的劫持, Proxy
是对对象的劫持;