本文主要是介绍Object.defineProperty、Proxy、Reflect-个人总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Object.defineProperty
前言
用于给一个对象添加或者修改一个属性,返回操作后的对象。
写法:Object.defineProperty(对象,属性,配置对象)
配置对象
通过对配置对象不同的配置,可以将属性分为数据属性和存取属性。
数据属性不包括get和set方法,包括value和writable属性;存取属性包括get和set方法,不能含有value和writable属性。即这四个具有一定的相斥性。
无论是数据属性还是存取属性都会包含configurable和enumerable属性。
具体看下图
【图来自王红元】
解释下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] = newValconsole.log('set',target,key,receiver)//下面3个等式告诉我们 target就是源对象(obj),receiver就是接收对象/代理对象(objProxy) console.log(target === receiver) //falseconsole.log(target === obj) //trueconsole.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
这篇关于Object.defineProperty、Proxy、Reflect-个人总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!