你能懂的 Reflect 反射

news2025/1/15 20:03:05

你能懂得 JS 之 Reflect 反射

上一篇文章《Proxy 代理对象使用详解即原理总结》我们详细介绍了 Proxy 代理对象的使用及其工作原理(不了解的同学可以去看看哦),今天我们来聊聊它的好兄弟 Reflect 反射。

特点

  1. Reflect 是一个内置全局对象,将 Object 对象的一些属于语言内部的方法也部署到 Reflect 对象上,并且未来新方法将只部署在 Reflect 对象上。这么做的目的是为了把一些底层实现提取成一个的 API,并且高度聚合到一个对象上,让代码更清晰。

  2. 状态标记:很多反射方法返回布尔值,表示目的操作是否成功,而原始对象的操作是抛出错误或返回操作后的对象。

let obj = {name:'孤城浪人'};
let flag = Reflect.defineProperty(obj, 'age', {value:18});
let flag1 = Object.defineProperty(obj,'sex',{value:'男'});
console.log(flag);//true
console.log(flag1);//{name: '孤城浪人', age: 18, sex: '男'}
  1. 用函数代替操作符:比如用 Reflect.get()代替属性访问操作符,用 Reflect.has() 代替 in 操作符或 with() 等等。例:
let obj = {name:'孤城浪人'};
console.log(obj.name);//孤城浪人
console.log(Reflect.get(obj,'name'));//孤城浪人
console.log('name' in obj);//true
console.log(Reflect.has(obj,'name'));//true
  1. Reflect 对象的静态方法有13个与 Proxy 代理可以捕获的 13 种不同的基本操作一一对应,所以无论 Proxy 怎么修改默认行为,总可以在Reflect上获取原始的默认行为

  2. 可以用 Reflact.apply() 代替 Function.prototype.apply.call()。这种情况往往发生在

现在你明白为什么说 Proxy 和 Reflect 是好兄弟了吧。主要原因就是 Proxy 会通过指定拦截函数修改代理对象的内部方法(即默认行为),但同时我们希望在修改默认行为的时候能够调用原来的默认行为,此时就需要 Reflect 对象了。Proxy 和 Reflect 相互配合往往能出奇效。

13个静态方法

Reflect 对象一共有 13 个静态方法,它们与 Proxy 代理可以捕获 13 种不同的基本操作一一对应。

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

简单使用

示例用法

下面是一些简单用法

const obj = {
  name: "孤城浪人",
  test() {
    return this == proxyObj;
  },
};
console.log(Reflect.get(obj, "name")); //  "孤城浪人"
console.log(Reflect.get(obj, "age")); //  undefined

Reflect.set(obj, "name", "大帅哥^o^");
console.log(obj.name); // 大帅哥^o^

console.log(Reflect.has(obj, "age")); // false
console.log(Reflect.has(obj, "name")); // true

function Person(name) {
  this.name = name;
}
const person = Reflect.construct(Person, ['张三']);
console.log(person);

console.log(Reflect.deleteProperty(obj, "age")); // true
console.log(obj); // { name: "孤城浪人",test() {return this == proxyObj;},}

console.log(Reflect.ownKeys(obj)); // => [ "name", "test","age" ]

上面这些方法如果传入的第一个参数不是对象会报错。而下面这些方法第一个参数不是对象会有自己的处理逻辑,具体请参考阮一峰老师的ECMAScript 6 入门 。

Reflect.defineProperty(obj, "age", { value: 18, configurable: true }); // 返回值 true|false
console.log(obj); // { name: "孤城浪人",age:18,test() {return this == proxyObj;},}

// 返回对象属性的描述对象
console.log(Reflect.getOwnPropertyDescriptor(obj, "name")); // {value: '大帅哥^o^', writable: true, enumerable: true, configurable: true}

console.log(Reflect.getPrototypeOf(obj) === Object.prototype); // true

Reflect.setPrototypeOf(obj, { name: "原型" });
console.log(obj.__proto__); //{ name: "原型" }

与 Proxy 代理对象配合

const obj = {
  name: "孤城浪人",
  age: 18,
  sex: "男",
};
const ProxyObj = new Proxy(obj, {
  get(target, property) {
    console.log(`get触发,读取${property}属性`);
    // return target[property];直接操作原始对象
    return Reflect.get(target, property);
    // return Reflect.get(...arguments); // 简写
  },
  set(target, property, value) {
    console.log(`set触发,赋值${property}属性`);
    // target[property] = value; 直接操作原始对象
    Reflect.set(target, property, value);
    // Reflect.set(...arguments); // 简写
  },
});
console.log(ProxyObj.name);
ProxyObj.age = 20;
console.log(ProxyObj.age);

在这里插入图片描述

这里使用 Proxy 对 obj 进行了代理,指定了读取和设置拦截逻辑,在自定义逻辑中使用 Reflect 对象的 API 方法操作原始对象数据,而不是直接操作原始对象,一眼就看出对原始对象做了什么操作,代码更清晰了。

解决 Proxy 代理对象的 this 指向问题

