Split a Javascript class (ES6) over multiple files? - javascript

I have a Javascript class (in ES6) that is getting quite long. To organize it better I'd like to split it over 2 or 3 different files. How can I do that?
Currently it looks like this in a single file:
class foo extends bar {
constructor(a, b) {} // Put in file 1
methodA(a, b) {} // Put in file 1
methodB(a, b) {} // Put in file 2
methodC(a, b) {} // Put in file 2
}
Thanks!

When you create a class
class Foo extends Bar {
constructor(a, b) {
}
}
you can later add methods to this class by assigning to its prototype:
// methodA(a, b) in class Foo
Foo.prototype.methodA = function(a, b) {
// do whatever...
}
You can also add static methods similarly by assigning directly to the class:
// static staticMethod(a, b) in class Foo
Foo.staticMethod = function(a, b) {
// do whatever...
}
You can put these functions in different files, as long as they run after the class has been declared.
However, the constructor must always be part of the class declaration (you cannot move that to another file). Also, you need to make sure that the files where the class methods are defined are run before they are used.

Here's my solution. It:
uses regular modern classes and .bind()ing, no prototype. (EDIT: Actually, see the comments for more on this, it may not be desirable.)
works with modules. (I'll show an alternative option if you don't use modules.)
supports easy conversion from existing code.
yields no concern for function order (if you do it right).
yields easy to read code.
is low maintenance.
unfortunately does not play well with static functions in the same class, you'll need to split those off.
First, place this in a globals file or as the first <script> tag etc.:
BindToClass(functionsObject, thisClass) {
for (let [ functionKey, functionValue ] of Object.entries(functionsObject)) {
thisClass[functionKey] = functionValue.bind(thisClass);
}
}
This loops through an object and assigns and binds each function, in that object, by its name, to the class. It .bind()'s it for the this context, so it's like it was in the class to begin with.
Then extract your function(s) from your class into a separate file like:
//Use this if you're using NodeJS/Webpack. If you're using regular modules,
//use `export` or `export default` instead of `module.exports`.
//If you're not using modules at all, you'll need to map this to some global
//variable or singleton class/object.
module.exports = {
myFunction: function() {
//...
},
myOtherFunction: function() {
//...
}
};
Finally, require the separate file and call BindToClass like this in the constructor() {} function of the class, before any other code that might rely upon these split off functions:
//If not using modules, use your global variable or singleton class/object instead.
let splitFunctions = require('./SplitFunctions');
class MySplitClass {
constructor() {
BindToClass(splitFunctions, this);
}
}
Then the rest of your code remains the same as it would if those functions were in the class to begin with:
let msc = new MySplitClass();
msc.myFunction();
msc.myOtherFunction();
Likewise, since nothing happens until the functions are actually called, as long as BindToClass() is called first, there's no need to worry about function order. Each function, inside and outside of the class file, can still access any property or function within the class, as usual.

I choose to have all privte variables/functions in an object called private, and pass it as the first argument to the external functions.
this way they have access to the local variables/functions.
note that they have implicit access to 'this' as well
file: person.js
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
// delegate getAge to PersonGetAge in an external file
// pass this,privates,args
this.getAge=function(...args) {
return PersonGetAge.apply(this,[privates].concat(args));
}
// delegate setAge to PersonSetAge in an external file
// pass this,privates,args
this.setAge=function(...args) {
return PersonSetAge.apply(this,[privates].concat(args));
}
}
file: person_age_functions.js
exports.PersonGetAge =function(privates)
{
// note: can use 'this' if requires
return privates.age;
}
exports.PersonSetAge =function(privates,age)
{
// note: can use 'this' if requires
privates.age=age;
}
file: main.js
const { Person } = require('./person.js');
let me = new Person();
me.setAge(17);
console.log(`I'm ${me.getAge()} years old`);
output:
I'm 17 years old
note that in order not to duplicate code on person.js, one can assign all functions in a loop.
e.g.
person.js option 2
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
{
// assign all external functions
let funcMappings={
getAge:PersonGetAge,
setAge:PersonSetAge
};
for (const local of Object.keys(funcMappings))
{
this[local]=function(...args) {
return funcMappings[local].apply(this,[privates].concat(args));
}
}
}
}

You can add mixins to YourClass like this:
class YourClass {
ownProp = 'prop'
}
class Extension {
extendedMethod() {
return `extended ${this.ownProp}`
}
}
addMixins(YourClass, Extension /*, Extension2, Extension3 */)
console.log('Extended method:', (new YourClass()).extendedMethod())
function addMixins() {
var cls, mixin, arg
cls = arguments[0].prototype
for(arg = 1; arg < arguments.length; ++ arg) {
mixin = arguments[arg].prototype
Object.getOwnPropertyNames(mixin).forEach(prop => {
if (prop == 'constructor') return
if (Object.getOwnPropertyNames(cls).includes(prop))
throw(`Class ${cls.constructor.name} already has field ${prop}, can't mixin ${mixin.constructor.name}`)
cls[prop] = mixin[prop]
})
}
}

TypeScript Solution
foo-methods.ts
import { MyClass } from './class.js'
export function foo(this: MyClass) {
return 'foo'
}
bar-methods.ts
import { MyClass } from './class.js'
export function bar(this: MyClass) {
return 'bar'
}
class.ts
import * as barMethods from './bar-methods.js'
import * as fooMethods from './foo-methods.js'
const myClassMethods = { ...barMethods, ...fooMethods }
class _MyClass {
baz: string
constructor(baz: string) {
this.baz = baz
Object.assign(this, myClassMethods);
}
}
export type MyClass = InstanceType<typeof _MyClass> &
typeof myClassMethods;
export const MyClass = _MyClass as unknown as {
new (
...args: ConstructorParameters<typeof _MyClass>
): MyClass;
};

