How to inherit static methods from base class in JavaScript? - javascript

I'm trying to achieve some basic OOP in JavaScript with the prototype way of inheritance. However, I find no way to inherit static members (methods) from the base class.
We can simulate the basic class model by using prototype:
SomeClass = function(){
var private_members;
this.public_method = function(){
//some instance stuff..
};
};
Class.static_method = function(){
//some static stuff;
};
//Inheritance
SubClass = function(){ //sub-class definition };
SubClass.prototype = new Class();
However, SubClass doesn't inherit static_method from Class.

In the classical (OO) inheritance pattern, the static methods do not actually get inherited down. Therefore if you have a static method, why not just call: SuperClass.static_method() whenever you need it, no need for JavaScript to keep extra references or copies of the same method.
You can also read this JavaScript Override Patterns to get a better understanding of how to implement inheritance in JavaScript.

For ES5 you will need to use Object.assign to copy static methods from BaseClass to SubClass but for ES6 it should work without using Object.assign
ES5 Example
var BaseClass = function(){
}
BaseClass.sayHi = function(){
console.log("Hi!");
}
var SubClass = function(){
}
Object.assign(SubClass , BaseClass);
BaseClass.sayHi(); //Hi
SubClass.sayHi(); //Hi
ES6 Example
class BaseClass {
static sayHi(){
console.log("Hi!");
}
}
class SubClass extends BaseClass{
}
BaseClass.sayHi() //Hi
SubClass.sayHi() //Hi

After your example code you can do this:
for (var i in Class) {
SomeClass[i] = Class[i];
}
to copy static members from Class to SubClass.
If you're using jQuery, have a look at the jQuery.Class plugin from JavaScriptMVC. Or you can extend John Resig's Simple JavaScript Inheritance for a more library-agnostic system.

Try this:
class BaseClass {
static baseMethod () {
console.log("Hello from baseMethod");
}
}
class MyClass extends BaseClass {
constructor(props) {
super(props);
}
}
Object.assign(MyClass, BaseClass);
They key is Object.assign which should be everyone's new best friend. You can now call any base method from BaseClass using MyClass as follows:
MyClass.baseMethod();
You can see this live and in action on this pen.
Enjoy!

How are you implementing 'static' methods? Since JavaScript doesn't have a native class/instance object model, it all depends on how you've designed your own class system.
If you want to be able to inherit anything it'll have to be using the SomeClass.prototype object, rather than putting anything directly on the SomeClass constructor function. So essentially you'll be defining static methods as normal instance methods, but ones that don't care about what value of this is passed into them.

you can access static fields via this.constructor[staticField];
class A {
static get Foo() { return 'Foo'; }
constructor() {
console.log(`Class ${this.constructor.name}`, this.constructor.Foo);
}
}
class B extends A {
static get Foo() { return 'Baz'; }
}
class C extends A {}
const a = new A();
const b = new B();
const c = new C()

Related

Is it possible for a javascript class to define a method which fires if the class is called?

In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.

JavaScript make a class's method static and available to its instances

How can you make a class's method static (available on the class itself) and accessible from its instances in JavaScript?
class MyClass {
constructor() {}
method() {}
}
const myInstance = new MyClass();
myInstance.method(); // calling from instance
MyClass.method(); // calling from class
You can use the static keyword to do so.
class A {
constructor(){}
static method(){}
}
const x = new A();
EDIT:
x.constructor.method(); // this is possible
A.method();
In Javascript, static methods are bound to class and not instances, so they can only be called by atleast using the constructor. A.prototype handles instance methods whereas the function A handles it's attributes.
Static method calls are made directly on the class and are not callable on instances of the class. But you can achieve the calls for static members from inside an instance.
Using syntax:
this.constructor.staticfunctionName();
ES6 Class Example:
class MyClass {
constructor() {}
static staticMethod() {
console.log('Static Method');
}
}
MyClass.staticVar = 777;
var myInstance = new MyClass();
// calling from instance
myInstance.constructor.staticMethod();
console.log('From Inside Class : ',myInstance.constructor.staticVar);
// calling from class
MyClass.staticMethod();
console.log('Class : ', MyClass.staticVar);
For ES5 Function Classes refer to my answer.

ES6: access to inherited class'es properties and methods from the parent class

ES6 has not abstract methods or properties, but can I get some methods or properties in the parent class from inherited class?
class ParentClass {
constructor(){
ParentClass.checkClildPropertyAccessibility();
ParentClass.checkClildMethodAccessibility();
ParentClass.checkClildStaticPropertyAccessibility();
ParentClass.checkClildStaticMethodAccessibility();
}
static checkClildPropertyAccessibility() {
console.log(ParentClass.childProperty);
}
static checkClildMethodAccessibility(){
ParentClass.childMethod()
}
static checkClildStaticPropertyAccessibility(){
console.log(ParentClass.childStaticProperty);
}
static checkClildStaticMethodAccessibility(){
ParentClass.clildStaticMethod()
}
}
class ChildClass extends ParentClass {
constructor(){
super();
ChildClass.childProperty = 'child\'s Property: OK';
}
childMethod(){
console.log('child\'s method OK');
}
// static property emulation is ES6
static get childStaticProperty() { return 'Child\'s static property: OK even ES6' }
static clildStaticMethod (){
console.log('Child\'s static method: OK');
}
}
let childClassInstance = new ChildClass();
The concept is "We must to define some properties and methods in child class, however the parent class needs them to use already in constructor".
Yes, it's possible to call methods only defined in a subclass of an ES2015 class.
class ParentClass {
constructor() {
this.childMethod();
this.initialize();
console.log(this.childProperty1);
console.log(this.childProperty2);
}
}
class ChildClass extends ParentClass {
constructor() {
super();
this.childProperty1 = 'child\'s Property: OK';
}
initialize() {
this.childProperty2 = 'child\'s Property 2: OK';
}
childMethod() {
console.log('Child\'s overriden method: OK');
}
}
let childClassInstance = new ChildClass();
Notice initialize() is used to assign an initial value to childProperty2. Parent constructor will always run before any other code in a subclass constructor, that's why childProperty1 isn't initialized when the console call was made. In a parent class, this will point to an object with the prototype of the subclass. In the comments section, Bergi points out the practice of calling an overridden method in a parent constructor should be avoided, given the overriden method may depend on state not yet setup by the child constructor.
And no, it doesn't work with static methods. Even though static methods are copied to subclasses, when you refer to ParentClass, you'll get the method defined there. There's no prototype chain to follow there.
EDIT: clarification from comments section.
It is possible to refer current constructor in parent class as this in static methods and as this.constructor in instance methods.
This results in potential design problem, because ParentClass doesn't have methods and properties that are defined in ChildClass, and new ParentClass will result in error when non-existent childMethod is called.

extended class, doesn't have its own methods

I try to extend a class with another, who has its constructor overrode, but when i instance this class, it doesn't have its own methods, but has its own properties.
Here's an example which doesn't work properly:
class A {
constructor () {
return {
pi: 3.14
}
}
}
class B extends A {
constructor () {
super();
this.c = 10;
}
d () {}
}
let b = new B();
console.log(b);
Here, b is :
Object {
c:10,
pi:3.14
}
So why the 'd' method is missing ?
EDIT:
Here is a concrete case:
I need to extend a class with HTMLElement, which i can instance and use like html element without registering with document.registerElement.
My code is:
class Element{
constructor(){
return document.createElement('div');
}
}
class Editor extends Element{
constructor(){
super();
}
}
and i want to use my class like this:
let editor = new Editor();
document.querySelector('body').appendChild(editor);
ECMAScript6 class methods are methods of an object's prototype object. You will find the method not in the object itself, but in obj.__proto__.
https://reinteractive.com/posts/235-es6-classes-and-javascript-prototypes
Here you can see and maybe understand what is going on internally with prototyped objects. And yes, the ES6 syntax is just a syntax change, not a new technology.

How to add mixins to ES6 javascript classes?

In an ES6 class with some instance variables and methods, how can you add a mixin to it? I've given an example below, though I don't know if the syntax for the mixin object is correct.
class Test {
constructor() {
this.var1 = 'var1'
}
method1() {
console.log(this.var1)
}
test() {
this.method2()
}
}
var mixin = {
var2: 'var2',
method2: {
console.log(this.var2)
}
}
If I run (new Test()).test(), it will fail because there's no method2 on the class, as it's in the mixin, that's why I need to add the mixin variables and methods to the class.
I see there's a lodash mixin function https://lodash.com/docs/4.17.4#mixin, but I don't know how I could use it with ES6 classes. I'm fine with using lodash for the solution, or even plain JS with no libraries to provide the mixin functionality.
Javascript's object/property system is much more dynamic than most languages, so it's very easy to add functionality to an object. As functions are first-class objects, they can be added to an object in exactly the same way. Object.assign is the way to add the properties of one object to another object. (Its behaviour is in many ways comparable to _.mixin.)
Classes in Javascript are only syntactic sugar that makes adding a constructor/prototype pair easy and clear. The functionality hasn't changed from pre-ES6 code.
You can add the property to the prototype:
Object.assign(Test.prototype, mixin);
You could add it in the constructor to every object created:
constructor() {
this.var1 = 'var1';
Object.assign(this, mixin);
}
You could add it in the constructor based on a condition:
constructor() {
this.var1 = 'var1';
if (someCondition) {
Object.assign(this, mixin);
}
}
Or you could assign it to an object after it is created:
let test = new Test();
Object.assign(test, mixin);
In es6 you can do this without assigning and you can even invoke the mixin constructor at the correct time!
http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/#bettermixinsthroughclassexpressions
This pattern uses class expressions to create a new base class for every mixin.
let MyMixin = (superclass) => class extends superclass {
foo() {
console.log('foo from MyMixin');
}
};
class MyClass extends MyMixin(MyBaseClass) {
/* ... */
}
You should probably look at Object.assign(). Gotta look something like this:
Object.assign(Test.prototype, mixin);
This will make sure all methods and properties from mixin will be copied into Test constructor's prototype object.
I'm surprised to find that none of the answers mentions what I would consider a mixin in the sense of composition (and in contrast to inheritance), which to me is a function that adds functionality to an object. Here's an example making use of both inheritance and composition:
class Pet { constructor(name) { this.name = name } }
class Cat extends Pet { expression = 'miaow' }
class Dog extends Pet { expression = 'bark' }
class Human { constructor(name, age) { this.name = name; this.age = age; } }
class American extends Human { expression = 'say howdy' }
function canSayHello(...contexts) {
for (const context of contexts) {
context.sayHello = function() {
console.log(`Hello my name is ${this.name} and I ${this.expression}`)
}
}
}
canSayHello(Pet.prototype, Human.prototype); // apply the mixin
const garfield = new Cat('garfield');
const pluto = new Dog('pluto');
const joebiden = new American('Joe Biden', 79);
garfield.sayHello();
pluto.sayHello();
joebiden.sayHello();

Categories