
Vue3.0版本会将数据劫持的方式从Object.defineProperty
切换为Proxy
,所以找了时间重新回顾了一下属性描述,并了解了以下Proxy
1. Object.defineProperty
给对象的属性设置属性描述,接受三个参数
/*
* obj:需要定义属性的对象
* prop:定义描述的属性名
* descriptor: 属性描述
*/
Object.defineProperty(obj, prop, descriptor);
1.1 descriptor
用于描述属性的对象,可以包含以下值
1.1.1 get
属性在获取值的时候会调用该方法
var obj = {}
Object.defineProperty(obj, 'a', {
get() {
console.log('get a');
return 1;
}
})
console.log(obj.a)
// 优先输出'get a', 之后输出:1
1.1.2 set
属性设置值的时候会调用该方法
var obj = {}
Object.defineProperty(obj, 'a', {
set(value) {
console.log(`set:${value}`);
}
})
obj.a = 1
// 输出 `set:1`
1.1.3 value
用于设置对象属性的初始值,无法同get
和set
方法同时设置
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1
})
console.log(obj.a) // 1
1.1.4 enumerable
设置属性是否可以枚举,用于for in
枚举属性时候是否可以获取
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
enumerable: false, // 设置false无法通过for in获取
})
console.log(obj.a) // 1
for(let key in obj) {
console.log(`key:${key}`) // 不会执行
}
1.1.5 configurable
设置属性是否可以再次定义属性描述
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false, // 设置false无法再次配置属性
})
Object.defineProperty(obj, 'a', {
value: 2,
})
// 抛出异常:Uncaught TypeError: Cannot redefine property 'a'
PS:对于已有属性可以修改value
和enumerable
和writable
的值(例如:如果obj = {a: 1}
这里修改属性a
的这三个值不会报错)
1.1.6 writable
设置属性是否可以赋值,无法同get
和set
方法同时设置
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
writable: false, // 设置false,无法被普通赋值
})
obj.a = 2
console.log(obj.a) // 1
1.2 Object.preventExtensions()
阻止对象扩展新的属性,不过并不限制对象原型上的属性扩展
var obj = {a: 1}
Object.preventExtensions(obj)
obj.b = 2 // 严格模式下抛出TypeError异常
console.log(obj) // {a: 1}
可以使用Object.isExtensible(obj)
来判断是否已经阻止扩展了
1.3 Object.seal()
将使得对象禁止扩展属性,同时禁止现有属性的configurable
var obj = {a: 1}
Object.seal(obj)
console.log(Object.isExtensible(obj)) // false
Object.defineProperty(obj, 'a', {
value: 2
}) // 抛出异常
相当于Object.preventExtensions
的基础上,将已有属性的configurable
都设置为false
var obj = {a: 1}
Object.preventExtensions(obj)
Object.defineProperty(obj, 'a', {
configurable: false
})
console.log(Object.isSealed(obj)) // true
可以使用Object.isSealed(obj)
判断是否属性属于该情况
1.4 Object.freeze()
在Object.seal()
的基础上,将属性的writable
设置为false
var obj = {a: 1}
Object.freeze(obj)
console.log(Object.isSealed(obj)) // true
obj.a = 2
console.log(obj.a) // 1
相当于
var obj = {a: 1}
Object.seal(obj)
Object.defineProperty(obj, 'a', {
writable: false
})
console.log(Object.isFrozen(obj)) // true
可以使用Object.isFrozen(obj)
判断是否属性属于该情况
2. Object.defineProperties
同Object.defineProperty
,可一次性批量定义多个对象属性
Object.defineProperties(obj, {
prop1: {
get() {}
set() {}
...
},
prop2: {
get() {}
set() {}
...
}
})
3. Proxy
使用Proxy可以创建一个对象的代理
2.1 new Proxy()
使用new Proxy(target, handler)
可以创建对象target
的proxy
对象,操作proxy
对象的时候,根据设置的handler
,可以设置对象操作的各时期的具体操作
getPrototypeOf()
:在调用Object.getPrototypeOf()
的时候setPrototypeOf()
:在使用Object.setPrototypeOf()
的时候isExtensible()
:在使用Object.isExtensible()
的时候preventExtensions()
:在使用Object.preventExtensions()
的时候getOwnPropertyDescriptor()
:在使用Object.getOwnPropertyDescriptor()
的时候defineProperty()
:在使用Object.defineProperty()
的时候has()
:在使用in
操作符的时候get()
:在获取属性值的是欧set()
:在设置属性值的时候deleteProperty()
:在delete
删除属性的时候ownKeys()
:在Object.getOwnPropertyNames()
和Object.getOwnPropertySymbols()
的时候apply()
:在对象作为方法调用的时候construct()
:在使用new
操作符的时候
以set
举例说明:
var obj = {}
var proxy = new Proxy(obj, {
set (target, prop, value) {
console.log('set value')
target[prop] = value
}
})
proxy.a = 1 // 'set value'
console.log(obj) // {a: 1}
操作proxy
对象可以修改对应对象的属性信息,但是直接操作target
对象,并不会触发proxy
对象中设置的操作:
var obj = {}
var proxy = new Proxy(obj, {
set (target, prop, value) {
console.log('set value')
target[prop] = value
}
})
obj.a = 1 // 并不会输出任何信息
console.log(obj) // {a: 1}
2.2 Proxy.revocable()
创建一个可以revocable对象,可以在需要废弃proxy对象的时候销毁
var revocable = Proxy.revocable({}, {
set (target, prop, value) {
console.log('set value')
target[prop] = value
}
})
revocable.proxy.a = 1 // 'set value'
revocable.revoke() // 销毁对象
revocable.proxy.a = 2 // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked
4. 总结
4.1 相同点
从目的来看Proxy
和defineProperty
都是为了扩展对象的特性,如果要用来实现MVVM,两种方案都可以完成
4.2 不同点:
从三个方面来说明
- 作用目标不同:
defineProperty
主要是用于对象定义属性,注重的是设置对象中属性的描述,而Proxy
用于处理对象,注重的是对象的相关操作 - 操作目标不同:
defineProperty
的时候,是需要直接操作对象本身,来触发相关属性设置,而Proxy
则需要操作new
创建的proxy
对象,对原对象操作并不会触发相关内容 - 提供的触发事件不同:
defineProperty
只提供了set
,get
方法可以作为切入口,而Proxy
提供了更丰富的对象操作切入口
总的来说vue3.0使用Proxy
的目的在于对对象劫持的时候,不用遍历所有属性,可以直接使用对象的proxy
对象,同时在对象追加属性的增加劫持的时候,不用再手动使用$set
添加劫持
当然和Proxy
密切相关的Reflect
,这个就留在下次再说了
6. 参考
本文存在的问题还望各位指正,谢谢