Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What will be a good minimalistic Javascript inheritance method?

I'm rewriting a JavaScript project, and I want to be able to use object oriented methodologies to organize the mess that the current code is. The main concern is that this JavaScript is supposed to run as a widget inside 3rd party websites and I can't have it conflicting with other JavaScript libraries that other websites may use.

So I'm looking for a way to write "class-like" inheritance in JavaScript that has the following requirements:

  1. No external libraries or things that would conflict with an external library (that precludes copy&paste from an external library).
  2. Minimalistic - I don't want the support code to be larger then a few lines of code and I don't want the developers to need a lot of boiler-plate every time they define a new class or methods.
  3. Should allow for dynamically extending parent objects so that child objects see the changes (prototype).
  4. Should allow for constructor chaining.
  5. Should allow for super type calls.
  6. Should still feel JavaScript-ish.

Initially I tried to work with simple prototype chaining:

function Shape(x,y) {
  this.x = x;
  this.y = y;

  this.draw = function() {
    throw new Error("Arbitrary shapes cannot be drawn");
  }
}

function Square(x,y,side) {
  this.x = x;
  this.y = y;
  this.side = side;

  this.draw = function() {
    gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); ...
  }
}
Square.prototype = new Shape();

And that solves requirements 1, 2 and 6 but id does not allow super calls (new functions override parent functions), constructor chaining and dynamically extending a parent does not provide the new methods to a child class.

Any suggestions will be welcome.

like image 256
Guss Avatar asked Sep 10 '09 10:09

Guss


People also ask

Which type of inheritance is supported by JavaScript?

In JavaScript, inheritance is supported by using prototype object. Some people call it "Prototypal Inheriatance" and some people call it "Behaviour Delegation". Let's see how we can achieve inheritance like functionality in JavaScript using prototype object.

How would you perform inheritance in JavaScript?

By calling the super() method in the constructor method, we call the parent's constructor method and gets access to the parent's properties and methods. Inheritance is useful for code reusability: reuse properties and methods of an existing class when you create a new class.

What is prototypal inheritance in JavaScript?

The Prototypal Inheritance is a feature in javascript used to add methods and properties in objects. It is a method by which an object can inherit the properties and methods of another object. Traditionally, in order to get and set the [[Prototype]] of an object, we use Object. getPrototypeOf and Object.


4 Answers

I'd suggest the following pattern which makes use of a clone function to inherit from the protoypes and not instances:

function Shape(x, y) {
    this.x = x;
    this.y = y;
}

Shape.prototype.draw = function() {
    throw new Error('Arbitrary shapes cannot be drawn');
};

function Square(x,y,side) {
    Shape.call(this, x, y); // call super constructor
    this.side = side;
}

// inherit from `Shape.prototype` and *not* an actual instance:
Square.prototype = clone(Shape.prototype);

// override `draw()` method
Square.prototype.draw = function() {
    gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); // ...
};

It's important that methods reside in the prototype (which is as it should be anyway for performance reasons) so you can call the methods of a super class via

SuperClass.prototype.aMethod.call(this, arg1, arg2);

With some syntactic sugar, you can make JS look like a classical class-based language:

var Shape = Class.extend({
    constructor : function(x, y) {
        this.x = x;
        this.y = y;
    },
    draw : function() {
        throw new Error('Arbitrary shapes cannot be drawn');
    }
});

var Square = Shape.extend({
    constructor : function(x, y, side) {
        Shape.call(this, x, y);
        this.side = side
    },
    draw : function() {
        gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); // ...
    }
});
like image 186
Christoph Avatar answered Nov 15 '22 07:11

Christoph


Douglas Crockford has good articles on both classical and prototypal inheritance in Javascript, which should make good starting points.

like image 20
Andrzej Doyle Avatar answered Nov 15 '22 08:11

Andrzej Doyle


OK, the trick with reproducing a class/instance-style system in JavaScript is that you can only use prototype inheritance on the instances. So you need to be able to make a ‘non-instance’ instance that is only used for inheritance, and have an initialiser method separate from the constructor function itself.

This is the minimal system I use (before adding frills), passing a special one-off value into the constructor to have it construct an object without initialising it:

Function.prototype.subclass= function() {
    var c= new Function(
        'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
        'if (arguments[0]!==Function.prototype.subclass._FLAG && this._init) this._init.apply(this, arguments); '
    );
    if (this!==Object)
        c.prototype= new this(Function.prototype.subclass._FLAG);
    return c;
};
Function.prototype.subclass._FLAG= {};

The use of new Function() is a way to avoid forming an unnecessary closure over subclass(). You can replace it with a prettier function() {...} expression if you prefer.

Usage is comparatively clean, and generally like Python-style objects only with slightly clumsier syntax:

var Shape= Object.subclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};
Shape.prototype.draw= function() {
    throw new Error("Arbitrary shapes cannot be drawn");
};

var Square= Shape.subclass();
Square.prototype._init= function(x, y, side) {
    Shape.prototype._init.call(this, x, y);
    this.side= side;
};
Square.prototype.draw= function() {
    gotoXY(this.x, this.y);
    lineTo(this.x+this.side, this.y); // ...
};

Monkey-patching a builtin (Function) is a bit questionable, but makes it pleasant to read, and no-one's likely to want to for...in over a Function.

like image 33
bobince Avatar answered Nov 15 '22 08:11

bobince


The most common pattern I found when researching this question is described on the Mozilla Developer Network. I've updated their example to include a call to a superclass method and to show the log in an alert message:

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);
  1. In the five years since you asked this question, it seems like browser support for inheritance has improved, so I don't think you need an external library.
  2. This the most minimal technique I've seen, I don't know if you consider that too much boilerplate.
  3. It uses the prototype, as requested, so adding new methods to the parent should provide them to the child objects as well.
  4. You can see constructor chaining in the example.
  5. Super type calls are also in the example.
  6. I'm not sure if it feels JavaScript-ish, you'll have to decide for yourself.
like image 26
Don Kirkby Avatar answered Nov 15 '22 08:11

Don Kirkby