聊聊常用的Proxy中的API吧

前言

上文说到,我们介绍了一下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方法拦截函数的调用、callapply操作的。它接受三个参数,分别是目标函数,目标对象的执行上下文对象(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函数的返回结果就会加倍,用callapply也一样有加倍的结果返回。打印如下:

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,请点击下面链接

参考链接

阮一峰 ECMAScript 6 (ES6) 标准入门教程 第三版

相关推荐
爱健身的小刘同学9 分钟前
安装 electron 依赖报错
前端·javascript·electron
耶啵奶膘10 分钟前
uniapp+vue2+uview2.0导航栏组件二次封装
前端·javascript·uni-app
雨中奔跑的小孩14 分钟前
electron打包部署vue项目
javascript·vue.js·electron
khatung36 分钟前
React——useReducer
前端·javascript·vscode·react.js·前端框架·1024程序员节
AndyGoWei2 小时前
react react-router-dom history 实现原理,看这篇就够了
前端·javascript·react.js
小仓桑2 小时前
深入理解 JavaScript 中的 AbortController
前端·javascript
换个名字不能让人发现我在摸鱼2 小时前
裁剪保存的图片黑边问题
前端·javascript
小牛itbull2 小时前
Mono Repository方案与ReactPress的PNPM实践
开发语言·前端·javascript·reactpress
小宋10212 小时前
实现Excel文件和其他文件导出为压缩包,并导入
java·javascript·excel·etl
码喽哈哈哈2 小时前
day01
前端·javascript·html