反思对象在JavaScript中做了什么? [英] What does the Reflect object do in JavaScript?

查看:117
本文介绍了反思对象在JavaScript中做了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我之前在MDN上看到一个空白的stub,因为javascript中的 Reflect 对象,但是在我的生活中,我找不到任何东西。今天我发现这个 http://people.mozilla.org/~jorendorff/es6- draft.html#sec-reflect-object ,除了领域和加载器功能外,它听起来类似于Proxy对象。



基本上,我不知道这个页面我发现只是解释了如何实现反思,或者我不能理解它的措辞。有人可以向我解释一般,反映的方法是什么?



例如,在我找到的页面上说,调用 Reflect.apply(target,thisArgument,argumentsList)
将返回使用参数thisArgument调用目标的[[Call]]内部方法的结果并指出。但是如何与调用 target.apply(thisArgument,argumentsList)之间的任何不同?



更新: p>

感谢@Blue,我在wiki上找到了这个页面
http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect
据我所知,据了解,反映对象提供方法版本的所有可以被代理人捕获的动作,使发送更容易。但这似乎对我来说有点怪异,因为我看不出它是完全有必要的。但是它似乎比这更多,特别是说双重提升的标准,但是指向旧的代理规范/

解决方案

更新2015:
正如 7th 回答,现在ES6(ECMAScript 2015)已​​经定稿,更合适的文档现在可用:







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



反思p 似乎已进展到草稿ECMAScript 6规范。本文档目前概述了 Reflect -object的方法,并且仅在反映 -object本身之间陈述以下内容:



反射对象是一个普通的对象。



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



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

但是,有一个简短的解释,它的目的在 ES Harmony


@reflect模块提供多个目的:

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




所以,反映对象提供了许多效用函数,其中许多函数似乎与全局对象上定义的ES5方法重叠。



但是,这并不能真正解释这个打算解决的现有问题或添加什么功能。我怀疑这可能会被抹去,确实,上述和声规范链接到非规范性,近似性这些方法的实现



检查该代码可以(进一步)提供关于它的使用的想法,但是幸运的是,还有一个wiki概述了 反射对象有用的一些原因

(我已经复制(并格式化)以下文本,以供将来参考,因为它们仅仅是我们可以找到的示例,除此之外,它们已经有意义了有一个很好的解释,并触及问题的申请示例。)




更有用的返回值



Reflect 中的许多操作与 Object ,如 Reflect.getOwnPropertyDescriptor Reflect.defineProperty 。然而,当成功定义属性时, Object.defineProperty(obj,name,desc)将返回 obj 或者抛出一个 TypeError 否则, Reflect.defineProperty(obj,name,desc)被简单地返回一个布尔值表示属性是否已成功定义。这允许您重构这个代码:

  try {
Object.defineProperty(obj ,name,desc);
//属性定义成功
} catch(e){
//可能失败(可能会意外捕获错误的异常)
}

为此:

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

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




一流的操作



在ES5中,检测对象 obj 是否定义或继承一定的属性名就是写(在obj中的名字)。同样,要删除一个属性,使用 delete obj [name] 。虽然专用语法是很好的和简短的,但也意味着您必须在函数中明确地将这些操作包含在函数中,当您想要以一级值的形式传递操作时。



反映,这些操作很容易定义为一流的功能:

Reflect.has(obj,name)的功能相当于(obj中的名称) Reflect.deleteProperty(obj,name)一个与相同的功能删除obj [name]。




更可靠的功能应用程序



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

  f.apply(obj,args)

但是, f 可能是有意或无意的对象定义了自己的应用方法。当您确实要确保内置的应用函数时,通常会写入:

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

不仅是这个冗长的,它很快变得难以理解。使用反映,您现在可以以更短和更容易理解的方式进行可靠的函数调用:

  Reflect.apply(f,obj,args)


可变参数构造函数



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

  var obj = new F(... args)

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

  var obj = Reflect.construct(F,args)


代理陷阱的默认转发行为



当使用代理对象来包装现有对象时,它是非常常见的是截取一个操作,做一些事情,然后做默认的事情,这通常是把被截取的操作应用到被包装的对象。例如,我想简单地记录对象的所有属性访问 obj

 get:function(target,name){
console.log(get,target,name);
//现在做默认的东西
}
});

反映代理 API 被串联设计,这样对于每个代理陷阱,在反映做默认的事情。因此,每当您发现自己想要在代理处理程序中执行默认操作时,正确的做法是始终调用 Reflect 对象中的相应方法: / p>

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

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