My solution is similar to the one by Erez (declare methods in files and then assign methods to this in the constructor), but
it uses class syntax instead of declaring constructor as a function
no option for truly private fields - but this was not a concern for this question anyway
it does not have the layer with the .apply() call - functions are inserted into the instance directly
one method per file: this is what works for me, but the solution can be modified
results in more concise class declaration
1. Assign methods in constructor
C.js
class C {
constructor() {
this.x = 1;
this.addToX = require('./addToX');
this.incX = require('./incX');
}
}
addToX.js
function addToX(val) {
this.x += val;
return this.x;
}
module.exports = addToX;
incX.js
function incX() {
return this.addToX(1);
}
module.exports = incX;
2. Same, but with instance fields syntax
Note that this syntax is a Stage 3 proposal as of now.
But it works in Node.js 14 - the platform I care about.
C.js
class C {
x = 1;
addToX = require('./addToX');
incX = require('./incX');
}
Test
const c = new C();
console.log('c.incX()', c.incX());
console.log('c.incX()', c.incX());

Related

Is it possible to instantiate a class without a `new` operator? [duplicate]

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
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 require 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 the new 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) };
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 browsers that support Proxies, calling _Foo(...) will now execute apply trap function instead of the original 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). Similarly, 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 transpiled or polyfilled, but still it's viable solution for having Scala-like class applied in JS in the future.
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
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
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 TypeError1 (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.
class MyClass {
constructor(param) {
// ...
}
static create(param) {
return new MyClass(param);
}
doSomething() {
// ...
}
}
MyClass.create('Hello World').doSomething();
Is that what you want?
If you need some logic when creating a new instance of MyClass, it could be helpful to implement a "CreationStrategy", to outsorce the logic (for example complex builder logic with validation)
Edit: As discussed in the comments It does not make sense to create some sort of Builder Pattern with a separate class in JavaScript. Removed related example.
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);
}
}
Now you can create a Student object without using new as follows:
var stud1 = Student('Kia');
Dug up this one in the draft
Constructors defined using class definition syntax throw when called as functions
So I guess that's not possible with classes.
Call class constructor manually can be usefull when refactoring code (having parts of the code in ES6, other parts beeing function & prototype definition)
I ended up with a small, yet usefull boilerplate, slicing 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() {}
I had problems extending classes converted with the transformation function mentioned in some other answers. The issue seems to be that node (as of v9.4.0) doesn't properly support the argument spread operator ((...args) =>).
This function based on the transpiled output of the classy-decorator (mentioned in another answer) works for me and doesn't require support for decorators or the argument spread operator.
// 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;
}
Usage:
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 to be fine with the old instanceof trick (well, it won't typecheck if used without new but it works anyhow):
class X {
constructor() {
if (!(this instanceof X)) {
return new X();
}
}
}
because it compiles it down to:
var X = /** #class */ (function () {
function X() {
if (!(this instanceof X)) {
return new X();
}
}
return X;
}());
Alright I have another answer here, and I think this one is pretty innovative.
Basically, the problem with doing something similar to Naomik's answer is that you create functions each and every time you chain methods together.
EDIT: This solution shares the same problem, however, this answer is being left up for educational purposes.
So here I'm offering a way to merely bind new values to your methods--which are basically just independent functions. This offer the additional benefit of being able to import functions from different modules into the newly constructed object.
Okay, so here it goes.
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's 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)
While you don't get ClassInstance.constructor you do have FunctorInstance.name (note, you may need to polyfill Function.name and/or not use an arrow function for export for browser compatibility with Function.name purposes)
export function Right(...args){
return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args)
}
PS - New name suggestions for prepareInstance welcomed, see Gist.
https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456
As pointed out by you and others
Foo("world").hello();
fails with an error because it is an error,
according to rules of ES6 syntax.
Others pointed out that
(new Foo("world")).hello();
works but is clunky because
It needs the 'new' AND
It needs the extra parenthesis.
I agree it is clunky. So I'm often using
this solution instead:
In your class Foo, create a static method
named 'new':
static new (...args)
{ return new this (...args);
}
Use it like this:
Foo.new("world").hello();
This way I hide the "clunkiness" inside
this static method 'new()'.
Note that this method new() is generic,
it will work as is also
when inherited to sub-classes. If you need
to customize it in a subclass you can first call:
super.new(...args)
and then add any other stuff you need in the
method in a subclass, before returning its result.
A recapped working "one-line" solution for ES6: explained
The answer posted above by Bergi is basically correct.
TLDR; skip to the end 😎 for the one-liner solution
Bergi's answer may seem a unclear when reading it. So, here is a more expanded code-sample that illustrates TWO new ES6 features to achieve the desired goals.
Together, they let a single function C (below) provide the dual-role of a factory and new-able fn; which constructs a B inst that derives from a A.
The B constructor utilizes super handling to invoke the A constructor with initialization arguments. In our final #3 - #4 examples constructed by C.
The A constructor demonstrates the semantics of the new.target psuedo-var to discover new was actually invoked with B.
First, we will make use of ES6 new.target psuedo-var that gives us the RHS of a new RHS() expression.
Technically, we could have gotten new.target as this?.__proto__?.constructor; they are equivalent.
Second, we will make use of ES6 Reflect.construct. Which is crucial to working around the ES6 class constructor invocation constraints; if we are bound and determined to not use new RHS(...).
Test the following and see for yourself its output (also provided in #1-4 below).
class A {
constructor(...a) {
const descendentType = new.target;
console.log(`A's constructor seeing 'new' invoked on ${descendentType?.name} with args: %o`,a);
}
}
class B extends A {
constructor(...a) {
super(...a);
}
}
// C is our DUAL mode Factory
function C(...a) {
console.log(`C's new.target => ${new.target?.name}`);
const inst = new.target ? Reflect.construct(B, a) : new B(...a);
console.log(`C has constructed a ${inst.__proto__.constructor.name} inst`);
return inst;
}
Which we can then invoke it in the following ways:
new A('NEW-A()')
output => "A's constructor seeing 'new' invoked on A with args: ['NEW-A()']"
new B('NEW-B()')
output => "A's constructor seeing 'new' invoked on B with args: ['NEW-B()']"
new C('NEW-C()')
output => "C's new.target => C"
output => "A's constructor seeing 'new' invoked on B with args: ['NEW-C()']"
output => "C has constructed a B inst"
C('PLAIN-C()')
output => "C's new.target => undefined"
output => "A's constructor seeing 'new' invoked on B with args: ['PLAIN-C()']"
output => "C has constructed a B inst"
Where #3 and #4 achieve the originally desired goals.
The simplified `C` looks like:
function C(...a) {return Reflect.construct(B, a);}
OR - if 3rd arg of Reflect.construct not utilized for init.
function C(...a) {return new B(...a);}
Beware: C must be a function not a class for this to both be allowed, and to work returning an alternate this on a new C() invocation, etc.
Also to circumvent strict mode rules for arguments.callee requires using a closure (smalltalk-block. Illustrated below:
class B extends A {
// embedding within a class and generically referencing it requires =>
static C = (() => {
const $class = this; return function(...a) {
return Reflect.construct($class, a);}})();
// Read more on `Reflect.construct` 3rd argument to see more capabilities
// for why it does MORE than just `new $class(...a)` would do.
}
exports.C = B.C;
⛐⚠️⛐ You could do some awful things like fiddle the __proto__ on the resulting inst and change out its constructor and name. Which would make it look and feel like a real subclass C of B depending on how far you want to go to manipulate the object-model. The subtleties abound in what happens with getters/setters, super and # privates. But for much of that you can STAY ES6 CLEAN and get clever with using extends and providing a template superclass flattened mixin tree; which I do a lot of in efekt for supporting tiny-but-complete µhtml reactive custom-elements parts and related PWA app models and responsive dynamic just-in-time versioned code bundling from EdgeS ESS backend servers. As in ... const M = $class => class extends $class {...}.
My motivations...
I posted this to help explain the semantics and a working ES6 solution, which is what I use to support subclassing Promise to provide FutureValue with better workflow handling capabilities in my github efekt library (EdgeS Front End Kit library).
In 2022, with ES6 onwards you can do it with the static method that can be called before the instance of the class is created, to create a instance of the class.
So the code should look something like this:
class Foo {
constructor(x) {
this.x = x;
}
//static class
static Init(x) {
return new Foo(x)
}
sayHello() {
return `hello ${this.x}`;
}
}
//so if i call
Foo.Init('world').sayHello();
//it prints: hello world
But if you are doing all this to make a chain of method you can also look at the following construct:
function MyName(name) {
if (this instanceof MyName) {
this.name = name,
this.prepend = function(n) {
this.name = `${n} ${this.name}`;
return this;
}
,
this.append = function(n) {
this.name = `${this.name} ${n} `;
return this;
}
,
this.show = function() {
return this.name;
}
} else {
return new MyName(name);
}
}
//Call
MyName('vinod').prepend('dev').append('hacks').show();
//prints: dev vinod hacks
The method above returns this at the end of each method which makes the object, properties & method avaialble.
The good part is these methods can be used again & again to create a sentence as
MyName('vinod').prepend('dev').append('hacks')
.prepend("the").append('javascript').append('for Stackoverflow').show();
I have used it as a stringBuilder or to generate xml dynamically.
Calling the class constructor without the new keyword is not possible.
The error message is quite specific.
See a blog post on 2ality and the spec:
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
I'm adding this as a follow up to a comment by naomik and utilizing on the method illustrated by Tim and Bergi. I'm also going to suggest an of function to use as a general case.
To do this in a functional way AND utilize the efficiency of prototypes (not re-create all method each time a new instance is created), one could use this pattern
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 specs
https://github.com/fantasyland/fantasy-land#of-method
I personally feel that it is cleaner to use the ES6 class syntax
class Foo {
static of(x) { new Foo(x)}
constructor(x) { this._value = x }
increment() { Foo.of(this._value+1) }
}
Now one could wrap this 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, ie the class could be FooClass and the function just Foo, etc.
This is better than place the class in the function because creating new instances doesn't burden us with creating new classes as well.
Yet another way is to create a an of function
const of = (classObj,...args) => (
classObj.of
? classObj.of(value)
: new classObj(args)
)
And then do something like of(Foo,5).increment()
Still finding interesting ways to use instanceof without relying on new or class keywords. In this example program, we compute the 100,000th fibonacci number in less than one second. The result is over 20,000 digits long -
const fib = x =>
Loop // <- no `new`
( (n, a, b) =>
n <= 0n
? String(a) // <- no `new`
: Recur(n - 1n, b, a + b) // <- no `new`
, BigInt(x) // <- no `new`
, 0n
, 1n
)
function Loop (f, ...init)
{ let r = f(...init)
while (r instanceof Recur) // <- instanceof works
r = f(...r)
return r
}
function Recur (...v)
{ return Object.create // <- not a class, but works
( Recur.prototype // <- set prototype
, { constructor: { value: Recur } // <- set constructor
, [Symbol.iterator]: { value: _ => v.values() } // <- whatever you want
}
)
}
document.body.textContent = fib(100000)
body { overflow-wrap: anywhere; }
I don't know why I haven't thought of this before -
function atom (T, v)
{ return Object.assign
( Object.create
( T.prototype
, { constructor: { value: T } }
)
, v
)
}
function pair (car, cdr)
{ return atom(pair, { car, cdr }) }
const p =
pair(1, 2)
console.log(p)
console.log(p instanceof pair)
Output -
{
"car": 1,
"cdr": 2
}
true
I wrote a small helper function which solves this problem. It effectively converts an ES6 class into an older ES5 constructor function which isn't subject to the same ruleset. This way you can create constructors which don't need new. You can also overload constructors in a similar way to the builtin Number, String etc.
function callableConstructor(c, f) {
function ret(...args) {
if(new.target) {
return new c(...args)
}
return f(...args)
}
ret.prototype = c.prototype
ret.prototype.constructor = ret
return ret
}
Test it below:
function callableConstructor(c, f) {
function ret(...args) {
if(new.target) {
return new c(...args)
}
return f(...args)
}
ret.prototype = c.prototype
ret.prototype.constructor = ret
return ret
}
// Usage
class Foo {
constructor(a, b) {
this.a = a
this.b = 2 * b
}
f() {
return this.a + this.b
}
}
Foo = callableConstructor(Foo, (...args) => new Foo(...args))
let foo = new Foo(2, 3)
console.log(foo) // Foo { a: 2, b: 6 }
console.log(foo.f()) // 8
console.log(foo instanceof Foo) // true
foo = Foo(2, 3)
console.log(foo) // Foo { a: 2, b: 6 }
console.log(foo.f()) // 8
console.log(foo instanceof Foo) // true
I came at this issue because I encountered the no-new "do not use new for side effects" eslint rule - which turns out it's a bad practice to use new for an object that is immediately discarded.
I still wanted to use the class syntax because I like it, but I agree that a regular class with new keyword for something that does not produce an object can be confusing.
The solution for me was simple. Define an unexported class in a module and export a function that instatinates it.
class SideEffects {
constructor() {
}
// ...
}
export function addSideEffects() {
// eslint-disable-next-line no-new
new SideEffects();
}
Yes, we are still using the new keyword, but it's used internally in the module and it's obvious from reading the module file that it's not a regular class - and the exported function also makes it clear that it does not create an object.
This might be a little contrived, 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()
You can't use a class without the new constructor, in my case I didn't want to use the new constructor any time I wanted to use my class, so what you can do is to wrap your class as follows (in my case it's a Dates utils library):
const defaultOptions = {
defaultFormatOptions: 'dd/MM/yyyy'
}
class DatesClass {
constructor(date = new Date(), options) {
this.date = date
this.options = { ...defaultOptions, ...options }
}
get value() {
return this.date
}
add() {}
...
}
export default (date, options) => new DateClass(date, options)
// then you can use it as follow
import dates from 'path/to/yourClass/from/above'
dates(new Date()).add({ unit: 'day', qty: 2}).value

Make JS class callable without side effects [duplicate]

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
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 require 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 the new 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) };
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 browsers that support Proxies, calling _Foo(...) will now execute apply trap function instead of the original 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). Similarly, 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 transpiled or polyfilled, but still it's viable solution for having Scala-like class applied in JS in the future.
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
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
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 TypeError1 (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.
class MyClass {
constructor(param) {
// ...
}
static create(param) {
return new MyClass(param);
}
doSomething() {
// ...
}
}
MyClass.create('Hello World').doSomething();
Is that what you want?
If you need some logic when creating a new instance of MyClass, it could be helpful to implement a "CreationStrategy", to outsorce the logic (for example complex builder logic with validation)
Edit: As discussed in the comments It does not make sense to create some sort of Builder Pattern with a separate class in JavaScript. Removed related example.
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);
}
}
Now you can create a Student object without using new as follows:
var stud1 = Student('Kia');
Dug up this one in the draft
Constructors defined using class definition syntax throw when called as functions
So I guess that's not possible with classes.
Call class constructor manually can be usefull when refactoring code (having parts of the code in ES6, other parts beeing function & prototype definition)
I ended up with a small, yet usefull boilerplate, slicing 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() {}
I had problems extending classes converted with the transformation function mentioned in some other answers. The issue seems to be that node (as of v9.4.0) doesn't properly support the argument spread operator ((...args) =>).
This function based on the transpiled output of the classy-decorator (mentioned in another answer) works for me and doesn't require support for decorators or the argument spread operator.
// 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;
}
Usage:
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 to be fine with the old instanceof trick (well, it won't typecheck if used without new but it works anyhow):
class X {
constructor() {
if (!(this instanceof X)) {
return new X();
}
}
}
because it compiles it down to:
var X = /** #class */ (function () {
function X() {
if (!(this instanceof X)) {
return new X();
}
}
return X;
}());
Alright I have another answer here, and I think this one is pretty innovative.
Basically, the problem with doing something similar to Naomik's answer is that you create functions each and every time you chain methods together.
EDIT: This solution shares the same problem, however, this answer is being left up for educational purposes.
So here I'm offering a way to merely bind new values to your methods--which are basically just independent functions. This offer the additional benefit of being able to import functions from different modules into the newly constructed object.
Okay, so here it goes.
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's 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)
While you don't get ClassInstance.constructor you do have FunctorInstance.name (note, you may need to polyfill Function.name and/or not use an arrow function for export for browser compatibility with Function.name purposes)
export function Right(...args){
return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args)
}
PS - New name suggestions for prepareInstance welcomed, see Gist.
https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456
As pointed out by you and others
Foo("world").hello();
fails with an error because it is an error,
according to rules of ES6 syntax.
Others pointed out that
(new Foo("world")).hello();
works but is clunky because
It needs the 'new' AND
It needs the extra parenthesis.
I agree it is clunky. So I'm often using
this solution instead:
In your class Foo, create a static method
named 'new':
static new (...args)
{ return new this (...args);
}
Use it like this:
Foo.new("world").hello();
This way I hide the "clunkiness" inside
this static method 'new()'.
Note that this method new() is generic,
it will work as is also
when inherited to sub-classes. If you need
to customize it in a subclass you can first call:
super.new(...args)
and then add any other stuff you need in the
method in a subclass, before returning its result.
A recapped working "one-line" solution for ES6: explained
The answer posted above by Bergi is basically correct.
TLDR; skip to the end 😎 for the one-liner solution
Bergi's answer may seem a unclear when reading it. So, here is a more expanded code-sample that illustrates TWO new ES6 features to achieve the desired goals.
Together, they let a single function C (below) provide the dual-role of a factory and new-able fn; which constructs a B inst that derives from a A.
The B constructor utilizes super handling to invoke the A constructor with initialization arguments. In our final #3 - #4 examples constructed by C.
The A constructor demonstrates the semantics of the new.target psuedo-var to discover new was actually invoked with B.
First, we will make use of ES6 new.target psuedo-var that gives us the RHS of a new RHS() expression.
Technically, we could have gotten new.target as this?.__proto__?.constructor; they are equivalent.
Second, we will make use of ES6 Reflect.construct. Which is crucial to working around the ES6 class constructor invocation constraints; if we are bound and determined to not use new RHS(...).
Test the following and see for yourself its output (also provided in #1-4 below).
class A {
constructor(...a) {
const descendentType = new.target;
console.log(`A's constructor seeing 'new' invoked on ${descendentType?.name} with args: %o`,a);
}
}
class B extends A {
constructor(...a) {
super(...a);
}
}
// C is our DUAL mode Factory
function C(...a) {
console.log(`C's new.target => ${new.target?.name}`);
const inst = new.target ? Reflect.construct(B, a) : new B(...a);
console.log(`C has constructed a ${inst.__proto__.constructor.name} inst`);
return inst;
}
Which we can then invoke it in the following ways:
new A('NEW-A()')
output => "A's constructor seeing 'new' invoked on A with args: ['NEW-A()']"
new B('NEW-B()')
output => "A's constructor seeing 'new' invoked on B with args: ['NEW-B()']"
new C('NEW-C()')
output => "C's new.target => C"
output => "A's constructor seeing 'new' invoked on B with args: ['NEW-C()']"
output => "C has constructed a B inst"
C('PLAIN-C()')
output => "C's new.target => undefined"
output => "A's constructor seeing 'new' invoked on B with args: ['PLAIN-C()']"
output => "C has constructed a B inst"
Where #3 and #4 achieve the originally desired goals.
The simplified `C` looks like:
function C(...a) {return Reflect.construct(B, a);}
OR - if 3rd arg of Reflect.construct not utilized for init.
function C(...a) {return new B(...a);}
Beware: C must be a function not a class for this to both be allowed, and to work returning an alternate this on a new C() invocation, etc.
Also to circumvent strict mode rules for arguments.callee requires using a closure (smalltalk-block. Illustrated below:
class B extends A {
// embedding within a class and generically referencing it requires =>
static C = (() => {
const $class = this; return function(...a) {
return Reflect.construct($class, a);}})();
// Read more on `Reflect.construct` 3rd argument to see more capabilities
// for why it does MORE than just `new $class(...a)` would do.
}
exports.C = B.C;
⛐⚠️⛐ You could do some awful things like fiddle the __proto__ on the resulting inst and change out its constructor and name. Which would make it look and feel like a real subclass C of B depending on how far you want to go to manipulate the object-model. The subtleties abound in what happens with getters/setters, super and # privates. But for much of that you can STAY ES6 CLEAN and get clever with using extends and providing a template superclass flattened mixin tree; which I do a lot of in efekt for supporting tiny-but-complete µhtml reactive custom-elements parts and related PWA app models and responsive dynamic just-in-time versioned code bundling from EdgeS ESS backend servers. As in ... const M = $class => class extends $class {...}.
My motivations...
I posted this to help explain the semantics and a working ES6 solution, which is what I use to support subclassing Promise to provide FutureValue with better workflow handling capabilities in my github efekt library (EdgeS Front End Kit library).
In 2022, with ES6 onwards you can do it with the static method that can be called before the instance of the class is created, to create a instance of the class.
So the code should look something like this:
class Foo {
constructor(x) {
this.x = x;
}
//static class
static Init(x) {
return new Foo(x)
}
sayHello() {
return `hello ${this.x}`;
}
}
//so if i call
Foo.Init('world').sayHello();
//it prints: hello world
But if you are doing all this to make a chain of method you can also look at the following construct:
function MyName(name) {
if (this instanceof MyName) {
this.name = name,
this.prepend = function(n) {
this.name = `${n} ${this.name}`;
return this;
}
,
this.append = function(n) {
this.name = `${this.name} ${n} `;
return this;
}
,
this.show = function() {
return this.name;
}
} else {
return new MyName(name);
}
}
//Call
MyName('vinod').prepend('dev').append('hacks').show();
//prints: dev vinod hacks
The method above returns this at the end of each method which makes the object, properties & method avaialble.
The good part is these methods can be used again & again to create a sentence as
MyName('vinod').prepend('dev').append('hacks')
.prepend("the").append('javascript').append('for Stackoverflow').show();
I have used it as a stringBuilder or to generate xml dynamically.
Calling the class constructor without the new keyword is not possible.
The error message is quite specific.
See a blog post on 2ality and the spec:
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
I'm adding this as a follow up to a comment by naomik and utilizing on the method illustrated by Tim and Bergi. I'm also going to suggest an of function to use as a general case.
To do this in a functional way AND utilize the efficiency of prototypes (not re-create all method each time a new instance is created), one could use this pattern
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 specs
https://github.com/fantasyland/fantasy-land#of-method
I personally feel that it is cleaner to use the ES6 class syntax
class Foo {
static of(x) { new Foo(x)}
constructor(x) { this._value = x }
increment() { Foo.of(this._value+1) }
}
Now one could wrap this 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, ie the class could be FooClass and the function just Foo, etc.
This is better than place the class in the function because creating new instances doesn't burden us with creating new classes as well.
Yet another way is to create a an of function
const of = (classObj,...args) => (
classObj.of
? classObj.of(value)
: new classObj(args)
)
And then do something like of(Foo,5).increment()
Still finding interesting ways to use instanceof without relying on new or class keywords. In this example program, we compute the 100,000th fibonacci number in less than one second. The result is over 20,000 digits long -
const fib = x =>
Loop // <- no `new`
( (n, a, b) =>
n <= 0n
? String(a) // <- no `new`
: Recur(n - 1n, b, a + b) // <- no `new`
, BigInt(x) // <- no `new`
, 0n
, 1n
)
function Loop (f, ...init)
{ let r = f(...init)
while (r instanceof Recur) // <- instanceof works
r = f(...r)
return r
}
function Recur (...v)
{ return Object.create // <- not a class, but works
( Recur.prototype // <- set prototype
, { constructor: { value: Recur } // <- set constructor
, [Symbol.iterator]: { value: _ => v.values() } // <- whatever you want
}
)
}
document.body.textContent = fib(100000)
body { overflow-wrap: anywhere; }
I don't know why I haven't thought of this before -
function atom (T, v)
{ return Object.assign
( Object.create
( T.prototype
, { constructor: { value: T } }
)
, v
)
}
function pair (car, cdr)
{ return atom(pair, { car, cdr }) }
const p =
pair(1, 2)
console.log(p)
console.log(p instanceof pair)
Output -
{
"car": 1,
"cdr": 2
}
true
I wrote a small helper function which solves this problem. It effectively converts an ES6 class into an older ES5 constructor function which isn't subject to the same ruleset. This way you can create constructors which don't need new. You can also overload constructors in a similar way to the builtin Number, String etc.
function callableConstructor(c, f) {
function ret(...args) {
if(new.target) {
return new c(...args)
}
return f(...args)
}
ret.prototype = c.prototype
ret.prototype.constructor = ret
return ret
}
Test it below:
function callableConstructor(c, f) {
function ret(...args) {
if(new.target) {
return new c(...args)
}
return f(...args)
}
ret.prototype = c.prototype
ret.prototype.constructor = ret
return ret
}
// Usage
class Foo {
constructor(a, b) {
this.a = a
this.b = 2 * b
}
f() {
return this.a + this.b
}
}
Foo = callableConstructor(Foo, (...args) => new Foo(...args))
let foo = new Foo(2, 3)
console.log(foo) // Foo { a: 2, b: 6 }
console.log(foo.f()) // 8
console.log(foo instanceof Foo) // true
foo = Foo(2, 3)
console.log(foo) // Foo { a: 2, b: 6 }
console.log(foo.f()) // 8
console.log(foo instanceof Foo) // true
I came at this issue because I encountered the no-new "do not use new for side effects" eslint rule - which turns out it's a bad practice to use new for an object that is immediately discarded.
I still wanted to use the class syntax because I like it, but I agree that a regular class with new keyword for something that does not produce an object can be confusing.
The solution for me was simple. Define an unexported class in a module and export a function that instatinates it.
class SideEffects {
constructor() {
}
// ...
}
export function addSideEffects() {
// eslint-disable-next-line no-new
new SideEffects();
}
Yes, we are still using the new keyword, but it's used internally in the module and it's obvious from reading the module file that it's not a regular class - and the exported function also makes it clear that it does not create an object.
This might be a little contrived, 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()
You can't use a class without the new constructor, in my case I didn't want to use the new constructor any time I wanted to use my class, so what you can do is to wrap your class as follows (in my case it's a Dates utils library):
const defaultOptions = {
defaultFormatOptions: 'dd/MM/yyyy'
}
class DatesClass {
constructor(date = new Date(), options) {
this.date = date
this.options = { ...defaultOptions, ...options }
}
get value() {
return this.date
}
add() {}
...
}
export default (date, options) => new DateClass(date, options)
// then you can use it as follow
import dates from 'path/to/yourClass/from/above'
dates(new Date()).add({ unit: 'day', qty: 2}).value

How do you define a function of a class in another Javascript file using Node require?

Let's say I have a class A defined in its own JavaScript file, like this:
A.js
class A {
constructor() {
// blah blah blah
}
func() {
// a long function
}
}
If I have a function (e.g. func()) that I want to be contained in its own file (for organizational purposes), how would I accomplish this?
What I want is something like this:
A.js
class A {
constructor() {
this.func = {};
}
} exports.A = A;
ADefinition.js
var A = require('./A.js');
A.func = () => {
// a long function
}
This obviously doesn't work, but how would one accomplish this?
Classes are mostly just syntax sugar. In ES5, you define prototype functions by assigning to the prototype:
function A() {
}
A.prototype.func = function() {
}
Classes can work the same way:
var A = require('./A.js');
A.prototype.func = () => {
// a long function
}
Though, note that if you use an arrow function, you won't have access to the instance - you may well need a full-fledged function instead:
A.prototype.func = function() {
// a long function
};
Also, personally, I think I'd prefer to put func on the class next to the class definition for code clarity, rather than running a module that performs side effects (like your current code is attempting to do), for example:
const func = require('./func.js');
class A {
constructor() {
}
}
A.prototype.func = func;

Referencing static members from instance method on dynamically extended JS class

I have a base ES6 class that I dynamically extend given a configuration object, like so:
class Model {
constructor () {
// ...
}
save () {
// ...
}
}
function createModelFromConfig (config) {
const Impl = class extends Model {};
Object.assign(Impl, config);
return Impl;
}
const User = createModelFromConfig({store: new DbStore()});
In the save() method on the abstract Model, I'd like to reference the static object store, which will exist on the class that extends Model. This means, of course, that I need to reference a static member but the extended class is anonymous.
Just in a quick test using the Chrome console, I tried
function X () {}
X.prototype.doSomething = function () { console.log(this.constructor); };
function Y () {}
Y.prototype = Object.create(X.prototype);
new Y().doSomething(); // function X () {}
I don't know if this is a reliable test, but it appears that this.constructor does not reference the Impl that I extended, but instead the original base class, which isn't helpful.
A less elegant way is to add Impl.prototype.Impl = Impl; so I can use this.Impl.store in my save function, but it'd be preferable if I could access the static members of the Impl class without this.
Is my prototypal test in the console inadequate? Or is there any other way to access the constructor class in an instance method from an inherited method?
In my testing, I've concluded that Y.prototype = Object.create(X.prototype); is not an adequate equivalent to the ES6 extends implementation.
In running in the Node REPL:
class X {
constructor () {}
save () { console.log(this.constructor.z); }
}
class Y extends X {}
Y.z = 'z';
new Y().save(); // 'z'

Declaring static constants in ES6 classes?

I want to implement constants in a class, because that's where it makes sense to locate them in the code.
So far, I have been implementing the following workaround with static methods:
class MyClass {
static constant1() { return 33; }
static constant2() { return 2; }
// ...
}
I know there is a possibility to fiddle with prototypes, but many recommend against this.
Is there a better way to implement constants in ES6 classes?
Here's a few things you could do:
Export a const from the module. Depending on your use case, you could just:
export const constant1 = 33;
And import that from the module where necessary. Or, building on your static method idea, you could declare a static get accessor:
const constant1 = 33,
constant2 = 2;
class Example {
static get constant1() {
return constant1;
}
static get constant2() {
return constant2;
}
}
That way, you won't need parenthesis:
const one = Example.constant1;
Babel REPL Example
Then, as you say, since a class is just syntactic sugar for a function you can just add a non-writable property like so:
class Example {
}
Object.defineProperty(Example, 'constant1', {
value: 33,
writable : false,
enumerable : true,
configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError
It may be nice if we could just do something like:
class Example {
static const constant1 = 33;
}
But unfortunately this class property syntax is only in an ES7 proposal, and even then it won't allow for adding const to the property.
class Whatever {
static get MyConst() { return 10; }
}
let a = Whatever.MyConst;
Seems to work for me.
I'm using babel and the following syntax is working for me:
class MyClass {
static constant1 = 33;
static constant2 = {
case1: 1,
case2: 2,
};
// ...
}
MyClass.constant1 === 33
MyClass.constant2.case1 === 1
Please consider that you need the preset "stage-0".
To install it:
npm install --save-dev babel-preset-stage-0
// in .babelrc
{
"presets": ["stage-0"]
}
Update for stage:
it was moved on stage-3.
Update Babel 7:
As per Babel 7 stage presets are deprecated.
The Babel plugin to use is #babel/plugin-proposal-class-properties.
npm i --save-dev #babel/plugin-proposal-class-properties
{
"plugins": ["#babel/plugin-proposal-class-properties"]
}
Note: This plugin is included in #babel/preset-env
In this document it states:
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property
This means that it is intentionally like this.
Maybe you can define a variable in the constructor?
constructor(){
this.key = value
}
It is also possible to use Object.freeze on you class(es6)/constructor function(es5) object to make it immutable:
class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true
MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true
delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true
Trying to alter the class will give you a soft-fail (won't throw any errors, it will simply have no effect).
Maybe just put all your constants in a frozen object?
class MyClass {
constructor() {
this.constants = Object.freeze({
constant1: 33,
constant2: 2,
});
}
static get constant1() {
return this.constants.constant1;
}
doThisAndThat() {
//...
let value = this.constants.constant2;
//...
}
}
You can create a way to define static constants on a class using an odd feature of ES6 classes. Since statics are inherited by their subclasses, you can do the following:
const withConsts = (map, BaseClass = Object) => {
class ConstClass extends BaseClass { }
Object.keys(map).forEach(key => {
Object.defineProperty(ConstClass, key, {
value: map[key],
writable : false,
enumerable : true,
configurable : false
});
});
return ConstClass;
};
class MyClass extends withConsts({ MY_CONST: 'this is defined' }) {
foo() {
console.log(MyClass.MY_CONST);
}
}
Like https://stackoverflow.com/users/2784136/rodrigo-botti said, I think you're looking for Object.freeze(). Here's an example of a class with immutable statics:
class User {
constructor(username, age) {
if (age < User.minimumAge) {
throw new Error('You are too young to be here!');
}
this.username = username;
this.age = age;
this.state = 'active';
}
}
User.minimumAge = 16;
User.validStates = ['active', 'inactive', 'archived'];
deepFreeze(User);
function deepFreeze(value) {
if (typeof value === 'object' && value !== null) {
Object.freeze(value);
Object.getOwnPropertyNames(value).forEach(property => {
deepFreeze(value[property]);
});
}
return value;
}
I did this.
class Circle
{
constuctor(radius)
{
this.radius = radius;
}
static get PI()
{
return 3.14159;
}
}
The value of PI is protected from being changed since it is a value being returned from a function. You can access it via Circle.PI. Any attempt to assign to it is simply dropped on the floor in a manner similar to an attempt to assign to a string character via [].
You could use import * as syntax. Although not a class, they are real const variables.
Constants.js
export const factor = 3;
export const pi = 3.141592;
index.js
import * as Constants from 'Constants.js'
console.log( Constants.factor );
You can make the "constants" read-only (immutable) by freezing the class. e.g.
class Foo {
static BAR = "bat"; //public static read-only
}
Object.freeze(Foo);
/*
Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo {
static BAR = "bat"; //public static read-only
}'
*/
Foo.BAR = "wut";
Here is one more way you can do
/*
one more way of declaring constants in a class,
Note - the constants have to be declared after the class is defined
*/
class Auto{
//other methods
}
Auto.CONSTANT1 = "const1";
Auto.CONSTANT2 = "const2";
console.log(Auto.CONSTANT1)
console.log(Auto.CONSTANT2);
Note - the Order is important, you cannot have the constants above
Usage
console.log(Auto.CONSTANT1);
The cleanest way I've found of doing this is with TypeScript - see How to implement class constants?
class MyClass {
static readonly CONST1: string = "one";
static readonly CONST2: string = "two";
static readonly CONST3: string = "three";
}
Just declare your variables as private and use a get method to retrieve them.
class MyClass {
#myConst = 'Something';
static #anotherConst = 'Something Else';
get myConst() {
return this.#myConst; // instance method
}
static get anotherConst() {
return MyClass.#anotherConst; // static method
}
}
let myClass = new MyClass();
console.log( myClass.myConst + ' is not ' + MyClass.anotherConst );
Users cannot change the original variable, and you can write the class to use the get methods rather than the private variables themselves.
One pattern that I use to expose error codes, i.e.,
I have many constants inside the module
I may not want to expose all constants to callers
I do not want to provide 1 static constant for one exposed constant
// inside the module
const Errors = {
INTERNAL: 100,
EMPTY_QUEUE: 101,
UNKNOWN_COMMAND: 102,
OK: 200,
MOVE: 201,
CREATE_DOT: 202,
PIXEL_MAPPING: 203
}
Object.freeze(Errors);
class PlotterError extends Error {
// use constant inside the module
code = Errors.INTERNAL;
constructor(message, code) {
super(message);
this.name = 'PlotterError';
this.code = code
}
}
// expose via static constant
Class Plotter {
.....
static get ERRORS() {
return Errors;
}
....
export Plotter;
// module ends
// in the caller
import {Plotter} from ...
try {
this.plotter.execute();
} catch(error) {
if(error.code == Plotter.ERRORS.EMPTY_QUEUE) {
//
}
}
We can also decide to expose only the constants we want by breaking the constants acr two objects.
If you are comfortable mixing and matching between function and class syntax you can declare constants after the class (the constants are 'lifted') . Note that Visual Studio Code will struggle to auto-format the mixed syntax, (though it works).
class MyClass {
// ...
}
MyClass.prototype.consts = {
constant1: 33,
constant2: 32
};
mc = new MyClass();
console.log(mc.consts.constant2);
Adding up to other answers you need to export the class to use in a different class. This is a typescript version of it.
//Constants.tsx
const DEBUG: boolean = true;
export class Constants {
static get DEBUG(): boolean {
return DEBUG;
}
}
//Anotherclass.tsx
import { Constants } from "Constants";
if (Constants.DEBUG) {
console.log("debug mode")
}
If trying to make a const/variable static to a class; try using the hash (#) to define a place holder, than a function to access it.
class Region {
// initially empty, not accessible from outside
static #empty_region = null;
/*
Make it visible to the outside and unchangeable
[note] created on first call to getter.
*/
static EMPTY() {
if (!this.#empty_region)
this.#empty_region = new Region(0, 0, 0, 0);
return this.#empty_region;
}
#reg = {x0:0, y0:0, x1:0, y1:0};
constructor(x0, y0, x1, y1) {
this.setRegion(x0, y0, x1, y1);
}
// setters/getters
}
Implementation:
let someRegion = Region.EMPTY();
let anotherRegion = Region.EMPTY();
Here You Go!
const Status = Object.freeze(class Status {
static Disabled = 0
static Live = 1
})

Categories

Resources