问题:Object.difineProperty
和 proxy
有什么区别?
Object.defineProperty
和 Proxy
是用于实现响应式数据的两种不同方式。
Object.defineProperty
Object.defineProperty
通过直接修改对象的属性描述符来实现数据的劫持。Vue 2.x 中就是通过 Object.defineProperty 来实现数据的响应式。但是 Object.defineProperty 有一些限制,比如只能劫持已经存在的属性,无法对新增属性或删除属性进行劫持。这就导致 Vue 2.x 需要在创建实例之前声明数据属性,否则无法实现响应式。
Object.defineProperty(obj, prop, descriptor)
属性 | 类型 | 描述 |
---|---|---|
value | 任意类型 | 设置属性的值。 |
writable | 布尔值 | 指定属性是否可写。默认为 false,即属性是只读的。 |
enumerable | 布尔值 | 指定属性是否可枚举。默认为 false,即属性不会出现在 for…in 循环和 Object.keys() 中。 |
configurable | 布尔值 | 指定属性是否可配置。默认为 false,即不可删除并且禁止修改属性的特性(writable、enumerable 和 configurable)。 |
get | 函数类型 | 获取属性值的函数。 |
set | 函数类型 | 设置属性值的函数。 |
示例:
let obj = {}
Object.defineProperty(obj, "b", {
get() {
return bValue;
},
set(newValue) {
bValue = newValue;
},
enumerable: true,
configurable: true,
});
proxy
而 Vue 3.0 使用了 Proxy,它是 ES6 新增的特性。**Proxy 可以对整个对象进行劫持,包括对新增属性和删除属性的劫持。**它提供了一种编程范式的改变,可以更加灵活地监听和代理对象的操作。
const p = new Proxy(target, handler)
方法 | 作用 |
---|---|
get(target, prop, receiver) | 拦截对目标对象属性的读取操作,并返回相应的值。 |
set(target, prop, value, receiver) | 拦截对目标对象属性的写入操作,并进行相应的处理。 |
has(target, prop) | 拦截对 in 操作符的操作,判断属性是否存在于目标对象中。 |
deleteProperty(target, prop) | 拦截对 delete 操作符的操作,删除目标对象的指定属性。 |
apply(target, thisArg, arguments) | 拦截函数的调用操作,并进行相应的处理。 |
construct(target, arguments, newTarget) | 拦截对目标对象使用 new 操作符创建实例的操作,并进行相应的处理。 |
getPrototypeOf(target) | 拦截对目标对象的原型的读取操作,并返回相应的值。 |
setPrototypeOf(target, proto) | 拦截对目标对象的原型的写入操作,并进行相应的处理。 |
isExtensible(target) | 拦截对目标对象是否可扩展的判断操作。 |
preventExtensions(target) | 拦截对目标对象添加新属性的操作,并进行相应的处理。 |
getOwnPropertyDescriptor(target, prop) | 拦截对目标对象属性描述符的读取操作,并返回相应的属性描述符。 |
defineProperty(target, prop, descriptor) | 拦截对目标对象属性描述符的写入操作,并进行相应的处理。 |
ownKeys(target) | 拦截对 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 的操作,返回目标对象所有自身属性的键名。 |
enumerate(target) | 拦截对 for…in 循环的操作,返回目标对象所有可枚举的属性名。 |
通过在 handler 中实现这些方法,我们可以自定义拦截操作的行为,并对底层的对象进行劫持和改变。这使得我们能够更灵活地操作和控制对象的访问、修改和行为。
示例:
onst handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37;
}
};
const p = new Proxy({}, handler);
对比
Proxy
相比于 Object.defineProperty
的优势:
- Proxy 可以代理整个对象,而不是单个属性,可以实现更细粒度的拦截和操作。
- Proxy 可以监听新增属性和删除属性的操作,不需要事先声明属性。
Object.defineProperty
相比于 Proxy
的·优势:
Object.defineProperty
支持 IE9+ 浏览器- 可处理原始类型的相应,vue3中的
ref
延伸
vue3 中,响应式分引用类型、原始类型。
reactive()
仅对引用类型有效,原始类型无效 – 因为 JavaScript 没有可以作用于所有值类型的 “引用” 机制;ref()
方法来允许我们创建可以使用任何值类型的响应式,传入参数的值包装为一个带.value
属性的 ref 对象。
Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用。
注意:替换一个响应式对象(引用地址 => 原始类型),也需要使用 ref
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key)
}
})
}
function ref(value) {
const refObject = {
get value() {
track(refObject, 'value')
return value
},
set value(newValue) {
value = newValue
trigger(refObject, 'value')
}
}
return refObject
}