Protected properties using Name objects

Brandon Benvie brandon at brandonbenvie.com
Fri Jun 15 12:27:20 PDT 2012


Currently there's no way to have a data property that is writable by some
but not all. This can't even really be achieved with accessors or proxies
directly. Rather, the underlying data is mutated through some other avenue,
and the value returned on access comes from this secondary source. Neither
method of course is remotely as efficient as a real data property as either.

Using Name objects, it seems like a full fledged elegant "protected"
property access API is possible to describe. For this context, I define a
protected property as a property on an object who's description or value
can only be modified after passing some access check. There's two levels of
access covered by what I propose: the base level ability to write a
property's data (`writable` descriptor property) and the ability to
configure a property (`configurability` descriptor property).

For writing to an object, this concept allows for a property to be {
writable: false } but be written through a Name object corresponding to the
property.

For modifying the property's descriptor, a fourth boolean descriptor
attribute is now defined named 'protected'. When a property is protected,
the Name object associated with it is required in order to modify it using
Object.defineProperty. A property which is protected acts the same as a
non-configurable property when modification is attempted using the
property's string name. A property can become unprotected later, unlike a
non-configurable property, and can be modified internally to engine as
well. Protected only acts like non-configurable for unauthorized access
from code.


Code (also available at https://gist.github.com/2938186)


let x = { a: 10 };

// #############################
// ### Protecting a property ###
// #############################

let writeA = Object.protectProperty(x, 'a');
// -- or --
let writeA = new Name;
Object.protectProperty(x, 'a', writeA);


// #######################################
// ### Describing a protected property ###
// #######################################

Object.isProtected(x, 'a');
//  true

Object.getOwnPropertyDescriptor(x, 'a')
//  { writable: false,     default protectProperty to set non-writable?
//    protected: true,
//    enumerable: true,
//    configurable: true }


// #####################################
// ### Writing to protected property ###
// #####################################

x.a = 50
// silent fail normally, throws in strict "Cannot assign to protected
read-only property 'x'"

x[writeA] = 50;
// x is  { a: 50 }


// ##################################################
// ### Modifying a protected property description ###
// ##################################################

Object.defineProperty(x, 'a', { writable: true });
// throw "Cannot modify protected property 'a'"

Object.defineProperty(x, writeA, { writable: true });
//  { writable: true,
//    protected: true,
//    enumerable: true,
//    configurable: true }


// ###############################
// ### Unprotecting a property ###
// ###############################

Object.defineProperty(x, writeA, { protected: false });
// -- or --
Object.protectProperty(x, writeA);


// #########################################
// ### Changing a protected property key ###
// #########################################

Object.defineProperty(x, writeA, { protected: false });
let newWriteA = Object.protectProperty(x, 'a');
// -- or --
let newWriteA = new Name();
Object.protectProperty(x, writeA, newWriteA);

x[writeA] = 100;
// throw 'Invalid protected property assignment'



// from
http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes

const pHealth = new Name();
const wAlive = new Name();

class Monster {
  constructor(name, health) {
    this.name = name;
    this.alive = true;
    this[pHealth] = health;
    Object.protectProperty(this, 'alive', wAlive);
  }

  attack(target) {
    log('The monster attacks ' + target);
  }

  defend(roll) {
    let damage = Math.random() * roll;
    this[pHealth] -= damage;
    if (this[pHealth] <= 0) {
      this[wAlive] = false;
    }
  }

  set health(value) {
    if (value < 0) {
      throw new Error('Health must be non-negative.');
    }
    this[pHealth] = value;
  }
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120615/3f0b8719/attachment.html>


More information about the es-discuss mailing list