3

I am trying to implement simple inheritance concept in JavaScript without using any external libraries. Here is my code.

<script>
    BaseClass = function () {
        this.doSomething = function () {
            console.log("base class");
        };
    };

    DerivedClass1 = function () {
        var bc = new BaseClass();
        bc.doSomething = function () {
            console.log("derived class");
        };
        return bc;
    };

    DerivedClass2 = function () {
        var bc = new BaseClass();
        return bc;
    }


    var cls1 = new DerivedClass1();
    cls1.doSomething();

    var cls2 = new DerivedClass2();
    cls2.doSomething();


</script>

Does it fall under any known design pattern?

Kumar
  • 31
  • 1
  • 2
  • 1
    http://codereview.stackexchange.com – Hanky Panky Nov 17 '13 at 05:14
  • 1
    Shared methods should be on the `prototype` and instance properties on the construtor, then you can inherit with `Object.create` and call super like `BaseClass.apply(this, arguments)` – elclanrs Nov 17 '13 at 05:14
  • There is quite a lot to learn about prototype and constructor functions. I have tried to make a complete answer here: http://stackoverflow.com/a/16063711/1641941 DerivedClass2 doesn't do anything, you may as well use new BaseClass() as that's what it returns. – HMR Nov 17 '13 at 06:53

3 Answers3

3

TL;DR

var BaseClass = function (bar) {
    this.foo = bar;
};
BaseClass.prototype.doSomething = function () {
    console.log("from base class");
};

var DerivedClass = function () {
    BaseClass.apply(this, arguments);
};
DerivedClass.prototype = Object.create(BaseClass.prototype);
DerivedClass.prototype.doSomething = function () {

    BaseClass.prototype.doSomething.call(this);
    console.log("from derived class");
};

var cls1 = new DerivedClass('I am cls1');

cls1.doSomething();
// -> from base class
// -> from derived class

console.log(cls1.foo);
// -> "I am cls1"

Update

I'm updating my answer thanks to @HMR's remarks (see his comment below, very useful):

  1. "You should not create a new instance of BaseClass when setting prototype of DerivedClass, use Object.create (and polyfill it if needed)"
  2. "You also forget to initialize the BaseClass and take ownership of it's instance variables by having BaseClass.apply(this,arguments)"

1/ Using Object.create

var BaseClass = function () {
    this.foo = 'bar';
};
BaseClass.prototype.doSomething = function () {
    console.log("base class");
};

var DerivedClass = function () {

};
DerivedClass.prototype = Object.create(BaseClass.prototype);

