Reflect对象在JavaScript中做了什么?

Jim*_*nes 77 javascript ecmascript-6

我刚才在MDN上看到了一个空白的存根,用于Reflectjavascript中的对象,但是我无法在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但但是指向旧的代理规范/

Git*_*LAB 111

更新2015: 正如第7 回答所指出的,现在ES6(ECMAScript 2015)已​​经完成,现在可以获得更合适的文档:


原始答案((历史性)理解和额外例子):

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

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方法重叠.

但是,这并没有真正解释这个打算解决的现有问题或者添加了哪些功能.我怀疑这可能会变得松软,事实上,上面的和谐规范与"这些方法的非规范,近似实施"有关.

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


更有用的返回值

许多操作Reflect类似于定义的ES5操作Object,例如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)
}
Run Code Online (Sandbox Code Playgroud)

对此:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}
Run Code Online (Sandbox Code Playgroud)

返回这种布尔成功状态的其他方法是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)
Run Code Online (Sandbox Code Playgroud)

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

Function.prototype.apply.call(f, obj, args)
Run Code Online (Sandbox Code Playgroud)

这不仅很冗长,而且很快变得难以理解.有了Reflect,您现在可以用更简单,更容易理解的方式进行可靠的函数调用:

Reflect.apply(f, obj, args)
Run Code Online (Sandbox Code Playgroud)


变量参数构造函数

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

var obj = new F(...args)
Run Code Online (Sandbox Code Playgroud)

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

var obj = Reflect.construct(F, args)
Run Code Online (Sandbox Code Playgroud)


代理陷阱的默认转发行为

当使用Proxy对象来包装现有对象时,通常拦截操作,执行某些操作,然后"执行默认操作",这通常是将截取的操作应用于包装对象.例如,假设我想简单地记录对象的所有属性访问obj:

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

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

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

的返回类型Reflect的方法是保证用的返回类型兼容的Proxy陷阱.


控制访问者的这种绑定

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

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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

var obj = {
  get foo() { return this.bar(); },
  bar: function() { ... }
}
Run Code Online (Sandbox Code Playgroud)

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


避免传统 __proto__

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


编辑:
侧面注释(对Q的评论):对Q:ES6模块与HTML Imports的解释RealmsLoader对象有一个简短而简单的答案.

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

领域对象使用自己的全局对象,标准库的副本和"内在函数"(未绑定到全局变量的标准对象,如Object.prototype的初始值)抽象出不同全局环境的概念.

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

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

希望这可以帮助!

  • `set`上也存在[setPrototypeOf](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/setPrototypeOf). (2认同)

Blu*_*lue 5

按照维基上的草案文件,

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

我们在草案中阐明了"单一普通对象"的界限.它还具有功能定义.

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

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

我发现谷歌的第一个链接,并没有任何运气通过直接搜索维基找到它.