描述符和代理类
描述符
Object.defineProperty()
与 getter 和 setter 功能更高级。对对象的属性添加和修改(劫持)
需要传入 3 个参数:
- obj:要在其上定义属性的对象。
- prop:要定义或修改的属性的名称。
- descriptor:将被定义或修改的属性描述符。
descriptor 对象的可定义的属性:
值(value)
值可修改性(writable)
属性值是否可以修改,改成 false 就是不可以修改
可枚举性(enumerable)
属性值是否可以枚举,当我们挨个访问属性值的时候可以访问得到与否
属性特点可修改性(configurable)
上述属性的特点是可以修改改的,如果设置 false,则上述可修改可枚举以及本身都不可以修改了,相当于被直接冻结
value 和 writable
let a = {
like: "food",
name: "Gin",
}
Object.defineProperty(a, "like", {
value: "goods",
writable: false,
})
console.log(a.like) //goods
a.like = "123"
console.log(a.like) //goods
enumerable
可枚举指的是是否能够被for……in
或者Object.keys
遍历到,默认为true
let a = {
like: "food",
name: "Gin",
}
Object.defineProperty(a, "like", {
value: "goods",
writable: false,
enumerable: false,
})
for (i in a) {
console.log(i)
} //"name"
Object.keys(a) //["name"]
注意:
给数组元素设置enumerable: false
可以不被上文的两种方法遍历到,
但是能否被for...of
遍历到,取决于它是否是生成器中一个 yield 的值。
get 和 set
在对属性操作(获取,修改)的时候会触发的操作,
不能 同时设置访问器 (get
和set
) 和wriable
或 value
let a = {}
Object.defineProperty(a, "like", {
get: (function() {
var t = 0
return function() {
return t++
}
})(),
})
//每次访问就会增加1,从0开始,并且不可修改
let a = {}
Object.defineProperty(a, "like", {
get: function() {
return this._t
},
set: function(newValue) {
console.log("你正在设置like的值")
return (this._t = newValue)
},
})
a.like = 2 //"你正在设置like的值"
console.log(a.like)
它可以帮助我们实现数据变动的监听。但是存在一个可以访问的私有变量。
Vue2 的双向数据绑定的原理就是借助这个方法。
除此之外还有 Object.defineProperties
var test = {}
Object.defineProperties(test, {
o: {
get() {},
set(v) {},
},
p: {
get() {},
set(v) {},
},
})
Object.create
创建新对象,第二个参数与描述符相仿
在传入一个参数的情况下,与 Object()方法的行为相同。
//创建一个原型为{}的对象
//有一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true,
},
})
它还可以作为类式继承(原型式继承)的规范写法
浅拷贝
拷贝对象使用Object.assign()
只能拷贝到可枚举属性,要完美的浅拷贝可以使用:
Object.create(
Object.getPrototypeOf(obj), //constructor
Object.getOwnPropertyDescriptors(obj) //包括描述符的所有属性
)
Proxy 和 Reflect
Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。
代理类 Proxy
const p = new Proxy(target, handler)
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。
Proxy 不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
提示
这里只示例最简单的组合用法,更多用法可以在MDN中查看有关文档。
let n = {
a: 0,
b: 1,
}
let m = new Proxy(n, {
get(target, key, value, proxy) {
return Reflect.get(target, key, value, proxy)
},
set(target, key, value, proxy) {
console.log("代理的对象是", target)
console.log("被修改的键是", key)
console.log("其值将会被修改为", value)
return Reflect.set(target, key, value, proxy)
},
})
m.a = 8
console.log(n.a)
其中 m 是 n 的代理对象 m 和 n 中间构成了所谓的单向数据绑定。
handler.set
捕捉 属性赋值handler.get
捕捉 属性值获取handler.has
捕捉 in 操作符handler.apply
捕捉函数调用操作handler.construct
捕捉 new 操作符handler.ownKeys()
捕捉Object.getOwnPropertyNames
方法和Object.getOwnPropertySymbols
方法
如果代理的对象是数组,则在调用一次数组方法时可能会执行多次 set 方法,因为 length 也会单独触发 set。
另外,直接n = new Proxy(n, {...})
可以覆盖自身
反映类 Reflect
Reflect 为操作对象提供了一种更优雅的方式,它使用函数的方式实现了 Object 的命令式操作。其方法与 Proxy 的 handler 参数是对应的。
检测一个对象是否存在特定属性
Reflect.has(duck, "color")
为对象添加一个新的属性
Reflect.set(duck, "eyes", "black")
更多见MDN