JavaScript 中的 Reflect 对象有什么作用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25421903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 04:29:27  来源:igfitidea点击:

What does the Reflect object do in JavaScript?

javascriptecmascript-6

提问by Jim Jones

I saw a blank stub on MDN a while ago for the Reflectobject in javascript but I can't for the life of me find anything on Google. Today I found this http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-objectand it sounds similar to the Proxy object apart from the realm and loader functionality.

不久前,我在 MDN 上看到了一个空白存根,用于Reflectjavascript 中的对象,但我一生都无法在 Google 上找到任何内容。今天我发现了这个http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object,除了领域和加载器功能之外,它听起来类似于代理对象。

Basically, I don't know whether this page I found only explains how to implement Reflect or if I just can't understand its wording. Could someone please explain to me generally what the methods of Reflectdo?

基本上,我不知道我找到的这个页面是否只解释了如何实现 Reflect,或者我是否只是无法理解它的措辞。有人可以向我解释一下一般的方法Reflect吗?

For instance, on the page I found says that calling Reflect.apply ( target, thisArgument, argumentsList )will "Return the result of calling the [[Call]] internal method of target with arguments thisArgument and args." but how is that any different than just calling target.apply(thisArgument, argumentsList)?

例如,在我发现的页面上,调用Reflect.apply ( target, thisArgument, argumentsList )将“返回调用目标的 [[Call]] 内部方法的结果,参数为 thisArgument 和 args。” 但这与只是打电话有target.apply(thisArgument, argumentsList)什么不同?

Update:

更新:

Thanks to @Blue, I found this page on the wiki http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflectwhich to the best of my knowledge says that the reflect object provides method versions of all the actions that can be trapped by proxies to make forwarding easier. But that seems a little weird to me since I don't see how it's entirely necessary. But it Seems to do a little more than that, particularly the par that says double-liftingbut that points to the old proxy spec/

感谢@Blue,我在 wiki http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect上找到了这个页面,据 我所知,reflect 对象提供了所有的方法版本代理可以捕获的操作,以使转发更容易。但这对我来说似乎有点奇怪,因为我不明白这是完全必要的。但它似乎做得更多,特别是说double-lifting但指向旧代理规范的标准/

回答by GitaarLAB

UPDATE 2015:As pointed out by 7th's answer, now that ES6 (ECMAScript 2015) has been finalized, more appropriate documentation is now available:

2015 年更新:正如7th回答所指出的,既然 ES6 (ECMAScript 2015) 已经完成,现在可以使用更合适的文档:



Original answer (for (historic) understanding and extra examples)原始答案(用于(历史)理解和额外示例)

The Reflection proposalseems to have progressed to the Draft ECMAScript 6 Specification. This document currently outlines the Reflect-object's methods and only states the following about the Reflect-object itself:

Reflection proposal似乎已经进展到草案的ECMAScript 6规范。本文档目前概述了Reflect-object 的方法,并且仅说明了有关Reflect-object 本身的以下内容:

The Reflect object is a single ordinary object.

The value of the [[Prototype]] internal slot of the Reflect object is the standard built-in Object prototype object (19.1.3).

The Reflect object is not a function object. It does not have a [[Construct]] internal method; it is not possible to use the Reflect object as a constructor with the newoperator. The Reflect object also does not have a [[Call]] internal method; it is not possible to invoke the Reflect object as a function.
Reflect 对象是一个普通的对象。

Reflect 对象的 [[Prototype]] 内部槽的值是标准的内置 Object 原型对象(19.1.3)。

Reflect 对象不是函数对象。它没有 [[Construct]] 内部方法;无法将 Reflect 对象用作具有new运算符的构造函数。Reflect 对象也没有 [[Call]] 内部方法;不可能将 Reflect 对象作为函数调用。

However, there is a short explanation about it's purpose in ES Harmony:

但是,在ES Harmony 中有一个关于它的用途的简短解释:

