【JavaScript高级】Proxy和Reflect

2023-11-17

存储属性描述符监听对象

用存储属性描述符来监听这个对象中的属性被设置或获取的过程:

const obj={
   name:"kaisa",
    age:18,
    height:1.88
}

const keys=Object.keys(obj);
for(const key of keys){
    let value=obj[key];

    Object.defineProperty(obj,key,{
        //setter
        set:function(newValue){
            console.log(`${key}属性设置成了${newValue}`);
            value=newValue;
        },

        //getter
        get:function(){
            console.log(`获取${key}的值`);
            return value;
        }
    })
}

//测试
obj.name="asd"
console.log(obj.age);

也就是说,我们可以存储属性描述符来监听对象,但是,有以下缺点:

  • 不对口:Object.defineProperty设计的初衷,不是为了去监听截止一个对象中所有的属性的
  • 我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符
  • 如果我们想监听更加丰富的操作,比如新增属性、删除属性,Object.defineProperty无法做到

存储数据描述符设计的初衷并不是为了去监听一个完整的对象

Proxy

基本使用

  • 如果我们希望监听一个对象的相关操作,我们可以先创建一个代理对象Proxy对象)
  • 之后对该对象的所有操作,都通过代理对象来完成:代理对象可以监听我们想要对原对象进行哪些操作

我们可以将上面的案例用Proxy来实现一次:我们需要new Proxy对象,并且传入需要侦听的对象以及一个处理对象,处理对象可以称之为handler

const p = new Proxy(target, handler);

我们之后的操作都是直接对Proxy的操作,而不是原有的对象,因为我们需要在handler里面进行侦听。

const obj={
    name:"kaisa",
    age:18,
    height:1.88
}

//创建proxy对象
const objPro=new Proxy(obj,{})

//对obj的操作都应该到objPro这个代理进行操作
console.log(objPro.name);
objPro.name="asd"
console.log(objPro.name);

//kaisa
//asd

set和get捕获器

如果我们想要侦听某些具体的操作,就可以在handler中添加对应的捕获器(Trap)。

set和get分别对应的是函数类型。

set的四个参数:

  1. target:目标对象(侦听的对象)
  2. property:将被设置的属性key
  3. value:新属性值
  4. receiver:调用的代理对象

get的三个参数:

  1. target:目标对象(侦听的对象)
  2. property:将被设置的属性key
  3. receiver:调用的代理对象

代码:

const obj={
    name:"kaisa",
    age:18,
    height:1.88
}

const objPro=new Proxy(obj,{
    set:function(target,key,newValue){
        console.log(`${key}属性被设置为${newValue}`);
        target[key]=newValue;
    },

    get:function(target,key){
        console.log(`${key}属性被访问`);
        return target[key];
    }
});

console.log(objPro.name);
objPro.name="asd"
console.log(objPro.name);

// name属性被访问
// kaisa
// name属性被设置为asd
// name属性被访问
// asd

Proxy的其他捕获器

handler.has()——监听 in 的捕获器

const obj={
    name:"kaisa",
    age:18,
    height:1.88
}

const objPro=new Proxy(obj,{
    has:function(target,key){
        console.log(`箭头in判断${key}属性`);
        return key in target;
    }
})

console.log("name" in obj);
console.log("namee" in obj);

//true
//false

handler.deleteProperty()——delete 操作符 的捕获器

get和set是没办法监听属性删除的。

const obj={
    name:"kaisa",
    age:18,
    height:1.88
}

const objPro=new Proxy(obj,{
    deleteProperty:function(target,key){
        console.log(`${key}属性被删除了`);
        delete obj[key]
    }
})

delete objPro.name
console.log(obj);

//name属性被删除了
//{age: 18, height: 1.88}

所有捕获器:

在这里插入图片描述

construct和apply捕获器

construct和apply是应用于函数对象的。

监听apply:

function foo(num1,num2){
    console.log(this,num1,num2);
}

const fooProxy=new Proxy(foo,{
    apply:function(target,thisArg,otherArg){
        console.log(`监听:foo函数执行了apply操作`);
        target.apply(thisArg,otherArg);
    }
})

fooProxy.apply(123,[10,20])
//监听:foo函数执行了apply操作
//Number {123} 10 20
//显然this是对象123,num1是10,num2是20

监听new:

function foo(num1){
    console.log(num1);
}

const fooProxy=new Proxy(foo,{
    construct:function(target,otherArr){
        console.log(`foo函数进行了new操作`);
        return new target(otherArr)
    }
})

new fooProxy(123);
//foo函数进行了new操作
//[123]

Reflect

作用

Reflect是ES6新增的一个API,它是一个对象,字面的意思是反射
Object和Reflect对象之间的API关系:比较 Reflect 和 Object 方法

