5

Say I have a classe in JS with prototype functions...

function Foo() {
    this.stuff = 7;
    this.otherStuff = 5;
}

Foo.prototype.doSomething = function() { };

Foo.prototype.doSomethingElse = function() { };

Now say I want to "extend" this class by subclassing it. In Java this would look like...

public class Bar extends Foo {}

Now I know in JS there really isn't a concept of class, everything can be altered and it all really just boils down to a crap-ton of dictionaries but nonetheless, I should be able to copy the prototype of one class and append it to the prototype of another, right?

What would the code for something like that look like in vanilla JS?

Cody Smith
  • 2,732
  • 3
  • 31
  • 43
  • You can read about inheritance and overriding using constructor functions here: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Jul 17 '13 at 07:16

5 Answers5

5

One of the way is like below,

function Foo() {
    this.stuff = 7;
    this.otherStuff = 5;
}
Foo.prototype.doSomething = function() { alert("some"); };

Foo.prototype.doSomethingElse = function() { };

function Bar() {
   Foo.call(this); // this line
}
Bar.prototype = Object.create(Foo.prototype);

var b = new Bar();
b.doSomething(); 
Vinay
  • 6,891
  • 4
  • 32
  • 50
  • Whoa what does Foo.call(this); do? – Cody Smith Jul 17 '13 at 05:18
  • call is used to chain constructors for an object. [more](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) – Vinay Jul 17 '13 at 05:18
  • Ahh cool! So that will run Foo's constructor? Kind of like super() in Java? – Cody Smith Jul 17 '13 at 05:19
  • Not sure. But don't use java concepts with javascript to learn javascript. They both are different and are OOP based. Each has its own way getting things done. I recommend you to go in that way. – Vinay Jul 17 '13 at 05:28
3

The solution that mohkhan provided is outdated. The new way to do inheritance is:

function Bar() {
    Foo.call(this, /* additional arguments if required */);
}

Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;              // don't forget to do this

If you want to make your code look more classical then take a look at the augment method. Using augment you can write the above code as:

var Bar = Foo.augment(function (base) {
    this.constructor = function () {
        base.constructor.call(this, /* additional arguments if required */);
    };
});

The augment method itself is just seven lines of code:

Function.prototype.augment = function (body) {
    var base = this.prototype;
    var prototype = Object.create(base);
    body.apply(prototype, Array.prototype.slice.call(arguments, 1).concat(base));
    if (!Object.hasOwnProperty.call(prototype, "constructor")) return prototype;
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
};

Seriously, use the new way. It's better. Here's why: Javascript inheritance: calling Object.create when setting a prototype

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
2

Something like this...

function Bar(){
    // your code
}

Bar.prototype = new Foo(); // Bar extends Foo
mohkhan
  • 11,925
  • 2
  • 24
  • 27
  • Would this also copy the stuff and otherstuff members or just the functions? – Cody Smith Jul 17 '13 at 05:16
  • 1
    No, it would copy the variables also, not just the functions. – mohkhan Jul 17 '13 at 05:17
  • In Bar you forgot to call `Foo.call(this);` to make Foo's variables declared as `this` a part of the newly created Bar: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Jul 17 '13 at 07:18
  • [Don't use `new` to create the prototype object!](https://stackoverflow.com/questions/12592913/what-is-the-reason-to-use-the-new-keyword-here) Admittedly, this answer is from 2013 where this was even more common, but it's wrong nonetheless. ES5 did exist even then. – Bergi Dec 11 '19 at 21:17
2

The accepted answer will definitely do the trick. And this "short" explanation turned into a ramble, but hopefully it's helpful.

There's one thing you have to be aware of with the accepted answer. When basically "inheriting" by doing Bar.prototype = new Foo() you're calling the constructor. So, if you have code in your constructor that isn't expecting to be used as a launch pad for another "class" you'll wind up with weird effects. Take for instance:

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

var Student = function (firstName, lastName, grade) {
    ....
};

Let's say that Student is an extension of Person. How are you going to build the prototype up on Student? Probably not like Student.prototype = new Person("John", "Doe");

A different way of handling it, which is a bit more complex but can be wrapped inside of another function is as follows:

var extend = function (child, parent) {
    var f = function () {};
    f.prototype = parent.prototype;
    child.prototype = new f();
    child.prototype.constructor = parent;
}

var Person = function (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}
Person.prototype.getName = function () {
    return this.firstName + " " + this.lastName;
}
Person.prototype.setAge = function (age) {
    this.age = age;
}


var Student = function (firstName, lastName, grade) {
    Person.call(this, firstName, lastName);
    this.grade = grade;
};

extend(Student, Person); //<< do this before adding stuff to the prototype

Student.prototype.getName = function () {
    var name = Person.prototype.getName.apply(this);
    return name + ", Grade " + this.grade;
}
Student.prototype.tooOldToBeAStudent = function () {
    if(this.age > 18) { return true; }
}

var p = new Person("Joe", "DiMaggio");
var s = new Student("Jack", "Sparrow", 12);
console.log(p.getName(), s.getName());
//consoles "Joe DiMaggio" "Jack Sparrow, Grade 12"
console.log(s instanceof Person); 
//true - even with the weird looking inheritance, Student is still an instance of Person.
s.setAge(30);
console.log(s.age);
//30, just like what you'd expect with a Person object.
console.log(s.tooOldToBeAStudent);
//true - as mentioned previously, 'age' is set via its inherited method.

This gets you not only the Person functionality, but allows you to build on it. Kind of like what you actually do with inheritance.

How does it work? Great question. First, objects are assigned around [basically] by reference. The prototype is an object. So, in the extend function, I create a blank function that will serve as the surrogate for the child. This copies the parent's prototype to itself, and then makes a new instance of itself the prototype of the child. This way the parent's constructor isn't called, but the parent's prototype is still used. To make sure that instanceof still works, the child.prototype.constructor is set to the parent - effectively informing javascript that the thing the child came from is the parent, rather than from the surrogate.

Also, when overriding methods, you can use the "class" you are inheriting from's prototype method and apply or call it with this - that runs the method with the scope of the current object, and you can pass any arguments you feel like however they are accepted by the function you choose. For example, Person.prototype.getName.apply(this); runs the Person's getName in the context of the current instance of the student. Let's say we wanted to override the setAge to simply console.log out the age.

Student.prototype.setAge = function () {
    Person.prototype.setAge.apply(this, arguments);
    console.log(this.age);
}

What's happening here is the Person's setAge is being called with the arguments that were passed to the Student's setAge. So, it basically allows the stuff to pass through without you having to know about the details of the original method's arguments.

Stephen
  • 5,362
  • 1
  • 22
  • 33
-3

the pattern of extending classes in javascript is different than java

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // Call the inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

var p = new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class
Loken Makwana
  • 3,788
  • 2
  • 21
  • 14
  • 2
    It would be beneficial to show some minimal set of code as an example for future readers if the link you provide ever dies. – Erik Philips Jul 17 '13 at 05:14