The “@reflect” module serves multiple purposes:
  • Now that we have modules, a “@reflect” module is a more natural place for many of the reflection methods previously defined on Object. For backwards-compatibility purposes, it is unlikely that the static methods on Object will disappear. However, new methods should likely be added to the “@reflect” module rather than to the Object constructor.
  • A natural home for proxies, avoiding the need for a global Proxy binding.
  • Most methods in this module map one-to-one onto Proxy traps. Proxy handlers need these methods to conveniently forward operations, as shown below.
“@reflect”模块有多种用途:
  • 现在我们有了模块,“@reflect”模块对于之前定义在 Object. 为了向后兼容,Object 上的静态方法不太可能消失。但是,新方法可能应该添加到“@reflect”模块而不是对象构造函数中。
  • 代理的天然家园,避免了全局代理绑定的需要。
  • 此模块中的大多数方法一对一地映射到代理陷阱。代理处理程序需要这些方法来方便地转发操作,如下所示。



So, the Reflectobject provides a number of utility functions, many of which appear to overlap with ES5 methods defined on the global Object.



因此,Reflect对象提供了许多实用函数,其中许多似乎与定义在全局对象上的 ES5 方法重叠。

However, that doesn't really explain what existing problems this intends to solve or what functionality is added. I suspected this could be shimmed and indeed, the above harmony-spec links to a 'non-normative, approximate implementation of these methods'.

但是,这并不能真正解释它打算解决哪些现有问题或添加了哪些功能。我怀疑这可能会被修正,事实上,上面的和谐规范链接到“这些方法的非规范、近似实现”

Examining that code could give (further) idea's about it's use, but thankfully there is also a wiki that outlines a number of reasons why the Reflect object is useful:
(I've copied (and formatted) the following text for future reference from that source as they are the onlyexamples I could find. Besides that, they make sense, already have a good explanation and touch the question's applyexample.)

检查该代码可以提供(进一步)关于它的使用的想法,但幸运的是,还有一个 wiki 概述了 Reflect 对象有用的许多原因:(
我已复制(并格式化)以下文本以供将来参考)来源,因为它们是我能找到的唯一示例。除此之外,它们很有意义,已经有了很好的解释并触及了问题的apply示例。)


More useful return values


更有用的返回值

Many operations in Reflectare similar to ES5 operations defined on Object, such as Reflect.getOwnPropertyDescriptorand Reflect.defineProperty. However, whereas Object.defineProperty(obj, name, desc)will either return objwhen the property was successfully defined, or throw a TypeErrorotherwise, Reflect.defineProperty(obj, name, desc)is specced to simply return a boolean that indicates whether or not the property was successfully defined. This allows you to refactor this code:

中的许多操作Reflect类似于定义在 上的 ES5 操作Object,例如Reflect.getOwnPropertyDescriptorReflect.defineProperty。然而,whileObject.defineProperty(obj, name, desc)obj在属性被成功定义时返回,或者在TypeError其他情况下抛出一个,Reflect.defineProperty(obj, name, desc)被指定为简单地返回一个布尔值,指示该属性是否被成功定义。这允许您重构此代码:

try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}

To this:

对此:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}

