1. 引言

反射是一种能够在程序运行时动态访问、修改某个类(对象)中属性和方法的机制

JavaScript在ES6中提供了Reflect 这一个内置的对象,它提供拦截 JavaScript 操作的方法

与之功能类似的有 proxy handler (en-US)Object.getOwnPropertyDescriptor()

以下记述利用反射来实现对JavaScript对象的方法注入,主要使用ReflectObject.getOwnPropertyDescriptor()来实现

2. Reflect

Reflect并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调,Reflect 的所有属性和方法都是静态的(就像 Math 对象),详细文档可参考MDN:Reflect - JavaScript | MDN (mozilla.org)

Reflect的主要功能就是对JavaScript对象进行操作,包括获取属性、修改属性等

在这里,要实现对JavaScript对象的原有方法进行注入,基本思路就是获取对象的原方法,设置一个新方法,在新方法中修改或调用原方法,最后将新方法替换原方法,示例代码如下:

// 定义一个对象
const obj = {
method: function () {
console.log('Original method');
}
}; // 获取对象的属性描述符
const descriptor = Reflect.getOwnPropertyDescriptor(obj, 'method'); // 保存原有方法的引用
const originalMethod = descriptor.value; // 修改方法的特性
descriptor.value = function () {
// 调用原有方法
Reflect.apply(originalMethod, this, arguments); console.log('Modified method');
}; // 定义修改后的方法
Reflect.defineProperty(obj, 'method', descriptor); // 调用方法
obj.method(); // 输出: Original method Modified method

3. Object

Object是JavaScript标准内置对象,其内置多个操作对象方法和属性的静态方法,包括获取属性、修改属性等,更为详细的文档可参考MDN:Object - JavaScript | MDN (mozilla.org)

在这里,要实现对JavaScript对象的原有方法进行注入,基本思路就是和上面是相同的:获取对象的原方法,设置一个新方法,在新方法中修改或调用原方法,最后将新方法替换原方法,示例代码如下:

// 定义一个对象
const obj = {
method: function () {
console.log('Original method');
}
}; // 获取对象的属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, 'method'); // 保存原有方法的引用
const originalMethod = descriptor.value; // 修改方法的特性
descriptor.value = function () {
// 调用原有方法
originalMethod.apply(this, arguments); console.log('Modified method');
}; // 定义修改后的方法
Object.defineProperty(obj, 'method', descriptor); // 调用方法
obj.method(); // 输出: Original method Modified method

其实使用Object的静态方法与使用Reflect对象的静态方法差不多,没有太大区别

4. 示例

4.1 添加新方法

JavaScript内置的Date对象没有输出指定格式的方法,可以使用反射进行注入,实现对象的扩展,示例如下:

