Proxy-Reflect使用
# 监听对象操作
在ES5时提到如果需要监听一个对象中属性的变化可以使用Object.defineProperty
的存储属性符来对属性进行监听
const obj = {
name: "zs",
age: 20,
};
Object.keys(obj).forEach((key) => {
let value = obj[key];
Object.defineProperty(obj, key, {
set: function (newValue) {
console.log(`监听到给${key}设置新的值`);
value = newValue;
},
get: function () {
console.log(`获取${key}属性的值`);
return value;
},
});
});
console.log(obj.name);
console.log(obj.age);
obj.name = "ls";
console.log(obj.name);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
缺点
Object.defineProperty
的设计初衷不是为了监听截获对象中的所有属性的。在定义某些属性的时候初衷是定义普通属性,但是使用时强行将它变成了数据属性描述符- 如果想监听更加丰富的操作,比如新增属性、删除属性,那么
Object.defineProperty
是做不到的
# Proxy的基本使用
在ES6中,新增加了一个Proxy
类。也就是说如果希望监听一个对象的相关操作,可以先创建一个代码对象(Proxy对象);之后对该对象的所有操作都用过代理对象来完成,代理对象可以监听想要对原生对象进行哪些操作。
使用Proxy的步骤
- 首先需要创建一个Proxy对象,并且传入需要侦听的对象以及一个处理对象,称为
handler
const p = new Proxy(target, handler)
- 之后的操作都是直接对Proxy的操作,而不是原有对象,因为需要在
handler
中进行监听
const obj = {
name: 'zs',
age: 20
}
const objProxy = new Proxy(obj, {})
1
2
3
4
5
2
3
4
5
# Proxy的set和get捕获器
如果需要侦听某些具体的操作,那么就可以在handler
中添加对应的捕捉器
set和get分别对应的是函数类型
set函数有四个参数
- target:目标对象(侦听的对象)
- property:将被设置的属性key
- value:新属性值
- receiver:调用的代理对象
get函数有三个参数
- target:目标对象(侦听的对象)
- property:被获取的属性key
- receiver:调用的代理对象
const obj = {
name: "zs",
age: 20,
};
const objProxy = new Proxy(obj, {
has: function (target, key) {
console.log("has捕获器", key);
return key in target;
},
set: function (target, key, value) {
console.log("set捕获器", key);
target[key] = value;
},
get: function (target, key) {
console.log("get捕获器", key);
return target[key];
},
deleteProperty: function (target, key) {
console.log("delete捕获器");
delete target[key];
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Proxy所有捕获器
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() | 属性读取操作的捕捉器 |
# Proxy的constructor和apply
捕获器中包含constructor
和apply
,主要应用于函数对象
function foo() {
console.log("foo函数被调用", this, arguments);
return "foo";
}
const fooProxy = new Proxy(foo, {
apply: function (target, thisArg, otherArgs) {
console.log("函数的apply侦听");
return target.apply(thisArg, otherArgs);
},
construct(target, argArray, newTarget) {
console.log(target, argArray, newTarget);
return new target();
},
});
foo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Reflect
Reflect
是ES6新增的API,是一个对象
Reflect的作用
- 提供了操作JavaScript对象的方法,类似于Object中操作对象的方式
- Reflect.getPrototypeOf(target)类似于Object.getPropertyOf()
- Reflect.defineProperty(target, propertyKey, attributes)类似于Object.defineProperty()
Reflect对比Object的优势,为什么需要新增Reflect
- 在早期ECMA规范中未考虑到对对象本身的操作如何设计更规范,因此将这些操作放到了Object上面
- 但是Object作为一个构造哈数,这些实际操作放到它身上并不合适
- 另外包括类似于in,delete操作符,让JS看起来有些奇怪
- 所以在ES6中新增了Reflect,让这些操作都集中在Reflect对象上
- 在使用Proxy时,可以不操作原对象
# Reflect和Object的区别
const obj = {
name: "zs",
age: 20,
};
Object.defineProperty(obj, "name", {
configurable: false,
});
// 1. 之前的方式删除属性
// delete obj.name; // 删除失败,并且在严格模式下报错
// if (obj.name) {
// console.log("name删除失败");
// } else {
// console.log("name删除成功");
// }
// Reflect删除属性 Reflect.deleteProperty返回一个布尔值
if(Reflect.deleteProperty(obj, 'name')){
console.log("name删除成功");
} else {
console.log("name删除失败");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Reflect使用对应的方法后会返回一个布尔值,可以更严谨的判断方法是否执行成功;而Object并不能精确的判断是否执行成功,还有可能会报错导致后续的代码不能继续执行
# Reflect常见的方法
Reflect.getPropertyOf(target) | 类似于Object.getPropertyOf() |
---|---|
Reflect.setPrototypeOf(target, prototype) | 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true |
Reflect.isExtensible(target) | 类似于 Object.isExtensible() |
Reflect.preventExtensions(target) | 类似于 Object.preventExtensions()。返回一个Boolean |
Reflect.getOwnPropertyDescriptor(target, propertyKey) | 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined |
Reflect.defineProperty(target, propertyKey, attributes) | 和 Object.defineProperty() 类似。如果设置成功就会返回 true |
Reflect.ownKeys(target) | 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于Object.keys(), 但不会受enumerable影响). |
Reflect.has(target, propertyKey) | 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同 |
Reflect.get(target, propertyKey[, receiver]) | 获取对象身上某个属性的值,类似于 target[name] |
Reflect.set(target, propertyKey, value[, receiver]) | 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true |
Reflect.deleteProperty(target, propertyKey) | 作为函数的delete操作符,相当于执行 delete target[name] |
Reflect.apply(target, thisArgument, argumentsList) | 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和Function.prototype.apply() 功能类似 |
Reflect.construct(target, argumentsList[, newTarget]) | 对构造函数进行 new 操作,相当于执行 new target(...args) |
# Reflect使用
一般情况下Reflect是和Proxy一起使用来共同完成代理的。
const objProxy = new Proxy(obj, {
has: function (target, key) {
return Reflect.has(target, key);
},
set: function (target, key, value) {
// 优势一:代理对象的目的是不再直接操作原对象
// 优势二:Reflect.set方法有返回Boolean值,可以判断本次操作是否成功
// 优势三:receiver就是外层的Proxy对象
// Reflect.set/get最后一个参数可以决定对象访问器中getter/setter中的this指向
// target[key] = value 这里是可操作的,但是还是直接操作原对象的形式
const isSuccess = Reflect.set(target, key, value);
if(!isSuccess){
throw new Error(`set ${key} failure`)
}
},
get: function (target, key) {
return Reflect.get(target, key);
},
deleteProperty: function (target, key) {
return Reflect.deleteProperty(target, key);
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Reflect操作优势
- 使用代理对象不再直接操作原对象
# Receiver的作用
在使用getter,setter的时候有一个receiver函数。
作用:如果源对象中有setter,getter的访问属性,那么可以通过receiver
来改变里面的this
const obj = {
name: 'zs',
age: 123
}
const objProxy = new Proxy(obj, {
has: function (target, key) {
return Reflect.has(target, key);
},
set: function (target, key, value, receiver) {
console.log(receiver === objProxy);
console.log("set捕获器", key);
Reflect.set(target, key, value, receiver);
},
get: function (target, key, receiver) {
console.log("get捕获器", key);
return Reflect.get(target, key, receiver);
},
deleteProperty: function (target, key) {
return Reflect.deleteProperty(target, key);
},
});
objProxy.name = '133'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
receiver
实际上就是代理对象
# Reflect的constructor
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student() {}
const stu = Reflect.construct(Person, ["zs", 20], Student); // (执行的构造函数,参数,最终创建的类型)
console.log(stu.__proto__ === Student.prototype); // true
1
2
3
4
5
6
7
2
3
4
5
6
7
上次更新: 2022/12/09, 08:40:50