ES6:调用没有新关键字的类构造函数

给定一个简单的类

class Foo { constructor(x) { if (!(this instanceof Foo)) return new Foo(x); this.x = x; } hello() { return `hello ${this.x}`; } } 

是否有可能没有new关键字调用类的构造函数?

应该允许使用

 (new Foo("world")).hello(); // "hello world" 

要么

 Foo("world").hello(); // "hello world" 

但后者失败了

 Cannot call a class as a function 

类有一个构造函数的“类体”。
如果你使用一个内部的constructor()函数,那么这个函数也会是同一个类的主体,并且当这个类被调用的时候被调用,所以一个类总是一个构造函数。

构造函数需要使用new运算符来创build一个新的实例,因为调用一个没有new运算符的类会导致错误,因为类构造函数需要创build一个新的实例。

错误信息也是非常具体,正确的

TypeError:类构造函数不能被调用没有“新”

你可以;

  • 要么使用一个普通的函数而不是一个类。
  • 始终用new致电课程。
  • 在包装常规函数中调用类,总是使用new ,这样你就可以获得类的好处,但是包装函数仍然可以在没有new操作符2的情况下调用。

1)

 function Foo(x) { if (!(this instanceof Foo)) return new Foo(x); this.x = x; this.hello = function() { return this.x; } } 

2)

 class Foo { constructor(x) { this.x = x; } hello() { return `hello ${this.x}`; } } var _old = Foo; Foo = function(...args) { return new _old(...args) }; 

正如其他人指出,ES2015规范严格规定,这样的调用应该抛出TypeError,但同时它提供了可以用来实现期望的结果,即代理的function 。

代理允许我们虚拟化一个对象的概念。 例如,它们可以用来改变特定对象的某些行为,而不会影响其他任何东西。

在你的具体用例class Foo是可以调用的Function object – 这通常意味着这个函数的主体将被执行。 但是这可以通过Proxy进行更改:

 const _Foo = new Proxy(Foo, { // target = Foo apply (target, thisArg, argumentsList) { return new target(...argumentsList); } }); _Foo("world").hello(); const f = _Foo("world"); f instanceof Foo; // true f instanceof _Foo; // true 

(请注意, _Foo现在是您要公开的类,所以标识符应该是相反的)

如果由支持代理的浏览器运行,调用_Foo(...)现在将执行apply陷阱函数而不是_Foo(...)构造函数。

同时,这个“新” _Foo类与原来的Foo没有什么区别(除了能够把它作为一个普通的函数)。 类似的,你可以通过Foo_Foo创build对象。

这样做的最大缺点就是它不能被transpilled或者pollyfilled ,但是它仍然是将来在JS中使用类Scala类的可行解决scheme。

这是我遇到的模式,真的帮助我。 它不使用一个class ,但它不需要使用new 。 赢/赢。

 const Foo = x => ({ x, hello: () => `hello ${x}`, increment: () => Foo(x + 1), add: ({x: y}) => Foo(x + y) }) console.log(Foo(1).x) // 1 console.log(Foo(1).hello()) // hello 1 console.log(Foo(1).increment().hello()) // hello 2 console.log(Foo(1).add(Foo(2)).hello()) // hello 3 

不,这是不可能的。 使用class关键字创build的构造函数只能使用new来构造,如果它们是[[call]]而不总是throw TypeError 1 (并且甚至没有从外部检测到这种方法)。
1:我不确定译员是否有这个权利

但是,您可以使用普通的函数作为解决方法:

 class Foo { constructor(x) { this.x = x; } hello() { return `hello ${this.x}`; } } { const _Foo = Foo; Foo = function(...args) { return new _Foo(...args); }; Foo.prototype = _Foo.prototype; } 

免责声明: instanceof和扩展Foo.prototype正常工作, Foo.length不,但.constructor和静态方法不能,但可以通过添加Foo.prototype.constructor = Foo; Object.setPrototypeOf(Foo, _Foo)如果需要的Object.setPrototypeOf(Foo, _Foo)

对于_FooFoo (而不是_Foo )的class Bar extends Foo … _Foo class Bar extends Foo … ,应该使用return Reflect.construct(_Foo, args, new.target)而不是new _Foo调用。 在ES5风格子类(与Foo.call(this, …) )是不可能的。

 class MyClass { constructor(param) { // ... } static create(param) { return new MyClass(param); } doSomething() { // ... } } MyClass.create('Hello World').doSomething(); 

那是你要的吗?

如果您在创buildMyClass的新实例时需要一些逻辑,则实施“CreationStrategy”可能会有所帮助,从而超出逻辑:

 class MyClassCreationStrategy { static create(param) { let instance = new MyClass(); if (!param) { // eg. handle empty param } instance.setParam(param); return instance; } } class DefaultCreationStrategy { static create(classConstruct) { return new classConstruct(); } } MyClassCreationStrategy.create(param).doSomething(); DefaultCreationStrategy.create(MyClass).doSomething(); 

在草稿中挖出这个

作为函数调用时使用类定义语法定义的构造函数抛出

所以我想这是不可能的类。

当重构代码时,调用类构造函数可以是有用的(在ES6中有部分代码,其他部分是函数和原型定义)

我结束了一个小而有用的样板,将构造函数切分为另一个函数。 期。

  class Foo { constructor() { //as i will not be able to call the constructor, just move everything to initialize this.initialize.apply(this, arguments) } initialize() { this.stuff = {}; //whatever you want } } function Bar () { Foo.prototype.initialize.call(this); } Bar.prototype.stuff = function() {} 

我刚刚为你制作了这个npm模块;)

https://www.npmjs.com/package/classy-decorator

 import classy from "classy-decorator"; @classy() class IamClassy { constructor() { console.log("IamClassy Instance!"); } } console.log(new IamClassy() instanceof IamClassy); // true console.log(IamClassy() instanceof IamClassy); // true 

调用不带new关键字的类构造函数是不可能的。

错误消息是非常具体的。

查看关于2ality和规范的博客文章:

 However, you can only invoke a class via new, not via a function call (Sect. 9.2.2 in the spec): > Point() TypeError: Classes can't be function-called 

这里是你可以使用“范围安全构造函数”的地方观察下面的代码:

 function Student(name) { if(this instanceof Student) { this.name = name; } else { return new Student(name); } } 

现在,您可以创build一个Student对象,而不使用new,如下所示:

 var stud1 = Student('Kia'); 

这可能是有点人为的,但它的作品

 function Foo(x){ "use strict" class Bar { constructor(x) { if (!(this instanceof Bar)) return new Bar(x); this.x = x; } hello() { return `hello ${this.x}`; } } return new Bar(x) } Foo("world").hello()