Proxy
代理
-
Proxy 用于修改某些操作的默认行为,属于一种 “元编程”,即对编程语言进行编程
-
通俗地讲,就是通过 Proxy 代理扩展了对象、方法的一些功能
-
ES6 提供 Proxy 构造函数,用来生成 Proxy 实例
let proxyObj = new Proxy(target, handle)
接收 2 个参数:
① target
:代理对象(目标对象)
② handler
:配置对象(拦截对象),用来定制拦截行为
如果handler
没有设置任何拦截,那就相当于直接操作原对象
let target = {}; // 目标对象
let handler = {}; // 拦截对象
let proxy = new Proxy(target, handler);
proxy.name = 'superman';
console.log("proxy - ", proxy); // proxy - Proxy {name: 'superman'}
console.log("target - ", target); // target - {name: 'superman'}
- 上面代码中,
handler
是一个空对象,没有任何拦截效果,∴ 访问proxy
就等同于访问target
拦截 Object
①
get(target, property[, receiver])
- 用于拦截对属性的 [获取] 操作
接收 3 个参数:
① target
:代理对象(目标对象),就是构造函数 Proxy()
的第 1 个参数
② property
:访问的属性名
③ receiver
:Proxy 对象 / 继承 Proxy 的对象
- 需要手动
return
返回值:为 [获取] 操作得到的值; 可以返回任何值
let obj = { name: 'superman' };
let proxyObj = new Proxy(obj, { // 设置拦截对象
get(target, property) { // getter
if (property in target) {
return target[property];
} else {
return `你访问的属性 ${property} 不存在!`;
}
}
});
console.log(proxyObj); // Proxy {name: 'superman'}
console.log(proxyObj.name); // superman —— (通过 . 获取)
console.log(proxyObj["age"]); // 你访问的属性 age 不存在! —— (通过 [] 获取)
上述方法中,getter 的参数 target
就是代理对象 obj
;参数 property
就是访问的属性名 name
、age
②
set(target, property, value[, receiver])
- 用于拦截对属性的 [添加]、[修改] 操作
接受 4 个参数:
① target
:目标对象,就是构造函数 Proxy()
的第 1 个参数
② property
:操作的属性
③ value
:被写入的属性值
④ receiver
:Proxy 对象 / 继承 Proxy 的对象
- 应当手动
return
返回值:true
-执行成功、false
-执行失败
[应当] 的意思是,可以不写
let obj = { name: "superwoman" }
let proxyObj = new Proxy(obj, {
set(target, property, value) {
if (property == 'age') {
if (!Number.isInteger(value))
throw new TypeError(`${property} 必须是整数`);
if (value > 200)
throw new RangeError(`${property} 得小于 200`);
}
target[property] = value;
}
});
proxyObj.name = 'superman'; // 修改属性值
proxyObj.age = 18; // 添加属性
console.log(obj); // { name: 'superman', age: 18 }
// 以下会报错
// proxyObj.age = 12.6; // TypeError: age 必须是整数
// proxyObj.age = 201; // RangeError: age 得小于 200
③
deleteProperty(target, property)
- 用于拦截对属性的 [删除] 操作
接受 2 个参数:
① target
:目标对象,就是构造函数 Proxy()
的第 1 个参数
② property
:待删除的属性名
- 需要手动
return
返回值:true
表示删除成功;false
表示删除失败
let obj = { name: 'superman', age: 18 };
let proxyObj = new Proxy(obj, {
deleteProperty(target, prop) {
console.log(`你要删除 ${prop} 属性`); // 你要删除 age 属性
return delete target[prop];
}
});
console.log(obj); // { name: 'superman', age: 18 }
console.log(delete proxyObj.age); // false
console.log(obj); // { name: 'superman' }
上述代码中,deleteProperty
方法的参数 target
就是 Proxy()
的第 1 个参数 obj
;参数 prop
就是 age
④
has(target, prop)
方法 - 用于拦截对属性的 [in
] 操作
接受 2 个参数:
① target
:目标对象
② prop
:需要检查是否存在的属性
- 需要手动
return
返回值:true
-目标对象存在该属性;false
-目标对象不存在该属性
let obj = { name: 'superman', age: 18 };
let proxyObj = new Proxy(obj, {
has(target, prop) {
console.log(`判断属性 ${prop} 是否在此对象中`); // 判断属性 name 是否在此对象中
return prop in target;
}
});
console.log('name' in proxyObj); // true
拦截 Function
- 所有的代理拦截中,只有
apply
和construct
的代理目标是一个函数 Function apply
和construct
拦截方法覆写函数内部的[[Call]]
和[[Construct]]
方法
①
apply(target, thisArg, argumentsList)
- 用于拦截 [直接调用] - 作用于普通函数
接受 3 个参数:
① target
:目标函数,就是构造函数 Proxy()
的第 1 个参数
② thisArg
:被调用时的上下文对象 ( this )
③ argumentsList
:被调用时的参数数组
- 返回值:可以返回任何值
function sum(a, b) { return a + b }
let handler = { // 设置拦截对象
apply(target, thisArg, argumentsList) { // 设置拦截对象的 apply 方法
console.log(`Calculate sum: ${argumentsList}`); // Calculate sum: 1,2
return target(argumentsList[0], argumentsList[1]) * 10;
}
};
let proxyObj = new Proxy(sum, handler);
console.log(proxyObj(1, 2)); // 30
②
construct(target, argumentsList[, newTarget])
- 用于拦截 [new
操作] - 作用于构造函数
- 为使
new
操作符在生成的 Proxy 对象上生效,目标函数必须具有[[Construct]]
方法(即new target()
必须是有效的)
接受 3 个参数:
① target
:目标函数,就是 Proxy()
的第 1 个参数
② argumentsList
:被调用时的参数数组
③ newTarget
:最初被调用的构造函数
- 返回值:一个实例对象
function People(name) { this.name = name };
let handler = { // 设置拦截对象
construct(target, args) { // 设置拦截对象的 construct 方法
console.log('拦截了 new 操作'); // 拦截了 new 操作
return new target(...args);
}
};
let ProxyPeople = new Proxy(People, handler);
let p = new ProxyPeople('superman');
console.log(p); // People { name: 'superman' }
可撤销代理对象
- 通过
new Proxy(target, handle)
创建的代理对象不可撤销 - 创建可撤销的代理对象的方式:
Proxy.revocable(target, handler);
- 接收 2 个参数:
①target
:目标对象
②handler
:拦截对象 - 返回值:
{"proxy": proxy, "revoke": revoke}
proxy
- 表示新创建的代理对象,和通过new Proxy(target, handler)
创建的代理对象一样,不过它可以被撤销掉
revoke
- 撤销方法,直接调用即可撤销掉和它一起生成的那个代理对象
let obj = { name: 'superman' };
let handler = {};
let { proxy, revoke } = Proxy.revocable(obj, handler);
console.log(proxy.name); // superman
revoke(); // 撤销代理
console.log(proxy.name); // 报错
Reflect
反射
Reflect
是一个内置的对象,提供拦截 JS 操作的方法
Reflect.defineProperty()
基本等同于Object.defineProperty()
方法,唯一不同是返回 Boolean 值
let obj = {}
let res1 = Reflect.defineProperty(obj, "name", {
get() {
console.log("getter1"); // getter1
return "superman";
}
})
console.log("res1", res1); // res1 true —— 执行成功
let res2 = Reflect.defineProperty(obj, "name", {
get() {
console.log("getter2");
return "superwoman";
}
})
console.log("res2", res2); // res2 false —— 执行失败
console.log("obj.name", obj.name); // obj.name superman
上例的 Reflect.defineProperty()
如果改为 Object.defineProperty()
,第 2 次调用将会直接抛出错误,代码不会继续往下执行
执行 Reflect.defineProperty()
,会返回一个布尔值:true
-执行成功、false
-执行失败
使用 Reflect.defineProperty()
则可通过判断其返回值是否为 ture
,得知操作是否执行成功,进而确定下一步操作
反射 Object
Reflect.get(target, property[, receiver])
- [获取] 属性
手动return
返回值:属性的值Reflect.set(target, property, value[, receiver])
- [添加]、[修改] 属性
手动return
返回值:true
-操作成功、false
-操作失败Reflect.deleteProperty(target, property)
- [删除] 属性
手动return
返回值:true
-操作成功、false
-操作失败
new Proxy(data, {
// 拦截 [获取] 属性
get(target, prop) {
return Reflect.get(target, prop)
},
// 拦截 [添加]、[修改] 属性
set(target, prop, value) {
Reflect.set(target, prop, value)
},
// 拦截 [删除] 属性
deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
反射 Function
Reflect.apply(target, thisArg, argumentsList)
- 相当于target(...argumentsList)
可以返回任意值Reflect.construct(target, argumentsList[, newTarget])
- 相当于new target(...argumentsList)
返回一个实例对象
console.log(Math.ceil(4.4)); // 向上取整 5
// 通过 [反射] 调用 Math.ceil; 没有 this 指向,传入 null; 参数数组
let num = Reflect.apply(Math.ceil, null, [5.1]);
console.log(num); // 6
其实,只是调用函数的不同的方式而已:
function show(...args) {
console.log(this);
console.log(args);
}
// 正常调用
show(1, 2, 3, 4); // this 是 window,args 是 [1,2,3,4]
// call 调用函数
show.call('aaa', 1, 2, 3, 4); // this 是 aaa,args 是 [1,2,3,4]
// apply 调用函数
show.apply('aaa', [1, 2, 3, 4]); // this 是 aaa,args 是 [1,2,3,4]
// reflect.apply 调用函数
Reflect.apply(show, 'aaa', [1, 2, 3, 4]); // this 是 aaa,args 是 [1,2,3,4]