Name objects - detailed proposal
This proposal introduces a new kind of object called a name object, which can be used to add truly private properties to any object. The semantics of property lookup is generalized such that whenever [[ToString]] was used for property names in ES5, this is replaced with a new [[ToPropertyName]] operation.
API
The @name
module provides the following API:
[new] Name(str = <unspecified new string>, priv = false)
Produces a new name object. The str
argument is used as the result of toString
. The priv
argument indicates whether the name is private.
isName(x)
Determines whether a given value is a name object.
Semantics
This proposal replaces the use of [[ToString]] for performing property access with a new [[ToPropertyName]] operation, which behaves exactly the same as [[ToString]] except with name objects, which are returned as is. Object property tables can be keyed by both strings and name objects.
Unlike string property names, when defining a new property with a name object, the enumerability attribute of the new property defaults to false.
Private names
A private name object is a name object that was created with the priv
flag set to true
.
Every private name object has a public
property that is a reference to a deeply frozen object that contains no references to the name object, and whose toString
method produces the same string as the private name object. The public
object is not itself a name object. (In reflection API's, this prevents an ambiguity between the private name and some other property value.)
Private names are not reflected by any of the core semantic algorithms except for proxy traps. In particular, private names are not reflected by:
Object.getOwnPropertyNames
for
...in
When proxy traps are invoked for a private name, they receive the name’s public
property instead of the name itself. This prevents unintended leakage of the private name, but still identifies the name to code that already has access to it. For example:
var key = new Name("key", true);
...
var proxy = new Proxy(target, {
...
get: function(receiver, name) {
if (name === key.public)
...
else
...
},
...
});