前一段时间我在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
,但指向旧的代理规范/
答案 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.getOwnPropertyDescriptor
和Reflect.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.apply
或F.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
}
});
Reflect
和Proxy
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.get
和Reflect.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'解释了Realms
和Loader
个对象。
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对象。有些人可能更愿意检查一下。