wootscootinboogie
wootscootinboogie

Reputation: 8695

Should properties ever be on the prototype?

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?

Upvotes: 0

Views: 89

Answers (2)

Bergi
Bergi

Reputation: 664969

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.

Upvotes: 1

Optimizer
Optimizer

Reputation: 263

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

Upvotes: 1

Related Questions