ES6: class class call without a new keyword

Given a simple class

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

Can a class constructor be called without the new keyword?

Use should allow

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

or

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

But the latter fails with

 Cannot call a class as a function 
+66
javascript constructor ecmascript-6 instance
Jun 07 '15 at 3:41
source share
15 answers

Classes have a "class body" that is a constructor.
If you use the constructor() internal function, this function will be the same body of the class and will be what is called when the class is called, therefore the class is always a constructor.

Constructors require the use of the new operator to create a new instance, because when a class is called without the new operator, an error occurs because it requires the class constructor to create a new instance.

The error message is also quite specific and correct.

TypeError: class constructors cannot be called without 'new'

Could you:

  • either use a regular function instead of class 1 .
  • Always call the class with new .
  • Calling a class inside a regular traversal function, always using new , this way you get the benefits of classes, but you can still call the wrapping function using the new 2 operator.



one)

 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) }; 
+37
Jun 07 '15 at 3:51 on
source share

As others have pointed out, the ES2015 specification strictly indicates that such a call should raise a TypeError, but at the same time it provides a function that can be used to achieve exactly the desired result, namely Proxy .

Proxies allow us to virtualize over the concept of an object. For example, they can be used to change any behavior of a particular object without affecting anything else.

In your particular case of using class Foo there is a Function object that can be called - this usually means that the body of this function will be executed. But this can be changed using 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 

(Note that _Foo now the class you want to open, so identifiers should probably be the other way around)

If you run a browser that supports Proxies, calling _Foo(...) will now execute the apply trap function instead of the orignal constructor.

At the same time, this "new" _Foo class is indistinguishable from the original Foo (besides the ability to call it a normal function). Similarly, there is no difference by which you can specify an object created using Foo and _Foo .

The biggest drawback of this is that it cannot be crowded or full , but still its viable solution for the Scala -like class is applicable in JS in the future.

+26
Feb 11 '16 at 12:46 on
source share

Here is a sample that I came across that really helps me. It does not use class , but does not require the use of new . Win / Win.

 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 
+20
Mar 08 '17 at 14:22
source share

No, It is Immpossible. Constructors created using the class keyword can only be built using new if they [[call]] ed without them throw a TypeError 1 (and there is not even a way to detect this from the outside).
1: I'm not sure that transpilers get this right

You can use the regular function as a workaround:

 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; } 

Disclaimer: instanceof and the Foo.prototype extension work as usual, Foo.length does not work, .constructor and static methods cannot be fixed by adding Foo.prototype.constructor = Foo; and Object.setPrototypeOf(Foo, _Foo) , if necessary.

To subclass Foo (not _Foo ) with class Bar extends Foo … you should use return Reflect.construct(_Foo, args, new.target) instead of calling new _Foo . An ES5-style subclass (with Foo.call(this, …) ) is not possible.

+12
Aug 03 '15 at 13:59 on
source share

