如何使用不同上下文的es6构造函数指令 [英] How to use es6 constructor instructions with a different context

查看:123
本文介绍了如何使用不同上下文的es6构造函数指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以通过更改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()等。

  • The class constructor is not actually called with this bound to b, it is called with this bound to an empty object directly inheriting from b. This may lead to problems if you or the class constructor rely on methods like Object.getOwnPropertyNames(), Object.getPrototypeOf() etc.

虽然不能在不导致 TypeError [[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 to undefined 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 cause ReferenceErrors if the class constructor was relying on them.
  • This approach will lead to a SyntaxError if applied on a derived class using super(), which is not valid syntax in ordinary functions.

您的问题没有完美的解决方案。如果你的用例很简单,你仍然可以达到你想要的效果。部分解决方法将伴随着他们自己的有害问题 - 谨慎行事!

这篇关于如何使用不同上下文的es6构造函数指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