Object.defineProperty()和Proxy相关

Vue3.0版本会将数据劫持的方式从Object.defineProperty切换为Proxy,因此找了时间从新回顾了一下属性描述,并了解了如下Proxyvue

1. Object.defineProperty

给对象的属性设置属性描述,接受三个参数bash

/*
* obj:须要定义属性的对象
* prop:定义描述的属性名
* descriptor: 属性描述
*/
Object.defineProperty(obj, prop, descriptor);
复制代码

1.1 descriptor

用于描述属性的对象,能够包含如下值app

1.1.1 get

属性在获取值的时候会调用该方法ui

var obj = {}
Object.defineProperty(obj, 'a', {
    get() {
        console.log('get a');
        return 1;
    }
})
console.log(obj.a)
// 优先输出'get a', 以后输出:1
复制代码

1.1.2 set

属性设置值的时候会调用该方法spa

var obj = {}
Object.defineProperty(obj, 'a', {
    set(value) {
        console.log(`set:${value}`);
    }
})
obj.a = 1
// 输出 `set:1`
复制代码

1.1.3 value

用于设置对象属性的初始值,没法同getset方法同时设置代理

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1
})
console.log(obj.a) // 1
复制代码

1.1.4 enumerable

设置属性是否能够枚举,用于for in枚举属性时候是否能够获取code

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

设置属性是否能够再次定义属性描述orm

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1,
    configurable: false, // 设置false没法再次配置属性
})
Object.defineProperty(obj, 'a', {
    value: 2,
})
// 抛出异常:Uncaught TypeError: Cannot redefine property 'a'
复制代码

PS:对于已有属性能够修改valueenumerablewritable的值(例如:若是obj = {a: 1}这里修改属性a的这三个值不会报错)cdn

1.1.6 writable

设置属性是否能够赋值,没法同getset方法同时设置对象

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)能够建立对象targetproxy对象,操做proxy对象的时候,根据设置的handler,能够设置对象操做的各时期的具体操做

  1. getPrototypeOf():在调用Object.getPrototypeOf()的时候
  2. setPrototypeOf():在使用Object.setPrototypeOf()的时候
  3. isExtensible():在使用Object.isExtensible()的时候
  4. preventExtensions():在使用Object.preventExtensions()的时候
  5. getOwnPropertyDescriptor():在使用Object.getOwnPropertyDescriptor()的时候
  6. defineProperty():在使用Object.defineProperty()的时候
  7. has():在使用in操做符的时候
  8. get():在获取属性值的是欧
  9. set():在设置属性值的时候
  10. deleteProperty():在delete删除属性的时候
  11. ownKeys():在Object.getOwnPropertyNames()Object.getOwnPropertySymbols()的时候
  12. apply():在对象做为方法调用的时候
  13. 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 相同点

从目的来看ProxydefineProperty都是为了扩展对象的特性,若是要用来实现MVVM,两种方案均可以完成

4.2 不一样点:

从三个方面来讲明

  1. 做用目标不一样:defineProperty主要是用于对象定义属性,注重的是设置对象中属性的描述,而Proxy用于处理对象,注重的是对象的相关操做
  2. 操做目标不一样:defineProperty的时候,是须要直接操做对象自己,来触发相关属性设置,而Proxy则须要操做new建立的proxy对象,对原对象操做并不会触发相关内容
  3. 提供的触发事件不一样:defineProperty只提供了set,get方法能够做为切入口,而Proxy提供了更丰富的对象操做切入口

总的来讲vue3.0使用Proxy的目的在于对对象劫持的时候,不用遍历全部属性,能够直接使用对象的proxy对象,同时在对象追加属性的增长劫持的时候,不用再手动使用$set添加劫持

固然和Proxy密切相关的Reflect,这个就留在下次再说了

6. 参考

MDN Object.defineProperty

MDN Proxy

本文存在的问题还望各位指正,谢谢

相关文章
相关标签/搜索