作用:

  • 提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法
  • Reflect.getPrototypeOf(target)类似于 Object.getPrototypeOf(traget)

为什么要有Reflect:

  • 在早期的ECMA规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面作为类方法,但Object作为一个构造函数,这些操作实际上放到它身上并不合适
  • ES6中新增了Reflect,让我们将这些操作都集中到了Reflect对象上,在使用Proxy时,可以做到不操作原对象

方法

和Proxy一一对应的,也是13个:

在这里插入图片描述

举个例子

上面的案例用Reflect写:

const obj={
    name:"kaisa",
    age:18,
    height:1.88
}

const objPro=new Proxy(obj,{
    set(target,key,newValue){
        console.log(`${key}属性发生改变`);

        //Reflect没有操作原对象
        const isSuccess=Reflect.set(target,key,newValue);
        if(isSuccess){
            console.log(`${key}属性设置set成功`);
        }else {
            console.log(`${key}属性设置set失败`);
        }

    },

    get(target,key){
        console.log(`获取get${key}属性`);
        return target[key];
    },

    has(target,key){
        console.log(`判断对象是否有has${key}属性`);
        return Reflect.has(target,key);
    },

    deleteProperty(target,key){
        console.log(`对象删除delete${key}属性`);
        return Reflect.deleteProperty(target,key);
    }
});

测试:

//get
console.log(objPro.name);
//set+get
objPro.age=1;
console.log(objPro.age);
//has
console.log("height" in objPro);
//delete
delete objPro.name

console.log(objPro);

//  获取getname属性
//  kaisa
//  age属性发生改变
//  age属性设置set成功
//  获取getage属性
//  1
//  判断对象是否有hasheight属性
//  true
//  对象删除deletename属性
//  Proxy {age: 1, height: 1.88}

Reflect的construct

用的少,但某些特殊场景会用到。

Reflect.construct(使用哪个类创建, [参数], 创建哪个类的实例)

如:我们有两个构造函数, 一个构造函数是没有对应的操作, 我们可以使用construct借用另一个构造函数创建类。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Student() {}

// 使用Person创建Student的实例
const stu = Reflect.construct(Person, ["kaisa", 18], Student);
// 同时创建的实例的隐式原型, 依然指向Student的显式原型
console.log(stu.__proto__ === Student.prototype); // true

Receiver

使用receiver参数改变this,让对象所有的操作都经过Proxy代理对象,都能被捕获器捕获。

具体看:Proxy和Reflect中的receiver到底是个什么东西

const obj={
   _name:"true_name",
    name:"fake",

    get name(){
        return this._name;
    },

    set name(newValue){
        this._name=newValue;
    }
}

const objPro=new Proxy(obj,{
    get:(target,key,receiver)=>{
        console.log(`get ${key}`);
        return Reflect.get(target,key,receiver);
    },

    set:(target,key,newValue,receiver)=>{
        console.log(`set ${key}`);
        return Reflect.set(target,key,newValue,receiver);
    },
})

console.log(objPro.name);
objPro.name="123";
console.log(objPro.name);

// get name
// get _name
// true_name
// set name
// set _name
// get name
// get _name
// 123

参考

coderwhy的课
Web前端Proxy和Reflect使用详解
Proxy-Reflect
比较 Reflect 和 Object 方法
Proxy和Reflect中的receiver到底是个什么东西

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【JavaScript高级】Proxy和Reflect 的相关文章

