前言
上文说到,我们介绍了一下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,请点击下面链接