Other methods that return such a boolean success status are Reflect.set(to update a property), Reflect.deleteProperty(to delete a property), Reflect.preventExtensions(to make an object non-extensible) and Reflect.setPrototypeOf(to update an object's prototype link).

返回这种布尔成功状态的其他方法是Reflect.set(更新属性)、Reflect.deleteProperty(删除属性)、Reflect.preventExtensions(使对象不可扩展)和Reflect.setPrototypeOf(更新对象的原型链接)。


First-class operations


一流的操作

In ES5, the way to detect whether an object objdefines or inherits a certain property name is to write (name in obj). Similarly, to delete a property, one uses delete obj[name]. While dedicated syntax is nice and short, it also means you must explicitly wrap these operations in functions when you want to pass the operation around as a first-class value.

在 ES5 中,检测对象是否obj定义或继承了某个属性名的方式是写(name in obj). 同样,要删除属性,可以使用delete obj[name]. 虽然专用语法很好且简短,但这也意味着当您希望将操作作为一流值传递时,您必须将这些操作显式包装在函数中。

With Reflect, these operations are readily defined as first-class functions:
Reflect.has(obj, name)is the functional equivalent of (name in obj)and Reflect.deleteProperty(obj, name)is a function that does the same as delete obj[name].

Reflect,这些操作是容易定义为第一级的功能:
Reflect.has(obj, name)是的功能等同物(name in obj)Reflect.deleteProperty(obj, name)是不一样的功能delete obj[name].


More reliable function application


更可靠的功能应用

In ES5, when one wants to call a function fwith a variable number of arguments packed as an array argsand binding the thisvalue to obj, one can write:

在 ES5 中,当你想调用一个f带有可变数量参数的函数,该函数打包为一个数组args并将this值绑定到obj,可以这样写:

f.apply(obj, args)

However, fcould be an object that intentionally or unintentionally defines its own applymethod. When you really want to make sure that the built-in applyfunction is called, one typically writes:

但是,f可能是有意或无意地定义了自己的apply方法的对象。当您真的想确保apply调用内置函数时,通常会这样写:

Function.prototype.apply.call(f, obj, args)

Not only is this verbose, it quickly becomes hard to understand. With Reflect, you can now make a reliable function call in a shorter and easier to understand way:

这不仅冗长,而且很快变得难以理解。使用Reflect,您现在可以以更短且更易于理解的方式进行可靠的函数调用:

Reflect.apply(f, obj, args)


Variable-argument constructors


可变参数构造函数

Imagine you want to call a constructor function with a variable number of arguments. In ES6, thanks to the new spread syntax, it will be possible to write code like:

想象一下,你想调用一个带有可变数量参数的构造函数。在 ES6 中,由于新的传播语法,可以编写如下代码:

var obj = new F(...args)

In ES5, this is harder to write, because one can only use F.applyor F.callto call a function with a variable number of arguments, but there is no F.constructfunction to newthe function with a variable number of arguments. With Reflect, one can now write, in ES5:

在 ES5 中,这更难写,因为只能使用F.applyF.call调用具有可变数量参数的函数,而具有可变数量参数的函数没有F.construct函数new。有了Reflect,现在可以在 ES5 中编写:

var obj = Reflect.construct(F, args)


Default forwarding behavior for Proxy traps


代理陷阱的默认转发行为

When using Proxyobjects to wrap existing objects, it is very common to intercept an operation, do something, and then to "do the default thing", which is typically to apply the intercepted operation to the wrapped object. For example, say I want to simply log all property accesses to an object obj:

在使用Proxy对象包装现有对象时,很常见的做法是拦截一个操作,做一些事情,然后“做默认的事情”,这通常是将拦截的操作应用到被包装的对象上。例如,假设我想简单地记录对一个对象的所有属性访问obj

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    // now do the default thing
  }
});

The Reflectand ProxyAPIs were designed in tandem, such that for each Proxytrap, there exists a corresponding method on Reflectthat "does the default thing". Hence, whenever you find yourself wanting to "do the default" thing inside a Proxy handler, the correct thing to do is to always call the corresponding method in the Reflectobject:

ReflectProxyAPI的设计串联,使得对于每个Proxy陷阱,存在一个相应的方法上Reflect说,“确实默认的事情”。因此,每当您发现自己想要在代理处理程序中“执行默认”操作时,正确的做法是始终调用Reflect对象中的相应方法:

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  }
});

The return type of the Reflectmethods is guaranteed to be compatible with the return type of the Proxytraps.

方法的返回类型Reflect保证与Proxy陷阱的返回类型兼容。


Control the this-binding of accessors


控制访问器的 this-binding

In ES5 it's fairly easy to do a generic property access or property update. For instance:

在 ES5 中,进行通用属性访问或属性更新相当容易。例如:

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update

