JS 反射机制及 Reflect 详解
一、什么是反射机制
反射机制是在编译阶段不知道是哪个类被加载,而是在运行的时候才加载、执行。
也就是说,反射机制指的是程序在运行时能够获取自身的信息。
js
中的 apply
就是反射机制。
二、Reflect
1、Reflect 定义
Reflect
是一个内建的对象,用来提供方法去拦截 JavaScript 的操作。
Reflect
不是一个函数对象,所以它是不可构造的,也就是说它不是一个构造器,不能通过 new
操作符去新建或者将其作为一个函数去调用 Reflect
对象。
Reflect
的所有属性和方法都是静态的。
Reflect
内部封装了一系列对对象的底层操作
Reflect
成员方法就是 Proxy
处理对象的默认实现
const proxy = new Proxy(obj, {
get(target, property) {
// 如果没有定义 get 方法,那么默认返回的就是 Reflect 的 get 方法
return Reflect.get(target, property)
}
})
2、Reflect API 汇总
Reflect
提供了一套用于操作对象的 API
,我们之前操作对象可以用 Object
上面的一些方法,也可以用 in、delete
这种操作符,使用 Reflect
就统一了操作方式
handler ⽅法 | 默认调⽤ | 功能 |
---|---|---|
get | Reflect.get() | 获取对象身上某个属性的值 |
set | Reflect.set() | 在对象上设置属性 |
has | Reflect.has() | 判断一个对象是否存在某个属性 |
deleteProperty | Reflect.deleteProperty() | 删除对象上的属性 |
getProperty | Reflect.getPrototypeOf() | 获取指定对象原型的函数 |
setProperty | Reflect.setPrototypeOf() | 设置或改变对象原型的函数 |
isExtensible | Reflect.isExtensible() | 判断一个对象是否可扩展 (即是否能够添加新的属性) |
preventExtensions | Reflect.preventExtensions() | 阻止新属性添加到对象 |
getOwnPropertyDescriptor | Reflect.getOwnPropertyDescriptor() | 获取给定属性的属性描述符 |
defineProperty | Reflect.defineProperty() | 定义或修改一个对象的属性 |
ownKeys | Reflect.ownKeys() | 返回由目标对象自身的属性键组成的数组 |
apply | Reflect.apply() | 对一个函数进行调用操作,同时可以传入一个数组作为调用参数 |
construct | Reflect.construct() | 对构造函数进行 new 操作,实现创建类的实例 |
.preventExtensions | Reflect.preventExtensions() | 阻止新属性添加到对象 |
3、.apply()
Reflect.apply(target, thisArgument, argumentsList)
- target:目标函数(必选)
- thisArgument:target 函数调用时绑定的 this 对象(可选)
- argumentsList:target 函数调用时传入的实参列表,该参数应该是一个类数组的对象(可选)
① ES5 用法
先指定方法,再去调用 apply
Math.floor.apply(null, [1.72]) // 1
② ES6 用法
先传递 apply,再指定是哪个方法
Reflect.apply(Math.floor, null, [1.72]) // 1
静态扫描时 Math.floor 是没有被执行,等到运行时再动态的将 Math.floor 作为参数传进来的
③ 实际应用
// ES5 用法
let price = 101.5
if (price > 100) {
price = Math.floor.apply(null, [price])
} else {
price = Math.ceil.apply(null, [price])
}
price // 101
// ES6 用法
let price = 101.5
Reflect.apply(price > 100 ? Math.floor : Math.ceil, null, [price]) // 101
4、.construct()
使用反射的方式去实现创建类的实例,类似于 new target(…args)
Reflect.construct(target, argumentsList[, newTarget])
- target:被运行的目标函数(必选)
- argumentsList:调用构造函数的数组或者伪数组(可选)
- newTarget:该参数为构造函数, 参考 new.target 操作符,如果没有 newTarget 参数, 默认和 target 一样(可选)
① ES5 用法
let a = new Date()
a.getTime() // 1632632744483
② ES6 用法
let b = Reflect.construct(Date, [])
b.getTime() // 1632632744484
5、.defineProperty()
静态方法 Reflect.defineProperty()
基本等同于 Object.defineProperty()
方法
Reflect.defineProperty(target, propertyKey, attributes)
- target:目标对象(必选)
- propertyKey:要定义或修改的属性的名称(可选)
- attributes:要定义或修改的属性的描述(可选)
① ES5 用法
const student = {}
const r = Object.defineProperty(student, 'name', { value: 'Mike' })
student // {name: "Mike"}
r // {name: "Mike"}
② ES6 用法
const student = {}
const r = Reflect.defineProperty(student, 'name', { value: 'Mike' })
student // {name: "Mike"}
r // true
这两个方法效果上来看是一摸一样的,都可以改变一个对象的值
区别在于返回值不同:Object
是返回这个值,Reflect
是返回true
PS: 在
W3C
中,以后所有的Object
上面的方法,都会慢慢迁移到Reflect
对象,可能以后会在Object
上面移除这些方法
6、.deleteProperty()
Reflect.deleteProperty
允许你删除一个对象上的属性,返回一个 Boolean
值表示该属性是否被成功删除,它几乎与非严格的 delete operator
相同
Reflect.deleteProperty(target, propertyKey)
- target:删除属性的目标对象
- propertyKey:将被删除的属性的名称
① ES5 用法
const obj = { x: 1, y: 2 }
const a = delete obj.x
obj // {y: 2}
a // true
② ES6 用法
const obj = { x: 1, y: 2 }
const a = Reflect.deleteProperty(obj, 'x')
obj // {y: 2}
a // true
7、.get()
Reflect.get()
方法的工作方式,就像从 object (target[propertyKey])
中获取属性,但它是作为一个函数执行的
Reflect.get(target, propertyKey[, receiver])
① ES5 用法
const obj = { x: 1, y: 2 }
obj.x // 1
obj['x'] // 1
② ES6 用法
const obj = { x: 1, y: 2 }
Reflect.get(obj, 'x') // 1
Reflect.get(['a', 'b', 'c'], 1) // b
8、.getOwnPropertyDescriptor()
静态方法 Reflect.getOwnPropertyDescriptor()
与 Object.getOwnPropertyDescriptor()
方法相似
如果在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined
Reflect.getOwnPropertyDescriptor(target, propertyKey)
① ES5 用法
const obj = { x: 1, y: 2 }
Object.getOwnPropertyDescriptor(obj, 'x')
// {value: 1, writable: true, enumerable: true, configurable: true}
② ES6 用法
const obj = { x: 1, y: 2 }
Reflect.getOwnPropertyDescriptor(obj, 'x')
// {value: 1, writable: true, enumerable: true, configurable: true}
Reflect.getOwnPropertyDescriptor({ x: 'hello' }, 'y')
// undefined
Reflect.getOwnPropertyDescriptor([], 'length')
// {value: 0, writable: true, enumerable: false, configurable: false}
③ 对比
如果 Reflect.getOwnPropertyDescriptor
的第一个参数不是一个对象(一个原始值),那么将造成 TypeError
错误
而对于 Object.getOwnPropertyDescriptor
,非对象的第一个参数将被强制转换为一个对象处理
Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not non-null object
Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }
9、.getPrototypeOf()
静态方法 Reflect.getPrototypeOf()
与 Object.getPrototypeOf()
方法是一样的,都是返回指定对象的原型(即,内部的 [[Prototype]]
属性的值)
Reflect.getPrototypeOf(target)
① ES5 用法
const d = New Date()
Object.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
② ES6 用法
const d = New Date()
Reflect.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
10、.has()
判断一个对象是否存在某个属性,和 in
运算符 的功能完全相同
Reflect.has(target, propertyKey)
const obj = { x: 1, y: 2 }
Reflect.has(obj, 'x') // true
Reflect.has(obj, 'z') // false
11、.isExtensible()
判断一个对象是否可扩展
Reflect.isExtensible
与 Object.isExtensible
方法一样,都是判断一个对象是否可扩展 (即是否能够添加新的属性)
Reflect.isExtensible(target)
const obj = { x: 1, y: 2 }
Reflect.isExtensible(obj) // true
Object.freeze(obj) // 阻止新属性添加到对象
obj.z = 3
Reflect.isExtensible(obj) // false
obj // {x: 1, y: 2}
12、.ownKeys()
判断对象自身属性
Reflect.ownKeys
方法返回一个由目标对象自身的属性键组成的数组,它的返回值等同于 `Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
Reflect.ownKeys(target)
const obj = { x: 1, y: 2 }
Reflect.ownKeys(obj) // ["x", "y"]
Reflect.ownKeys([]) // ["length"]
Reflect.ownKeys([1, 2]) // ["0", "1", "length"]
13、.preventExtensions()
阻止新属性添加到对象,等同于Object.freeze()
Reflect.preventExtensions
方法阻止新属性添加到对象,例如:防止将来对对象的扩展被添加到对象中,与 Object.preventExtensions()
方法一致
Reflect.preventExtensions(target)
const obj = { x: 1, y: 2 }
Reflect.isExtensible(obj) // true
Reflect.preventExtensions(obj) // 阻止新属性添加到对象
obj.z = 3
Reflect.isExtensible(obj) // false
obj // {x: 1, y: 2}
14、.set()
写数据
Reflect.set
方法允许你在对象上设置属性,用来给属性赋值,类似 property accessor
的语法,但它是以函数的方式
Reflect.set(target, propertyKey, value[, receiver])
const obj = { x: 1, y: 2 }
Reflect.set(obj, 'z', 4)
obj // {x: 1, y: 2, z: 4}
const arr = ['apple', 'pear']
Reflect.set(arr, 1, 'banana')
arr // ["apple", "banana"]
15、.setPrototypeOf()
Reflect.setPrototypeOf
方法改变指定对象的原型 (即内部的 [[Prototype]]
属性值)
Reflect.setPrototypeOf(target, prototype)
const arr = ['apple', 'pear']
Reflect.getPrototypeOf(arr)
// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ,…]
Reflect.setPrototypeOf(arr, String.prototype)
Reflect.getPrototypeOf(arr)
// String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}
JS 反射机制及 Reflect 详解的更多相关文章
- JAVA反射概念及使用详解(超详细)
JAVA反射概念及使用详解 一.什么是反射? 反射:框架设计的灵魂 框架:半成品软件.可以在框架的基础上进行软件开发,简化编码 反射:将类的各个组成部分封装为其他对象,这就是反射机制 好处: ...
- JS中的event 对象详解
JS中的event 对象详解 JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...
- js中鼠标滚轮事件详解
js中鼠标滚轮事件详解 (以下内容部分内容参考了http://adomas.org/javascript-mouse-wheel/ ) 之前js 仿Photoshop鼠标滚轮控制输入框取值中已使用 ...
- ext.js的mvc开发模式详解
ext.js的mvc开发模式详解和环境配置 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开 ...
- 微信JS接口汇总及使用详解
这篇文章主要介绍了微信JS接口汇总及使用详解,十分的全面.详尽,包含分享到朋友圈,分享给朋友,分享到QQ,拍照或从手机相册中选图,识别音频并返回识别结果,使用微信内置地图查看位置等接口,有需要的小伙伴 ...
- js中中括号,大括号使用详解
js中中括号,大括号使用详解 一.总结 一句话总结:{ } 是一个对象,[ ] 是一个数组 1.js大括号{}表示什么意思? 对象 { } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或 ...
- js对象浅拷贝和深拷贝详解
js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具 ...
- amazeui中的js插件有哪些(详解功能)
amazeui中的js插件有哪些(详解功能) 一.总结 一句话总结: 二.amazeui中的js插件有哪些 1.UI 增强 警告框Alert 按钮交互Button 折叠面板Collapse 下拉组件D ...
- 大数据学习笔记——Spark工作机制以及API详解
Spark工作机制以及API详解 本篇文章将会承接上篇关于如何部署Spark分布式集群的博客,会先对RDD编程中常见的API进行一个整理,接着再结合源代码以及注释详细地解读spark的作业提交流程,调 ...
随机推荐
- 2021 DevOpsDays 东京站完美收官 | CODING 专家受邀分享最新技术资讯
DevOpsDays 是一个全球知名的系列技术会议品牌,内容涵盖了软件开发.自动化.测试.安全.组织文化以及 IT 运营的社区会议等.DevOpsDays 由 DevOps 之父 Patrick De ...
- KMP(梅开三度之数据结构详解版
前言 KMP算法是一种字符串匹配算法,其重中之重是next数组的构建,其代码的简洁与神奇使其广受关注. 但不难发现,acm中学到的KMP和数据结构里面学到的KMP并不一样o(︶︿︶)o 之前我写过ac ...
- 1068 Find More Coins
Eva loves to collect coins from all over the universe, including some other planets like Mars. One d ...
- hdu4282 x^z+y^z+x*y*z=k 解的个数
题意: x^z + y^z + x*y*z = k; (x < y ,z > 1),给你一个k问有多少组解. 思路: 暴力枚举z,y,然后二分查找x.注意一点最好用 ...
- hdu3622 二分+2sat
题意: 给你N组炸弹,每组2个,让你在这N组里面选取N个放置,要求(1)每组只能也必须选取一个(2)炸弹与炸弹之间的半径相等(3)不能相互炸到对方.求最大的可放置半径. 思路: 二 ...
- POJ1178枚举三个地方(所有点都去同一个点)
题意: 有一个国王和很多骑士,他们都要到某一个点去集合,然后问所有人都到达某个终点的距离和最小是多少?过程中如果国王遇到了一个骑士的话,国王就可以和骑士一起按照骑士的走法走,这是两个人算一 ...
- 【翻译】WPF中的数据绑定表达式
有很多文章讨论绑定的概念,并讲解如何使用StaticResources和DynamicResources绑定属性.这些概念使用WPF提供的数据绑定表达式.在本文中,让我们研究WPF提供的不同类型的数据 ...
- [花式栈溢出]栈上的 partial overwrite
[花式栈溢出]栈上的 partial overwrite 希望能在这几天对Pwn中的栈上的各种利用和其他一些较小的分支做一个收尾,以便全力投入学习堆的相关知识.初步计划是对照ctf-wiki查缺补漏. ...
- 【SpringMVC】添加操作时返回400
本博客老魏原创,如需转载请留言 问题描述: springmvc向数据库添加新的记录时,发生400错误,控制台没有抛出异常. 问题原因: 视图中的提交数据的某一个字段不不匹配导致. 解决方法: 不要怀疑 ...
- Hive企业级性能优化
Hive作为大数据平台举足轻重的框架,以其稳定性和简单易用性也成为当前构建企业级数据仓库时使用最多的框架之一. 但是如果我们只局限于会使用Hive,而不考虑性能问题,就难搭建出一个完美的数仓,所以Hi ...