深入浅出 JavaScript Reflect API
作为开发人员,你需要创建能够处理动态代码的系统和应用程序。这些程序应该能够在运行时操作变量、属性和对象方法。为此,ES6 中引入了一个新的全局对象 Reflect
,它能够处理简单的代码操作。
本文的目的是帮助你更好地理解 JavaScript 中 Reflect
的概念以及如何使用提供的各种方法。Reflect
使你能够轻松地修改现有对象的功能,同时仍然提供其默认行为。
1. 什么是 JavaScript Reflect?
JavaScript Reflect
是一个内置的 ES6 全局对象,它提供了在运行时操作「属性」、「变量」和「对象方法」的能力。它不是构造函数,因此不能对它使用 new
操作符。
2. Proxy 和 Reflect 之间的区别?
Proxy
和 Reflect
都是在 ES6 中引入的,用于执行任务,但它们有一点不同。
与 Reflect
不同,JavaScript 的 Proxy
没有任何属性。相反,它封装另一个对象并拦截其操作。同时,Reflect
是一个内建对象,它简化了 Proxy
的创建,并使调用内部方法成为可能。
Proxy
只接受两个参数:
target
:Proxy
将包装处理程序的对象handler
:将拦截目标操作的代理配置
这里有一个例子:
const profile = { name: 'xiaan', age:21 } const handler = { get(target, prop, receiver) { if (target[prop]) { return target[prop] } return `"${prop}" prop don't exist on this object !` } } const profileProxy = new Proxy (profile, handler) console.log(profileProxy.name) // xiaan console.log(profileProxy.profession) // "profession" prop don't exist on this object !
上面的例子等价于 Reflect.get()
,后者将在本指南后面介绍,但是,Reflect.get()
技术更简单、更直接。
3. 使用 JavaScript Reflect API 的方法
让我们仔细看看 Reflect
对象的方法。所有这些方法都是静态的,也就是说,它们只能用于 Reflect
对象,而不能用于任何实例。
3.1 Reflect.construct()
Reflect.construct()
方法和 new
操作符有所差异,但与 new target(...args)
相似,它有选择不同原型的选项。Reflect.construct()
接受三个参数:
target
:被运行的目标构造函数args
:类数组,目标构造函数调用时的参数newTarget
:一个可选的构造函数,作为新创建对象的原型对象的constructor
属性,如果不指定,默认值为target
- 「返回值」:以
target
(如果newTarget
存在,则为newTarget
)函数为构造函数,argumentList
为其初始化参数的对象实例。
考虑下面的例子:
function summation(x,y,z){ this.add = x + y +z } const sum = Reflect.construct(summation, [1,2,3,4,5]) console.log(sum) // 结果: summation {add: 6}
Reflect.construct()
生成 target
或 newTarget
的新实例(如果指定了),该实例是用提供的参数 args
数组构造的。在引入 Reflect.construct()
之前,我们将结合构造函数和原型来创建一个对象:object.create()
。
function OneClass() { this.name = 'one'; } function OtherClass() { this.name = 'other'; } // 创建一个对象 var obj1 = Reflect.construct(OneClass, args, OtherClass); // 与上述方法等效 var obj2 = Object.create(OtherClass.prototype); OneClass.apply(obj2, args); console.log(obj1.name); // 'one' console.log(obj2.name); // 'one' console.log(obj1 instanceof OneClass); // false console.log(obj2 instanceof OneClass); // false console.log(obj1 instanceof OtherClass); // true console.log(obj2 instanceof OtherClass); // true
虽然两种方式结果相同,但在创建对象过程中仍一点不同。 当使用 Object.create()
和 Function.prototype.apply()
时,如果不使用 new
操作符调用构造函数,构造函数内部的 new.target
值会指向 undefined
。
当调用 Reflect.construct()
来创建对象,new.target
值会自动指定到 target
(或者 newTarget
,前提是 newTarget
指定了)。
function OneClass() { console.log('OneClass'); console.log(new.target); } function OtherClass() { console.log('OtherClass'); console.log(new.target); } var obj1 = Reflect.construct(OneClass, args); // 输出: // OneClass // function OneClass { ... } var obj2 = Reflect.construct(OneClass, args, OtherClass); // 输出: // OneClass // function OtherClass { ... } var obj3 = Object.create(OtherClass.prototype); OneClass.apply(obj3, args); // 输出: // OneClass // undefined
3.2 Reflect.apply()
Reflect.apply()
是一种使用提供的参数调用目标函数的简单而直接的方法。它包含三个参数:
target
:要调用的函数thisArgument
:target
函数调用时绑定的this
对象argumentsList
:target
函数调用时传入的实参列表,该参数应该是一个类数组的对象- 「返回值:」 调用完带着指定参数和
this
值的给定的函数后返回的结果
让我们看看下面的例子:
const arr = [3,5,20,3,31] const a = Reflect.apply(Math.max, undefined, arr) console.log(a) // 结果: 31
在引入 Reflect.apply()
之前,我们可以使用 Function.prototype.apply()
方法来执行类似的任务,如下所示:
const arr = [3,5,20,3,31] const a = Function.prototype.apply.call(Math.max, undefined, arr); console.log(a) // 结果: 31
3.3 Reflect.defineProperty()
要创建或编辑对象上的属性,可以使用 Reflect.defineProperty()
方法。它返回一个布尔值,指示属性是否已成功定义。该方法有三个参数:
target
:目标对象propertyKey
:要定义或修改的属性的名称attributes
:要定义或修改的属性的描述- 「返回值:」 Boolean 值指示了属性是否被成功定义
让我们看看下面的例子:
const obj = {} Reflect.defineProperty(obj, 'prop', {value: 70}) console.log(obj.prop) // 结果: 70
Reflect.defineProperty()
方法允许精确添加或修改对象上的属性。更多的细节请参阅类似 Object.defineProperty 。
3.4 Reflect.get()
顾名思义,Reflect.get()
用于从对象检索属性。它接受三个参数:
target
:需要取值的目标对象propertyKey
:需要获取的值的键值receiver
:如果target
对象中指定了getter
,receiver
则为getter
调用时的this
值- 「返回值:」 属性的值
让我们看看下面的例子:
const b = [10,11,12,13,14] console.log(Reflect.get(b, 2)) // 结果: 12 const obj = {name: "Pascal", age: 23} console.log(Reflect.get(obj, 'age')) // 结果: 23
3.5 Reflect.getPrototypeOf()
Reflect.getPrototypeOf()
函数返回所提供目标的原型,很像 Object.getPrototypeOf()
。该方法只接受一个参数:
target
:获取原型的目标对象- 「返回值:」 给定对象的原型。如果给定对象没有继承的属性,则返回
null
让我们看看下面的例子:
const profile = { name: 'Pascal' }; const pro = Reflect.getPrototypeOf(profile); console.log(pro);
3.6 Reflect.set()
Reflect.set()
方法用于为对象属性赋值。它返回 true
,表示属性已成功设置。这个函数有四个参数:
target
:设置属性的目标对象propertyKey
:设置的属性的名称value
:设置的值receiver
:如果遇到setter
,receiver
则为setter
调用时的this
值- 「返回值:」 返回一个 Boolean 值表明是否成功设置属性
让我们看看下面的例子:
const arr1 = []; Reflect.set(arr1, 0, 'first'); Reflect.set(arr1, 1, 'second'); Reflect.set(arr1, 2, 'third'); console.log(arr1); // 结果:['first', 'second', 'third']
3.7 Reflect.deleteProperty()
Reflect.deleteProperty()
是一个从对象中删除属性的方法。如果该属性被正确删除,则返回 true
。这个函数有两个参数:
target
:删除属性的目标对象propertyKey
:需要删除的属性的名称- 「返回值:」 Boolean 值表明该属性是否被成功删除
让我们看看下面的例子:
var obj = { x: 1, y: 2 }; Reflect.deleteProperty(obj, "x"); // true obj; // { y: 2 } var arr = [1, 2, 3, 4, 5]; Reflect.deleteProperty(arr, "3"); // true arr; // [1, 2, 3, , 5] // 如果属性不存在,返回 true Reflect.deleteProperty({}, "foo"); // true // 如果属性不可配置,返回 false Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false
3.8 Reflect.isExtensible()
和 Object.isExtensible()
一样,Reflect.isExtensible()
是一个检测对象是否可扩展的方法(例如,是否可以向其添加其他属性)。Reflect.isExtensible()
返回一个布尔值来指示目标是否可扩展。它只考虑一个参数:
target
:检查是否可扩展的目标对象- 「返回值:」 返回一个 Boolean 值表明该对象是否可扩展
让我们看看下面的例子:
const user = { name: "xiaan" }; console.log(Reflect.isExtensible(user)) // true Reflect.preventExtensions(user); console.log(Reflect.isExtensible(user)) // false
3.9 Reflect.ownKeys()
Reflect.ownKeys()
方法基本上返回一个包含目标对象的属性键的数组。它只考虑一个参数:
target
:获取自身属性键的目标对象- 「返回值:」 由目标对象的自身属性键组成的 Array
让我们看看下面的例子:
const obj = { name: "xiaan", age: 21 }; const array1 = []; console.log(Reflect.ownKeys(obj)); // ["name", "age"] console.log(Reflect.ownKeys(array1)); // ["length"]
3.10 Reflect.getOwnPropertyDescriptor()
Reflect.getOwnPropertyDescriptor()
方法返回一个描述符,它定义了给定对象上的特定属性是如何配置的。它需要两个参数:
target
:需要寻找属性的目标对象propertyKey
:获取自己的属性描述符的属性的名称- 「返回值:」 如果属性存在于给定的目标对象中,则返回属性描述符;否则,返回
undefined
。
让我们看看下面的例子:
const obj = { name: "xiaan", age: 21 }; console.log(Reflect.getOwnPropertyDescriptor(obj, 'name').value); // "xiaan" console.log(Reflect.getOwnPropertyDescriptor(obj, 'age')); // {value: 21, writable: true, enumerable: true, configurable: true} console.log(Reflect.getOwnPropertyDescriptor(obj, 'age').writable); // true
属性描述符可以包含以下属性:
value
:与属性相关联的值writable
:一个布尔值,仅当属性的相关值可修改时返回true
configurable
:一个布尔值,仅当属性描述符的类型可以修改且属性可以从相关对象中删除时返回true
enumerable
:一个布尔值,仅当属性在相关对象的属性枚举过程中出现时返回true
3.11 Reflect.has()
Reflect.has()
方法验证是否在目标对象中定义了属性。它返回一个布尔值。Reflect.has()
执行与 in
操作符类似的操作,并接受两个形参:
target
:目标对象propertyKey
:属性名,需要检查目标对象是否存在此属性- 「返回值:」 一个 Boolean 类型的对象指示是否存在此属性
让我们看看下面的例子:
const obj = { name: "xiaan" }; console.log(Reflect.has(obj, 'name')); // true console.log(Reflect.has(obj, 'age')); // false console.log(Reflect.has(obj, 'toString')); // true
4. 小结
在本文中,我们研究了 JavaScript Reflect
对象,并讨论了 Proxy
和 Reflect
之间的区别。我们还研究了如何使用各种 Reflect
方法的示例,包括用于返回对象属性值的 Reflect.get()
、用于删除对象属性的 Reflect. deleteProperty()
和用于返回对象属性键的 Reflect. ownKeys()
。