总结:
Proxy 是用来监听js对象的相关操作
- 如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象)。
- 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要
对原对象进行哪些操作
。
Reflect 提供操作js对象的方法
它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
- 比如
Reflect.getPrototypeOf(target)
类似于Object.getPrototypeOf()
; - 比如
Reflect.defineProperty(target, propertyKey, attributes)
类似于Object.defineProperty()
。
Proxy
相较于 Object.defineProperty 劫持某个属性,Proxy则更彻底,不在局限某个属性,而是直接对整个对象进行代理。
1. Proxy基本使用
语法:
var proxy = new Proxy(target, handler);
Proxy
本身是一个构造函数,通过new Proxy
生成拦截的实例对象,让外界进行访问;
构造函数中的target
就是我们需要代理的目标对象,可以是对象或者数组;
handler
和Object.defineProperty
中的descriptor
描述符有些类似,也是一个对象,用来定制代理规则。
之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作
2. set
和 get
捕获器
如果我们想要侦听某些具体的操作,那么就可以在handler
中添加对应的捕捉器(Trap)
。
set和get分别对应的是函数类型;
-
set
函数有四个参数: target
:目标对象(侦听的对象);property
:将被设置的属性key;value
:新属性值;receiver
:调用的代理对象;-
get
函数有三个参数: target
:目标对象(侦听的对象);property
:被获取的属性key;receiver
:调用的代理对象;
const obj = {
name: "zhLeon521",
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get: function (target, key) {
console.log(`监听到对象的${key}属性被访问了`, target)
return target[key]
},
// 设置值时的捕获器
set: function (target, key, newValue) {
console.log(`监听到对象的${key}属性被设置值`, target)
target[key] = newValue
}
})
console.log(objProxy.name)
console.log(objProxy.age)
objProxy.name = "blueheart"
objProxy.age = 30
console.log(obj.name)
console.log(obj.age)
3. Proxy 所有捕获器
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
Proxy
4. Proxy的construct和apply
捕捉器中还有construct
和apply
,它们是应用于函数对象的。
function foo() {
}
const fooProxy = new Proxy(foo, {
apply: function (target, thisArg, argArray) {
console.log("对foo函数进行了apply调用")
return target.apply(thisArg, argArray)
},
construct: function (target, argArray, newTarget) {
console.log("对foo函数进行了new调用")
return new target(...argArray)
}
})
fooProxy.apply({}, ["abc", "cba"])
new fooProxy("abc", "cba")
Reflect
Reflect
也是ES6新增的一个API,它是一个对象,字面的意思是反射
Reflect
主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
- 比如
Reflect.getPrototypeOf(target)
类似于Object.getPrototypeOf()
; - 比如
Reflect.defineProperty(target, propertyKey, attributes)
类似于Object.defineProperty()
。
如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
- 在早期的ECMA规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面。
- 但是
Object
作为一个构造函数,这些操作实际上放到它身上并不合适; - 另外还包含一些类似于
in
、delete
操作符,让JS看起来是会有一些奇怪的; - 所以在ES6中新增了Reflect,让我们这些操作都集中到了Reflect对象上
Object和Reflect对象之间的API关系
1. Reflect的常见方法
Reflect
中有哪些常见的方法呢?它和Proxy
是一一对应的,也是13个:
2. Reflect 的使用
const obj = {
name: "zhLeon521",
age: 18
}
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
console.log("get---------")
return Reflect.get(target, key)
},
set: function (target, key, value) {
console.log("set---------")
return Reflect.set(target, key, value)
}
})
objProxy.name = "blueheart"
console.log(objProxy.name)
3. Receiver的作用
我们发现在使用getter
、setter
的时候有一个receiver
的参数,它的作用是什么呢?
- 如果我们的源对象(obj)有
setter
、getter
的访问器属性,那么可以通过receiver
来改变里面的this
。
const obj = {
_name: "zhLeon521",
get name() {
return this._name
},
set name(newValue) {
this._name = newValue
}
}
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
// receiver是创建出来的代理对象
console.log("get方法被访问--------", key, receiver)
console.log(receiver === objProxy)
return Reflect.get(target, key, receiver)
},
set: function (target, key, newValue, receiver) {
console.log("set方法被访问--------", key, receiver)
console.log(receiver === objProxy)
Reflect.set(target, key, newValue, receiver)
}
})
objProxy.name = "blueheart"
console.log(objProxy.name)
4. Reflect.construct()
function Student(name, age) {
this.name = name
this.age = age
}
function Teacher() {
}
const stu = new Student("zhLeon521", 18)
console.log(stu)
console.log(stu.__proto__ === Student.prototype)
// 执行Student函数中的内容, 但是创建出来对象是Teacher对象
const teacher = Reflect.construct(Student, ["blueheart", 18], Teacher)
console.log(teacher)
console.log(teacher.__proto__ === Teacher.prototype)