《Proxy 代理对象使用详解即原理总结》这篇文章曾说过 Reflect 可以解决代理对象的 this 问题,下面我们就好好聊聊它。

get 的第三个参数 receiver

如果对象部署了读取函数(getter),则读取函数的 this 会自动绑定 receiver。如下例中,第一次输出 this 就是 me,第二次输出由于传入了 she 作为第三个参数 receiver,所以 this 指向 she,因此输出与第一次不同。

const me = {
  name: "孤城浪人",
  age: 18,
  sex: "男",
  get personInfo() {
    return `个人信息:姓名:${this.name},年龄:${this.age},性别:${this.sex}`;
  },
};
const she = {
  name: "李四",
  age: 20,
  sex: "女",
};
console.log(Reflect.get(me, "personInfo")); // 个人信息:姓名:孤城浪人,年龄:18,性别:男
console.log(Reflect.get(me, "personInfo", she));// 个人信息:姓名:李四,年龄:20,性别:女

解决问题

我们对这篇文章《Proxy 代理对象使用详解即原理总结》抛出问题的代码做如下简单改造:

const obj = {
  _name: "孤城浪人",
  get name() {
    console.log(this)
    return this._name;
  },
};
const proxyObj = new Proxy(obj, {
  get(target, property, receiver) {
    //return target[property];
    return Reflect.get(target,property,receiver);
  },
});
const obj1 = {
  __proto__:proxyObj,
  _name:'李四'
}
console.log(obj.name);
console.log(obj1.name);

在这里插入图片描述

我们知道代理对象的 get 拦截函数有第三个参数,当 Proxy 代理对象作为其他对象的原型时,receiver 会指向被继承的对象(上面的代码 receiver 就是 obj1)。再将这个receiver 作为 Reflect.get 的第三个参数,那么此时 obj 中的 get 钩子中 this 就正确的指向了 obj1,也就能正确打印obj1.name了。

参考

非常感谢这些文章给予我的帮助。

  • 了解学习 Proxy 的好朋友 - Reflect,为什么需要 Reflect
  • ECMAScript 6 入门 --阮一峰

总结

Reflect 对象就是内置的全局对象,因为它的静态方法与 Proxy 的捕获器一一对应,所以它们就像是一对孪生兄弟,常常放在一起使用,并且效果还不错。

好了,Reflect 的相关内容就介绍到这了,我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎大家关注!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24250.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[go学习笔记.第十六章.TCP编程] 4.项目-海量用户即时通讯系统-显示在线用户列表,群聊

1.实现功能-完成登录时能返回当前在线用户 用户登录后,可以得到当前在线用户列表 示意图如下: 步骤: (1).编写了server/processBlock/userManager.go package processBlockimport ("fmt" )//因为UserManager实例在服务器中有且只有一个,并且在很多地方都要使用,因…

博客系统前端页面

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 目录 博客系统 前言 一.预期效果 博客列表页效果 博客正文页效果 博客登录页效果 博客编辑页效果 二.实现博客列表页 实现导航栏 实现版心 实现个人信息 …

【DELM分类】基于matlab麻雀搜索算法改进深度学习极限学习机数据分类【含Matlab源码 2235期】

⛄一、麻雀搜索算法改进深度学习极限学习机数据分类 1 深度极限学习机 深度极限学习机算法(Deep Extreme Learning Machine, DELM)又称多层极限学习机.存在N个训练样本,其中 2 麻雀搜索算法(SSA) 2020年,薛建凯等人提出了麻雀搜索算法.麻雀搜索算法是一…

python判断语句

一、布尔类型和比较运算符 布尔类型 只有两个结果 是 / 否 定义变量存储布尔类型数据: 变量名称布尔类型字面量 布尔类型的数据不仅可以通过定义得到也可以通过比较运算符比较内容得到 比较运算符: 二、if判断语句基本格式 程序中的判断&#…

如何鉴别一个成功的Scrum 教练?

成功的Scrum教练:如何鉴别一个成功的Scrum教练?了解更多关于优秀的Scrum专家的特征。 前言: 成功的Scrum 教练 - 我认为的九大标准 如何判别Scrum 教练是否是合格的?Scrum 教练成功的标准是什么?经常使用Confluence回顾性模板是不…

java创建线程的方式到底有几种?(详解)

创建线程的方式到底有几种?一,创建多线程的方式1,官方解释2,实现Runnable接口3,继承Thread类3,二者区别3.1,本质区别3.2,优先考虑使用第一种二,误以为是创建线程的几种新…

react(子传父、父传子)

目录 1. 父传子 数组/对象 的两种写法 2. 子传父&#xff1a; 3. 生成唯一id的库&#xff1a; 4. 对接收的组件进行验证 1. 父传子 数组/对象 的两种写法 function App() {const obj [{age:19},{age:19}]return (<div className"App"><header classNa…

Jmeter常用参数化技巧总结

说起接口测试&#xff0c;相信大家在工作中用的最多的还是Jmeter。 JMeter是一个100&#xff05;的纯Java桌面应用&#xff0c;由Apache组织的开放源代码项目&#xff0c;它是功能和性能测试的工具。具有高可扩展性、支持Web(HTTP/HTTPS)、SOAP、FTP、JAVA 等多种协议。 在做…

