前言
上文说到,我们介绍了一下Porxy的概念和简单的用法,同时也和大家聊了一些使用Proxy的优点和为什么Vue3使用Proxy的原因。那接下来我再来和大家聊聊Proxy中的第二个参数handler对象提供出来的一组处理目标对象的方法。那下面我就来和大家介绍几种常用的方法
get()
首先我们先来介绍我们在使用 Proxy中最常用的两个方法之一的get方法。 get方法它是用于拦截属性读取的一个操作,它接受三个参数,分别是目标对象、属性名和实例本身。
基本用法
首先我们来看一段get方法的基本用法代码:
javascript
const target = {
name: 'zhangsan'
}
const handler = {
get: function(target, propKey) {
console.log('target:', target);
console.log('propKey:', propKey);
if(propKey in target) return target[propKey]
else return '没有该属性'
}
}
let proxy = new Proxy(target, handler)
console.log(proxy.name );// "张三"
console.log(proxy.age); // "没有该属性"
如上述代码,我们使用get方法做了一个拦截target属性的方法,当target内有该属性,我们正常返回,若没有我们则直接打印出没有该属性字段。打印结果如下:

注意事项
需要return
我们在使用get方法需要有返回值 ,不然我们读取操作只能打印出undifined。如下代码所示:
javascript
const target = {
name: 'zhangsan'
}
const handler = {
get: function(target, propKey) {
}
}
let proxy = new Proxy(target, handler)
console.log(proxy.name);
console.log(proxy.age);
如下图:就算target有该属性我们依旧打印出的是undifined

可以继承
通过Proxy构造的对象可以通过原型继成给其他对象使用,如下所示:
javascript
let proxy = new Proxy({}, {
get(target, propertyKey) {
console.log('propertyKey:',propertyKey);
return target[propertyKey];
}
});
let obj = Object.create(proxy);
console.log(obj.name);
如上述代码,当我们将proxy作为原型继成给了obj对象,当我们去访问obj上不存在的属性时,它就会往原型上查找,即触发了proxy对象的get方法。这就说明,get方法可以继承。
不生效情况
当我们将目标函数的某个属性设置为不可配置(configurable)且不可写(vritable),则Proxy不能修改该属性,如果通过 Proxy 对象访问该属性则会报错。如下代码所示:
javascript
const target = Object.defineProperties({},{
foo: {
value: 123,
writable: false, //不可写
configurable: false //不可配置
}
})
const handler = {
get(target, propKey) {
return '123'
}
};
const proxy = new Proxy(target, handler)
proxy.foo
如上述这段代码,当我们运行时,它就会报如下的错误:

这段错误的大概意思就是: foo为代理目标上的只读不可配置属性,所以get方法不能修改foo属性的值。所以这个就是get方法不生效报错的情况。也需要我们在使用时注意。
set()
在介绍完get方法后,那我们就需要来介绍最常用的两个方法之二的set方法了。set方法是用来拦截某个属性的赋值(设置)操作, 它接受四个参数,分别是目标对象、属性名、属性值和实例本身。
基本用法
我们先来看看set方法是怎么用的:
ini
const handler = {
set: function(target, propKey, value) {
if(propKey == 'age') {
if(value > 200) {
throw new TypeError('The age seems invalid')
}
}
target[propKey] = value
}
}
let proxy = new Proxy({}, handler);
proxy.age = 100; // 100
proxy.age = 201; // error
如上述代码,当我给proxy对象赋值为 100 时,它可以正常的走通set方法,赋值为 201 时,则出现了报错。打印结果如下:

使用set方法, 我们可以实现对某个数据的验证,当其不符合我们的验证条件时,就会抛出一个错误。同时我们还可以实现数据绑定,当对象发生变化时,自动的去更新DOM,也就是我们Vue框架中的响应式数据原理。
注意事项
严格模式
当我们在严格模式下,set方法需要返回值且返回值转布尔值为ture,否则就会报错。代码如下:
javascript
'use strict'
const handler = {
set(target, propKey, value) {
target[propKey] = value;
return false
}
};
const proxy = new Proxy({}, handler)
proxy.name = 'zhangsan'
当我们运行上述代码时,则会报错,错误如下:

在严格模式下,Proxy 对象的 set 方法确实要求必须返回一个布尔值,以判断属性设置是否成功。若没有成功则会抛出错误阻止函数的继续执行。
不生效情况
set 方法与get方法的不生效情况相同,当目标对象的某个属性设置为不可写且不可配置,则set方法不生效。
apply()
apply方法拦截函数的调用、call和apply操作的。它接受三个参数,分别是目标函数,目标对象的执行上下文对象(this)和目标对象的参数数组。
基本用法
我们先看看apply是怎么用的:
javascript
const handler = {
apply (target, ctx, args) {
return target.apply(ctx, args) * 2;
}
};
function sum (left, right) {
return left + right;
};
var proxy = new Proxy(sum, handler);
console.log(proxy(1, 2)); // 6
console.log(proxy.call(null, 5, 6) );// 22
console.log(proxy.apply(null, [7, 8])); // 30
如上述代码,我们可以看到定义的函数sum,在调用时我们对其结果进行了加倍的操作,则调用代理过后的sum函数的返回结果就会加倍,用call、apply也一样有加倍的结果返回。打印如下:

has()
has方法是用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。HasProperty操作就是in运算符。它接受两个参数,分别是目标对象,属性名。
HasProperty 会同时检查对象自身和原型链上的属性,hasOwnProperty 只会检查对象自身是否具有属性,而不检查原型链上的属性。
基本用法
下面我们来看一段has方法基本使用的代码:
javascript
const target = {
_name: 'zhangsan',
name: 'lisi'
}
const handler = {
has(target, propKey) {
if(propKey[0] === '_') return false
return propKey in target
}
}
const proxy = new Proxy(target, handler)
console.log('name' in proxy); // true
console.log('_name' in proxy); // false
如上述代码,我们使用has方法将target对象中带_的属性隐藏,使它不被in操作符发现。
注意事项
拦截报错
当目标对象设置为不可配置(configurable)或禁止扩展时,has不能将属性隐藏,隐藏则会报错。代码如下:
javascript
const target = { name: 'zhangsan' };
Object.preventExtensions(target); //禁止扩展
const p = new Proxy(target, {
has(target, prop) {
return false;
}
});
'name' in p
当我们将目标对象设置为不可扩展时,我们不能使用has方法返回false,否则就会有如下报错

对for in 不生效
虽然for..in循环中用到了in操作符,但是has方法对for..in循环不生效。代码如下:
javascript
const target = {
age: 17
}
const handler = {
has(target, propKey) {
if(propKey === 'age' && target[propKey] < 18) {
console.log('未成年');
return false
}
return propKey in target
}
}
const proxy = new Proxy(target, handler)
for(let item in proxy) {
console.log(proxy[item]); // 17
}
如上述代码, 我们使用has拦截for..in并没有生效。虽然用到了in操作符,但是has方法不能拦截for..in循环。
总结
如想了解全部API,请点击下面链接