535

What is the difference between the following two declarations?

Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }

Is it okay to think of the first statement as a declaration of a static method, and the second statement as a declaration of an instance method?

Rob W
  • 341,306
  • 83
  • 791
  • 678
postrational
  • 6,306
  • 3
  • 22
  • 27

5 Answers5

739

Yes, the first function has no relationship with an object instance of that constructor function, you can consider it like a 'static method'.

In JavaScript functions are first-class objects, that means you can treat them just like any object, in this case, you are only adding a property to the function object.

The second function, as you are extending the constructor function prototype, it will be available to all the object instances created with the new keyword, and the context within that function (the this keyword) will refer to the actual object instance where you call it.

Consider this example:

// constructor function
function MyClass () {
  var privateVariable; // private member only available within the constructor fn

  this.privilegedMethod = function () { // it can access private members
    //..
  };
}

// A 'static method', it's just like a normal function 
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
  // the 'this' keyword refers to the object instance
  // you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
32

Yes, the first one is a static method also called class method, while the second one is an instance method.

Consider the following examples, to understand it in more detail.

In ES5

function Person(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}

Person.isPerson = function(obj) {
   return obj.constructor === Person;
}

Person.prototype.sayHi = function() {
   return "Hi " + this.firstName;
}

In the above code, isPerson is a static method, while sayHi is an instance method of Person.

Below, is how to create an object from Person constructor.

var aminu = new Person("Aminu", "Abubakar");

Using the static method isPerson.

Person.isPerson(aminu); // will return true

Using the instance method sayHi.

aminu.sayHi(); // will return "Hi Aminu"

In ES6

class Person {
   constructor(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }

   static isPerson(obj) {
      return obj.constructor === Person;
   }

   sayHi() {
      return `Hi ${this.firstName}`;
   }
}

Look at how static keyword was used to declare the static method isPerson.

To create an object of Person class.

const aminu = new Person("Aminu", "Abubakar");

Using the static method isPerson.

Person.isPerson(aminu); // will return true

Using the instance method sayHi.

aminu.sayHi(); // will return "Hi Aminu"

NOTE: Both examples are essentially the same, JavaScript remains a classless language. The class introduced in ES6 is primarily a syntactical sugar over the existing prototype-based inheritance model.

