反射指编译期还不知道加载哪个类,到运行时才加载、执行;程序运行中能拿到自身信息。JS 里 apply 就是一种反射用法。
ES6 的 Reflect 把对对象的底层操作收成一套静态 API,并作为 Proxy 拦截器的默认实现。它不是构造函数,不能 new,也不能当普通函数调用。
一、什么是 Reflect
Reflect 是内建对象,用来拦截 JavaScript 操作。所有属性和方法都是静态的,内部封装了一系列对象底层操作。
若 Proxy 没写 get,默认就是 Reflect.get:
const proxy = new Proxy(obj, {
get(target, property) {
// 如果没有定义 get 方法,那么默认返回的就是 Reflect 的 get 方法
return Reflect.get(target, property)
}
})
以前改对象可以用 Object 上的方法,也可以用 in、delete 运算符。Reflect 把操作方式统一成函数调用。API 一览:

二、apply
Reflect.apply(target, thisArgument, argumentsList)
-
-
thisArgument:调用时绑定的 this(可选)
-
argumentsList:实参列表,应是类数组(可选)
ES5
先指定方法,再 apply:
Math.floor.apply(null, [1.72]) // 1
ES6
先交出 apply,再指定方法:
Reflect.apply(Math.floor, null, [1.72]) // 1
静态扫描时 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
三、construct
用反射创建实例,类似 new target(…args)。
Reflect.construct(target, argumentsList[, newTarget])
-
-
argumentsList:参数数组或伪数组(可选)
-
newTarget:参考 new.target;省略则与 target 相同(可选)
let a = new Date()
a.getTime() // 1632632744483
let b = Reflect.construct(Date, [])
b.getTime() // 1632632744484
四、defineProperty / deleteProperty
Reflect.defineProperty 与 Object.defineProperty 几乎一样:Reflect.defineProperty(target, propertyKey, attributes)。
const student = {}
const r = Object.defineProperty(student, 'name', { value: 'Mike' })
student // {name: "Mike"}
r // {name: "Mike"}
const student = {}
const r = Reflect.defineProperty(student, 'name', { value: 'Mike' })
student // {name: "Mike"}
r // true
效果一样,都能改对象;差别在返回值:Object 返回对象本身,Reflect 返回 true。W3C 方向是逐步把 Object 上这类方法迁到 Reflect。
Reflect.deleteProperty 删除属性,返回是否成功,和非严格模式 delete 几乎相同。
const obj = { x: 1, y: 2 }
const a = delete obj.x
obj // {y: 2}
a // true
const obj = { x: 1, y: 2 }
const a = Reflect.deleteProperty(obj, 'x')
obj // {y: 2}
a // true
五、get / getOwnPropertyDescriptor / getPrototypeOf
Reflect.get 像读 target[propertyKey],但是函数形式。
const obj = { x: 1, y: 2 }
obj.x // 1
obj['x'] // 1
const obj = { x: 1, y: 2 }
Reflect.get(obj, 'x') // 1
Reflect.get(['a', 'b', 'c'], 1) // b
Reflect.getOwnPropertyDescriptor 类似 Object.getOwnPropertyDescriptor:有则返回描述符,无则 undefined。
const obj = { x: 1, y: 2 }
Object.getOwnPropertyDescriptor(obj, 'x')
// {value: 1, writable: true, enumerable: true, configurable: true}
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 抛 TypeError;Object 会先强制转成对象。
Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not a non-null object
Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }
Reflect.getPrototypeOf 与 Object.getPrototypeOf 一样,返回 [[Prototype]]。
const d = new Date()
Object.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
const d = new Date()
Reflect.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
原文写成了 New Date(),构造函数应小写 new。
六、has / isExtensible / ownKeys
Reflect.has 判断属性是否存在,和 in 相同。
const obj = { x: 1, y: 2 }
Reflect.has(obj, 'x') // true
Reflect.has(obj, 'z') // false
Reflect.isExtensible 与 Object.isExtensible 一样,看能不能加新属性。
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}
Reflect.ownKeys 返回自身属性键,等价于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。
const obj = { x: 1, y: 2 }
Reflect.ownKeys(obj) // ["x", "y"]
Reflect.ownKeys([]) // ["length"]
Reflect.ownKeys([1, 2]) // ["0", "1", "length"]
七、preventExtensions / set / setPrototypeOf
Reflect.preventExtensions 阻止再添加新属性,和 Object.preventExtensions() 一致。它不等于 Object.freeze():freeze 还会让已有属性不可写、不可配置。
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}
Reflect.set 以函数方式赋值,类似属性访问器。
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"]
Reflect.setPrototypeOf 改 [[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: ƒ, …}
| 方法 |
对应旧写法 |
差别 |
apply |
fn.apply(this, args) |
函数当参数传入,运行时再选 |
construct |
new Ctor(...args) |
反射创建实例 |
defineProperty |
Object.defineProperty |
返回 true/false,不是对象 |
deleteProperty |
delete obj.x |
返回是否删除成功 |
get / set |
属性读写 |
函数形式,便于和 Proxy 对齐 |
has |
in |
判断属性存在 |
preventExtensions |
Object.preventExtensions |
只禁扩展,不是 freeze |
原文:https://www.cnblogs.com/Leophen/p/14838608.html
一句话总结:Reflect 把对象操作收成静态函数,既是 Proxy 的默认实现,也比 Object 方法更统一、失败时更常返回布尔或抛 TypeError。