Object.defineProperty、Proxy、Reflect-个人总结

Object.defineProperty

前言

        用于给一个对象添加或者修改一个属性,返回操作后的对象。

        写法:Object.defineProperty(对象,属性,配置对象)

配置对象

        通过对配置对象不同的配置,可以将属性分为数据属性和存取属性。

        数据属性不包括get和set方法,包括value和writable属性;存取属性包括get和set方法,不能含有value和writable属性。即这四个具有一定的相斥性。

        无论是数据属性还是存取属性都会包含configurable和enumerable属性。

        具体看下图

Object.defineProperty、Proxy、Reflect-个人总结_第1张图片

【图来自王红元】 

解释下configurable、enumerable、writable三个属性

1、configurable:控制能否对属性描述符(也就是配置对象)的修改(false时会爆redefine的错误)能否删除该属性

2、enumerable:控制属性是否可以枚举。【forin会能遍历自身和原型上的枚举属性,Object.keys能遍历自身的枚举属性】

3、writable:控制属性值的修改,(注意是属性值,不是配置对象)

 这三个属性都是布尔类型,默认都是false。

应用

        vue2的响应式原理就是应用,在get和set时做对应的响应式处理。

注意

不要在set方法中直接修改对象的属性

        在set中直接修改对象的属性会触发“set陷阱”,陷入死循环。

        解决办法是:可以在Object.definproperty的外面定一个变量,set去修改这个变量,get去读取这个变量,以达到类似的效果,比如下方代码的val。

let obj = {
    name:'aa',
    age:12
}
Object.keys(key=>{
    let val = obj[key]	//闭包变量,不会随着遍历结束消失。又因为get也是读这个变量,看起来对象的属性值就变了
    Object.defineProperty(obj,key,{
        set(newVal){
            console.log('set',newVal)
            val = newVal
            // 不能直接obj[key] =newVal ,会死循环
        },
        get(){
            console.log('get')
            return val
        }
    })
})

obj.name = 111
console.log(obj.age)

Proxy

前言

        Proxy能创建出一个代理对象,之后对源对象的操作,都可以通过这个代理对象完成,也就实现了监听整个对象的功能。        

         写法 : new Proxy(源对象,捕获器对象)

基本使用

 直接看下面一段包含set和get捕获器的代码。

let obj = {
    name:'aa',
    age:12
}
let objProxy = new Proxy(obj,{
    // 各种捕获器
    get(target,key,receiver){
        console.log('get',target,key,receiver)
        return target[key]
    },
    set(target,key,newVal,receiver){
        target[key] = newVal
        console.log('set',target,key,receiver)
		//下面3个等式告诉我们 target就是源对象(obj),receiver就是接收对象/代理对象(objProxy)	
        console.log(target === receiver)	//false
        console.log(target === obj)	//true
        console.log(receiver === objProxy) //true
    }
    
})
objProxy.name = 'rr'//修改代理后的对象

代码说明:

        get和set捕获器的入参第一个会拿到target(源对象),最后一个入参是receiver(代理对象)。

        set中对源对象的修改就直接修改target,而不用去改obj,并且不需要借助外部变量了,也不再陷入死循环。

        【触发捕获器需要修改代理对象,而不是源对象

其他捕获器

        上面的set和get捕获器就能实现Object.defineProperty的功能,并且更强大。下面介绍其他捕获器,总共13个。见下图。

【图来自王红元】 

总结:

触发的方法/操作 捕获器名
Object.getPropertyOf()
拿对象的隐式原型
getPropertyOf()
Object.setPropertyOf()
设置对象的隐式原型
setPropertyOf()
Object.isExtensible()
用于判断对象是否可以拓展
Object.preventExtensions()能禁止拓展
isExtensiable()
Object.preventExtensions()
用于禁止往对象上添加属性,即禁止拓展
preventExtensions()
Object.getOwnPropertyDescriptor()
获取对象的属性描述符
getOwnPropertyDescriptor()
Object.defineProperty() 
用于设置对象属性
defineProperty()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
拿对象自身的普通属性和symbol属性名,
无论是否枚举
ownKeys()
in操作符 has()
属性读取 get()
属性设置 set()
delete操作符 deleteProperty()
函数调用的apply apply()
new操作符 construct()

Object.defineProperty和Proxy区别

        他们都可以用于拦截和设置对象属性,但是Proxy提供了更多的拦截操作。Proxy可以拦截所有属性,包括新增的,而defineProperty只能拦截定义时的属性,还不能拦截delete属性。

Reflect

介绍

        Reflect是一个内置的全局对象。

        Reflect也有Proxy捕获器上那13个方法。见下图:

【图来时王红元】 

设计Reflect的目的:【ai回答:】

1. 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。 现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。

2. 修改某些Object方法的返回结果,让其变得更合理。 比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

3. 让Object操作都变成函数行为。 某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect有对应的方法Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)。

4. Reflect对象的方法与Proxy对象的方法一一对应。 无论Proxy对象怎么修改默认行为,你总可以在Reflect上获取默认行为。

个人总结:之前Object太臃肿了,现在设计Reflect是为了规范js的对象操作。

补充

对象的限制方法

说明 方法
禁止往对象添加属性,返回对象 Object.preventExtensions(Obj)
禁止设置对象属性的配置 和 属性值的删除,就是将configurable设为false Object.seal(obj)
禁止对象属性的修改,冻结对象,不允许修改属性值,是浅层冻结。 Object.freeze(obj)

END

你可能感兴趣的:(js芝士,javascript,前端,vue.js)