8

I've been thinking quite a bit about how to do OOP in JS, especially when it comes to encapsulation and inheritance, recently.

According to Crockford, classical is harmful because of new(), and both prototypal and classical are limited because their use of constructor.prototype means you can't use closures for encapsulation.

Recently, I've considered the following couple of points about encapsulation:

  1. Encapsulation kills performance. It makes you add functions to EACH member object rather than to the prototype, because each object's methods have different closures (each object has different private members).

  2. Encapsulation forces the ugly "var that = this" workaround, to get private helper functions to have access to the instance they're attached to. Either that or make sure you call them with privateFunction.apply(this) everytime.

Are there workarounds for either of two issues I mentioned? if not, do you still consider encapsulation to be worth it?

Sidenote: The functional pattern Crockford describes doesn't even let you add public methods that only touch public members, since it completely forgoes the use of new() and constructor.prototype. Wouldn't a hybrid approach where you use classical inheritance and new(), but also call Super.apply(this, arguments) to initialize private members and privileged methods, be superior?

3
  • I think there are some JavaScript libraries that provide all this, e.g. Dojo. There's also TypeScript.
    – MarkJ
    Nov 1, 2012 at 14:09
  • @MarkJ Dojo's class-system seems identical to the gazillion pseudo-classical inheritance libraries, and doesn't offer anything with regard to encapsulation.
    – Jonathan
    Nov 1, 2012 at 14:48
  • Btw, that article is downplaying the memory usage. It doesn't consider at all that the memory usage increases with the amount of methods.. e.g. if he had 20 methods on the "class", the functional pattern would take 3000 mb with his numbers but the prototypal would still take 150mb regardless of the amount of methods. It's like comparing O(1) to O(n) with n = 2...
    – Esailija
    Nov 2, 2012 at 19:21

1 Answer 1

2

Encapsulation is really tricky, and probably not worth the effort in JavaScript. A method that works, and does everything you would wish (private fields, access to the superclass and private methods) would be this (probably very ugly and improvable) solution:

function SomeClass() {

    var parent = SomeSuperClass(),
        somePrivateVar;

    return Object.create(parent, {
        somePrivateVar: {
            get: function() {
                return somePrivateVar;
            },
            set: function(value) {
                somePrivateVar = value;
            }
        },
        doSomething: {
            value: function(data) {
                someHelperFunction.call(this, data);
            }
        }
    });
}

function someHelperFunction(data) {
    this.someMethod(data);
}

This sort of works, allows you to have private fields and methods and inheritance as one would expect it. However, I don't like it - but I like it the best of all possibilities you have in pure JavaScript.

If I had to start a new project with a lot of JavaScript, I probably would have a closer look at TypeScript. It improves JavaScript where necessary (IMHO), gives you static typing, inheritance, encapsulation and everything.. But is just JavaScript, after all.


Now, is it worth it? I think there are two important things to consider. Encapsulation probably helps you at development time, but certainly doesn't at runtime. I think you have to decide; Either no encapsulation and better performance, or encapsulation, not-so-beautiful code, probably a better design but some overhead at runtime.

2
  • I'm aware of this pattern: it's what Crockford calls functional, with the slight difference that you set the parent as the child's prototype. The question is precisely: is the encapsulation worth the overhead mentioned in my OP (and present in your code)? Is there any way of getting around said overhead? TypeScript does seem interesting because the encapsulation checking is done at compile-time, which means there is no overhead in the compiled JS, but for the time being, I'm sticking with raw JS. BTW, for consistency, your code should probably read var parent = SomeSuperClass(), without new.
    – Jonathan
    Nov 1, 2012 at 14:57
  • @Jonathan: I added some more information on the "worth" question. Nov 2, 2012 at 8:41

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.