随机推荐

  • 在Ubuntu上基于wayland/weston源码构建weston桌面

    Ubuntu构建weston桌面 简介 下载工具 库 安装ninja 设置环境变量 构建wayland 构建wayland protocols 构建weston 运行weston weston的一些测试程序 简介 wayland官网 htt
  • mybatis choose when 多条件_Mybatis—动态SQL详解

    前言 今天我们来聊聊Mybatis的动态SQL的使用 动态SQL可以说是mybatis的核心 可以对SQL语句进行灵活操作 通过表达式进行判断 对SQL进行灵活拼接 组装 在实际项目开发中 我们还可以将在业务层处理的逻辑转移到SQL中进行处
  • ubuntu下开启端口

    查看端口启动情况 sudo ufw status 开启端口号命令 sudo ufw allow lt 端口号 gt v6的意思就是 ipv6 开启防火墙 sudo ufw enable
  • 小米9开源linux内核,小米开源 Redmi Note 8 Pro 和 Note 9 Pro 系列的内核源码

    小米开源了基于 Android Q 的 Redmi Note 8 Pro 代号 begonia 以及 Redmi Note 9 Pro Note 9 Pro Max 代号 curtana 的内核源码 按照 XDA 的说法 GPLv2 要求所
  • 初学者的卡尔曼滤波——扩展卡尔曼滤波

    简介 转自 http www cnblogs com ymxiansen p 5368547 html 已经历经了半个世纪的卡尔曼滤波至今仍然是研究的热点 相关的文章不断被发表 其中许多文章是关于卡尔曼滤波器的新应用 但也不乏改善和扩展滤波
  • vue中监听元素尺寸变化

    data中 widthPlay 0 元素宽 heightPlay 0 元素高 observer null mounted中监听 let ResizeObserver window ResizeObserver window WebKitRe
  • 极光笔记

    PART 01 前 言 随着网络技术的发展 从粗犷型到精细化运营型 再到现在的数字化运营 数据变得越来越细分和重要 不仅可以进行策略调整 还可以实现自动化的精细化运营 而数据价值的起点就是埋点 只有合理地埋点 规范地上报 数据才会产生价值
  • SpringBoot 使用 log4j2

    一 新建工程 选择一些基础依赖 填写工程名称和项目路径 二 工程配置 修改文件编码格式 设置Java Compiler 修改maven配置文件路径 三 pom xml的web依赖中排除掉logging依赖 并且引入log4j2依赖
  • Apikit 自学日记:API 异常监控-监控报告

    在 api 管理中 查看 api 异常监控的监控报告 在 apikit 中也是常用的功能 通常你可以在流程综合报告页中看到当前流程在选定时间段内的整体监控情况 在 APIkit 中监控报告有这几种类别 单接口监控报告 流程监控报告 项目监控
  • 【C#学习笔记】读文件

    using System using System IO namespace ConsoleApplication class Program static void Main string args FileStream file new
  • 解决Win11休眠一段时间后自动关机的问题

    1 Win11系统有以下工作状态 S0 工作状态 系统完全可用 S0 睡眠 现代待机 低功耗空闲 网络可用 S1 睡眠 CPU停止工作 S2 睡眠 CPU关闭 S3 睡眠 仅保留内存工作 S1 S3 S4 混合睡眠 睡眠和休眠状态的组合 S
  • 最小生成树和最短路径算法

    图的概念 图表明了一种多对多的关系 图由两个元素组成 节点 vertex 和边 edge 有向图 如果给每条边规定一个方向 那么就是一个有向图 入度出度 对于有向图而言 指向一个顶点的边数称为一个点的入度 从一个顶点指出的边的个数就是出度
  • 【深度探索STL】空间配置器(二) 第一级配置器

    了解内存配置后的对象构造行为和内存释放前的对象析构行为后 参见博文 http blog csdn net wenqian1991 article details 19545049 空间配置器 构造与析构 我们再来学习内存的配置与释放 定义在
  • Keil5/MDK结构体无法自动指示成员变量

    方法总结 1 点击魔术棒法 1条消息 MDK5的结构体变量成员不提示问题的解决办法 weixin 45722312的博客 CSDN博客 keil5结构体加点后没有提示成员https blog csdn net weixin 45722312
  • elasticsearch基础5——文档处理解析、数据入盘流程、文档分片存储

    文章目录 一 同步和异步 阻塞和非阻塞 1 1 四种组合 二 客户端 2 1 高级客户端文档解析 2 2 文档索引 2 3 构建JSON文档 2 4 文档处理过程解析 2 5 数据入盘流程 2 6 与MongoDB比较 三 文档分片存储 3
  • java 生成uuid 16位_java生成16位随机UUID数

    public void testUid for int i 0 i UUID uuid UUID randomUUID System out println uuid toString 9e0db051 5ca9 46f4 958f ebd
  • STM32CubeMX学习六 之ADC配置

    文章目录 前言 一 本地环境 二 开始 1 定时器配置 2 引脚配置 在这里插入图片描述 https img blog csdnimg cn e5b6f155a1b8468cb15046a0a9d031cd png 3 内部时钟配置 4 A
  • redis必杀命令:列表(List)

    题记 Redis列表是简单的字符串列表 按照插入顺序排序 你可以添加一个元素导列表的头部 左边 或者尾部 右边 一个列表最多可以包含 232 1 个元素 4294967295 每个列表超过40亿个元素 例如 wd wd usr local
  • idea+springboot启动报错 ERROR org.apache.catalina.core.ContainerBase

    用idea导入了一个spring boot的项目 结果启动报错 ERROR org apache catalina core ContainerBase A child container failed during start tomca
  • 【JavaScript高级】Proxy和Reflect

    文章目录 存储属性描述符监听对象 Proxy 基本使用 set和get捕获器 Proxy的其他捕获器 construct和apply捕获器 Reflect 作用 方法 举个例子 Reflect的construct Receiver 参考 存