博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解ES6笔记(十一)代理(Proxy)和反射(Reflection)API(12)
阅读量:5733 次
发布时间:2019-06-18

本文共 3219 字,大约阅读时间需要 10 分钟。

主要知识点:代理和反射的定义、常用的陷阱函数、可被撤销的代理、将代理对象作为原型使用、将代理作为类的原型
图片描述

代理与反射

  • 代理是什么?

通过调用 new Proxy() ,你可以创建一个代理用来替代另一个对象(被称之为目目标对象) ,这个代理对目标对象进行了虚拟,因此该代理与该目标对象表面上可以被当作同一个对象来对待。

代理允许你拦截目标对象上的底层操作,而这本来是JS引擎的内部能力,拦截行为适用了一个能响应特定操作的函数(被称之为陷阱);

  • 反射是什么?

被Reflect对象所代表的反射接口,是给底层操作提供默认行为的方法的集合,这些操作是能够被代理重写的。每个代理陷阱都有一个对应的反射方法,每个方法都与对应的陷阱函数同名,并且接收的参数也与之一致。

创建一个简单的代理

使用Proxy构建可以创建一个简单的代理对象,需要传递两个参数:目标对象以及一个处理器,后者是定义一个或多个陷阱函数的对象。如果不定义陷阱函数,则依然使用目标对象的默认行为:

let target = {};let proxy = new Proxy(target, {});proxy.name = "proxy";console.log(proxy.name); // "proxy"console.log(target.name); // "proxy"target.name = "target";console.log(proxy.name); // "target"console.log(target.name); // "target"

使用 set 陷阱函数验证属性值

假设你想要创建一个对象,并要求其属性值只能是数值,这就意味着该对象的每个新增属性都要被验证,并且在属性值不为数值类型时应当抛出错误。为此你需要定义 set 陷阱函数来重写设置属性值时的默认行为,该陷阱函数能接受四个参数:

  1. trapTarget :将接收属性的对象(即代理的目标对象);
  2. key :需要写入的属性的键(字符串类型或符号类型);
  3. value :将被写入属性的值;
  4. receiver :操作发生的对象(通常是代理对象)。

Reflect.set() 是 set 陷阱函数对应的反射方法,同时也是 set 操作的默认行为。

Reflect.set() 方法与 set 陷阱函数一样,能接受这四个参数,让该方法能在陷阱函数内部被方便使用:

let target = {    name: "target"};let proxy = new Proxy(target, {    set(trapTarget, key, value, receiver) {        // 忽略已有属性,避免影响它们        if (!trapTarget.hasOwnProperty(key)) {            if (isNaN(value)) {                throw new TypeError("Property must be a number.");            }        }        // 添加属性        return Reflect.set(trapTarget, key, value, receiver);    }});// 添加一个新属性proxy.count = 1;console.log(proxy.count); // 1console.log(target.count); // 1// 你可以为 name 赋一个非数值类型的值,因为该属性已经存在proxy.name = "proxy";console.log(proxy.name); // "proxy"console.log(target.name); // "proxy"// 抛出错误proxy.anotherName = "proxy";

使用 get 陷阱函数进行对象外形验证

JS 语言有趣但有时却令人困惑的特性之一,就是读取对象不存在的属性时并不会抛出错误,而会把 undefined 当作该属性的值,例如:

let target = {};console.log(target.name); // undefined

在多数语言中,试图读取 target.name 属性都会抛出错误,因为该属性并不存在;但 JS 语言却会使用 undefined 。

对象外形( Object Shape )指的是对象已有的属性与方法的集合,由于该属性验证只须在读取属性时被触发,因此只要使用 get 陷阱函数。该陷阱函数会在读取属性时被调用,即使该属性在对象中并不存在,它能接受三个参数:

  1. trapTarget :将会被读取属性的对象(即代理的目标对象);
  2. key :需要读取的属性的键(字符串类型或符号类型);
  3. receiver :操作发生的对象(通常是代理对象)。

相应的Reflect.get()方法同样拥有这三个参数。进行对象外形验证的示例代码:

//import {color,sum,magicNumber} from "./export.js"import * as example from "./export.js"console.log(example.color)console.log(example.magicNumber)console.log(example.    sum(76, 2))let proxy = new Proxy({}, {    get(trapTarget, key, receiver) {        if (!(key in receiver)) {            throw new TypeError("Property " + key + " doesn't exist.");        }        return Reflect.get(trapTarget, key, receiver);    }});// 添加属性的功能正常proxy.name = "proxy";console.log(proxy.name); // "proxy"// 读取不存在属性会抛出错误console.log(proxy.nme); // 抛出错误

使用 has 陷阱函数隐藏属性

in运算符用于判断指定对象中是否存在某个属性,如果对象的属性名与指定的字符串或符号值相匹配,那么in运算符就会返回true。无论该属性是对象自身的属性还是其原型的属性。

has陷阱函数会在使用in运算符的情况下被调用,控制in运算符返回不同的结果,has陷阱函数会传入两个参数:

trapTarget:代理的目标对象;

key:属性键;

Reflect.has()方法接收相同的参数,并向in运算符返回默认的响应结果,用于返回默认响应结果。

let target = {    name: "target",    value: 42};let proxy = new Proxy(target, {    has(trapTarget, key) {        if (key === "value") {            return false;        } else {            return Reflect.has(trapTarget, key);        }    }});console.log("value" in proxy); // falseconsole.log("name" in proxy); // trueconsole.log("toString" in proxy); // true

转载地址:http://sumwx.baihongyu.com/

你可能感兴趣的文章
小区划分 计蒜客提高组模拟赛(三)Day2 动态规划 区间DP NOIP模拟赛
查看>>
QML插件扩展(一)
查看>>
Worktile协同特色之二:任务看板管理
查看>>
67. Add Binary
查看>>
linux下监控jvm 使用的方法
查看>>
Windows+Linux----打造和谐的开发环境
查看>>
mvc中日志的原理和使用步骤
查看>>
C# 监测每个方法的执行次数和占用时间(测试3)
查看>>
[洛谷U40581]树上统计treecnt
查看>>
[ZJOI2012]灾难
查看>>
Code Signal_练习题_almostIncreasingSequence
查看>>
C++程序设计(第2版)课后习题答案--第13章
查看>>
C#设置日期为指定的月份和日子
查看>>
根据EntityFramework写的重写sql语句的类
查看>>
滑动窗口的最大值
查看>>
[转载]大话MVP
查看>>
基于DOM的XML文件操作
查看>>
【Day29 】Soket编程
查看>>
Angular的MVC理解--节自Adam Free的Pro angularv2
查看>>
C/S打包(图文)
查看>>