控制此访问器的绑定



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

  var name = ... //将属性名称作为字符串
obj [name] //通用属性查找
obj [name] = value //通用属性更新

Reflect.get Reflect.set 方法允许你做同样的事情,但另外接受作为最后一个可选参数 receive 参数,允许您显式设置 -binding当您的属性get / set是一个访问器:

  var name = ... //获取属性名称一个字符串
Reflect.get(obj,name,wrapper)//如果obj [name]是一个访问器,它使用`this === wrapper`运行
Reflect.set(obj,name,值,包装)

当您包装 obj ,并且您希望访问者内的任何自发信息被重新路由到您的包装器,例如如果 obj 定义为:

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

调用 Reflect.get(obj,foo,wrapper)将导致 this.bar()调用获取


避免遗留 __ proto __



在某些浏览器上, __ proto __ 被定义为允许访问的特殊属性对象的原型。 ES5标准化了一种新方法 Object.getPrototypeOf(obj)来查询原型。 Reflect.getPrototypeOf(obj)完全一样,除了 Reflect 还定义了一个相应的 Reflect.setPrototypeOf(obj,newProto)设置对象的原型。这是更新对象原型的新的符合ES6的新方法。

请注意: setPrototypeOf 存在于 Object (正如 Knu comment )!






编辑:

旁注(对Q的注释):有一个简短而简单的关于Q:ES6模块与HTML导入的回复,解释了 Realms 装载机对象。



另一个解释是由此链接


一个领域对象抽象出一个不同的全局环境的概念,
和它自己的全局对象,标准库的副本,和
intrinsics(不绑定到全局变量的标准对象,
像Object.prototype的初始值)。



可扩展网络:这是一个动态的等价于相同来源的
< iframe> 没有DOM。


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



希望这有帮助!


I saw a blank stub on MDN a while ago for the Reflect object in javascript but I can't for the life of me find anything on Google. Today I found this http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object and it sounds similar to the Proxy object apart from the realm and loader functionality.

Basically, I don't know whether this page I found only explains how to implement Reflect or if I just can't understand its wording. Could someone please explain to me generally what the methods of Reflect do?

For instance, on the page I found says that calling Reflect.apply ( target, thisArgument, argumentsList ) will "Return the result of calling the [[Call]] internal method of target with arguments thisArgument and args." but how is that any different than just calling target.apply(thisArgument, argumentsList)?

Update:

Thanks to @Blue, I found this page on the wiki http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect which to the best of my knowledge says that the reflect object provides method versions of all the actions that can be trapped by proxies to make forwarding easier. But that seems a little weird to me since I don't see how it's entirely necessary. But it Seems to do a little more than that, particularly the par that says double-lifting but that points to the old proxy spec/

解决方案

UPDATE 2015: As pointed out by 7th's answer, now that ES6 (ECMAScript 2015) has been finalized, more appropriate documentation is now available:


Original answer (for (historic) understanding and extra examples):

The Reflection proposal seems to have progressed to the Draft ECMAScript 6 Specification. This document currently outlines the Reflect-object's methods and only states the following about the Reflect-object itself:

The Reflect object is a single ordinary object.

The value of the [[Prototype]] internal slot of the Reflect object is the standard built-in Object prototype object (19.1.3).

The Reflect object is not a function object. It does not have a [[Construct]] internal method; it is not possible to use the Reflect object as a constructor with the new operator. The Reflect object also does not have a [[Call]] internal method; it is not possible to invoke the Reflect object as a function.

However, there is a short explanation about it's purpose in ES Harmony:

The "@reflect" module serves multiple purposes:
  • Now that we have modules, a "@reflect" module is a more natural place for many of the reflection methods previously defined on Object. For backwards-compatibility purposes, it is unlikely that the static methods on Object will disappear. However, new methods should likely be added to the "@reflect" module rather than to the Object constructor.
  • A natural home for proxies, avoiding the need for a global Proxy binding.
  • Most methods in this module map one-to-one onto Proxy traps. Proxy handlers need these methods to conveniently forward operations, as shown below.



So, the Reflect object provides a number of utility functions, many of which appear to overlap with ES5 methods defined on the global Object.

However, that doesn't really explain what existing problems this intends to solve or what functionality is added. I suspected this could be shimmed and indeed, the above harmony-spec links to a 'non-normative, approximate implementation of these methods'.

Examining that code could give (further) idea's about it's use, but thankfully there is also a wiki that outlines a number of reasons why the Reflect object is useful:
(I've copied (and formatted) the following text for future reference from that source as they are the only examples I could find. Besides that, they make sense, already have a good explanation and touch the question's apply example.)


