Reflect对象在JavaScript中做了什么?

时间:2014-08-21 08:44:54

标签: javascript ecmascript-6

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

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

例如,在我找到的页面上说调用Reflect.apply ( target, thisArgument, argumentsList ) 将“返回使用参数thisArgument和args调用目标的[[Call]]内部方法的结果。”但是,除了调用target.apply(thisArgument, argumentsList)之外,还有什么不同?

更新

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

3 个答案:

答案 0 :(得分:103)

2015年更新: 正如7th answer指出的那样,现在ES6(ECMAScript 2015)已​​经完成,现在可以获得更合适的文档:

<小时/> 原始答案((历史性)理解和额外示例)

Reflection proposal似乎已进展到Draft ECMAScript 6 Specification。本文档目前概述了Reflect - 对象的方法,并仅对Reflect - 对象本身进行了以下说明:

Reflect对象是一个普通对象。

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

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

但是,在ES Harmony中有一个关于它的目的的简短说明:

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



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

然而,这并没有真正解释这个想要解决的现有问题或者添加了哪些功能。我怀疑这可能是shimmed,事实上,上面的和声规范链接到'non-normative, approximate implementation of these methods'

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


更有用的返回值

Reflect中的许多操作与Object上定义的ES5操作类似,例如Reflect.getOwnPropertyDescriptorReflect.defineProperty。但是,Object.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(更新对象的原型链接)。


一流的运作

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

使用Reflect,这些操作很容易定义为第一类功能:
Reflect.has(obj, name)的功能等同于(name in obj)Reflect.deleteProperty(obj, name)是一个与delete obj[name].

相同的函数


更可靠的功能应用

在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)


变量参数构造函数

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

var obj = new F(...args)

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

var obj = Reflect.construct(F, args)


代理陷阱的默认转发行为

当使用Proxy个对象来包装现有对象时,拦截操作,执行某些操作,然后执行默认操作&#34;这通常是应用截获的操作是很常见的对包装对象的操作。例如,假设我想简单地记录对象obj的所有属性访问:

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

ReflectProxy API 是串联设计的,因此对于每个Proxy陷阱,Reflect上都存在相应的方法&#34;做默认事情&#34;。因此,每当你发现自己想要&#34;做默认&#34;在代理处理程序内部的事情,正确的做法是始终调用Reflect对象中的相应方法:

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

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


控制访问者的这种绑定

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

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

Reflect.getReflect.set方法允许您执行相同的操作,但另外接受receiver参数作为最后一个可选参数,允许您显式设置this当你获取/设置的属性是一个访问者时,-binding:

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 also exists on Object(由Knu&#39; s comment正确指出)!


修改
附注(对Q的评论):有一个简短的answer on 'Q: ES6 Modules vs. HTML Imports'解释了RealmsLoader个对象。

this link提供了另一种解释:

  

领域对象抽象出一个独特的全球环境的概念,   拥有自己的全局对象,标准库的副本,以及   &#34;内在&#34; (未绑定到全局变量的标准对象,   比如Object.prototype的初始值。

     

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

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

希望这有帮助!

答案 1 :(得分:5)

通过维基上的草案文件,

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

我们得到关于&#34;单个普通对象&#34;它在草案中澄清了。它还有函数定义。

wiki应该可靠,因为你可以从emcascript网站找到它的链接

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

我找到了google的第一个链接,并且通过直接搜索wiki没有找到任何运气。

答案 2 :(得分:3)

GitaarLab的回答非常好,但我想指出,MDN including details and examples to all of its methods已经完全记录了Reflect对象。有些人可能更愿意检查一下。