0

Below I've got a simple little inheritance chain in JavaScript:

var Base  =(function(){
          function Base(config){
            this.name = function(){
                return config.name;
            }
            this.department = function(){
                return config.department;
            }
        }
        Base.prototype.calculate = function(){
            return this.name().length + this.department().length;
        }
        return Base;
    })();
    var base = new Base({
        name: 'me',
        department: 'this one'
    });
    console.log(base.calculate());
    //inheritance
    var Concrete = (function(){
        Concrete.prototype = Base.prototype;
        function Concrete(config){
            Base.apply(this,arguments);
            this.number = function(){
                return config.number;
            }
        }
        return Concrete;
    })();
    var concrete = new Concrete({
        name: 'concrete',
        department: 'the best',
        number: 3
    });
    console.log(concrete.number()); //3
    console.log(concrete.name()); //concrete

and it works as expected. I'm curious, though about how correct it would be to place method properties on the prototype of an object. Canonically I know that instance data should go with the instance of the class and the methods the object actually uses should be on the prototype, but what about the situation where the properties themselves are methods (like name and department in Base in this example)? In the interest of keeping things immutable I'd rather not let users change the properties of one of these objects after they've been initialized. In this case does it make any sense to use the prototype instead of the constructor to add functionality to an object?

Or it is only correct to place properties in the constructor so you have access to them when you do something like Base.prototype.whatever?

wootscootinboogie
  • 8,461
  • 33
  • 112
  • 197
  • 2
    Why do you bother making these objects immutable? Are we talking about client-side JavaScript? Then a user can change whatever he wants. This "private attributes" pattern is highly overrated. There are only two things that you should take into consideration when writing a client-side code: efficiency and the cost of maintenance. From that point of view I think that putting initial values on the instance and methods on the prototype is better. Probably more efficient (depends on the amount of objects) and from my experience easier to maintain. – freakish Sep 08 '14 at 13:20
  • 1
    @freakish: Immutability is not about security. Using accessor methods (whether private or not, the efficiency is not that different) often has better maintainability. Using private attributes can help in projects with many coders, or when writing libraries; regardless whether client- or serverside. – Bergi Sep 08 '14 at 13:34

2 Answers2

1

what about the situation where the properties themselves are methods? In the interest of keeping things immutable I'd rather not let users change the properties of one of these objects after they've been initialized.

I wouldn't call these "properties" any more, but "accessor" or "getter" methods.

In this case does it make any sense to use the prototype instead of the constructor to add functionality to an object?

Yes, if you mean functionality like calculate. If you mean getters like name, department or number you need to place them in the constructor as they need privileged access to the config.

Canonically I know that instance data should go with the instance of the class and the methods the object actually uses should be on the prototype

It's a bit different actually:

  • Everything instance-specific (data, accessor methods with distinct scopes) needs to go on the instance and is usually set up in the constructor
  • Everything that is shared amongst all instances (typically methods, but also data in some rare cases) should go on the prototype.
Concrete.prototype = Base.prototype;

No! Use Concrete.prototype = Object.create(Base.prototype); instead.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I was thinking about this last night and almost posted a question on it. Why prefer Object.create(Base.prototype) over Concrete.prototype = Base.prototype? – wootscootinboogie Sep 08 '14 at 13:51
  • 1
    See [Javascript inheritance: calling Object.create when setting a prototype](http://stackoverflow.com/q/15045080/1048572) and the questions in "linked from" [here](http://stackoverflow.com/q/12500637/1048572) – Bergi Sep 08 '14 at 15:00
1

The heading of your question is a bit misleading.

A direct answer to your heading - Yes, prototype properties are encouraged. They might not make a lot of difference wrt writing the code and the usage of the code, but internally, how JS manages things, like memory, accessing the variables inside closures etc it much better when done via prototype properties.


Now your actual question, is hiding immutable configurations using closures and private scoped variables the only way to do things ?

Right now - I think yes, as ES6 is still not supported completely by every browser yet.

But soon, the support will spread out in all release version of browsers and then you can use this nifty ES6 data type WeakMap . Just like a Map but here the keys are objects. So your Base definition can be written like:

var Base = (function() {
  var properties = new WeakMap(); // You have to keep variable private

  function Base(config) {
    properties.set(this, config);
  }

  Base.prototype = {
    name: function() {
      return properties.get(this).name;
    },

    department: function() {
      return properties.get(this).department;
    },

    calculate: function() {
      return this.name().length + this.department().length;
    }
  };

  return Base;
})();

Yes, you still need to keep the variable properties out of reach, so the outer closure. But the number of closures have decreased by 1 in this case.

Similar approach can be for ES6 Symbol but the outer closure will still be required.

What we truly need here are classes which will truly solve the issue, as explained in this very well written blog

Optimizer
  • 263
  • 2
  • 8