How to export a prototype method?
I created a prototype method:
Array.prototype.remove = function(elementToRemove: any): void {
var __idx = this.indexOf(elementToRemove);
if (__idx >= 0) {
this.splice(__idx, 1);
} else {
throw new Error(`Cannot find element ${elementToRemove}`)
}
}
and I want to export it and use in another file. But I don't know how to add export to it:
both
export Array.prototype.remove = function(elementToRemove: any)
and
Array.prototype.remove = export function(elementToRemove: any)
doesn't work, I received 'Expression expected' Error. So how can I export it?
ps: I'm not using nodejs, I'm using web frontend in chrome with HTML and CSS.
The fundamental issue is that your code performs a side-effect - it adds something to Array.prototype.
One option is to export a function that, when called, adds the function to the prototype:
export const addRemoveToPrototype = () => {
Array.prototype.remove = function( // ...
and then consumers can use it by doing
import { addRemoveToPrototype } from './addRemoveToPrototype';
addRemoveToPrototype();
Another option is to simply run that code at the top level of the module:
Array.prototype.remove = function(elementToRemove: any): void {
and then consumers can just import the module, but not do anything with it:
import './addRemoveToPrototype';
That said, I'd really recommend not mutating built-in objects - it makes the code fragile and more easily breakable, especially when other scripts run in the same environment.
Referencing a private member E.G. this.#rts() gives the error:
SyntaxError: Private field '#rts' must be declared in an enclosing class
Although when that line is evaluated, the function has been assigned to an instance method and this is correctly bound.
Is there a way to achieve this, I.E. to reference private members across files?
Note: I'm using Node 13.
Example:
import {Cpu6502} from "./cpu6502.mjs";
console.log((new Cpu6502).beq());
cpu6502.mjs:
import {beq} from "./instructions.mjs";
export class Cpu6502 {
beq = beq // `this` is correctly bound
#rts = () => "RTS"
}
instructions.mjs:
export function beq() {
return this.#rts() // If this line references a public member instead,
// it works fine and `this` is correctly bound.
}
You can use this approach but probably you want somenthing like a mixin
import { lda } from './instructions.mjs'
export class Cpu6502 {
constructor() {
this.lda = lda.bind(this);
}
A = 0xFF
#rts() {
return "RTS";
}
ldx() {
return this.lda();
}
}`
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
})
I have following code:
export class Utils{
constructor() {
this.dateFormat = "MM-DD-YY";
}
static getFormat() {
return this.dateFormat;
}
}
when I am trying to import this class to other file and try to call the static method gteFormat it return undefined.
Here is how I am doing it:
import * as Utils from "./commons/Utils.js";
class ABC {
init(){
console.log(Utils.Utils.getFormat());// gives undefined
}
}
How can I make this static method return the dateFormat property?
If you are conceptually working with a bunch of functions, you can consider relying on the module scope itself for privacy, rather than the class structure. Then you can export functions or values directly. Like
const dateFormat = "MM-DD-YY";
export function getFormat() {
return dateFormat;
}
with usage like
import * as Utils from "./commons/Utils.js";
console.log(Utils.getFormat())
or even
import { getFormat } from "./commons/Utils.js";
console.log(getFormat())
or if it's literally a constant you can export it directly
export const DATE_FORMAT = "MM-DD-YY";
then
import { DATE_FORMAT } from "./commons/Utils.js";
console.log(DATE_FORMAT);
Exporting a class with a bunch of static methods is a very Java-y way to write it, and the class itself adds nothing.
Constructors are for instances
Think about it: a constructor is called when an instance is created, setting static defaults is probably better done elsewhere, for example when declaring the static variable
class Utils {
static dateFormat = "MM-DD-YY";
static getFormat() {
return this.dateFormat;
}
}
console.log(Utils.getFormat())
If, for some reason, you have to set this in a constructor anyways, the correct syntax will be Utils.dateFormat = "...". Funny side fact is that you can use this when reading (in the return statement). However, you would still have to instanciate an instance of Utils in order for dateFormat to be sth. other than undefined:
class Utils {
static dateFormat;
constructor() {
Utils.dateFormat = "MM-DD-YY"; // use Utils.dateFormat when writing
}
static getFormat() {
return this.dateFormat; // use whatever you like when reading... :/
}
}
console.log(`Before instanciating: ${Utils.getFormat()}`);
var dummy = new Utils();
console.log(`After instanciating: ${Utils.getFormat()}`);
A sidenote on your import statement
you could avoid having to call Utils.Utils.getFormat() everytime, which looks a bit weird, by refactoring your import-statement like so:
// import * as Utils from "./commons/Utils.js";
import { Utils } from "./commons/Utils.js";
class ABC {
init(){
//console.log(Utils.Utils.getFormat());
console.log(Utils.getFormat());
}
}