Proxy 、Relect、响应式
Proxy 、Relect、响应式
1. 监听对象的操作
-
需求:有一个对象,我们希望监听这个对象中的属性被设置或获取的过程
- 可以通过属性描述符中的存储属性描述符来做到
-
这段代码就利用了
Object.defineProperty
的存储属性描述符来对属性的操作进行监听const obj = { name: 'why', age: 18 } Object.keys(obj).forEach((key) => { let value = obj[key] Object.defineProperty(obj, key, { get: function () { console.log(`监听到obj对象的${ key}属性被访问了`) return value }, set: function (newValue) { console.log(`监听到obj对象的${ key}属性被设置值`) value = newValue } }) }) obj.name = 'kobe' obj.age = 30 console.log(obj.name) console.log(obj.age) /* 监听到obj对象的name属性被设置值 监听到obj对象的age属性被设置值 监听到obj对象的name属性被访问了 kobe 监听到obj对象的age属性被访问了 30 */
-
属性描述符监听对象的缺点:
- 首先,
Object.defineProperty
设计的初衷,不是为了去监听截止一个对象中所有的属性的- 我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符
- 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么
Object.defineProperty
是无能为力的 - 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象
- Ps: 原来的对象是 数据属性描述符,通过
Object.defineProperty
变成了 访问属性描述符
- 首先,
2. Proxy基本使用
-
在ES6中,新增了一个Proxy类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的:
- 也就是说,如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象)
- 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作
-
将上面的案例用
Proxy
来实现一次:- 首先,我们需要
new Proxy
对象,并且传入需要侦听的对象以及一个处理对象,可以称之为handler
;
- 首先,我们需要
-
const p = new Proxy(target, handler)
-
其次,我们之后的操作都是直接对
Proxy
的操作,而不是原有的对象,因为我们需要在handler
里面进行侦听const obj = { name: 'why', age: 18 } const objProxy = new Proxy(obj, { // 获取值时的捕获器 get: function (target, key) { console.log(`监听到obj对象的${ key}属性被访问了`) return target[key] }, // 设置值时的捕获器 set: function (target, key, newValue) { console.log(`监听到obj对象的${ key}属性被设置值`) target[key] = newValue } }) console.log(objProxy.name) console.log(objProxy.age) objProxy.name = 'kobe' objProxy.age = 30 console.log(obj.name) console.log(obj.age) /* 监听到obj对象的name属性被访问了 why 监听到obj对象的age属性被访问了 18 监听到obj对象的name属性被设置值 监听到obj对象的age属性被设置值 kobe 30 */
2.1 Proxy 的 set 和 get 捕获器
- 如果我们想要侦听某些具体的操作,那么就可以在
handler
中添加对应的捕捉器(Trap) set
和get
分别对应的是函数类型set
函数有四个参数:target
:目标对象(侦听的对象)property
:将被设置的属性key
value
:新属性值receiver
:调用的代理对象
get
函数有三个参数target
:目标对象(侦听的对象)property
:被获取的属性key
receiver
:调用的代理对象
2.2 Proxy 所有捕获器 (13个)
-
handler.getPrototypeOf()
Object.getPrototypeOf
方法的捕捉器
-
handler.setPrototypeOf()
Object.setPrototypeOf
方法的捕捉器
-
handler.isExtensible()
Object.isExtensible
方法的捕捉器
-
handler.preventExtensions()
Object.preventExtensions
方法的捕捉器
-
handler.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor
方法的捕捉器
-
handler.defineProperty()
Object.defineProperty
方法的捕捉器
-
handler.ownKeys()
Object.getOwnPropertyNames
方法和Object.getOwnPropertySymbols
方法的捕捉器
-
handler.has()
in
操作符的捕捉器
-
handler.get()
- 属性读取操作的捕捉器
-
handler.set()
- 属性设置操作的捕捉器
-
handler.deleteProperty()
delete
操作符的捕捉器
-
handler.apply()
- 函数调用操作的捕捉器
-
handler.construct()
new
操作符的捕捉器
const obj = {
name: 'why',
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get: function (target, key) {
console.log(`监听到obj对象的${
key}属性被访问了`)
return target[key]
},
// 设置值时的捕获器
set: function (target, key, newValue) {
console.log(`监听到obj对象的${
key}属性被设置值`)
target[key] = newValue
},
// 监听 in 的捕获器
has: function (target, key) {
console.log(`监听到obj对象的${
key}属性的in操作`)
return key in target
},
// 监听 delete 的捕获器
deleteProperty: function (target, key) {
console.log(`监听到obj对象的${
key}属性的delete操作`)
delete target[key]
}
})
// in 操作符
console.log('name' in objProxy)
// delete 操作
delete objProxy.name
/*
监听到obj对象的name属性的in操作
true
监听到obj对象的name属性的delete操作
*/
2.3 Proxy 的 construct 和 apply
- 到捕捉器中还有
construct
和apply
,它们是应用于函数对象的
function foo() {
console.log('调用了 foo')
}
const fooProxy = new Proxy(foo, {
apply: function (target, thisArg, argArray) {
console.log(`对 foo 函数进行了 apply 调用`)
target.apply(thisArg, argArray)
},
construct: function (target, argArray, newTarget) {
console.log(`对 foo 函数进行了 new 调用`)
return new target(...argArray)
}
})