对象规则限制
-
我们在项目中书写对象成员和调用浏览器内置的对象API时候,经常会发现一种情况:自己设置的成员 可以枚举,但是浏览器内置的则 不可枚举。
-
我们在查看对象中某个属性或者某个对象中所有属性的规则时,有以下两种方法:
Object.getOwnPropertyDescriptor(objName,[attr])Object.getOwnPropertyDescriptors(objName)
-
任何一个对象的属性/方法,都存在自己的规则: (不)可枚举、(不)可修改、(不)可删除
let obj = { x: 123; } Object.getOwnPropertyDescriptor(obj, 'x'); ===> { configurable: true, 是否可删除 enumerable: true, 是否可枚举 writable: true, 是否可修改 value: 123, 成员值 } -
当我们使用
getOwnPrototypeSescriptor进行检测时候,可以得到四个属性- configurable 是否可以删除
- enumerable 是否可枚举
- writable 是否可修改
- value 成员值
- 当我们自己创建的私有属性成员,我们可以看到三个属性都是 TRUE,都是 可枚举、可删除、可修改的
-
Object.getOwnPropertyDescriptor(Object.prototype, 'toString') ====> { configurable: true, enumerable: false, value: ƒ toString(), writable: true, }- 检测浏览器内置属性/方法: 不可枚举、可删除、可修改
自定义属性规则
-
当我们也想修改自定义的属性和方法规则时:
Object.definePropertyObject.defineProperty(Object, attr,{ // 规则设置 }) -
defineProperty 特点:
- 成员存在,则修改该成员规则;
- 成员不存在,则新增该成员并创建规则
let obj = { x: 123 } Object.defineProperty(obj, 'x', { configurable: false, enumerable: false, value: 999, writable: true }); Object.defineProperty(obj, 'name', { configurable: false, enumerable: true, value: 'wj', writable: true })- 使用
defineProperty, 新增成员,默认规则均为 FALSE - 使用
defineProperty, 修改成员,之前是什么规则,现在依然是什么规则
let obj = { x: 123 } Object.defineProperty(obj, 'x', { value: 999, }); Object.defineProperty(obj, 'name', { value: 'wj', }); log(Object.getOwnPropertyDescriptors(obj)); ==> name: {value: 'wj', writable: false, enumerable: false, configurable: false} x: {value: 999, writable: true, enumerable: true, configurable: true}
-
defineProperty 可以对 对象中的属性 进行“数据劫持”
-
获取成员值时,触发 get 函数,get的返回值就是成员访问的结果;
-
设置成员值时,触发 set 函数,形参对应的val值就是最新设置的值;
-
设置 get\set 后,不能设置value & writable 属性,两者属于冲突状态。
let obj = { x: 123 }; let proxy = { ...obj }; Object.defineProperty(obj,'x',{ get() { // return obj.x; // 死循环 不断触发 get函数 return proxy.x; } set(val) { // obj.x = val; // 死循环,不断触发 set 函数 proxy.x = val; } }) -
为啥要进行代理:如果获取obj.x,进行get操作,返回还是本身,那么就会不断触发get。进而会死循环。所以需要有个代理对象,这个对象和原对象的数据完全相同,每次代理到这个对象上进行操作。
-
ES6 Proxy
-
proxy: 创建一个对象的代理,实现对其基本操作的拦截和自定义。【比如查找、赋值、枚举、调用等】
let obj = { a: 111, b: 222, arr: [333, 444], o: {x: 1} } let p1 = new Proxy(obj, { get(target, key, value) { log('获取',target,key); return Reflect.get(target, key) }, set(target, key, value) { log('获取', target, key, value); return Reflect.set(target, key, value) }, deleteProperty(target, key) { log('删除', target, key); return Reflect.deleteProperty(target, key) } })- p1 : 代理对象
- Reflect 可以返回操作后的结果。
- 我们后期通过p1 来操作数据,然后通过Reflect 来修改原数据,返回是否成功的结果。
-
优势:
- 直接对整体对象进行数据劫持。在vue2中使用
defineProperty需要循环每一项,判断是否为数组进行分别劫持处理。proxy可以直接对对象进行数据劫持 - 不仅可以进行set、get的操作,还可以对删除、查找has等进行操作。
- 直接对整体对象进行数据劫持。在vue2中使用