如何使用不同上下文的es6构造函数指令

ChG*_*ChG 6 javascript constructor ecmascript-5 ecmascript-6

是否可以通过更改"this"上下文(call,apply或其他)来在另一个实例上使用es6构造函数指令?这可以使用es5"类".这是我的意思的一个小例子:

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?
Run Code Online (Sandbox Code Playgroud)

编辑: 我的问题与新关键字无关.我正在寻找的答案是如何使用另一个"this"上下文(带或不带new关键字)运行es6构造函数中的指令.

Oli*_*eke 5

正如评论和您自己所指出的那样,this如果有任何解决方法,尝试使用自定义上下文调用类构造函数确实不是您想要尝试的事情。这是故意使劲的!

如果由于某些原因这足以证明棘手的解决方法是合理的,您可以在下面找到两个部分解决方案。它们都以自己的方式不完美 - 根据您的具体情况,其中之一可能仍然适合您的需求。


解决方法 1

虽然无法this在构造函数调用中直接设置,但可以将 的原型设置this为您选择的对象

为此,您可以使用自定义值Reflect.construct()调用内部[[Construct]]方法new.targetthis然后将被初始化为从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 !
Run Code Online (Sandbox Code Playgroud)

(具体的工作原理Reflect.construct()和内部[[Construct]]方法在规范的26.1.29.2.2节中描述)

潜在问题

  • 类构造函数实际上不是this绑定到调用的b,而是this绑定到直接继承自 的空对象调用的b。如果您或类构造函数依赖于 等方法Object.getOwnPropertyNames(),这可能会导致问题Object.getPrototypeOf()

解决方法 2

虽然在[[Call]]不引起 的情况下调用类构造函数的内部方法是不可能的TypeError但可以提取附加到类构造函数的代码块并从中创建一个普通函数,然后您可以使用自定义this绑定调用该函数。

您可以使用该Function.prototype.toString()方法将类构造函数的代码块提取为字符串。然后Function()构造函数可以从这个字符串中创建一个普通函数,您可以this通过自定义绑定调用它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 !
Run Code Online (Sandbox Code Playgroud)

请注意,出于演示目的,此代码段使用了极其简化的正则表达式。为了使事情变得健壮,您需要考虑字符串和注释中的嵌套花括号和花括号。如果需要,您还需要提取构造函数参数。

(根据规范的第19.2.3.5节,您可以依靠足够一致的输出Function.prototype.toString()使这种方法跨实现工作。)

潜在问题

  • new.targetundefined在执行普通函数时设置为(与[[Call]]调用的情况一样),如果类构造函数正在使用它,这可能会导致问题。
  • 原始类构造函数的闭包将丢失给使用Function()( MDN )创建的新函数,ReferenceErrors如果类构造函数依赖它们,这可能会导致。
  • 这种方法将导致SyntaxErrorif 应用于派生类 using super(),这在普通函数中是无效的语法。

结论

您的问题没有完美的解决方案。如果您的用例足够简单,那么您仍然可以实现您想要的。部分解决方法将带来其自身的有害问题 - 小心行事!