JavaScript: Class.method vs. Class.prototype.method
Asked Answered
C

5

536

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?

Claudineclaudio answered 28/10, 2009 at 3:59 Comment(0)
B
738

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();
Backplate answered 28/10, 2009 at 4:8 Comment(3)
But why is Function.prototype.method == Function.method ?Gravedigger
@Gravedigger it isn'tNett
@Menda your link is deadPelham
C
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.

Cutler answered 10/6, 2018 at 14:50 Comment(7)
"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.Girlhood
@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.Counterblow
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...)Nicolanicolai
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.)Nicolanicolai
@Nicolanicolai 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. 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.Wyatan
@BobStein Yeah that diagram doesn't help me at all, it makes it way worse. The top answer there is useful though.Nicolanicolai
@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://mcmap.net/q/24619/-split-a-javascript-class-es6-over-multiple-filesNicolanicolai
I
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.

Indemnity answered 2/9, 2013 at 10:8 Comment(1)
"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?Sarcoma
P
20

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+/

Porphyry answered 3/6, 2017 at 6:43 Comment(0)
D
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
Donoho answered 26/7, 2020 at 21:7 Comment(2)
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.Sarcoma
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.Keever

© 2022 - 2024 — McMap. All rights reserved.