uniapp之使用map组件显示接收过来的经纬度

目录 前言 效果图 提示 总代码 分析 1.显示自己位置的属性 2.markers 点标记 前言 由于项目的需求&#xff0c;我需要从主页面接收经纬度&#xff0c;并渲染至地图上面&#xff0c;同时呢&#xff0c;也要在该位置上显示图标标记点&#xff08;红色&#xff09;&#x…

兴业数金 测试 面试真题|面经

兴业数金测试服务中心技术面&#xff08; 一面二面&#xff09; 时间线流程 8.12一面&#xff08;30min&#xff09;->8.31邮件通知二面&#xff0c;填写职位申请表->9.2二面&#xff08;25min&#xff0c;二面需要用小鱼易连&#xff0c;需提前下载和注册&#xff09; …

儿童台灯怎么选对眼睛最好?2022年的现在如何挑选儿童台灯呢

儿童台灯选择首要考虑因素就是护眼&#xff0c;而是否护眼&#xff0c;可以从以下几个角度去看。 一、照度。根据国家标准划分&#xff0c;照度可分为国A和国AA两级&#xff0c;它们可以衡量光线明亮程度和均匀程度&#xff0c;儿童台灯选择国AA级对眼睛最好。 二、显色指数。…

Java#19(面向对象三大特征之一:多态)

目录 一.多态 二.多态中调用成员的特点 三.instanceof关键字 一.多态 多态:同类型的对象,表现出的不同形态 格式:父类类型 对象名称 子类对象; 前提: (1)有继承关系 (2)有父类引用指向子类对象 (3)有方法重构 优点: (1)使用父类作为参数,可以接收所有子类对象 (2)体现多态的…

科技金融企业助力乡村振兴,能有多大新意?

最近几年&#xff0c;越来越多科技互联网企业开始承担起他们的社会责任&#xff0c;成为乡村振兴领域一股不可忽视的力量。作为电商平台&#xff0c;阿里、拼多多、京东助力农产品上行&#xff0c;解决农产品的销售难题&#xff0c;直接为乡村振兴领域做出大贡献&#xff0c;但…

罗丹明PEG活性酯 RB-PEG-NHS,罗丹明聚乙二醇活性酯,Rhodamine-PEG-NHS

产品名称 罗丹明聚乙二醇活性酯 RB-PEG-NHS 中文名称 罗丹明PEG活性酯 活性酯PEG罗丹明 活性酯聚乙二醇罗丹明 英文名称 RB-PEG-NHS RB-PEG-SC Rhodamine-PEG-NHS 分子量 400 600 1000 2000 3400 5000 10000 结构式&#xff1a; CAS N/A 溶解度 溶于DMSO,DMF,DCM&#xff…

Linux进阶-编译工具链

gcc编译器&#xff08;预处理、编译&#xff09; binutils工具集&#xff08;汇编、链接&#xff09; 本地编译&#xff1a;编译工具链和目标程序运行在相同的架构平台。 交叉编译&#xff1a;编译工具链和目标程序运行在不同的架构平台。 ARM-GCC是GCC编译工具链的一个分支…

Spring Data JPA审计

Spring Data JPA为跟踪持久性层的变化提供了很好的支持。通过使用审核&#xff0c;我们可以存储或记录有关实体更改的信息&#xff0c;例如谁创建或更改了实体以及何时进行更改。 我们可以利用实体字段上的CreatedBy,CreatedDate,LastModifiedDate,LastModifiedBy注释来指示 S…

PointNet 和 PointNet++ 作者讲座学习笔记

文章目录前人的工作三维数据的表达形式把点云转化为体素&#xff0c;再用3D CNNPointNet两个挑战置换不变性旋转不变性PointNet的分类网络PointNet的分割网络PointNet的限制PointNet多级点云特征学习分类分割小区域大小参考资料前人的工作 三维数据的表达形式 点云&#xff1…

Adaptive AUTOSAR Technology Sharing

文章目录一、目录二、未来汽车基础设施需求三、整车架构四、CP vs AP五、AP架构1.Execution Management与State Management的关系2.Service-oriented communication2.Diagnostic Management3.Persistency4.Log and Trace5.安全支持6.安全方法7.信息安全8. AutoSar&#xff1a;T…

Selenium4之CDP

相较于以前的版本&#xff0c;Selenium4除了推出了relative Locators&#xff0c;还有一个比较重要的更新就是对于Chrome Dev Tools Protocol的支持。 Chrome Dev Tools Protocol帮助用户监测、检查、调试和模版化Chrome浏览器以及基于Chromium的其它浏览器&#xff08;比如EDG…

Spring Boot 2.x系列【27】应用篇之代码混淆

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot版本2.7.0 文章目录概述代码混淆ProGuard使用Maven 插件直接使用工具混淆概述 代码混淆 代码混淆(Obfuscated code)亦称花指令&#xff0c;是将计算机程序的代码&#xff0c;转换成…