Search code examples
javascriptclassinheritanceecmascript-6prototype

Should we include methods in prototypes for ES6 Classes for performance?


So as I learn more about the Prototypal Inheritance in JS, I read the following in MDN article (read lines just above this link)

Note that when we are calling our constructor function, we are defining greeting() every time, which isn't ideal. To avoid this, we can define functions on the prototype instead, which we will look at later.

The idea recommended is to add properties on Function and methods on the prototype (read here)

Person.prototype.farewell = function() {
  alert(this.name.first + ' has left the building. Bye for now!');
};

I read at multiple places that this way

  • The object creation is faster, since the farewell is not created for every object creation. This is because it is created once and attached to the prototype, and all the objects link to the prototype.

  • The methods are looked up on the prototypal chain so every object links to the same method on the prototype.

Now comes the ES6 class. I have the following code

class Person {
    constructor(first, last, age, gender, interests) {
        this.name = {
            first: first,
            last: last
        };
        this.age = age;
        this.gender = gender;
        this.interests = interests;
    }

    greeting () {
        console.log("Hi! I am", this.name.first);
    }

    farewell () {
        console.log(this.name.first, "has left the building. Bye for now!");
    }
}

It seems that with this approach greeting and farewell will be created again (following up on same logic as Functions since class is a syntactic sugar)

So, I changed the class to

class Person {
    constructor(first, last, age, gender, interests) {
        this.name = {
            first: first,
            last: last
        };
        this.age = age;
        this.gender = gender;
        this.interests = interests;
    }
}

Person.prototype.greeting = function () {
    console.log("Hi! I am", this.name.first);
}

Person.prototype.farewell = function () {
    console.log(this.name.first, "has left the building. Bye for now!");
}

Question
1. Does the latter way (Adding methods on prototype in ES6 class) is a recommended way?
2. Does the logic of class and traditional new Function to create a new object align? Copy vs Method on Prototype?

Please let me know if there is anything else I am missing

Thanks


UPDATE

After few answers, I retried my examples and confirmed the answers. My code looks like

class Phone {
  constructor(company, name, color, price) {
    this.company = company;
    this.name = name;
    this.color = color;
    this.price = price;
  }

  print() {
    console.log(this.company, this.name, this.color, this.price);
  }
}

class Apple extends Phone {
    constructor(name, color, price) {
        super("Apple", name, color, price);
        this.companyWork = "ClosedSource";
    }
}

let iPhone11 = new Apple("iPhone11", "black", 900);
iPhone11.print()

After I ran this code, I can confirm that print() is available on Phone.prototype
enter image description here


Solution

  • As you've already discovered, class definitions put methods on the prototype. You asked for a reference so here's a little code you can run to see for yourself:

    class Person {
        constructor(first, last, age, gender, interests) {
            this.name = {
                first: first,
                last: last
            };
            this.age = age;
            this.gender = gender;
            this.interests = interests;
        }
    
        greeting () {
            console.log("Hi! I am", this.name.first);
        }
    
        farewell () {
            console.log(this.name.first, "has left the building. Bye for now!");
        }
    }
    
    let p = new Person("Jack", "Beanstalk", 201, "giant", ["crushing things", "stomping things"]);
    
    console.log("Person", Object.getOwnPropertyNames(Person));
    
    console.log("p", Object.getOwnPropertyNames(p));
    
    let p_prototype = Object.getPrototypeOf(p);
    
    console.log("p_prototype === Person.prototype is ", p_prototype === Person.prototype);
    
    console.log("Person.prototype", Object.getOwnPropertyNames(Person.prototype));

    That generates this output:

    Person [ 'length', 'prototype', 'name' ]
    p [ 'name', 'age', 'gender', 'interests' ]
    p_prototype === Person.prototype is true
    Person.prototype [ 'constructor', 'greeting', 'farewell' ]
    

    So, you can draw these conclusions:

    1. The Person class has only the expected properties for a constructor function and the prototype object, none of the class-defined methods are there.

    2. An instance of the class has only the expected data properties that are assigned in the constructor, none of the class-defined methods are on the object itself.

    3. The prototype you get from an instance is the same as the prototype of the class (thus no new object is created for each instance).

    4. The Person.prototype has the expected methods from the class definition on it plus a property that points to the constructor itself (used in inheriting and calling derived constructor).

    So, you can verify from this that methods defined with the class syntax go on the Classname.prototype object and that object is shared with all instances.