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

Tha*_*you 71 javascript constructor instance ecmascript-6

给出一个简单的类

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

是否可以在没有new关键字的情况下调用类构造函数?

用法应该允许

(new Foo("world")).hello(); // "hello world"
Run Code Online (Sandbox Code Playgroud)

要么

Foo("world").hello();       // "hello world"
Run Code Online (Sandbox Code Playgroud)

但后者失败了

Cannot call a class as a function
Run Code Online (Sandbox Code Playgroud)

ade*_*neo 38

类有一个"类体",它是一个构造函数.
如果使用内部constructor()函数,那么该函数也将是同一个类体,并且将在调用类时调用,因此类始终是构造函数.

构造函数需要使用的new运算符来创建一个新的实例,因此调用类没有new一个错误操作的结果,因为它需要的类的构造函数来创建一个新的实例.

错误消息也非常具体,而且正确

TypeError:没有'new'时不能调用类构造函数

你可以;

  • 使用常规函数而不是类1.
  • 总是打电话给班级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;
    }
}
Run Code Online (Sandbox Code Playgroud)

2)

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

var _old = Foo;
Foo = function(...args) { return new _old(...args) };
Run Code Online (Sandbox Code Playgroud)

  • 在接下来的版本中将添加调用构造函数:`class Cat { call constructor(){ new Cat() } }` (2认同)

小智 29

正如其他人已经指出的那样,ES2015规范严格规定这样的调用应该抛出TypeError,但同时它提供了可用于实现精确所需结果的特性,即Proxies.

代理允许我们虚拟化对象的概念.例如,它们可用于更改特定对象的某些行为,而不会影响其他任何内容.

在您的特定使用案例class FooFunction 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
Run Code Online (Sandbox Code Playgroud)

(注意,_Foo现在是您要公开的类,因此标识符可能应该是另一种方式)

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

同时,这个"新" _Foo类与原始类无法区分Foo(除了能够将其称为正常函数).类似地,你可以告诉使用Foo和创建的对象没有区别_Foo.

这方面的最大缺点是它无法被发现或被pollyfilled,但仍然是其可行的解决方案,以便将来在JS中应用类似Scala的类.

  • @wandalen - 这显然不是*唯一*有效的答案,事实上这个问题的正确答案只是*“不,这是不可能的”*。这是一个*不同的*答案,它使用代理而不是使用“new”创建的实例,这是解决问题的一种巧妙方法。 (3认同)
  • 这只是工作解决方案。所有其他答案在某些情况下不起作用。令人惊讶的是StackOverflow评分系统多么不准确,这是列表底部的唯一正确答案。 (2认同)

Tha*_*you 23

这是我遇到的一种模式,真的对我很有帮助.它不使用a 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
Run Code Online (Sandbox Code Playgroud)

  • 这值得一提.我真的想知道在JS中添加`class`是否有所改进.这显示了JS代码应该是什么样子.对于想知道为什么在任何地方都没有"this"的人来说,创建的对象只是使用传递给'构造函数'的`x`(箭头函数).每当需要进行变异时,它都会返回一个*new*对象.对象是不可变的. (6认同)
  • 技术问题是每次调用Foo时,都必须再次创建所有方法.使用类,可以在实例之间有效地共享`prototype`方法,而无需为每个实例重新创建.因为重新创建了这些方法,所以也会占用更多内存.出于生产目的,最好使用与Tim的答案类似的东西并使用方法来创建新类. (5认同)
  • 它将创建新功能 (3认同)

Ber*_*rgi 14

不,这是不可能的.正在使用所创建的构造class关键字只能与构造new,如果它们是[[呼叫]] ED而不他们总是throw一个TypeError1(和甚至有不从外部检测到该一个方式).
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;
}
Run Code Online (Sandbox Code Playgroud)

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

对于子类化Foo(非_Foo)class Bar extends Foo …,您应该使用return Reflect.construct(_Foo, args, new.target)而不是new _Foo调用.Foo.call(this, …)无法使用ES5样式(带)进行子类化.


Tim*_*Tim 6

class MyClass {

  constructor(param) {
     // ...
  }

  static create(param) {
    return new MyClass(param);
  }

  doSomething() {
    // ...
  }

}

MyClass.create('Hello World').doSomething();
Run Code Online (Sandbox Code Playgroud)

那是你要的吗?

如果在创建新实例时需要一些逻辑MyClass,那么实现"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();
Run Code Online (Sandbox Code Playgroud)

  • 在JavaScript中,如果你需要做一件事,你就可以做到.您不需要编写类并为其创建它的实例.那是荒谬的臃肿.只需使用一个简单的功能. (4认同)
  • 提示:只有静态成员的类不应该是"class"而是普通对象.如果只有一个成员,他们甚至根本不应该. (2认同)

Far*_*uti 6

我刚给你做了这个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 
Run Code Online (Sandbox Code Playgroud)

  • 回答史上最被低估的答案之一。 (2认同)

use*_*935 5

Here's a where you can use a 'scope safe constructor' Observe this code:

function Student(name) {
  if(this instanceof Student) {
    this.name = name;
  } else {
    return new Student(name);
  }
}
Run Code Online (Sandbox Code Playgroud)

Now you can create a Student object without using new as follows:

var stud1 = Student('Kia');
Run Code Online (Sandbox Code Playgroud)