为了支持旧的浏览器,我们都使用BabelJS将ES6转成ES5。当Babel编译一个扩展另一个类的类时,有一部分编译代码看起来像这样:
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
我知道_createSuper
函数的目的是创建一个函数来 Package 调用超类的过程,它将this
绑定为子类的接收者,以便子类可以继承其超类的属性。为了达到这个目的,我们只需要让result = Super.apply(this, arguments)
。但是代码检查环境是否支持Reflect
并优先使用Reflect.construct
。实际上,我不知道为什么我们需要result = Reflect.construct(Super, arguments, NewTarget)
以及它的作用。有人能详细解释一下吗?
1条答案
按热度按时间bvjxkvbb1#
对象构造(由
Reflect.construct
执行)和Function.prototype.apply
的语义是不相同的。在ECMAScript 6之前,它们非常相似(至少对于用户代码函数),但也有一些明显的差异,这些差异只是随着时间的推移而积累起来的。机会性地使用Reflect.construct
允许在可能的情况下应用真正的构造语义,而不是仅仅近似的函数调用语义。对于一个简单的情况:原生ECMAScript 6类根本不能像函数那样调用(直接,通过
Function.prototype.call
或Function.prototype.apply
);只能使用new
操作符或Reflect.construct
将它们作为构造函数调用。当一个转译类的基类恰好是一个原生的ECMAScript 6类时,使用Reflect.construct
允许子类化(大部分)透明地发生。当子类化某些内置类(如
Map
和Promise
)或DOM类(如Element
)时,也会出现类似的问题;这些构造函数也不能作为函数调用。这并不是说内建子类首先是一个好主意,但是如果直接运行的代码可以做到这一点,那么编译的代码也应该可以。事实上,在ECMAScript 6中,通过检查
new.target
是对象还是未定义,即使是普通函数也可以区分正常调用和作为构造函数调用。使用Reflect.construct
代替Function.prototype.apply
也消除了这种差异。