Aminu Kano
  • 2,595
  • 1
  • 24
  • 26
  • "In ES6" you describe a syntax sugar only. This is not the "ES2015" (please everyone stop using ES6 use the proper term ES2015) way of doing it. It's simply another way of doing it and in my opinion the incorrect way. – basickarl Sep 09 '19 at 07:08
  • 3
    @KarlMorrison Aminu did not write "way of doing it" you just wrote that yourself and took exception to it. Your point might be fair about ES6 vs ES2015 but in conversation people often resort to a shorter convention for efficiency, so I think removing it from writing is not possible or for sure advisable. – wuliwong Dec 17 '19 at 18:46
  • Thank you for the ES6 part of your answer; that clarifies a lot, especially when combined with the 2 "public + privileged" answers above. I am however **thoroughly** confused by your `obj.constructor === Person` being `true` example though... Whaaaat? How can a class instance's constructor `===` the class itself...? (That's like saying a subset of a set is the set itself, etc...) – Andrew Apr 02 '20 at 05:03
  • Ohhh... is this all to say then that literally, the constructor is all that a JS class really is at the end of the day? Everything else is either piled into the constructor or totally a static construct isolated from the class except by name/concept (and like an implied "this" being made available obviously)? (Thus, what I **thought** was a subset of the set was actually not a subset.) – Andrew Apr 02 '20 at 05:05
  • @Andrew perhaps the surreal feeling comes from this: In JavaScript, **the class and the constructor function are the same thing**. By the way, I keep coming back to [this diagram](https://stackoverflow.com/q/9959727/673991). It is freaky but rewards study. Eventually it really parted the clouds for me about the way JavaScript does classes, or pretends to. Key to understanding the constructor property is John Sonderson's comment: `b.constructor` like any class property resolves to `b.__proto__.constructor` and thereby points to `Foo`. – Bob Stein Jul 09 '20 at 17:07
  • @BobStein Yeah that diagram doesn't help me at all, it makes it way worse. The top answer there is useful though. – Andrew Jul 09 '20 at 22:55
  • @BobStein I don't think the key takeaway here is that the class and the constructor are the same thing; that to me is totally sensible because you do e.g. `new MyClass()`, which calls `constructor()`. (Btw fun fact: Constructors can return something totally different than the class instance! Useful in some cases...) The key takeaway is that the **rest** of the properties/functions outside of the `constructor()` are basically just bound to the instance on `new`. This knowledge led me to this: https://stackoverflow.com/a/62142995/1599699 – Andrew Jul 09 '20 at 22:55
20

When you create more than one instance of MyClass , you will still only have only one instance of publicMethod in memory but in case of privilegedMethod you will end up creating lots of instances and staticMethod has no relationship with an object instance.

That's why prototypes save memory.

Also, if you change the parent object's properties, is the child's corresponding property hasn't been changed, it'll be updated.

user2440156
  • 217
  • 2
  • 2
  • "Also, if you change the parent object's properties, if the child's corresponding property hasn't been changed, it'll be updated." Not sure what you mean by this. Is this so only if you change the prototype properties of the parent object? – Peter Out Feb 24 '21 at 16:34
19

For visual learners, when defining the function without .prototype

ExampleClass = function(){};
ExampleClass.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
    // >> error! `someInstance.method is not a function`  

With same code, if .prototype is added,

ExampleClass.prototype.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method();  
      // > error! `ExampleClass.method is not a function.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
                 // > output: `Called from instance`

To make it clearer,

ExampleClass = function(){};
ExampleClass.directM = function(){}  //M for method
ExampleClass.prototype.protoM = function(){}

var instanceOfExample = new ExampleClass();

ExampleClass.directM();     ✓ works
instanceOfExample.directM();   x Error!

ExampleClass.protoM();     x Error!
instanceOfExample.protoM();  ✓ works

****Note for the example above, someInstance.method() won't be executed as,
ExampleClass.method() causes error & execution cannot continue.
But for the sake of illustration & easy understanding, I've kept this sequence.****

Results generated from chrome developer console & JS Bin
Click on the jsbin link above to step through the code.
Toggle commented section with ctrl+/

SAm
  • 2,154
  • 28
  • 28
4

A. Static Method:

      Class.method = function () { /* code */ }
  1. method() here is a function property added to an another function (here Class).
  2. You can directly access the method() by the class / function name. Class.method();
  3. No need for creating any object/instance (new Class()) for accessing the method(). So you could call it as a static method.

B. Prototype Method (Shared across all the instances):

     Class.prototype.method = function () { /* code using this.values */ }
  1. method() here is a function property added to an another function protype (here Class.prototype).
  2. You can either directly access by class name or by an object/instance (new Class()).
  3. Added advantage - this way of method() definition will create only one copy of method() in the memory and will be shared across all the object's/instance's created from the Class

C. Class Method (Each instance has its own copy):

   function Class () {
      this.method = function () { /* do something with the private members */};
   }
  1. method() here is a method defined inside an another function (here Class).
  2. You can't directly access the method() by the class / function name. Class.method();
  3. You need to create an object/instance (new Class()) for the method() access.
  4. This way of method() definition will create a unique copy of the method() for each and every objects created using the constructor function (new Class()).
  5. Added advantage - Bcos of the method() scope it has the full right to access the local members(also called private members) declared inside the constructor function (here Class)

Example:

    function Class() {
        var str = "Constructor method"; // private variable
        this.method = function () { console.log(str); };
    }
    Class.prototype.method = function() { console.log("Prototype method"); };
    Class.method = function() { console.log("Static method"); };

    new Class().method();     // Constructor method
    // Bcos Constructor method() has more priority over the Prototype method()

    // Bcos of the existence of the Constructor method(), the Prototype method 
    // will not be looked up. But you call it by explicity, if you want.
    // Using instance
    new Class().constructor.prototype.method(); // Prototype method

    // Using class name
    Class.prototype.method(); // Prototype method

    // Access the static method by class name
    Class.method();           // Static method
danronmoon
  • 3,814
  • 5
  • 34
  • 56
SridharKritha
  • 8,481
  • 2
  • 52
  • 43
  • A. "No need for creating any object/instance (new Class()) for accessing the method(). So you could call it as a static method." Is it possible to access a static method from an instance? If so how. It'd be nice to remove this ambiguity. B. "You can either directly access by class name or by an object/instance (new Class())." I think it'll be helpful to add an example of accessing via class name(Class.prototype.method()) to clarify. It confused me at first as I know that Class.method() doesn't work for prototype methods. Your answer really helped my understanding, thanks a lot. – Peter Out Feb 24 '21 at 17:34
  • This answer contradicts the answer by @Aminu Kano in what is the definition of a class method... Regardless, I think "class method" by itself is really a bad name due to the level of the confusion. – kakacii Sep 17 '22 at 05:45