[javascript] Class vs. static method in JavaScript

I know this will work:

function Foo() {};
Foo.prototype.talk = function () {
    alert('hello~\n');
};

var a = new Foo;
a.talk(); // 'hello~\n'

But if I want to call

Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly

I find some methods to make Foo.talk work,

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

Are there other ways to do this? I don’t know whether it is right to do so. Do you use class methods or static methods in your JavaScript code?

This question is related to javascript oop

The answer is


If you have to write static methods in ES5 I found a great tutorial for that:

//Constructor
var Person = function (name, age){
//private properties
var priv = {};

//Public properties
this.name = name;
this.age = age;

//Public methods
this.sayHi = function(){
    alert('hello');
}
}


// A static method; this method only 
// exists on the class and doesn't exist  
// on child objects
Person.sayName = function() {
   alert("I am a Person object ;)");  
};

see @https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/


Static method calls are made directly on the class and are not callable on instances of the class. Static methods are often used to create utility function

Pretty clear description

Taken Directly from mozilla.org

Foo needs to be bound to your class Then when you create a new instance you can call myNewInstance.foo() If you import your class you can call a static method


There are tree ways methods and properties are implemented on function or class objects, and on they instances.

  1. On the class (or function) itself : Foo.method() or Foo.prop. Those are static methods or properties
  2. On its prototype : Foo.prototype.method() or Foo.prototype.prop. When created, the instances will inherit those object via the prototype witch is {method:function(){...}, prop:...}. So the foo object will receive, as prototype, a copy of the Foo.prototype object.
  3. On the instance itself : the method or property is added to the object itself. foo={method:function(){...}, prop:...}

The this keyword will represent and act differently according to the context. In a static method, it will represent the class itself (witch is after all an instance of Function : class Foo {} is quite equivalent to let Foo = new Function({})

With ECMAScript 2015, that seems well implemented today, it is clearer to see the difference between class (static) methods and properties, instance methods and properties and own methods ans properties. You can thus create three method or properties having the same name, but being different because they apply to different objects, the this keyword, in methods, will apply to, respectively, the class object itself and the instance object, by the prototype or by its own.

class Foo {
  constructor(){super();}
  
  static prop = "I am static" // see 1.
  static method(str) {alert("static method"+str+" :"+this.prop)} // see 1.
  
  prop="I am of an instance"; // see 2.
  method(str) {alert("instance method"+str+" : "+this.prop)} // see 2.
}

var foo= new Foo();
foo.prop = "I am of own";  // see 3.
foo.func = function(str){alert("own method" + str + this.prop)} // see 3.

Javascript has no actual classes rather it uses a system of prototypal inheritance in which objects 'inherit' from other objects via their prototype chain. This is best explained via code itself:

_x000D_
_x000D_
function Foo() {};_x000D_
// creates a new function object_x000D_
_x000D_
Foo.prototype.talk = function () {_x000D_
    console.log('hello~\n');_x000D_
};_x000D_
// put a new function (object) on the prototype (object) of the Foo function object_x000D_
_x000D_
var a = new Foo;_x000D_
// When foo is created using the new keyword it automatically has a reference _x000D_
// to the prototype property of the Foo function_x000D_
_x000D_
// We can show this with the following code_x000D_
console.log(Object.getPrototypeOf(a) === Foo.prototype); _x000D_
_x000D_
a.talk(); // 'hello~\n'_x000D_
// When the talk method is invoked it will first look on the object a for the talk method,_x000D_
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)_x000D_
_x000D_
// When you want to call_x000D_
// Foo.talk();_x000D_
// this will not work because you haven't put the talk() property on the Foo_x000D_
// function object. Rather it is located on the prototype property of Foo._x000D_
_x000D_
// We could make it work like this:_x000D_
Foo.sayhi = function () {_x000D_
    console.log('hello there');_x000D_
};_x000D_
_x000D_
Foo.sayhi();_x000D_
// This works now. However it will not be present on the prototype chain _x000D_
// of objects we create out of Foo
_x000D_
_x000D_
_x000D_


