ES6: call class constructor without new keyword
Solution 1
Classes have a "class body" that is a constructor.
If you use an internal constructor()
function, that function would be the same class body as well, and would be what is called when the class is called, hence a class is always a constructor.
Constructors requires the use of the new
operator to create a new instance, as such invoking a class without the new
operator results in an error, as it's required for the class constructor to create a new instance.
The error message is also quite specific, and correct
TypeError: Class constructors cannot be invoked without 'new'
You could;
- either use a regular function instead of a class1.
- Always call the class with
new
. - Call the class inside a wrapping regular function, always using
new
, that way you get the benefits of classes, but the wrapping function can still be called with and without thenew
operator2.
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) };
Solution 2
As others have pointed out ES2015 spec strictly states that such call should throw TypeError, but at the same time it provides feature that can be used to achieve exactly the desired result, namely Proxies.
Proxies allows us to virtualize over a concept of an object. For instance they can be used to change some behaviour of particular object without affecting anything else.
In your specific use case class Foo
is Function object
which can be called -- this normally means that body of this function will be executed. But this can be changed with 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
is now the class you want to expose, so identifiers should probably be the other way round)
If run by browser that support Proxies, calling _Foo(...)
will now execute apply
trap function instead of the orignal constructor.
At the same time this "new" _Foo
class is indistinguishable from original Foo
(apart from being able to call it as a normal function). Similarily there is no difference by which you can tell object created with Foo
and _Foo
.
The biggest downside of this is that it cannot be transpilled or pollyfilled, but still its viable solution for having Scala-like class apply in JS in the future.
Solution 3
Here's a pattern I've come across that really helps me. It doesn't use a class
, but it doesn't require the use of new
either. 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
Solution 4
No, this is not possible. Constructors that are created using the class
keyword can only be constructed with new
, if they are [[call]]ed without they always throw
a TypeError
1 (and there's not even a way to detect this from the outside).
1: I'm not sure whether transpilers get this right
You can use a normal function as a workaround, though:
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 extending Foo.prototype
work as normal, Foo.length
does not, .constructor
and static methods do not but can be fixed by adding Foo.prototype.constructor = Foo;
and Object.setPrototypeOf(Foo, _Foo)
if required.
For subclassing Foo
(not _Foo
) with class Bar extends Foo …
, you should use return Reflect.construct(_Foo, args, new.target)
instead of the new _Foo
call. Subclassing in ES5 style (with Foo.call(this, …)
) is not possible.
Solution 5
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
Mulan
Updated on December 08, 2021Comments
-
Mulan over 2 years
Given a simple class
class Foo { constructor(x) { if (!(this instanceof Foo)) return new Foo(x); this.x = x; } hello() { return `hello ${this.x}`; } }
Is it possible to call the class constructor without the
new
keyword?Usage 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
-
loganfsmyth almost 9 yearsI'm confused, why would you have the
instanceof
check, since you aren't even exposing the class? This answer doesn't really address the main issue. -
Mulan almost 9 yearsThis also doesn't work because
Foo('world') instanceof Foo
returnsfalse
. -
Bergi over 8 yearsHint: classes that have only static members should not be
class
es but plain objects. In case of only a single member, they shouldn't even be at all. -
Tim over 8 yearsnormally I wouldn't define the
create
method static. in this example I'm usingstatic
, because the creator wants to create objects without thenew
keyword. -
Bergi over 8 yearsJust if you didn't notice it: I'm talking about your
Strategy
classes. I hope you're not advocating to makecreate
an instance method of those?static
is totally fine. -
Tim over 8 yearsI'm talking about the strategies too. Don't really understand the problem. I only recommand to use a CreationStrategy, to create objects if they need "complex" logic.
-
Bergi over 8 yearsIn JavaScript, if you need to do a thing, you can just do it. You don't need to write a class and create an instance of it for that. That's ridiculous bloat. Just use a simple function.
-
Tim over 8 yearsWhy not use some es6 features? It helps you to organize your code. A lot of people complain about javascripts syntax or exception etc. So why don't write clear code and increase the usability?
-
Bergi over 8 yearsBecause declaring a
class
only to create a single function (and to call it a "method") does not organise code. Just declare the function. Do not use ES6 features only because they are there or because they make your code look like Java. -
Tim over 8 yearsThat has nothing to do with java. And again: Its an example, to outsorce "complex" creation logic. "Please avoid extended discussions in comments. Would you like to...."
-
Bergi over 8 yearsWell all those classes start looking very much like Java… and that's not a good thing. There's nothing wrong with outsourcing complex logic, but you don't need classes for everything.
-
Bergi over 8 yearsAnd in this particular case, where the creation logic belongs to the class, I don't see any reason to outsource anything. Just leave it in that
static create
method. -
Maxmaxmaximus about 7 yearsIn the next versions will add call constructors:
class Cat { call constructor(){ new Cat() } }
-
Stijn de Witt about 7 yearsThis deserves points. I really wonder whether adding
class
to JS was an improvement. This shows what JS code should look like. For people wondering why there is nothis
anywhere, the created object is just using thex
that was passed in to the 'constructor' (arrow function). Whenever it needs to be mutated, it returns a new object. The objects are immutable. -
Kos almost 7 yearsThis is only working solution. All other answers do not work under some circumstances. Astonished how inaccurate StackOverflow rating system is that the only right answer in the bottom of the list.
-
yyny over 6 yearsI wonder if it will optimize the functions into the prototype, though, or if it will create new functions for each object. Maybe with
Object.freeze
it will optimize? -
Mulan over 6 yearsit will create new functions
-
Benjamin Riggs over 6 yearsI actually like to use this pattern when I can, but subclassing is hell with it. Granted, it wouldn't be terribly difficult to add a couple language features to off-set that, but they don't exist.
-
Mulan over 6 years@BenjaminRiggs functional programmers don’t use subclassing - they use composition
-
Benjamin Riggs over 6 years@naomik Building objects through composition is easy. The problem is that runtime-testing of whether or not an object satisfies an interface of a would-be class requires excessive
thing.hasOwnProperty('attribute') || thing.hasOwnProperty('other_attribute') || ...
checks. And even then, if those are method calls that have different type signatures, you're still screwed. -
Mulan over 6 yearsjavascript doesn’t have interfaces; i don’t know what you’re talking about
-
Benjamin Riggs over 6 yearsYes, Javascript lacking interfaces is part of the problem. Surely you're familiar with the general concept of an interface to a class. In Javascript, you test that with the
instanceOf
operator. When you're creating objects purely through composition, that operator doesn't work, so you have to degrade to a bunch of OR'd.hadOwnProperty
calls. And even those can fail because methods on objects with identical names can return different things. -
Benjamin Riggs over 6 yearsBasically, functional programming is great when you're doing simple stuff, or when there's a robust type system. But doing complex functional programming in a language that uses classes as its type system, without using classes, is hard.
-
Mulan over 6 yearsI guess I don’t understand what you’re getting at - I haven’t used
instanceof
or.hasOwnProperty
in quite some time; not missing them - To me it sounds like your issue is trying to enforce an interface in a language without interfaces. I don’t share your struggle so I’m afraid I don’t know how to help you. Sorry :/ -
Benjamin Riggs over 6 yearsLet us continue this discussion in chat.
-
kasbah over 6 yearsCurrently node (v9.4.0) doesn't seem to properly support the arguments spread operator and it was causing me issues. I made a version based on the transpiled output of the classy-decorator mentioned in another answer. ``` 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; } ```
-
adeneo over 6 years@wandalen - it's clearly not the only working answer, in fact the right answer to the question is just "no, it's not possible". This is a different answer, that uses proxies instead of instances created with
new
, and it's a neat way to deal with the problem. -
Babakness about 6 yearsThe problem with technique is that each time Foo is invoked, it has to create all methods again. With classes, the
prototype
methods are efficiently shared between instances without having to re-create then per instance. Because the methods are re-created, you use up more memory as well. For production purposes, it is better to use something similar to the answer by Tim and use a method to create a new class. -
Babakness about 6 yearsI've added an answer using
of
and references to theFantasy-Land
specs -
Babakness about 6 years@naomik I've added a new answer stackoverflow.com/a/48911634/1560484, this one is similar to the one here except it doesn't create new functions each time. :-)
-
Mulan about 6 yearsI think I'm seeing a fixable problem, but I could be wrong. Each time we apply Right (eg
Right(1)
,Right(2)
), theObject.entries($methods).reduce
bit is called. I think you intend to perform this reduce only once. Is that correct? -
Babakness about 6 years@naomik Thanks! Hmm... You still need to bind the new value(s) in the functor container to the methods on the functor you return? I just optimized the code by placing the reducer outside of the reduce function to prevent it from being re-created each call.
-
Mulan about 6 yearsHmm indeed... but it makes sense at a fundamental level: Just taking
map: (x,f) => Right(f(x))
, ifx
is ever going to represent a different value,map
must be re-bind
ed with that value. Re-binding creates a new function, so we're back in the same boat. -
Babakness about 6 yearsI just did some reading--you are right about that, I'll update my answer--in a world were bind is optimized to only partially apply to a function without re-creating it, maybe this code will become the new fashion :-)
-
Mulan about 6 yearsI'm going to play around with it a bit. Your edit still calls the
Object.entries( $methods ).reduce(
each time we construct a new value. Binding delays evaluation, so you'd have to address this in a different way. Thanks for sharing this fun exercise. -
Babakness about 6 yearsSo I can know about it, please share a link here to whatever you come up with while experimenting!
-
Mulan about 6 yearsLet us continue this discussion in chat.
-
Daniel Sokolowski about 6 yearsThis syntax made no sense to me until I realized it is equivalent to
const Foo = x => {return {x, ...}}
. -
Mulan about 6 yearsDaniel, correct. It’s a function that returns an object ^^
-
jordanbtucker over 5 yearsIf the class is declared first, then you don't need to use different names for the Proxy and class.
class Foo {}; const Foo = new Proxy(Foo, {apply(target, thisArg, args) { return new target(...args) }})
. However,Foo
now references the Proxy instead of the original class. -
kinger6621 over 5 yearsThis is an interesting pattern. Might use this one somewhere soon. A really cool idea and simple. Could make some cool helper functions with this pattern.
-
mhulse about 5 yearsIn your third example, I see:
static of(x) { new _Foo(x)}
… What is the purpose of the underscore? Sorry if I am missing something obvious here. Thanks for the example! -
Tomáš Hübelbauer almost 5 years@Maxmaxmaximus I think you should post that as an answer and add a source. It was news to me and very interesting.
-
Griffork over 4 yearsThis was the only solution that worked for what I wanted, as due to making a dynamic class heirarchy (mixins defined as classes instead of functions) I needed to be able to instantiate the base class with the child class' prototype.
-
Admin about 3 yearsOne of the most underrated answers in the history of answering.
-
Mulan almost 3 years
-
smallscript over 2 yearsOn QKS Smalltalk engine [I wrote] from 1998 (unchanged since then) run today on 2.60 GHz Dell Laptop: => [100_000 fibonacci] millisecondsToRun => 286 ms => 100_000 fibonacci asString size => 20899 FYI: V8 was born of Animorphic Smalltalk 1994 (Lars Bak) => Java HotSpot => V8 JavaScript. Try running "30000 factorial" if you want to see how good-or-bad the GC and BigInt system is doing on JavaScript. (p.s., I was lead JavaScript architect at Microsoft for many years).
-
Mulan over 2 yearsimpressive! i've always wanted to learn smalltalk but never into it. do you have any recommendations for getting smalltalk up and running in a modern macos environment?
-
smallscript over 2 yearsCheck this out: wiki.squeak.org/squeak/2801
-
trusktr over 2 yearsAlthough that example works, it will break if someone tries
class Bar extends Foo {}
because it will no longer extend the expected class. -
Maciej Krawczyk over 2 yearsWhat's wrong with the
new
keyword in the first place? For some reason it's considered a bad practice to use class for something of which no reference is held (side effects) but why? The alternatives are uglier and some are less performant too. -
kungfooman almost 2 yearsThis code just throws an error: VM60:4 Uncaught TypeError: Found non-callable @@iterator at reducer (<anonymous>:4:71) at Array.reduce (<anonymous>)