const getFormattedDate = function (date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}-${month}-${day}`;
}; const date = new Date(); Reflect.defineProperty(date, 'getFormattedDate', {
value: getFormattedDate
}); console.log(date.getFormattedDate(date)); // 输出: '2023-7-24'

4.2 扩展原方法

JavaScript内置的Date.prototype.getMonth()方法,返回一个指定的日期对象的月份,为基于 0 的值(0 表示一年中的第一月)

const date = new Date();
console.log(date.getMonth()); // 输出: 6

现在修改为基于 1 的值(1 表示一年中的第一月)

    const date = new Date();

    const originalMethod = Reflect.get(date, 'getMonth');

    Reflect.defineProperty(date, 'getMonth', {
value: function () {
return originalMethod.apply(this, arguments) + 1;
}
}); console.log(date.getMonth()); // 输出: 7

5. 参考资料

[1] Reflect - JavaScript | MDN (mozilla.org)

[2] Object - JavaScript | MDN (mozilla.org)

[3] Date - JavaScript | MDN (mozilla.org)

JavaScript利用反射实现方法注入的更多相关文章

  1. 利用反射调用方法时,处理ref,out参数需要注意的问题(转)

    转自:http://www.68idc.cn/help/buildlang/ask/20150318283817.html 项目中如下的泛型方法,因为要在运行时,动态指定类型参数,所以要利用反射来实现 ...

  2. laravel中如何利用反射实现依赖注入

    依赖注入 在一个类中经常会依赖于其他的对象,先看一下经典的写法 class Foo { public $bar; public function __construct() { $this->b ...

  3. asp.net一般处理程序利用反射定位方法

    asp.net的一般处理程序我想大家用得都不少,经常会如下如下的代码: using System; using System.Collections.Generic; using System.Lin ...

  4. javascript利用拷贝的方法实现导出excel(可以导出表格线)

    Js代码: <script language=javascript> function preview() { window.clipboardData.setData("Tex ...

  5. java工具类-接受请求参数,并利用反射调用方法

    public String a(HttpServletRequest request,HttpServletResponse response) throws JSONException, IOExc ...

  6. Python基础篇【第3篇】: Python异常处理、反射、动态导入、利用反射的web框架

    异常处理 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常.异常是Python对象,表示一个错误.当P ...

  7. 反射main方法

    利用Java反射机制去调用其他类的main方法基于这种情形: 当程序中的某个类在运行到某处需要去调用其他类的main方法时,如果此程序并不知道此main方法所属类的名称,而只是在程序中接受某一代表此m ...

  8. C#利用反射调用PB编译的COM组件

    问题: 1.根据COM组件的ProgID,得到COM组件公开的类型 2.创建COM组件提供的类型的对象 3.调用执行方法 正确姿势 C#利用反射调用(后期绑定)PB编译的COM组件 C#调用COM组件 ...

  9. <五>JDBC_利用反射及JDBC元数据编写通用的查询方法

    此类针对javaBean类写了一个通用的查询方法,List<javaBean> 通用查询更新中...:通过学习,深刻体会到学会反射就等于掌握了java基础的半壁江山! 一.使用JDBC驱动 ...

  10. 利用反射及JDBC元数据编写通用查询方法

    元数据:描述数据的数据,ResultSetMetaData是描述ResultSet的元数据对象,从它可以得到数据集有多少了,每一列的列名... ResultSetMetaData可以通过ResultS ...

随机推荐

  1. ABC 305

    题目列表 前三题过水,第四题分类讨论两个端点之间的距离和所在位置是清醒或睡眠 即可. E 题意:一张图上有一些结点有保安,每个保安有不同的警戒度 \(h_i\),定义 一个结点是安全的 为这个结点可以 ...

  2. 鹏程杯子2023 pwn

    主要就是修改stdin的最后几位,使他变为write,然后泄露libc,为所欲为即可. 本人是卡在不知道stdin那里可以修改. 然后使用一下jmp qword rbp这个gadget 0x400a9 ...

  3. NC16466 [NOIP2015]信息传递

    题目链接 题目 题目描述 有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为Ti的同学. 游戏开始时, ...

  4. html知识概念(不含标签)

    CS架构与BS架构 C/S架构 Client(客户端) / Server(服务器) 需要安装客户端使用,哔哩哔哩客户端.腾讯视频客户端等 需求安装.需要更新.不跨平台 B/S架构 Browser(浏览 ...

  5. python 中记录dmesg信息并进行分析的方法(@)

    在Python中记录系统的dmesg 信息,并进行分析的方法简述: 基本命令: dmesg -C 清除之前的dmesg  信息: dmesg -T > testExample.msg      ...

  6. 【Unity3D】程序纹理简单应用

    1 几何纹理应用 ​ 本文所有案例的完整资源详见→Unity3D程序纹理简单应用. 1.1 边框 ​ 1)边框子图 ​ Border.shadersubgraph ​ 说明:Any 节点用于判断输入向 ...

  7. comm命令

    comm命令 comm命令用于比较两个已排过序的文件,该命令会一列列地比较两个已排序文件的差异,并将其结果显示出来,如果没有指定任何参数,则会把结果分成3列显示:第1列仅是在第1个文件中出现过的列,第 ...

  8. Springboot集成Druid连接池并实现数据库密码加密

    Druid介绍 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBoss ...

  9. Failed to bind properties under ” to com.zaxxer.hikari.HikariDataSource

    1.问题说明 今天配置spring boot多数据源,同时用到了oracle和postgresql,结果配置完毕后启动报这个错. 2.原因分析 忘记添加postgresql驱动了!!! 3.解决方案 ...

  10. SpringBoot整合Groovy脚本,实现动态编程

    Groovy简介 Groovy 是增强 Java 平台的唯一的脚本语言.它提供了类似于 Java 的语法,内置映射(Map).列表(List).方法.类.闭包(closure)以及生成器.脚本语言不会 ...