如何使用不同上下文的es6构造函数指令 [英] How to use es6 constructor instructions with a different context
问题描述
是否可以通过更改this上下文(call,apply或other)在另一个实例上使用es6构造函数指令?这可以使用es5类。以下是我的意思的一个小例子:
Is it possible to use es6 constructor instructions on another instance by changing the "this" context (call, apply or other)? This is possible using es5 "classes". Here is a small example of what I mean:
function ES5() {
this.foo = 'foo';
}
class ES6 {
constructor() {
this.bar = 'bar';
}
}
var a = new ES6();
ES5.call(a);
console.log(a.foo + a.bar); //foobar
var b = new ES5();
//Reflect.construct(ES6); ??
ES6.call(b); //TypeError: Class constructor ES6 cannot be invoked without 'new'
console.log(b.foo + b.bar); //how to get foobar here too?
编辑:
我的问题与new关键字无关。我正在寻找的答案是如何使用另一个this上下文(带或不带new关键字)运行es6构造函数中的指令。
My question has nothing to do with the new keyword. The answer I'm looking for is how to run the instructions placed in an es6 constructor using another "this" context (with or without the new keyword).
推荐答案
正如评论和你自己所指出的那样,试图用自定义这个
上下文来调用类构造函数真的不是如果有任何方法,你想尝试。故意这样做很难!
As the comments and yourself have pointed out, trying to invoke class constructors with a custom this
context is really not something you want to attempt if there is any way around it. This was made hard intentionally!
如果由于某些原因这是不可避免的,足以证明棘手的解决方法,你可以在下面找到两个部分解决方案。它们各自都不完美 - 取决于您的具体情况,其中一个可能仍然符合您的需求。
If for some reasons this is unavoidable enough to justify tricky workarounds, you can find two partial solutions below. They are both imperfect in their own ways - depending on your exact situation one of them may still fit your needs.
虽然无法在构造函数调用中直接设置此
,但可以设置原型
到您选择的对象。
While it is impossible to set this
directly in a constructor call, it is possible to set the prototype of this
to an object of your choice.
为此,您可以使用 Reflect.construct()
使用自定义 new调用内部
值。 [[Construct]]
方法.target 然后将
初始化为继承自 new.target.prototype
的对象。
To do so you can use Reflect.construct()
to call the internal [[Construct]]
method with a custom new.target
value. this
will then get initialised to an object inheriting from new.target.prototype
.
以您的示例为基础:
function ES5() {
this.foo = 'foo';
}
class ES6 {
constructor() {
this.bar = 'bar';
}
}
let b = new ES5();
function TemporaryHelperConstructor() {}
TemporaryHelperConstructor.prototype = b;
b = Reflect.construct( ES6, [], TemporaryHelperConstructor ); // The third argument corresponds to the value of new.target
console.log( b.foo + b.bar ); // foobar !
(<$ c $的确切运作方式c> Reflect.construct()和内部 [[Construct]]
方法在 26.1.2 和 9.2.2 规范)
(The exact workings of Reflect.construct()
and the internal [[Construct]]
method are described in sections 26.1.2 and 9.2.2 of the specs)
- 实际上没有使用
调用类构造函数这个
绑定到b
,它被调用,这个
绑定到一个直接继承的空对象来自b
。如果您或类构造函数依赖于Object.getOwnPropertyNames()
,Object.getPrototypeOf()$ c $等方法,这可能会导致问题c>等。
- The class constructor is not actually called with
this
bound tob
, it is called withthis
bound to an empty object directly inheriting fromb
. This may lead to problems if you or the class constructor rely on methods likeObject.getOwnPropertyNames()
,Object.getPrototypeOf()
etc.
虽然不能在不导致 TypeError $ c的情况下调用类构造函数的内部
[[Call]]
方法$ c>,可以提取附加到类构造函数的代码块并从中创建一个普通函数,然后可以使用自定义调用此
绑定。
While it is impossible to invoke the internal [[Call]]
method of a class constructor without causing a TypeError
, it is possible to extract the code block attached to the class constructor and create an ordinary function out of it, which you may then call with a custom this
binding.
您可以使用 Function.prototype.toString()
方法来提取类构造函数的代码块作为字符串。然后 Function()
构造函数可以从这个字符串中创建一个普通函数,你可以用自定义调用这个
通过 Function.prototype.apply()
进行绑定。
You can use the Function.prototype.toString()
method to extract the code block of the class constructor as a string. The Function()
constructor can then make an ordinary function out of this string, which you may call with a custom this
binding through Function.prototype.apply()
.
以您的示例为基础:
function ES5() {
this.foo = 'foo';
}
class ES6 {
constructor() {
this.bar = 'bar';
}
}
const b = new ES5();
const constructorBody = ES6.toString().match( /(?<=constructor\(\) ){[^}]*}/ )[0]
const ordinaryFunction = Function( constructorBody )
ordinaryFunction.apply( b ); // No TypeError
console.log( b.foo + b.bar ); // foobar !
请注意,此代码段使用的是极为简化的正则表达式,用于演示目为了使功能更强大,您需要考虑字符串和注释中的嵌套花括号和花括号。如果需要,您还需要提取构造函数参数。
(根据 19.2.3.5 的规格,你可以依赖足够的输出 Function.prototype.toString()
这种方法跨实现工作。)
(According to section 19.2.3.5 of the specs, you can rely on a consistent enough output of Function.prototype.toString()
for this approach to work across implementations.)
-
new.target
将设置为undefined
执行普通函数时(如[[Call]]
invocations一样),如果类构造函数正在使用它,可能会导致问题。 - 使用
Function()
创建的新函数将丢失原始类构造函数的闭包( MDN ),如果类构造函数依赖它们,可能会导致ReferenceErrors
。 - 如果使用
super()
在派生类上应用此方法将导致SyntaxError
,这是无效的普通函数中的语法。
new.target
will be set toundefined
when executing the ordinary function (as is always the case with[[Call]]
invocations), which may cause problems if the class constructor was using it.- Closures of the original class constructor will be lost to the new function created with
Function()
(MDN), which may causeReferenceErrors
if the class constructor was relying on them. - This approach will lead to a
SyntaxError
if applied on a derived class usingsuper()
, which is not valid syntax in ordinary functions.
您的问题没有完美的解决方案。如果你的用例很简单,你仍然可以达到你想要的效果。部分解决方法将伴随着他们自己的有害问题 - 谨慎行事!
这篇关于如何使用不同上下文的es6构造函数指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!