More useful return values

Many operations in Reflect are similar to ES5 operations defined on Object, such as Reflect.getOwnPropertyDescriptor and Reflect.defineProperty. However, whereas Object.defineProperty(obj, name, desc) will either return obj when the property was successfully defined, or throw a TypeError otherwise, Reflect.defineProperty(obj, name, desc) is specced to simply return a boolean that indicates whether or not the property was successfully defined. This allows you to refactor this code:

try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}

To this:

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

Other methods that return such a boolean success status are Reflect.set (to update a property), Reflect.deleteProperty (to delete a property), Reflect.preventExtensions (to make an object non-extensible) and Reflect.setPrototypeOf (to update an object's prototype link).


First-class operations

In ES5, the way to detect whether an object obj defines or inherits a certain property name is to write (name in obj). Similarly, to delete a property, one uses delete obj[name]. While dedicated syntax is nice and short, it also means you must explicitly wrap these operations in functions when you want to pass the operation around as a first-class value.

With Reflect, these operations are readily defined as first-class functions:
Reflect.has(obj, name) is the functional equivalent of (name in obj) and Reflect.deleteProperty(obj, name) is a function that does the same as delete obj[name].


More reliable function application

In ES5, when one wants to call a function f with a variable number of arguments packed as an array args and binding the this value to obj, one can write:

f.apply(obj, args)

However, f could be an object that intentionally or unintentionally defines its own apply method. When you really want to make sure that the built-in apply function is called, one typically writes:

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

Not only is this verbose, it quickly becomes hard to understand. With Reflect, you can now make a reliable function call in a shorter and easier to understand way:

Reflect.apply(f, obj, args)


Variable-argument constructors

Imagine you want to call a constructor function with a variable number of arguments. In ES6, thanks to the new spread syntax, it will be possible to write code like:

var obj = new F(...args)

In ES5, this is harder to write, because one can only use F.apply or F.call to call a function with a variable number of arguments, but there is no F.construct function to new the function with a variable number of arguments. With Reflect, one can now write, in ES5:

var obj = Reflect.construct(F, args)


Default forwarding behavior for Proxy traps

When using Proxy objects to wrap existing objects, it is very common to intercept an operation, do something, and then to "do the default thing", which is typically to apply the intercepted operation to the wrapped object. For example, say I want to simply log all property accesses to an object obj:

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

The Reflect and Proxy APIs were designed in tandem, such that for each Proxy trap, there exists a corresponding method on Reflect that "does the default thing". Hence, whenever you find yourself wanting to "do the default" thing inside a Proxy handler, the correct thing to do is to always call the corresponding method in the Reflect object:

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

The return type of the Reflect methods is guaranteed to be compatible with the return type of the Proxy traps.


Control the this-binding of accessors

In ES5 it's fairly easy to do a generic property access or property update. For instance:

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

The Reflect.get and Reflect.set methods allow you to do the same thing, but additionally accept as a last optional argument a receiver parameter that allows you to explicitly set the this-binding when the property that you get/set is an accessor:

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)

This is occasionally useful when you're wrapping obj and you want any self-sends within the accessor to get re-routed to your wrapper, e.g. if obj is defined as:

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

Calling Reflect.get(obj, "foo", wrapper) will cause the this.bar() call to get rerouted to wrapper.


Avoid legacy __proto__

On some browsers, __proto__ is defined as a special property that gives access to an object's prototype. ES5 standardized a new method Object.getPrototypeOf(obj) to query the prototype. Reflect.getPrototypeOf(obj) does exactly the same, except that Reflect also defines a corresponding Reflect.setPrototypeOf(obj, newProto) to set the object's prototype. This is the new ES6-compliant way of updating an object's prototype.
Note that: setPrototypeOf also exists on Object (as correctly pointed out by Knu's comment)!


EDIT:
Side-note (addressing comments to the Q): There is a short and simple answer on 'Q: ES6 Modules vs. HTML Imports' that explains Realms and Loader objects.

Another explanation is offered by this link:

A realm object abstracts the notion of a distinct global environment, with its own global object, copy of the standard library, and "intrinsics" (standard objects that are not bound to global variables, like the initial value of Object.prototype).

Extensible web: This is the dynamic equivalent of a same-origin <iframe> without DOM.

Worth mentioning though: all this is still in draft, this is not a specification etched in stone! It's ES6, so keep browser-compatibility in mind!

Hope this helps!

这篇关于反思对象在JavaScript中做了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