I have a base class that deals with finding/saving items. To reduce the save function complexity (since it uses insert or update depending on whether it's been saved), I thought it best to split it into two classes since a lot of my other methods (not written here) depends on whether or not it's been saved. This is a bare-bones example:
class ItemBase {
constructor (initData) {
this.data = initData
}
find (id) {
const found = Model.find(this.data)
return new SavedItemBase(found)
}
save () {
const saved = this.insert(this.data)
return new SavedItemBase(saved)
}
insert () {}
update () {}
}
class SavedItemBase extends ItemBase {
save () {
this.update()
return this
}
}
Toy:
class Toy extends ItemBase {
constructor () {
this.name = 123
this.id = 456
}
someMethod () {} // This is lost if I return a new SavedItemBase
}
The problem with Toy is that it only extends Item. If I return a new SavedItem in the parent class Item, the original Toy class is lost. Here is a picture of my problem:
How would I get the Toy to return a SavedItemBase instance of itself when it's returned from Base, without rewriting class for Toy (e.g. SavedToyBase)? Or is there a better way to accomplish this?
Class Life {
constructor(name = 'no name') {
this.name = name;
}
getName() {
return this.name;
}
setName(name = 'no name') {
this.name = name;
}
}
const MyLife = new Life();
export { getName, setName } = MyLife;
export default MyLife;
How can I use the same instance of new Life() i.e, MyLife throughout my code?
Things I have tried;
const MyLife = new Life();
export const getName = MyLife.getName.bind(MyLife);
export const setName = MyLife.setName.bind(MyLife);
export default MyLife;
Every time I try to use this in another file i.e, myOther.js
import { setName } from '../path-of-class.js`
setName('Luke Skywalker'); // I get undefined.
What am I doing wrong here?
P.S: the class is in another library, I compile it with webpack & then use in another <package>, The class above works fine if used locally within the same <package> but when I try to use from <package-a> to <package-b> I get setName of undefined.
Try saving your instance of Life into a static variable probably near the class and return it with a static method
class Life {
static getInstance() {
if (!Life.instance) {
Life.instance = new Life();
}
return Life.instance;
}
constructor(name = 'no name') {
this.name = name;
}
getName() {
return this.name;
}
setName(name = 'no name') {
this.name = name;
}
}
Life.instance = null;
export default Life;
in another file:
import Life from 'module';
const myLife = Life.getInstance();
I'm not completely certain how webpack handles the imports. But having a single instance of a class and using it everywhere is a common pattern in object oriented programming (lookup singleton), usually solved by attaching a static variable of the instance to a class and getting it with a static method. This example achieves the same thing by having a static instance and using getInstance() to return it or create one if it wasn't already. This is a classic implementation of this pattern.
Ok say we have this:
class Car {
constructor(name) {
this.kind = 'Car';
this.name = name;
}
printName() {
console.log('this.name');
}
}
what I want to do is define printName, something like this:
class Car {
constructor(name) {
this.kind = 'Car';
this.name = name;
}
// we want to define printName using a different scope
// this syntax is close, but is *not* quite correct
printName: makePrintName(foo, bar, baz)
}
where makePrintName is a functor, something like this:
exports.makePrintName = function(foo, bar, baz){
return function(){ ... }
};
is this possible with ES6? My editor and TypeScript is not liking this
NOTE: using ES5, this was easy to do, and looks like this:
var Car = function(){...};
Car.prototype.printName = makePrintName(foo, bar, baz);
Using class syntax, currently the best thing that is working for me, is this:
const printName = makePrintName(foo,bar,baz);
class Car {
constructor(){...}
printName(){
return printName.apply(this,arguments);
}
}
but that is not ideal. You will see the problem if you try to use class syntax to do what ES5 syntax can do. The ES6 class wrapper, is therefore a leaky abstraction.
To see the real-life use case, see:
https://github.com/sumanjs/suman/blob/master/lib/test-suite-helpers/make-test-suite.ts#L171
the problem with using TestBlock.prototype.startSuite = ..., is that in that case, I cannot simply return the class on line:
https://github.com/sumanjs/suman/blob/master/lib/test-suite-helpers/make-test-suite.ts#L67
The preferable ways to do this in JavaScript and TypeScript may differ due to limitations in typing system, but if printName is supposed to be a prototype method and not instance method (the former is beneficial for several reasons), there are not so many options.
Prototype method can be retrieved via accessor. In this case it should be preferably memoized or cached to a variable.
const cachedPrintName = makePrintName(foo, bar, baz);
class Car {
...
get printName(): () => void {
return cachedPrintName;
}
}
And it can be lazily evaluated:
let cachedPrintName;
class Car {
...
get printName(): () => void {
return cachedPrintName || cachedPrintName = makePrintName(foo, bar, baz);
}
}
Or it can be assigned to class prototype directly. In this case it should be additionally typed as class property because TypeScript ignores prototype assignments:
class Car {
...
printName(): () => void;
}
Car.prototype.printName = makePrintName(foo, bar, baz);
Where () => void is the type of a function that makePrintName returns.
A way that is natural to TypeScript is to not modify class prototypes but extend prototype chain and introduce new or modified methods via mixin classes. This introduces unnecessary complexity in JavaScript yet keeps TypeScript happy about types:
function makePrintNameMixin(foo, bar, baz){
return function (Class) {
return class extends Class {
printName() {...}
}
}
}
const Car = makePrintNameMixin(foo, bar, baz)(
class Car {
constructor() {...}
}
);
TypeScript decorator cannot be seamlessly used at this point because class mutation is not supported at this moment. The class should be additionally supplemented with interface to suppress type errors:
interface Car {
printName: () => void;
}
#makePrintNameMixin(foo, bar, baz)
class Car {
constructor() {...}
}
Just replace : with =
printName = makePrintName()
: goes for type notation.
Edit
As noted in comments the above will not change the prototype. Instead, you can do this outside the class definition, using ES5 syntax:
// workaround: extracting function return type
const dummyPrintName = !true && makePrintName();
type PrintNameType = typeof dummyPrintName;
class Car {
// ...
printName: PrintNameType;
}
Car.prototype.printName = makePrintName();
Playground
Another idea I haven't seen mentioned yet is to use a method decorator. The following decorator takes a method implementation and puts it on the prototype, as desired:
function setMethod<T extends Function>(value: T) {
return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<T>): void {
descriptor.value = value;
delete descriptor.get;
delete descriptor.set;
};
}
Put it in a library somewhere. Here's how you'd use it:
class Car {
constructor(name) {
this.kind = 'Car';
this.name = name;
}
#setMethod(makePrintName(foo, bar, baz))
printName() {} // dummy implementation
}
The only downside is that you have to put a dummy implementation of the method with the right signature in the class, since the decorator needs something to decorate. But it behaves exactly as you want at runtime (it isn't an instance method which costs a new function definition for each instance, or an accessor which costs an extra function call at each use).
Does that help?
Why don't you try something like this
class Car {
constructor(name) {
this.kind = 'Car';
this.name = name;
this.printName = makePrintName(foo, bar, baz);
}
}
am I dont getting it or ..??
class keyword normally is just a syntax sugar of the delegate prototype in ES5. I just tried it in typescript
class Car {
private kind: string;
private name : string;
printName :Function;
constructor(name) {
this.kind = 'Car';
this.name = name;
}
}
var makePrintName = function (foo, bar, baz) {
return function () { console.log(foo, bar, baz); };
};
Car.prototype.printName = makePrintName('hello', 'world', 'did you get me');
var bmw = new Car('bmw');
bmw.printName();
console.log(bmw.hasOwnProperty('printName'));
Here is the thing. I have a main class called A.
I want this class to extend class B.
class A extends B {}
But in fact, I want the class B to extend C, D or E on a specific condition:
class B extends B1 {}
or
class B extends B2 {}
or
class B extends B3 {}
So the B class would be a "fake" class, just to check a condition and then extend the right class.
In the final, the result would be the same as:
class A extends B1 {}
or
class A extends B2 {}
or
class A extends B3 {}
I know this is possible in PHP, with abstract classes or wrapper classes for example.
But how to do that in JavaScript ES6?
Thanks
Weird, but possible:
class subClassFirst {
report() {
console.log(`Extended ${this.name} from the first class`);
}
}
class subClassSecond {
report() {
console.log(`Extended ${this.name} from the second class`);
}
}
class subClassThird {
report() {
console.log(`Extended ${this.name} from the third class`);
}
}
function classCreator(condition) {
let sub;
switch (condition) {
case 'first':
sub = subClassFirst;
break;
case 'second':
sub = subClassSecond;
break;
case 'third':
sub = subClassThird;
break;
}
return (class extends sub {
constructor(name) {
super();
this.name = name;
}
});
}
let myClass;
myClass = classCreator('first');
let mcf = new myClass('f');
myClass = classCreator('second');
let mcs = new myClass('s');
myClass = classCreator('third');
let mct = new myClass('t');
mcf.report();
mcs.report();
mct.report();
I found blog post that gave an easy es6 way that doesn't use util.inherits
https://www.mikedoesweb.com/2017/dynamic-super-classes-extends-in-es6/
Here is how I used a passed option to determine which class to extend and then obfuscated that in the export
import ClassB from ' '
import ClassA from ' '
const ext = {
classA: ClassA, // the default
classB: ClassB
// can do as many as you want
}
function ExtendsMyClass (opts= {}) {
if (!new.target) {
throw new Error('Uncaught TypeError: Class constructor Interrupt cannot be invoked without \'new\'')
}
// one could vet opts here too including opts.extend
class MyClass extends ext[opts.extend || 'classA'] {
constructor(opts = {}) {
super(opts)
....
}
} // end MyClass
return new MyClass(opts)
} // end dynamic extend
export default ExtendsMyClass
export { ExtendsMyClass as MyClass }
I'll probably make this into "wrapper" utility function that accepts also the child class as well. That way one can dynamically extend any class as opposed to the one off implementation above. Could even implement dynamic imports if it was set up an async function.
So classes in javascript are really not setup in the same classical inheritance way as other languages, the best way to do what you want is to set the prototype of the object you are dealing with. There are a few ways.
Object.setPrototypeOf(currentObj, newPrototype);
Where newPrototype is the object you are wanting to inherit from. Here are a couple good articles on it if you want to learn the inner workings.
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch5.md
There's a Node JS function for that
const util = require("util");
class MySubClass {}
class MySuperClass {}
util.inherits(MySubClass, MySuperClass);
Is there any way to determine if a subclass implements a constructor from within a static method (in a base class)?
I'm trying to write a static create method (that acts like the new keyword) that by default works by passing attribute values as a properties object:
class Person extends Class {
greet() { return 'hello from ' + this.name; }
}
var p = Person.create({name: 'world'}; // create a new Person object and set its `name` property to `'world'`
console.log(p.greet()); // => "hello from world"
but hands off to the class' constructor if it has one:
class Person2 extends Class {
constructor(name) {
super();
this.name = name;
}
greet() { return 'hello from ' + this.name; }
}
var p = Person2.create('world');
console.log(p.greet()); // => "hello from world"
I'm stuck at finding out if the subclass defines its own constructor..
class Class {
static create(...args) {
let has_ctor = ?? // true iff the current subclass defines a constructor..
if (has_ctor) {
// let the constructor handle everything
return new this(...args);
} else {
// assume that `args` contains exactly 1 pojo that defines instance variables to be overridden..
var instance = new this();
let props = args[0];
for (let prop in props) instance[prop] = props[prop];
return instance;
}
}
}
is this even possible?
Seems like it would be much easier to do
class Class {
static create(...args) {
// let the constructor handle everything
return new this(...args);
}
constructor(props){
Object.assign(this, props);
}
}
then if things override the constructor, then can choose to pass props to super() or to assign them manually themselves.
Just to answer your original question
Is there a way to discover if a javascript 6 class defines its own constructor?
No, there is not. Every class does have its own constructor, because a "class" basically is just the constructor function.
If a class definition does not include a constructor method, then it is automatically supplied by the language (see ยง14.5.14); either as
constructor(...args){ super (...args);}
if there is a super class or as
constructor(){ }
if there is none. The result is not distinguishable from a class where such a constructor was explicitly declared.