如何在不同的上下文中使用 es6 构造函数指令
How to use es6 constructor instructions with a different context
是否可以通过更改 "this" 上下文(调用、应用或其他)在另一个实例上使用 es6 构造函数指令?这可以使用 es5 "classes"。这是我的意思的一个小例子:
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 构造函数中的指令。
正如评论和您自己所指出的,尝试使用自定义 this
上下文调用 class 构造函数确实不是您想尝试的事情如果有任何办法解决它。这是故意设置的!
如果出于某些原因这是不可避免的,足以证明需要采取棘手的解决方法,您可以在下面找到两个部分解决方案。它们在各自方面都不完美 - 根据您的具体情况,其中之一可能仍能满足您的需求。
解决方法 1
虽然无法在构造函数调用中直接设置 this
,可以将 this
的原型设置为您选择的对象.
为此,您可以使用 Reflect.construct()
调用带有自定义 new.target
值的内部 [[Construct]]
方法。 this
然后将被初始化为一个继承自 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 !
(Reflect.construct()
和内部 [[Construct]]
方法的确切工作在规范的 26.1.2 and 9.2.2 部分中描述)
潜在问题
- class构造函数实际上并不是用
this
绑定到b
调用的,它是用this
绑定到直接继承自[=23的空对象调用的=].如果您或 class 构造函数依赖 Object.getOwnPropertyNames()
、Object.getPrototypeOf()
等方法,这可能会导致问题
解决方法 2
虽然不可能在不引起 TypeError
的情况下调用 class 构造函数的内部 [[Call]]
方法,但可以提取附加的代码块 到 class 构造函数并从中创建一个普通函数,然后您可以使用自定义 this
绑定调用它。
可以使用Function.prototype.toString()
方法将class构造函数的代码块提取为字符串。然后 Function()
构造函数可以从这个字符串中创建一个普通函数,您可以通过 Function.prototype.apply()
使用自定义 this
绑定来调用它。
以您的示例为基础:
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()
的足够一致的输出来使这种方法跨实现工作。)
潜在问题
new.target
将在执行普通函数时设置为 undefined
(与 [[Call]]
调用一样),这可能会导致问题,如果 class构造函数正在使用它。
- 原始class构造函数的闭包将丢失到使用
Function()
(MDN)创建的新函数中,这可能导致ReferenceErrors
如果class 构造函数依赖于它们。
- 如果使用
super()
将此方法应用于派生的 class,则此方法将导致 SyntaxError
,这在普通函数中是无效语法。
结论
您的问题没有完美的解决方案。如果您的用例足够简单,您仍然可以实现您想要的。部分变通办法会带来它们自己的有害问题 - 小心行事!
是否可以通过更改 "this" 上下文(调用、应用或其他)在另一个实例上使用 es6 构造函数指令?这可以使用 es5 "classes"。这是我的意思的一个小例子:
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 构造函数中的指令。
正如评论和您自己所指出的,尝试使用自定义 this
上下文调用 class 构造函数确实不是您想尝试的事情如果有任何办法解决它。这是故意设置的!
如果出于某些原因这是不可避免的,足以证明需要采取棘手的解决方法,您可以在下面找到两个部分解决方案。它们在各自方面都不完美 - 根据您的具体情况,其中之一可能仍能满足您的需求。
解决方法 1
虽然无法在构造函数调用中直接设置 this
,可以将 this
的原型设置为您选择的对象.
为此,您可以使用 Reflect.construct()
调用带有自定义 new.target
值的内部 [[Construct]]
方法。 this
然后将被初始化为一个继承自 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 !
(Reflect.construct()
和内部 [[Construct]]
方法的确切工作在规范的 26.1.2 and 9.2.2 部分中描述)
潜在问题
- class构造函数实际上并不是用
this
绑定到b
调用的,它是用this
绑定到直接继承自[=23的空对象调用的=].如果您或 class 构造函数依赖Object.getOwnPropertyNames()
、Object.getPrototypeOf()
等方法,这可能会导致问题
解决方法 2
虽然不可能在不引起 TypeError
的情况下调用 class 构造函数的内部 [[Call]]
方法,但可以提取附加的代码块 到 class 构造函数并从中创建一个普通函数,然后您可以使用自定义 this
绑定调用它。
可以使用Function.prototype.toString()
方法将class构造函数的代码块提取为字符串。然后 Function()
构造函数可以从这个字符串中创建一个普通函数,您可以通过 Function.prototype.apply()
使用自定义 this
绑定来调用它。
以您的示例为基础:
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()
的足够一致的输出来使这种方法跨实现工作。)
潜在问题
new.target
将在执行普通函数时设置为undefined
(与[[Call]]
调用一样),这可能会导致问题,如果 class构造函数正在使用它。- 原始class构造函数的闭包将丢失到使用
Function()
(MDN)创建的新函数中,这可能导致ReferenceErrors
如果class 构造函数依赖于它们。 - 如果使用
super()
将此方法应用于派生的 class,则此方法将导致SyntaxError
,这在普通函数中是无效语法。
结论
您的问题没有完美的解决方案。如果您的用例足够简单,您仍然可以实现您想要的。部分变通办法会带来它们自己的有害问题 - 小心行事!