read only names

# raul mihaila (12 years ago)

Was there any proposal for object properties that can be read from anywhere but are writable only from the execution context where the name associated with the property was defined? Something like:

function Obj() {
    ro x; // read only name x
    this.x = 2;
    this.setX = function (val) {
        this.x = val;
    };
}

var o = new Obj;

o.x; // 2
o.x = 43; // throws some error
o.x; // 2
o.setX(100);
o.x; // 100

The advantages would be that you are sure that there are no side effects when the property is accessed and also I'm guessing you have no function call overhead.

# Jeremy Martin (12 years ago)

This can more or less achieved with getters/setters:

function Obj() {
    var x = 'foo';
    Object.defineProperty(this, 'x', {
        get: function() {
            return x;
        },
        set: function() {
            throw new Error('Cannot set property "x"!');
        }
    });
}

var obj = new Obj();
console.log(obj.x); // 'foo'
obj.x = 'bar'; // Error!

I think ES6 classes will provide some nicer facilities, but I'm not current on how a public getter / private setter pattern would be achieved outside of Object.defineProperty() at this point.

# Claude Pache (12 years ago)

If you are not paranoid, you can just use nonwritable properties. That forces you to use Object.defineProperty to change the value, so you are protected against accidental assignments.

function Obj() {
    Object.defineProperty(this, 'x', { value: 2, writable: false, enumerable: true, configurable: true })
    this.setX = function (val) {
        Object.defineProperty(this, 'x', { value: val })
    }
}

o = new Obj
o.x = 17 // assignment will fail; moreover, in strict mode, an error is thrown
o.x // 2
o.setX(18)
o.x // 18

It doesn't prevent the user from hanging themself with Object.defineProperty(o, 'x', { value: 17 }); but at least, it won't be by chance!

# raul mihaila (12 years ago)

@Jeremy: something similar could be achieved with a private name (I want object properties) and accessor properties, but it lacks the advantages that I mentioned (also outside classes and object literals, the accessor properties are ugly). @Claude: that's not easy to use, and also it can be done outside the execution context.

# Andrea Giammarchi (12 years ago)

the closer/best option would be probably this one:

function Obj() {
  var xValue = 2;
  Object.defineProperties(this, {
    x: {
      enumerable: true,
      configurable: false,
      get: function x() {
        return xValue;
      },
      set: function x(value) {
        throw 'not allowed';
      }
    },
    setX: {
      enumerable: true,
      configurable: false,
      writable: false,
      value: function setX(value) {
        xValue = value;
      }
    }
  });
}

However, this patter stinks for many reasons:

  1. bad performance per each object initialization, avoid if you need to create many Obj instances
  2. it's unclear why the setX() is better than just using the setter ... you have a public directly accessible property but you want a method to set its value ... you are not making its value settable only privatly, you are exposing the possibility so you gain nothing in terms of "security"

Best

# Andrea Giammarchi (12 years ago)

P.S. I swear I thought this was JSMentors ML ... sorry for that

# raul mihaila (12 years ago)

I used setX only to show that the value can be set with a method. The scope for ro names could be the same as for private names. Btw, are private names still in? I couldn't find them in the html version of the latest draft.

# raul mihaila (12 years ago)

Correction: The scope for ro names could be the same as for private names, except that they can be read from outside.

# Andrea Giammarchi (12 years ago)

then with current status you need to access the caller against known methods ... as example:


var Obj = (function(){
  var
    ObjProto = Obj.prototype,
    methods
  ;
  function Obj() {
    this.x = 2;
  }
  Object.defineProperties(ObjProto, {
    x: {
      enumerable: true,
      configurable: false,
      get: function x() {
        return this._x;
      },
      set: function x(value) {
        if (methods.indexOf(x.caller) < 0) throw 'nope';
        this._x = value;
      }
    }
  });
  methods = [Obj].concat(
    Object.getOwnPropertyNames(ObjProto)
    .filter(function(p){
      return typeof ObjProto[p] === 'function';
    })
    .map(function(p){
      return ObjProto[p];
    })
  );
  return Obj;
}());

var o = new Obj;
o.x; // 2
try {
  o.x = 3;
} catch(o_O) {
  o_O == 'nope'; // true
}

watch out caller access will throw if you are putting that code under 'use strict' directive.

Just hinting solutions, nothing about specs that I recognize ... Symbols are private though but not sure the latest state regarding these.

Best

# Brendan Eich (12 years ago)

raul mihaila wrote:

Btw, are private names still in? I couldn't find them in the html version of the latest draft.

You can tell that private names are not in by noticing the strawman: namespace in the wiki, not the harmony: namespace.

strawman:private_names

I don't think it's wise to build on them yet. The objections are recorded in es-discuss and meeting notes:

esdiscuss.org/topic/private-names-in-text-javascript

etc.

# raul mihaila (12 years ago)

thanks