I just made this npm module for you;)

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 
+7
May 19 '17 at 4:56 a.m.
source share
 class MyClass { constructor(param) { // ... } static create(param) { return new MyClass(param); } doSomething() { // ... } } MyClass.create('Hello World').doSomething(); 

Is this what you want?

If you need logic when creating a new instance of MyClass , it would be useful to implement "CreationStrategy" to refute the logic:

 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(); 
+6
Nov 12 '15 at 10:59
source share

Dug this one in the project

Constructors defined using class definition syntax when calling functions

So I think this is not possible with classes.

+5
Jun. 07 '15 at 3:51 on
source share

Manual call class constructor can be useful when refactoring code (indicating parts of code in ES6, other functions, and defining prototypes of other parts)

As a result, I got a small but useful template that cuts the constructor into another function. Period.

  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() {} 
+2
Aug 23 '16 at 20:59
source share

Here, where you can use the "area security constructor", observe this code:

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

Now you can create a Student object without using a new one, as shown below:

 var stud1 = Student('Kia'); 
+2
Apr 15 '17 at 21:06 on
source share

Ok, I have another answer here, and I think this is a fairly innovative approach.

Basically, the problem with doing something similar to Naomik's answer is that you create functions every time you combine methods together.

EDIT: This solution has the same problem, however this answer remains for educational purposes.

So here, I suggest simply binding the new values ​​to your methods - which are basically just independent functions. This gives an additional advantage - the ability to import functions from different modules into a new object.

Okay, that’s all.

 const assoc = (prop, value, obj) => Object.assign({},obj,{[prop]: value}) const reducer = ( $values, accumulate, [key,val] ) => assoc( key, val.bind( undefined,...$values ), accumulate ) const bindValuesToMethods = ( $methods, ...$values ) => Object.entries( $methods ).reduce( reducer.bind( undefined, ...$values), {} ) const prepareInstance = (instanceMethods, staticMethods = ({}) ) => Object.assign( bindValuesToMethods.bind( undefined, instanceMethods ), staticMethods ) // Let make our class-like function const RightInstanceMethods = ({ chain: (x,f) => f(x), map: (x,f) => Right(f(x)), fold: (x,l,r) => r(x), inspect: (x) => `Right(${x})` }) const RightStaticMethods = ({ of: x => Right(x) }) const Right = prepareInstance(RightInstanceMethods,RightStaticMethods) 

Now you can do

 Right(4) .map(x=>x+1) .map(x=>x*2) .inspect() 

You can also do

 Right.of(4) .map(x=>x+1) .map(x=>x*2) .inspect() 

You also have the added benefit of being able to export from modules as such.

 export const Right = prepareInstance(RightInstanceMethods,RightStaticMethods) 

Until you get ClassInstance.constructor , you have FunctorInstance.name (note that you may need polyfill Function.name and / or not use the arrow function to export for browser compatibility with Function.name )

 export function Right(...args){ return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args) } 

PS - New name suggestions for prepareInstance are welcome, see Gist.

https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456

+2
Feb 21 '18 at 17:07
source share

I had problems extending classes converted with the conversion function mentioned in some other answers. The problem is that node (starting from version v.9.4.0) does not support the argument spread operator ( (...args) => ).

This function, based on the transmitted output of a classy decorator (mentioned in another answer ), works for me and does not require support for decorators or spreading the operator argument.

 // function that calls `new` for you on class constructors, simply call // YourClass = bindNew(YourClass) function bindNew(Class) { function _Class() { for ( var len = arguments.length, rest = Array(len), key = 0; key < len; key++ ) { rest[key] = arguments[key]; } return new (Function.prototype.bind.apply(Class, [null].concat(rest)))(); } _Class.prototype = Class.prototype; return _Class; } 

Using:

 class X {} X = bindNew(X); // or const Y = bindNew(class Y {}); const x = new X(); const x2 = X(); // woohoo x instanceof X; // true x2 instanceof X; // true class Z extends X {} // works too 



As a bonus, TypeScript (with "es5" output) seems fine with the old instanceof trick (well, it won't be checked by typecheck if used without new , but it works anyway):

 class X { constructor() { if (!(this instanceof X)) { return new X(); } } } 

because he will compile it:

 var X = /** @class */ (function () { function X() { if (!(this instanceof X)) { return new X(); } } return X; }()); 
+1
Jan 18 '18 at 17:19
source share

Calling the class constructor without the new keyword is not possible.

The error message is quite specific.

See the 2ality and spec blog post:

 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 
0
Jun 07 '15 at 3:51 on
source share

I add this as a continuation of Naomik's commentary and use the method illustrated by Tim and Berghi. I am also going to suggest a function of for use as a general case.

Make it a functional way. And use the effectiveness of prototypes (do not recreate the whole method every time a new instance is created), you can use this template

 const Foo = function(x){ this._value = x ... } Foo.of = function(x){ return new Foo(x) } Foo.prototype = { increment(){ return Foo.of(this._value + 1) }, ... } 

Please note that this is consistent with fantasy-land JS specifications.

https://github.com/fantasyland/fantasy-land#of-method

I personally find it cleaner to use ES6 class syntax

 class Foo { static of(x) { new Foo(x)} constructor(x) { this._value = x } increment() { Foo.of(this._value+1) } } 

Now you can wrap it in a closure as such

 class Foo { static of(x) { new _Foo(x)} constructor(x) { this._value = x } increment() { Foo.of(this._value+1) } } function FooOf (x) { return Foo.of(x) } 

Or rename FooOf and Foo as desired, i.e. the class can be FooClass and the function is just Foo , etc.

This is better than placing a class in a function, because creating new instances does not prevent us from creating new classes.

Another way is to create the of function

 const of = (classObj,...args) => ( classObj.of ? classObj.of(value) : new classObj(args) ) 

And then do something like of(Foo,5).increment()

0
Feb 20 '18 at 23:43
source share

You cannot use a class without a new constructor, in my case I did not want to use a new constructor whenever I wanted to use my class, so you can do so to wrap your class like this (in my case, it is the Dates utils library):

enter image description here

0
Jun 26 '19 at 9:34 am
source share

It may be a little far-fetched, but it works

 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() 
-one
Jun 07 '15 at 21:00
source share



All Articles