1

Coming from a C++ / Objective-C background, I'm trying to learn how to correctly and efficiently reproduce the patterns of inheritance and encapsulation in Javascript. I've done plenty of reading (Crockford etc.) and while there are plenty of examples of how to achieve one or the other, I'm struggling with how to combine them without introducing significant negatives.

At the moment, I have this code:

var BaseClass = (function() {

    function doThing() {
        console.log("[%s] Base-class's 'doThing'", this.name);
    }

    function reportThing() {
        console.log("[%s] Base-class's 'reportThing'", this.name);
    }

    return function(name) {
        var self = Object.create({});

        self.name = name;

        self.doThing = doThing;
        self.reportThing = reportThing;

        return self;
    }

}());

var SubClass = (function(base) {

    function extraThing() {
        console.log("[%s] Sub-class's 'extraThing'", this.name);
    }

    function doThing() {
        console.log("[%s] Sub-class's replacement 'doThing'", this.name);
    }    

    return function(name) {
        // Create an instance of the base object, passing our 'name' to it.
        var self = Object.create(base(name));    


        // We need to bind the new method to replace the old
        self.doThing = doThing;
        self.extraThing = extraThing;

        return self;
    }

}(BaseClass));

It mostly does what I want:

// Create an instance of the base class and call it's two methods
var base = BaseClass("Bert");

base.doThing();         // "[Bert] Base-class's 'doThing'"
base.reportThing();     // "[Bert] Base-class's 'reportThing'"

var other = BaseClass("Fred");


// Create an instance of the sub-class and call it's three methods (two from the base, one of it's own)
var sub = SubClass("Alfred");

sub.doThing();          // "[Alfred] Sub-class's replacement 'doThing'"
sub.extraThing();       // "[Alfred] Sub-class's 'extraThing'"
sub.reportThing();      // "[Alfred] Base-class's 'reportThing'"

But, there's (at least!) two issues:

  • I'm not convinced the prototype chain is intact. If I substitute a method in the prototype via one instance of a sub-class, other instances don't see it:
  • No encapsulation of .name property

I'm replacing the prototype's implementation of a function like this:

Object.getPrototypeOf(oneInstance).reportThing = function() { ... }
otherInstance.reportThing()    // Original version is still called

That's perhaps not a significant problem, but it is causing me to doubt my understanding.

Private variables is something I want to implement efficiently though. The module pattern of variable hiding doesn't help here, as it causes function definitions to exist per-object. I'm probably missing a way of combining patterns, so is there a way of achieving private variables without duplicating functions?

4
  • "I'm trying to learn how to correctly and efficiently reproduce the patterns of inheritance and encapsulation in Javascript" - don't assume that the patterns that you know from C++ will work effectively in JavaScript. Commented Feb 17, 2015 at 9:31
  • @joews I'm not assuming there'll be a 1:1 correspondence, but as it's definitely possible to incorporate the two patterns individually, I'm hopeful that there's a clean way of combining them. Commented Feb 17, 2015 at 9:38
  • To me this is one of the most annoying aspects of JavaScript, that so many people want to enforce their Java / C++ habits on it. Commented Feb 17, 2015 at 12:09
  • It's not that I want to preserve a habit as such. I'm quite happy for the code to be structured in whatever way is best, but I don't see the downside of any of the concepts I'm looking to emulate. Commented Feb 17, 2015 at 14:26

3 Answers 3

1

This is usually how I tackle inheritance and encapsulation in JavaScript. The defclass function is used to create a new class that doesn't inherit from any other class and the extend function is used to create a new class which extends another class:

var base = new BaseClass("Bert");

base.doThing();     // "Bert BaseClass doThing"
base.reportThing(); // "Bert BaseClass reportThing"

var sub = new SubClass("Alfred");

sub.doThing();     // "Alfred SubClass replacement doThing"
sub.extraThing();  // "Alfred SubClass extraThing"
sub.reportThing(); // "Alfred BaseClass reportThing"

var other = new SubClass("Fred");

