Safely inheriting prototypes in JavaScript

diplosaurus

Let's say I'm shooting for some basic inheritance in my application, I could achieve this by setting the prototype of my child to the parent.

// Parent "class"
var Car = function(year) {
    this.year = year;
};

Car.prototype.drive = function() {
    console.log('Vrooom');
};

// Child "class"
var Toyota = function() {
    Car.apply(this, arguments);
};

Toyota.prototype = Car.prototype;

var myCar = new Car(2010), myToyota = new Toyota(2010);

myCar.drive(); // Vrooom
myToyota.drive(); // Vrooom

Seems to work, but is obviously bad because if I set a new method on my Toyota.prototype, it will be set on Car's prototype.

Toyota.prototype.stop = function() {
    console.log('stooop');
};

myCar.stop(); // stooop  <--- bad
myToyota.stop(); // stooop  <--- good

To solve this, instead of Toyota.prototype = Car.prototype; I can add an intermediary:

var ctor = function() { };
ctor.prototype = Car.prototype;

Toyota.prototype = new ctor();

Toyota.prototype.stop = function() {
    console.log('stooop');
};

myCar.stop(); // throws undefined error  <--- good
myToyota.stop(); // stooop  <--- good

But I don't understand why this works. ctor creates a new instance with its prototype set to Car's prototype, and then Toyota sets its prototype to that new instance.

But why create an empty object with a prototype that gets referenced by Toyota's prototype?
Why doesn't setting a new method on Toyota set it on Car like it does in the first example? What if I want several layers of inheritance, it seems like I need to glue them together with a new ctor every time?

istos

But why create an empty object with a prototype that gets referenced by Toyota's prototype?

Because if you do this: Toyota.prototype = new Car();

Now Toyota's prototype is an instance of Car, which means that in addition to having Car's prototype in the chain, it also has any properties set in the Car constructor itself. Basically you wouldn't want Toyota.prototype to have a property called year like so: Toyota.prototype.year. Because of this it's a lot better to have an empty constructor like so:

var ctor = function() { };
ctor.prototype = Car.prototype;

Toyota.prototype = new ctor();

Now Toyota.prototype has the new ctor() instance as it's prototype, which in turn has Car.prototype in its own chain. This means that instances of Toyota now can execute methods that exist in Car.prototype.


Why doesn't setting a new method on Toyota set it on Car like it does in the first example?

With this: Toyota.prototype = Car.prototype; you're setting Toyota' prototype to be the same exact object as what Car.prototype contains. Since it is the same object, changing it in one place also changes it everywhere else. This means that objects are passed be reference and not by value, which is another way of saying that when you assign an object to 3 different variables, it is the same object regardless of which variable you use. Strings for example are passed by value. Here is an example with strings:

var str1 = 'alpha';
var str2 = str1;
var str3 = str1;

str2 = 'beta';

// Changing str2 doesn't change the rest.
console.log(str1); //=> "alpha"
console.log(str3); //=> "alpha"
console.log(str2); //=> "beta"

Now compare to objects and their properties;

var obj1 = {name: 'joe doe'};
var obj2 = obj1;
var obj3 = obj1;

console.log(obj1.name); //=> "joe doe"
console.log(obj2.name); //=> "joe doe"
console.log(obj3.name); //=> "joe doe"

obj2.name = 'JOHN SMITH';

console.log(obj1.name); //=> "JOHN SMITH"
console.log(obj2.name); //=> "JOHN SMITH"
console.log(obj3.name); //=> "JOHN SMITH"


What if I want several layers of inheritance...

Here is another way of creating layers of inheritance:

var Car = function(year) {
    this.year = year;
};

Car.prototype.drive = function() {
    console.log('Vrooom');
};

var Toyota = function() {
    Car.apply(this, arguments);
};

// `Object.create` does basically the same thing as what you were doing with `ctor()`
// Check out the documentation for `Object.create` as it takes a 2nd argument too.
Toyota.prototype = Object.create(Car.prototype);


// Create instances
var 
  myCar = new Car(2010), 
  myToyota = new Toyota(2010);


// Add method to Toyota's prototype
Toyota.prototype.stop = function() {
    console.log('stooop');
};

Let's try it out now:

myToyota.stop(); //=> 'stooop'
myCar.stop(); //=> 'TypeError: undefined is not a function'

myCar.drive(); //=> Vrooom
myToyota.drive(); //=> Vrooom

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Safely inheriting prototypes in JavaScript

From Dev

JavaScript functions and prototypes

From Dev

Understanding of JavaScript Prototypes

From Dev

Javascript nested prototypes

From Dev

Constructor and prototypes in javascript

From Dev

Prototypes and property inheritence in JavaScript

From Dev

Prototypes issue in javascript

From Dev

JavaScript prototypes: replace a function

From Dev

Javascript nested prototypes

From Dev

Prototypes and usage Javascript

From Dev

Javascript not inheriting prototype properties

From Dev

Inheriting a Javascript Function

From Dev

Understanding how JavaScript Prototypes work

From Dev

JavaScript Inheritance with Prototypes -- 'constructor' property?

From Dev

Creating functions in function prototypes for Javascript

From Dev

Extending prototypes in Javascript - good way?

From Dev

How to avoid using "this" in Javascript prototypes

From Dev

Javascript - Help w/ prototypes and properties?

From Dev

JavaScript Inheritance with Prototypes -- 'constructor' property?

From Dev

How to avoid using "this" in Javascript prototypes

From Dev

More prototypes at once (chaining) in javascript

From Dev

using a group of functions as a prototypes in javascript

From Dev

Jasmine and Spying/Mocking - JavaScript Prototypes

From Dev

javascript extending prebuilt prototypes performance

From Dev

JavaScript - The Good Parts: Function prototypes vs Object prototypes

From Dev

Safely creating namespaces in JavaScript

From Dev

Inheriting javascript Number to change toString

From Dev

Javascript Objects / Prototypes. Is my understanding wrong

From Dev

Popping prototypes off a stack in javascript results in undefined

Related Related

HotTag

Archive