Reflect对象在JavaScript中做什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (115)

前一段时间,Reflect我在JavaScript中看到了MDN上的空白存根,但我不能在Google上找到任何东西。今天我找到了这个http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object,除了领域和加载程序功能外,它听起来和Proxy对象很相似。

基本上,我不知道我发现的这个页面是否仅仅解释了如何实现反射或者我不明白它的措辞。有人能向我解释一般的Reflect做法吗?

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

提问于
用户回答回答于

正如指出的第七答案,现在ES6(ECMAScript中2015年)已经完成,更适当的文件现已推出:

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

反射对象是一个单一的普通对象。 Reflect对象的[[Prototype]]内部槽的值是标准的内置对象原型对象(19.1.3)。 反射对象不是函数对象。它没有[[Construct]]内部方法; 不可能将Reflect对象用作new运算符的构造函数。反射对象也没有[[Call]]内部方法; 作为函数调用Reflect对象是不可能的。

但是,关于ES Harmony的目的,有一个简短的解释:

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

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

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

但是,这并不能真正解释这些打算解决的问题或添加了哪些功能。我怀疑这可能会被削弱,事实上,上述和谐规范与“这些方法的非规范性近似实现”有关

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

更有用的返回值

许多操作Reflect类似于定义的ES5操作Object,例如Reflect.getOwnPropertyDescriptorReflect.defineProperty。然而,尽管Object.defineProperty(obj, name, desc)要么返回obj时被成功定义的属性,或者抛出TypeError,否则Reflect.defineProperty(obj, name, desc)被specced只返回一个布尔值,指示是否已成功定义的属性。这可以让你重构这段代码:

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对象封装现有对象时,拦截操作,执行某些操作并执行“默认操作”非常常见,通常是将截取的操作应用于封装对象。例如,假设我想简单地将所有的属性访问记录到一个对象中obj

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

ReflectProxyAPI的设计串联,使得对于每个Proxy陷阱,存在相应的方法上Reflect说,“确实默认的事情”。因此,无论何时您发现自己希望在代理处理程序中“执行默认”操作,正确的做法是始终调用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-结合时,你的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)

这在包装时偶尔很有用,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:ES6模块与HTML导入'的解释RealmsLoader对象有一个简短的回答

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

一个领域对象抽象出一个独特的全局环境的概念,它具有它自己的全局对象,标准库的副本和“内部函数”(未绑定到全局变量的标准对象,如Object.prototype的初始值)。 可扩展网页:这是<iframe>没有DOM 的同一个来源的动态等价物 。

用户回答回答于

按照维基上的文档草稿,

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

我们得到了关于草稿中澄清的“单一普通对象”的界限。它也有函数定义。

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

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

我找到了谷歌的第一个链接,但没有任何运气通过直接搜索wiki找到它。

扫码关注云+社区

领取腾讯云代金券