JavaScript. Creating objects with private properties. How?


I delve into the OOP of JavaScript. I'm trying to figure out how to create objects with private methods and properties.

In JavaScript, you can create objects via new

function Constructor() {
    var name = 'foo';
    this.get_name = function() {
        return name;
    }
}    
var foo = new Constructor();

And you can do it without

function ConstructorWithoutNew() {
    var name = 'bar';
    var that = {};
    that.get_name = function() {
        return name;
    }
    return that;
}
var bar = ConstructorWithoutNew();

The first question is: is there a fundamental difference between these approaches? Under what conditions should I use one or the other, or perhaps there is a third, more universal/productive/correct one?

1 answers

The difference between these approaches is that in the first case, inheritance mechanisms in js are activated, and in the second case, duck typing mechanisms are used.

When using the second method, the instanceof operator will not work - but this way you can "inherit" from the function, which can not be done normally with prototypes.

But both methods are not ideal - each of them creates a separate method for each instance of the object. If you (mistakenly) try to apply such a method on a "foreign" instance of an object via call or apply strange things are possible that will not be immediately obvious.

Therefore, the most modern way to create private fields is through symbols. Briefly: calling Symbol("...") will give you some object that can be used as a field name, while calling Symbol again will give you a new object. That is, if you "hide" the symbol, then the field will be private.

Here is an example, here the symbol is "hidden" using the pattern "module":

var Constructor;
!function() {
    var sName = Symbol("name")
    Constructor = function Constructor() { }
    Constructor.prototype[sName] = "foo"
    Constructor.prototype.get_name = function() {
        return this[sName];
    }
}()
var foo = new Constructor();

The sName variable is not visible outside the module - and therefore there is no access to the private field name from the outside, although it is quite easy to access it inside the module.

Characters are supported at the time of writing this answer, unfortunately, only by Chrome and Ognelis from the main browsers. But for other browsers (IE), you can use polyfil:

!function() {
    if (typeof window.Symbol === "function") return;

    var counter = 0;
    window.Symbol = function (name) {
      return "__" + name + "@" + ++counter;
    }
}()

Such a character will not have most of the properties of "real" characters - but to create almost-private variables will do fine.

 5
Author: Pavel Mayorov, 2016-05-16 13:54:11