3

This is a really old subject about which much has been written, but I haven't found exactly this spin on it so please bear with me.

After having spent a bit of time trying to get my head around the new and f.prototype constructor constructs in JavaScript, and reading about how it's a prototypal language, not to mention Crockford's illuminating comments on the subject, I have reached the conclusion that the following is a much more natural way to simulate traditional class-based inheritance in JavaScript, should one want to:

// emphasise that there's no superclass instead of writing A = {}
var A = Create.object(null);

// define the 'instance initializer', which half of what a constructor is
A.init = function(x, y) {
    this.x = x;
    this.y = y;
    return this;
}

// a method
A.sum = function() {
    return this.x + this.y;
}

// instantiate A
var a = Object.create(A).init(3);

// a subclass
var B = Object.create(A);

B.init = function(x, y, w) {
    A.init.call(this, x, y);
    this.w = w;
    return this;
}

B.weightedSum = function() {
    return (1.0 - this.w) * this.x + this.w * this.y;
}

// instantiate B
var b = Object.create(B).init(1, 2, 0.3);

// equivalent of `instanceof`
var bInstanceOfA = A.isPrototypeOf(b);

What I like about this is that it exposes what's really going as there is clear separation between object creation (which applies to both instantiation and subclassing) and initialization (which only applies to instantiation). Also there is symmetry between creating a base class and a subclass. The code doesn't require externally defined functions or libraries, but isn't particularly verbose either.

My question, therefore, is the following: could those of you who have more experience with JavaScript tell me if there are problems with the approach that I am not considering, or if it is a good pattern?

ehremo
  • 387
  • 2
  • 8
  • 1
    [`Object.create()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Cross-browser_compatibility) works only in IE >= 9. – Eugene Naydenov Oct 22 '13 at 11:03
  • Excellent thanks... I believe it should be quite straightforward to add it though if one wants to? I am programming mostly in node so it's not a major concern for me. Any more issues? – ehremo Oct 22 '13 at 11:05
  • Anyway there is a [polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill) for `Object.create()`. But you can't use `new` keyword with this approach. This extends the object, not a prototype. – Eugene Naydenov Oct 22 '13 at 11:12
  • 3
    The biggest question is "why". Even Crockford has said that attempting to imitate classic inheritance is not a good idea and that he doesn't see the need for complex inheritance schemes in javascript. – RobG Oct 22 '13 at 11:33
  • yes I agree, but it's not easy clear your head of traditional OOP when coming from other languages... this is why i like this approach... there's no 'behind the scenes magic' going on like with `new` so you can start getting used to the new ideas instead of remaining anchored to the old – ehremo Oct 22 '13 at 12:15
  • Maybe this answer will help, it has some detail about constructor functions so not directly related to your way of inheritance: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Oct 22 '13 at 13:20

1 Answers1

0

You lose a new keyword with this approach. So you can't say new A(128, 256).

But you can use Object.create() for the prototype inheritance and create the regular objects with new keyword in this way:

var Employee = function(name) {
    this.name = name;
    return this;
};

Employee.prototype = {
    doWork: function() {
        console.log('%s is doing some abstract work', this.name);
    }
};

var Driver = function(name) {
    return Employee.call(this, name);
};

Driver.prototype = Object.create(Employee.prototype, {
    doWork: {
        value: function() {
            console.log('%s is driving a car', this.name);
        }
    },
    fixEngine: {
        value: function() {
            console.log('%s is fixing an engine', this.name);
        }
    }
});

var employee = new Employee('Jim');
var driver = new Driver('Bill');

employee.doWork(); // Jim is doing some abstract work 
driver.doWork(); // Bill is driving a car
driver.fixEngine(); // Bill is fixing an engine 

http://jsfiddle.net/f0t0n/HHqEQ/

Eugene Naydenov
  • 7,165
  • 2
  • 25
  • 43
  • Well that's my whole point, I don't want to be able to use the `new` keyword because it's feels like a language feature that was tagged on to make JavaScript look like it has classes. My pattern reminds you about that as far as JavaScript is concerned, instances are just initialized subclasses (or subclasses are uninitialized instances :D). The `new` keyword obscures this symmetry. – ehremo Oct 22 '13 at 12:11