SubClass.prototype.reportThing = function () {
    console.log(this.name + " SubClass replacement reportThing");
};

other.reportThing(); // Fred SubClass replacement reportThing
<script>
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

function extend(constructor, keys) {
    var prototype = Object.create(constructor.prototype);
    for (var key in keys) prototype[key] = keys[key];
    return defclass(prototype);
}

var BaseClass = defclass({
    constructor: function (name) {
        this.name = name;
    },
    doThing: function () {
        console.log(this.name + " BaseClass doThing");
    },
    reportThing: function () {
        console.log(this.name + " BaseClass reportThing");
    }
});

var SubClass = extend(BaseClass, {
    constructor: function (name) {
        BaseClass.call(this, name);
    },
    doThing: function () {
        console.log(this.name + " SubClass replacement doThing");
    },
    extraThing: function () {
        console.log(this.name + " SubClass extraThing");
    }
});
</script>

Read the following answer to understand how inheritance works in JavaScript:

What are the downsides of defining functions on prototype this way?

It explains the difference between prototypes and constructors. In addition, it also shows how prototypes and classes are isomorphic and how to create “classes” in JavaScript.

Hope that helps.

Sign up to request clarification or add additional context in comments.

3 Comments

Nicely contained class definitions - I like it. Very reminiscent of the Mootools approach.
Indeed. In addition, the defclass and extend functions are just three lines of code each.
An elegant solution but does this solve the data hiding issue though? I can still call other.name to return 'Fred', thus I do not require accessor methods. I still haven't found a solution to this problem. Perhaps ECMA6 solves this, but ECMA5 and below don't offer data hiding. This means that any variable that is defined on the base class can be accessed from any derived object... eeek!
1

The simple recipe follows:

function BaseClass(someParams)
{
   // Setup the public properties, e.g.
   this.name = someParams.name;
}

BaseClass.prototype.someMethod = function(){
   // Do something with the public properties
}

Now the inheritance occurs this way

function SubClass(someParams)
{ 
    // Reuse the base class constructor
    BaseClass.call(this, someParams);

    // Keep initializing stuff that wasn't initialized by the base class
    this.anotherProperty= someParams.anotherProperty;
}

// Copy the prototype from the BaseClass
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

// Start extending or overriding stuff
SubClass.prototype.someMethod = function(){

   // In case you still wanna have the side effects of the original method
   // This is opt-in code so it depends on your scenario.
   BaseClass.prototype.someMethod.apply(this, arguments);

   // Override the method here       
}

Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

P.S. Object.create may not be supported on all old browsers, but don't worry, there's a polyfill for that in this link. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

3 Comments

In Java when you override a function, you typically call the super function too. You can do the same with Javascript: gist.github.com/StephanBijzitter/b2495ebf9702b3866c91
That's opt-in, so, nothing keeps you from using "call" or "apply" on BaseClass.prototype.someMethod. I'll update the code to reflect this. Thanks...
Showing how to call methods on the super class is going to be useful, thanks.
0

If you want to preserve the prototype chain, you must override and use .prototype: Example: Main Class:

function BaseClass(){

}
BaseClass.prototype.doThing = function(){...}

SubClass:

function SubClass(){
}
SubClass.prototype= new BaseClass();
SubClass.prototype.extraThing = function(){};

Now, whenever you change extraThing or doThing it gets replaced everywhere. The name property is accessible as a public variable (it's not static).

If you want it static, you must put it in prototype.

If you want it private, you mast make it function local:

function BaseClass(nameParam){
 var name = nameParam;
}

To create an object simply call the function:

var testObj = new BaseClass("test");
testObj.doThing();

If you want to combine private variables with rewritable functions, you might find your answer here. But if you are able to rewrite the function that has access to the private variable, it's not really a private variable anymore.

2 Comments

I was under the impression that 'new' wasn't considered the correct approach these days and was expecting Object.create(base) to take care of the prototype chain. My understanding clearly needs work.
The problem isn't that you shouldn't use new, the problem is if you forget the "new" for creating a new instance

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.