Here is a good example to demonstrate how Javascript works with static/instance variables and methods.

function Animal(name) {
    Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
    this.name = name; //instance variable, using "this"
}

Animal.showCount = function () {//static method
    alert(Animal.count)
}

Animal.prototype.showName=function(){//instance method
    alert(this.name);
}

var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");

Animal.showCount();  // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from  Java

Just additional notes. Using class ES6, When we create static methods..the Javacsript engine set the descriptor attribute a lil bit different from the old-school "static" method

function Car() {

}

Car.brand = function() {
  console.log('Honda');
}

console.log(
  Object.getOwnPropertyDescriptors(Car)
);

it sets internal attribute (descriptor property) for brand() to

..
brand: [object Object] {
    configurable: true,
    enumerable: true,
    value: ..
    writable: true

}
..

compared to

class Car2 {
   static brand() {
     console.log('Honda');
   }
}

console.log(
  Object.getOwnPropertyDescriptors(Car2)
);

that sets internal attribute for brand() to

..
brand: [object Object] {
    configurable: true,
    enumerable: false,
    value:..
    writable: true
  }

..

see that enumerable is set to false for static method in ES6.

it means you cant use the for-in loop to check the object

for (let prop in Car) {
  console.log(prop); // brand
}

for (let prop in Car2) {
  console.log(prop); // nothing here
}

static method in ES6 is treated like other's class private property (name, length, constructor) except that static method is still writable thus the descriptor writable is set to true { writable: true }. it also means that we can override it

Car2.brand = function() {
   console.log('Toyota');
};

console.log(
  Car2.brand() // is now changed to toyota
);

When i faced such a situation, i have done something like this:

Logger = {
    info: function (message, tag) {
        var fullMessage = '';        
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.log(fullMessage);
        }
    },
    warning: function (message, tag) {
        var fullMessage = '';
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.warn(fullMessage);`enter code here`
        }
    },
    _getFormatedMessage: function () {}
};

so now i can call the info method as Logger.info("my Msg", "Tag");


Call a static method from an instance:

function Clazz() {};
Clazz.staticMethod = function() {
    alert('STATIC!!!');
};

Clazz.prototype.func = function() {
    this.constructor.staticMethod();
}

var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"

Simple Javascript Class Project: https://github.com/reduardo7/sjsClass


ES6 supports now class & static keywords like a charm :

class Foo {
    constructor() {}

    talk() {
        console.log("i am not static");
    }

    static saying() {
        console.log(this.speech);
    }

    static get speech() {
        return "i am static method";
    }

}

In additions, now it is possible to do with class and static

'use strict'

class Foo {
 static talk() {
     console.log('talk')
 };

 speak() {
     console.log('speak')
 };

};

will give

var a = new Foo();
Foo.talk();  // 'talk'
a.talk();    // err 'is not a function'
a.speak();   // 'speak'
Foo.speak(); // err 'is not a function'

You can achieve it as below:

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

You can now invoke "talk" function as below:

Foo.talk();

You can do this because in JavaScript, functions are objects as well.


In your case, if you want to Foo.talk():

function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
    alert('hello~\n');
};

Foo.talk(); // 'hello~\n'

But it's an inefficient way to implement, using prototype is better.


Another way, My way is defined as static class:

var Foo = new function() {
  this.talk = function () {
    alert('hello~\n');
    };
};

Foo.talk(); // 'hello~\n'

Above static class doesn't need to use prototype because it will be only constructed once as static usage.

https://github.com/yidas/js-design-patterns/tree/master/class


When you try to call Foo.talk, the JS tries to search a function talk through __proto__ and, of course, it can't be found.

Foo.__proto__ is Function.prototype.


I use namespaces:

var Foo = {
     element: document.getElementById("id-here"),

     Talk: function(message) {
            alert("talking..." + message);
     },

     ChangeElement: function() {
            this.element.style.color = "red";
     }
};

And to use it:

Foo.Talk("Testing");

Or

Foo.ChangeElement();