不久前,我在MDN上看到了javascript中Reflect
对象的一个空白存根,但我在Google上找不到任何东西。今天我找到了这个http://people.mozilla.org/~jorendorff/es6 draft.html#sec 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对象提供了代理可以捕获的所有操作的方法版本,以使转发更容易。但这对我来说似乎有点奇怪,因为我不认为这是完全必要的。但它似乎有点多,尤其是说“代码>双重提升,但这指向旧的代理规范/
2015年更新:
正如第7条的回答所指出的,既然ES6(ECMAScript 2015)已经定稿,现在可以获得更合适的文档:
- ES6规范,反射
- MDN Reflect(包括所有方法的细节和示例)
原始答案(用于(历史)理解和额外示例):
反射提案
似乎已经发展到了ECMAScript 6规范草案。本文档目前概述了Reflect
-对象的方法,仅说明了Reflect
-对象本身的以下内容:
反射对象是单个普通对象。
反射对象的[[Prototype]]内部插槽的值为标准内置对象Prototype对象(19.1.3)。
反射对象不是函数对象。它没有[[Construct]]内部方法;使用new运算符无法将反射对象用作构造函数。反射对象也没有[[Call]]内部方法;无法将反射对象作为函数调用。
然而,关于ES Harmony中的目的,有一个简短的解释:
“@reflect”模块有多种用途:
- 现在我们有了模块,“@reflect”模块对于之前在对象上定义的许多反射方法来说是一个更自然的地方。
为了向后兼容,对象上的静态方法不太可能消失。但是,新方法可能应该添加到“@reflect”模块,而不是对象构造函数。- 代理的天然家,避免了全局代理绑定的需要。
- 本模块中的大多数方法将一对一映射到代理陷阱。代理处理程序需要这些方法来方便地转发操作,如下所示。
因此,Reflect
对象提供了许多实用功能,其中许多功能似乎与全局对象上定义的ES5方法重叠
然而,这并不能真正解释它打算解决哪些现有问题,或者添加了哪些功能。我怀疑这可能会被掩盖,事实上,上面的harmony spec链接到了“这些方法的非规范性近似实现”
检查该代码可以(进一步)了解它的用途,但谢天谢地,还有一个wiki概述了反射对象有用的一些原因:
(我已从该来源复制(并格式化)了以下文本,以备将来参考,因为它们是我能找到的唯一的示例。此外,它们很有意义,已经有了很好的解释,并触及了问题的应用
示例。)
更有用的返回值
Reflect
中的许多操作类似于在对象上定义的ES5操作,例如
Reflect.getOwnPropertyDescriptor
和Reflect.defineProperty
。但是,鉴于Object.defineProperty(obj,name,desc)
在成功定义属性时将返回obj
,或者抛出TypeError
否则,Reflect.defineProperty(obj,name,desc)
被指定为只返回一个布尔值,指示是否成功定义了属性。这允许您重构此代码:
试试看{
Object.defineProperty(对象、名称、描述);
//已成功定义属性
}捕获(e){
//可能的故障(并且可能意外捕获错误的异常)
}
为此:
if(Reflect.defineProperty(obj,name,desc)){
//成功
}否则{
//失败
}
返回此类布尔成功状态的其他方法有Reflect.set
(更新属性)、Reflect.deleteProperty
(删除属性)、Reflect.preventExtensions
(使对象不可扩展)和Reflect.setPrototypeOf
(更新对象的原型链接)
一流运营
在ES5中,检测对象obj
是否定义或继承某个属性名的方法是写入(obj中的名称)
。类似地,要删除属性,可以使用delete obj[name]
。虽然专用语法很好而且很短,但这也意味着当您希望将操作作为一级值传递时,必须在函数中显式地包装这些操作
使用Reflect
,这些操作很容易被定义为一级函数:
Reflect.has(obj,name)
是(obj中的name)
和Reflect.deleteProperty(obj,name)
的功能等同于delete obj[name]。
更可靠的功能应用
在ES5中,当人们想要调用一个函数f
,该函数包含一个数组args
,并将这个
值绑定到
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
函数调用具有可变参数数的函数。使用Reflect
,现在可以在ES5中编写:
var obj=Reflect.construct(F,args)
代理陷阱的默认转发行为
使用代理
对象包装现有对象时,通常会截取一个操作,执行某些操作,然后执行“默认操作”,这通常是将截取的操作应用于包装的对象。例如,假设我只想记录对对象的所有属性访问obj
:
var loggedObj=新代理(obj{
get:函数(目标、名称){
log(“get”、目标、名称);
//现在做默认的事情
}
});
Reflect
和Proxy
api是串联设计的,这样对于每个Proxy
陷阱,Reflect
上都有一个相应的方法“做默认的事情”。因此,每当您发现自己想要在代理处理程序中“执行默认”操作时,正确的做法是始终在Reflect
对象中调用相应的方法:
var loggedObj=新代理(obj{
get:函数(目标、名称){
log(“get”、目标、名称);
返回Reflect.get(目标、名称);
}
});
Reflect
方法的返回类型保证与代理的返回类型兼容
控制访问器的此绑定
在ES5中,执行通用属性访问或属性更新相当容易。例如:
var name=…//以字符串形式获取属性名称
obj[name]//通用属性查找
obj[name]=值//通用属性更新
Reflect.get
和Reflect.set
方法允许您执行相同的操作,但另外接受一个receiver
参数作为最后一个可选参数,该参数允许您