The Reflect.getand Reflect.setmethods allow you to do the same thing, but additionally accept as a last optional argument a receiverparameter that allows you to explicitly set the this-binding when the property that you get/set is an accessor:

Reflect.getReflect.set方法允许你做同样的事情,而且还接受作为最后的可选参数一个receiver参数,使您可以明确设置this属性时,你得到结合位/集是访问:

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)

This is occasionally useful when you're wrapping objand you want any self-sends within the accessor to get re-routed to your wrapper, e.g. if objis defined as:

当您进行包装obj并且希望访问器中的任何自我发送重新路由到您的包装器时,这有时很有用,例如,如果obj定义为:

var obj = {
  get foo() { return this.bar(); },
  bar: function() { ... }
}

Calling Reflect.get(obj, "foo", wrapper)will cause the this.bar()call to get rerouted to wrapper.

调用Reflect.get(obj, "foo", wrapper)将导致this.bar()调用重新路由到wrapper


Avoid legacy __proto__


避免遗留 __proto__

On some browsers, __proto__is defined as a special property that gives access to an object's prototype. ES5 standardized a new method Object.getPrototypeOf(obj)to query the prototype. Reflect.getPrototypeOf(obj)does exactly the same, except that Reflectalso defines a corresponding Reflect.setPrototypeOf(obj, newProto)to set the object's prototype. This is the new ES6-compliant way of updating an object's prototype.
Note that: setPrototypeOfalsoexists on Object(as correctly pointed out by Knu's comment)!

在某些浏览器上,它__proto__被定义为一个特殊的属性,可以访问对象的原型。ES5 标准化了一种新的方法Object.getPrototypeOf(obj)来查询原型。Reflect.getPrototypeOf(obj)做的完全一样,除了Reflect还定义了一个对应Reflect.setPrototypeOf(obj, newProto)的设置对象的原型。这是更新对象原型的新的 ES6 兼容方式。
请注意:setPrototypeOf存在于Object(正如Knu评论正确指出的那样)!



EDIT:
Side-note (addressing comments to the Q): There is a short and simple answer on 'Q: ES6 Modules vs. HTML Imports'that explains Realmsand Loaderobjects.

编辑:
旁注(针对 Q 的评论):关于“Q:ES6 模块与 HTML 导入”有一个简短而简单的答案,解释RealmsLoader对象。

Another explanation is offered by this link:

此链接提供了另一种解释:

A realm object abstracts the notion of a distinct global environment, with its own global object, copy of the standard library, and "intrinsics" (standard objects that are not bound to global variables, like the initial value of Object.prototype).

Extensible web: This is the dynamic equivalent of a same-origin <iframe>without DOM.

一个领域对象抽象了一个独特的全局环境的概念,它有自己的全局对象、标准库的副本和“内部”(未绑定到全局变量的标准对象,如 Object.prototype 的初始值)。

可扩展网络:这是<iframe>没有 DOM的同源的动态等价物 。

Worth mentioning though: all this is still in draft, this is not a specification etched in stone!It's ES6, so keep browser-compatibility in mind!

值得一提的是:所有这些都还在草案中,这不是刻在石头上的规范!它是 ES6,所以请记住浏览器兼容性!

Hope this helps!

希望这可以帮助!

回答by Blue

Going by the draft document found on the wiki,

通过在维基上找到的草稿文件,

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

We get the line about "single ordinary object" which it clarifies in the draft. It also has the function definitions.

我们得到了草案中澄清的关于“单一普通对象”的内容。它还具有函数定义。

The wiki should be reliable since you can find a link to it from the emcascript website

wiki 应该是可靠的,因为您可以从 emcascript 网站找到指向它的链接

http://www.ecmascript.org/dev.php

http://www.ecmascript.org/dev.php

I found the first link by google though and didn't have any luck finding it by searching the wiki directly.

不过,我通过 google 找到了第一个链接,但没有通过直接搜索 wiki 找到它。