Notes:

  • Object.create "copies" the prototype of Base into Derived
  • The public property this.foo is not copied over to Derived (because it's not part of the prototype) - See below point 2/

More information about Object.create here.

2/ BaseClass.apply(this, arguments)

As stated above this.foo is not available into Derived instances. To make it available we need to apply the Base constructor into the Derived constructor..

So all the privileged properties of Base (this.foo,..) are applied to the new instances of Derived.

var DerivedClass = function () {

    // Calls `Base` constructor with `Derived` context
    BaseClass.apply(this, arguments);
};
DerivedClass.prototype = Object.create(BaseClass.prototype);

More details about javascript inheritance in popular HMR's answer.


I leave my original answer for comparison & educational purposes.

The problem with your technique (while it could work as expected) is that the doSomething method is copied over to every single instances of BaseClass (because it's declared as a simple public property).

To avoid this, and thus share the doSomething method accross all instances of BaseClass, you should add it to the prototype of BaseClass:

var BaseClass = function () {

};

BaseClass.prototype.doSomething = function () {
    console.log("base class");
};

You cannot notice any difference in the end result but, this way, the doSomething method is "inherited", not copied.

Now knowing that, to achieve prototypal inheritance in Javascript:

// Derived Class 1
var DerivedClass1 = function () {

};
DerivedClass1.prototype = new BaseClass();

var cls1 = new DerivedClass1();
cls1.doSomething();

// -> "base class"


// Derived Class 2
var DerivedClass2 = function () {

};
DerivedClass2.prototype = new BaseClass();
DerivedClass2.prototype.doSomething = function () {
    console.log("derived class (2)");
};

var cls2 = new DerivedClass1();
cls2.doSomething();

// -> "derived class (2)"

Bonus, if you want to call the parent method from the DerivedClass:

// Derived Class 3
var DerivedClass3 = function () {

};
DerivedClass3.prototype = new BaseClass();
DerivedClass3.prototype.doSomething = function () {

    BaseClass.prototype.doSomething.call(this);

    console.log("derived class (3)");
};

var cls3 = new DerivedClass1();
cls3.doSomething();

// -> "base class"
// -> "derived class (3)"
Community
  • 1
  • 1
eightyfive
  • 4,601
  • 3
  • 35
  • 44
  • You should not create new instance of Parent when setting prototype of Child, use Object.create (and polyfill it if needed). Or use a helper function. You also forget to initialize the Parent and take ownership of it's instance variables by having `Parent.apply(this,arguments)` Because there is so much to take care of when using prototype and questions like this are asked about 2 times a day I've added this answer: http://stackoverflow.com/a/16063711/1641941 – HMR Nov 17 '13 at 07:02
  • Thanks for the insights! I played around with the `console` and understood more in depth what's happening. I updated my answer in order to reflect your remarks. Let me know if I missed something. – eightyfive Nov 17 '13 at 09:05
  • Wow, you understood that quickly and made a pretty good answer of it +1. For completeness in your first block of code I usually do `DerivedClass.prototype.constructor = DerivedClass;` as constructor can sometimes be used in instances to point to the function that created them (it's always there on prototype but gets overwritten when you set inheritance). I see why Douglas Crockford isn't a big fan of constructor functions as it's complicated and error prone. But since it's the way it's done in JS I personally stick with it and hope JS interpreters do a better job running my code because of it – HMR Nov 17 '13 at 10:41
  • One note of caution: in JavaScript it's not so easy to deep copy objects and Object.create makes what's called a shallow copy. An example of why that could be a problem can be found here: http://stackoverflow.com/a/19879651/1641941 (second block of code with the comment: "Will be a problem with inheritance:"). Haven't added that in the prototype answer because I wanted to stick to the basics and it's long enough as it is. It may be a problem when you're using complex patterns but then you may be making your code too complicated and can solve the problem somewhere else. – HMR Nov 17 '13 at 10:45
  • Thanks. Very instructive. – eightyfive Nov 21 '13 at 10:26
2

You are better off using the prototype chain for your inheritance in JavaScript. In this example you're redefining the objects for every instantiating. It's more efficient to do this using the prototype since you can then define the types once and reuse the definitions..

Here is somre more info: http://net.tutsplus.com/tutorials/javascript-ajax/prototypes-in-javascript-what-you-need-to-know/

TGH
  • 38,769
  • 12
  • 102
  • 135
0

Inheritance is a way to create a class as a specialized version of one or more classes ( only supports single inheritance).
The specialized class is commonly called the child, and the other class is commonly called the parent. In JavaScript you do this by assigning an instance of the parent class to the child class, and then specializing it. In modern browsers you can also use Object.create to implement inheritance.

Note: JavaScript does not detect the child class prototype.constructor (see Object.prototype), so we must state that manually. See the question Why is it necessary to set the prototype constructor?

In the example below, we define the class Student as a child class of Person.
Then we redefine the sayHello() method and add the sayGoodBye() method.

// Define the Person constructor
var Person = function(firstName) {
  this.firstName = firstName;
};

// Add a couple of methods to Person.prototype
Person.prototype.walk = function(){
  console.log("I am walking!");
};

Person.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName);
};

// Define the Student constructor
function Student(firstName, subject) {
  // Call the parent constructor, making sure (using call)
  // that "this" is set correctly during the call
  Person.call(this, firstName);

  // Initialize our Student-specific properties
  this.subject = subject;
}

// Create a Student.prototype object that inherits from Person.prototype.
// Note: A common error here is to use "new Person()" to create the
// Student.prototype. That's incorrect for several reasons, not least 
// that we don't have anything to give Person for the "firstName" 
// argument. The correct place to call Person is above, where we call 
// it from Student.
Student.prototype = Object.create(Person.prototype); // See note below

// Set the "constructor" property to refer to Student
Student.prototype.constructor = Student;

// Replace the "sayHello" method
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName + ". I'm studying "
              + this.subject + ".");
};

// Add a "sayGoodBye" method
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

// Example usage:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(student1 instanceof Person);  // true 
console.log(student1 instanceof Student); // true
GorvGoyl
  • 42,508
  • 29
  • 229
  • 225