Reflect对象在JavaScript中做什么?


87

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

基本上,我不知道我找到的该页面是否仅说明了如何实现Reflect,还是我只是不明白其措辞。有人可以向我总体解释该Reflect怎么做吗?

例如,在我发现的页面上说,调用Reflect.apply ( target, thisArgument, argumentsList ) 将“返回使用参数thisArgument和args调用target的[[Call]]内部方法的结果。” 但这跟打电话不一样target.apply(thisArgument, argumentsList)吗?

更新:

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


1
规范说“反射对象是单个普通对象。”据我了解,Reflect它只是RealmLoader对象的容器,但我也不知道后者做什么。
simonzack 2014年

谢谢:),在链接到的页面上(似乎不合法),我似乎每个领域都是其自己的“ java脚本上下文”,并且根据反射之间的相似性,加载程序会加载模块或类似内容的领域和代理和事实,即代理排序内置功能“超载”的可能Reflect.Loader,并Reflect.Realm有超载模块功能的东西?
吉姆·琼斯

1
像它看起来是一个“静态类”(如JSON)与静态方法isExtensibleownKeys等等。在ES 6,与实际的课程,这是非常有用的了解更多类(target16.1.2我认为)。
鲁迪2014年

Answers:


129

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



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

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

检查该代码可以使人们对它的使用有更进一步的了解,但值得庆幸的是,还有一个Wiki概述了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)
}

对此:

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

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


一流的运营

在ES5中,检测对象是否obj定义或继承某个属性名称的方法是write (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对象包装现有对象时,通常会先截取一个操作,执行某些操作,然后“执行默认操作”,这通常是将截取的操作应用于包装的对象。例如,假设我只想记录对对象的所有属性访问obj

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

ReflectProxyAPI的设计串联,使得对于每个Proxy陷阱,存在一个相应的方法上Reflect说,“确实默认的事情”。因此,每当您发现自己想要在Proxy处理程序中“执行默认操作”时,正确的操作是始终在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属性时,你得到结合位/集是访问:

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对象有一个简短的答案

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

领域对象通过其自己的全局对象,标准库的副本和“ intrinsics”(未绑定至全局变量的标准对象,如Object.prototype的初始值)抽象出独特的全局环境的概念。

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

值得一提的是:这一切仍在草案中,这不是刻板的规范!它是ES6,因此请牢记浏览器的兼容性!

希望这可以帮助!


@Spencer Killen:恩..这个答案是否将您的想法指向了正确的方向,并解释了这与Reflect.apply和之间的区别如何target.apply?或者我应该在赏金结束之前添加什么?
GitaarLAB 2014年

2
setPrototypeOf也存在于上Object
克伦民族联盟

1
请注意,Reflect.get如果您要使用原型属性代理对象,则将其用作代理获取的默认实现不能很好地工作。它只是抱怨它不起作用。但是,如果您使用Reflect.get(target, property)而不通过receiver,则它确实可以工作。
CMCDragonkai '17

我的测试表明,您始终通过代理访问属性的情况target是,代理是原始包装的目标,而receiver代理本身是。但是同样,如果您设法以不同的方式访问属性,则可能会有所不同。
CMCDragonkai '17年

5

翻阅Wiki上的文档草案,

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

我们在草稿中阐明了有关“单个普通对象”的内容。它还具有功能定义。

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

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

我找到了Google的第一个链接,但没有直接通过Wiki搜索找到它的运气。


谢谢,正式规范中列出的每种方法被调用时所采取的步骤似乎与我的链接中的步骤几乎相同,但是我仍然无法弄清楚每种方法在调用时的作用,例如在Reflect下。应用列出的步骤。4.执行PrepareForTailCall抽象操作。5.返回带有thisArgument和args参数的调用target的[[Call]]内部方法的结果-------这是什么意思?
吉姆·琼斯

从我看来,我最好的猜测是它在步骤4中引用尾递归,而步骤5是对原型函数的引用。看起来一般的想法是验证apply方法可以在其应用到的对象上运行(步骤1-2),错误句柄(步骤3),然后调用正在运行的apply方法(步骤4-5)。通过阅读文档,我最好的猜测是,“反射”模块的关键是当您执行功能时需要对对象进行某种形式的自省。调用的使用也可能是为什么它“没有[[Call]]内部方法”的原因。
蓝色
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.