Object.defineProperty 和 Proxy
相同点
- 可以劫持数据,并对属性描述
- 拥有 getter,setter 函数
- 劫持数据类型:Object,Aarry
不同点
- defineProperty 只能劫持对象的属性
- defineProperty 对新增的属性劫持不到,需要手动进行 Observe
- defineProperty 劫持数组,下标当成 key 值用(由于性能问题,vue 放弃这个特性)
- 数组通过 pop 或 shift 删除元素,会删除并更新索引,也会触发 getter,setter 方法
- 通过 push 或 unshift 会增加索引,新增加的索引(需要重新 Observe)
- 插入,删除操作会使被劫持的索引发生改变,发生改变的索引会触发 getter,setter 操作
- Proxy 直接代理对象
- 可以监听到 Object 新增的属性
- Proxy 劫持数组便利
- Proxy 浏览器兼容不太好
- Proxy 支持 13 种拦截操作
- get(target, propKey, receiver):拦截对象属性的读取,比如 proxy.foo 和 proxy[‘foo’]。
- set(target, propKey, value, receiver):拦截对象属性的设置,比如 proxy.foo = v 或 proxy[‘foo’] = v,返回一个布尔值。
- has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值。
- deleteProperty(target, propKey):拦截 delete proxy[propKey] 的操作,返回一个布尔值。
- ownKeys(target):拦截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in 循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而 Object.keys() 的返回结果仅包括目标对象自身的可遍历属性。
- getOwnPropertyDescriptor(target, propKey):拦截 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
- defineProperty(target, propKey, propDesc):拦截 Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
- preventExtensions(target):拦截 Object.preventExtensions(proxy),返回一个布尔值。
- getPrototypeOf(target):拦截 Object.getPrototypeOf(proxy),返回一个对象。
- isExtensible(target):拦截 Object.isExtensible(proxy),返回一个布尔值。
- setPrototypeOf(target, proto):拦截 Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
- apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如 proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy(…args)。
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function defineGet() {
console.log(`get key: ${key} value: ${value}`)
return value
},
set: function defineSet(newVal) {
console.log(`set key: ${key} value: ${newVal}`)
value = newVal
}
})
}
function observe(data) {
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key])
})
}
let arr =[1, 2, 3]
observe(arr)
arr.splice(1,1)
输出:
get key: 1 value: 2
get key: 2 value: 3
set key: 1 value: 3