Reflect对象在JavaScript中做什么?

我刚才在JavaScript中看到了一个MDN上的空白存根,但是我不能在Google上find任何东西。 今天,我发现这个http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object ,听起来和代理对象除了领域和加载器function不同。

基本上,我不知道我发现的这个页面是否只解释了如何实现reflection,或者如果我不明白它的措辞。 有人能向我解释一下Reflect的方法是什么吗?

例如,在我发现的页面上说,调用Reflect.apply ( target, thisArgument, argumentsList )将返callback用目标[[Call]]内部方法的结果,参数为thisArgument和args。 但是,不同于只调用target.apply(thisArgument, argumentsList)呢?

更新:

感谢@Blue,我在维基http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect上find了这个页面,据我所知,reflection对象提供了所有的方法版本可以被代理困住的操作使转发更容易。 但是,这对我来说似乎有点不可思议,因为我不明白它是如何完全有必要的。 但它似乎做的比这更多一点,特别是那个double-lifting的标准,但是指向旧的代理规格/

2015年更新:正如7日的回答指出的那样,现在ES6(ECMAScript 2015)已​​经完成,现在有了更适合的文档:

  • ES6规格,reflection
  • MDN反映(包括所有方法的细节和例子)

原始答案(对(历史)理解和额外的例子)

Reflection proposal似乎已经进展到ECMAScript 6规范草案 。 这个文件目前概述了Reflect的方法,并且只说明了Reflect本身的以下内容:

reflection对象是一个单一的普通对象。

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

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

然而,关于ES Harmony的目的,有一个简短的解释:

“@reflect”模块有多种用途:

  • 现在我们有了模块,“@reflect”模块对于以前在Object中定义的许多reflection方法来说更自然。 为了向后兼容的目的,Object上的静态方法不可能消失。 但是,新的方法可能应该添加到“@reflect”模块而不是Object构造函数。
  • 代理的自然之家,避免了全局代理绑定的需要。
  • 本模块中的大多数方法都是一对一映射到代理陷阱。 代理处理程序需要这些方法来方便地转发操作,如下所示。

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

但是,这并不能真正解释这个打算解决的问题或添加了哪些function。 我怀疑这可能会被削弱,事实上,上述和谐规范与“这些方法的非规范性,大致的实施”相联系 。

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

更有用的返回值

Reflect中的许多操作与Object定义的ES5操作类似,如Reflect.getOwnPropertyDescriptorReflect.defineProperty 。 然而,尽pipeObject.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) } 

对此:

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

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

一stream的运作

在ES5中,检测某个obj对象是否定义或inheritance某个属性名称的方法是写入(name in obj) 。 同样,要删除一个属性,使用delete obj[name] 。 虽然专用的语法很好,但是也意味着当你想把这些操作作为第一类值传递时,你必须明确地将这些操作包含在函数中。

通过Reflect ,这些操作很容易被定义为一stream的function:
Reflect.has(obj, name)Reflect.has(obj, name)的function等价物,而Reflect.deleteProperty(obj, name)是一个与delete obj[name].相同的函数delete obj[name].

更可靠的function应用

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

 f.apply(obj, args) 

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

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

这不仅是冗长的,它很快变得难以理解。 通过Reflect ,您现在可以使用更简单易懂的方式进行可靠的函数调用:

 Reflect.apply(f, obj, args) 

variables参数的构造函数

想象一下,你想用一个可变数量的参数调用一个构造函数。 在ES6中,得益于新的扩展语法,可以编写如下代码:

 var obj = new F(...args) 

在ES5中,这很难写,因为只能使用F.apply或者F.call来调用一个带有可变参数的函数,但是没有F.construct函数可以使用可变数量的参数。 通过Reflect ,现在可以在ES5中写出:

 var obj = Reflect.construct(F, args) 

代理陷阱的默认转发行为

当使用Proxy对象封装现有对象时,通常拦截一个操作,执行某些操作,然后执行“默认操作”,通常是将被拦截的操作应用于被包装的对象。 例如,我想简单地将所有的属性访问logging到对象obj

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

ReflectProxy API 是串联devise的 ,因此对于每个Proxy陷阱,在Reflect上都存在一个相应的方法:“执行默认的事情”。 因此,无论何时您发现自己想要在代理处理程序中“执行默认”的事情,正确的做法是始终调用Reflect对象中的相应方法:

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

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

控制访问器的这种绑定

在ES5中,执行通用属性访问或属性更新相当简单。 例如:

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

Reflect.getReflect.set方法允许你做同样的事情,但另外接受一个receiver参数作为最后一个可选参数,当你获取/设置的属性是一个存取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) 

这在包装obj时偶尔很有用,而且您希望访问器中的任何自我发送都可以重新路由到包装器,例如,如果obj被定义为:

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

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

避免遗留__proto__

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


编辑:
注意事项(对Q的注释): 关于'Q:ES6模块与HTML导入' ,解释RealmsLoader对象有一个简短的回答 。

这个链接提供了另一种解释:

一个领域对象抽象出一个独特的全局环境的概念,用它自己的全局对象,标准库的副本和“内部函数”(没有绑定到全局variables的标准对象,比如Object.prototype的初始值)。

可扩展的web :这是一个没有DOM的同源<iframe>的dynamic等价物。

值得一提的是: 这一切还在草案中,这不是一个刻骨铭心的规范! 这是ES6,所以请保持浏览器兼容性!

希望这可以帮助!

按照wiki上的文档草稿,

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

在草稿中明确表示“单一的普通的对象”的线。 它也有函数定义。

维基应该是可靠的,因为你可以从emcascript网站find它的链接

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

我find了谷歌的第一个链接,但没有任何运气通过直接searchwikifind它。

GitaarLab的答案是非常好的,但我想指出,reflection对象已被MDN完整logging, 包括其所有方法的详细信息和示例 。 有些人可能会喜欢检查,而不是。