Proposal: Storage for getters and setters

# Xavier MONTILLET (13 years ago)

I've been playing with getters and setters for a little while and there's one thing really bothering me: You can't store the value in the object in a hidden way. Here is an example:

jsfiddle.net/xavierm02/UzNK3/13

function Point( x, y ) { this.x = x; this.y = y; } Object.defineProperties( Point.prototype, { x: { get: function ( ) { // this would return the stored value }, set: function ( value ) { value = parseInt( value ); // and here, you would want to set "this.x" to value but you can't since this would create an infinite loop } }, y: { // same here } } );

You could do it by:

  • using Object.defineProperties in the constructor and therefore but then, you would have several times the same function created so it kinda sucks.

jsfiddle.net/xavierm02/UzNK3/14

function Point( x, y ) { Object.defineProperties( Point.prototype, { x: { get: function ( ) { return x; }, set: function ( value ) { x = parseInt( value, 10 ); } }, y: { // same here } } ); }

  • have some kind of array where you store values and another array where you store the this and let those arrays be accessible only from the methods needing it. Then you would get the index of the instance by doing indexOf( this ) on the array of instances and then you would search for the value at this index. Sucks too.

jsfiddle.net/xavierm02/UzNK3/15

( function ( ) { var instances = [ ], xs = [ ], ys = [ ]; function Point( x, y ) { instances.push( this ); xs.push( x ); ys.push( y ); } Object.defineProperties( Point.prototype, { x: { get: function ( ) { return xs[ instances.indexOf( this ) ]; }, set: function ( value ) { xs[ instances.indexOf( this ) ] = parseInt( value ); } }, y: { // same here } } ); this.Point = Point; } )( );

I don't know know what you think of this but these methods really aren't convenient from my point of view...

There are potential new features that could allow you to do this too:

  • private properties but then that would mean giving access to private properties to the methods in the prototype, which would mean that adding a method in the prototype would allow to change the values, which isn't what you want... Even though you could freeze the prototype to avoid it...
  • I remember thinking of another one but I can't remember which one atm...

So here'swhat I propose:

  • the getter gets a parameter which is the stored value
  • the setter's return value is set as value for the "private" property

Here's an implementation of what I would like having (It sucks sinces it prevents garbage collecting and could easilly be optimized but still works):

jsfiddle.net/xavierm02/UzNK3/17

Object.defineProperties = ( function ( ) { var defineProperties = Object.defineProperties; return function ( object, propertyDescriptors ) { for ( var name in propertyDescriptors ) { if ( propertyDescriptors.hasOwnProperty( name ) ) { ( function ( propertyDescriptor ) { var get; var set; if ( propertyDescriptor.hasOwnProperty( 'get' ) ) { get = propertyDescriptor.get; propertyDescriptor.get = function ( ) { return get.call( this, propertyDescriptor.value ); }; } if ( propertyDescriptor.hasOwnProperty( 'set' ) ) { set = propertyDescriptor.set; propertyDescriptor.set = function ( value ) { return propertyDescriptor.value = set.call( this, value ); }; } } )( propertyDescriptors[ name ] ); } } defineProperties( object, propertyDescriptors ); }; } )( );

And you would use it that way:

jsfiddle.net/xavierm02/UzNK3/17

function Point( x, y ) { this.x = x; this.y = y; } Object.defineProperties( Point.prototype, { x: { get: function ( value ) { return value; }, set: function ( value ) { value = parseInt( value ); return value; } }, y: { // same here } } );

Of course, if it were implemented, it would probably allow setting a defaut setter that woudl simply assign and a a default getter that would simply return the value by putting true instead of the function. Or somthing similar.

Of course, for this example, implementing type checking would make it useless. But that really was just a example. Something where it would have a real use is, for example:

jsfiddle.net/xavierm02/UzNK3/20

// isn't suposed to work // here just for wyntax coloration function WYSIWYGEditor( ) { Object.defineProperties( this, { iframe: { value: document.createElement( 'iframe' ), configurable: false, enumerable: true, writable: false }, textarea: { value: document.createElement( 'textarea' ), configurable: false, enumerable: true, writable: false }, container: { value: document.createElement( 'div' ), configurable: false, enumerable: true, writable: false } } ); Object.defineProperty( this, 'wysiwygField', { value: this.iframe./* get the container */, configurable: false, enumerable: true, writable: false } ) this.container.appendChild( iframe ); } Object.defineProperties( WYSIWYGEditor.prototype, { wysiwyg: { value: true, configurable: false, enumerable: true, get: function ( value ) {// or simply true or somethign similar is shorcuts are implemented return value }, set: function ( value ) { value = !!value; if ( value ) { this.wysiwygField.innerHTML = this.textarea.value; this.container.removeChild( this.textarea ); this.container.appendChild( this.iframe ); } else { this.textarea.value = this.wysiwygField.innerHTML; this.container.removeChild( this.iframe ); this.container.appendChild( this.textarea ); } return value; } } } );

What do you think?

# Xavier MONTILLET (13 years ago)

My implementation sucks and doesn't keep per-instance values. Posting revision soon. jsfiddle.net/xavierm02/UzNK3/21

# Dean Landolt (13 years ago)

On Fri, Sep 30, 2011 at 9:50 AM, Xavier MONTILLET <xavierm02.net at gmail.com>wrote:

Hi,

I've been playing with getters and setters for a little while and there's one thing really bothering me: You can't store the value in the object in a hidden way. Here is an example:

jsfiddle.net/xavierm02/UzNK3/13

function Point( x, y ) { this.x = x; this.y = y; } Object.defineProperties( Point.prototype, { x: { get: function ( ) { // this would return the stored value }, set: function ( value ) { value = parseInt( value ); // and here, you would want to set "this.x" to value but you can't since this would create an infinite loop } }, y: { // same here } } );

You could do it by:

  • using Object.defineProperties in the constructor and therefore but then, you would have several times the same function created so it kinda sucks.

jsfiddle.net/xavierm02/UzNK3/14

function Point( x, y ) { Object.defineProperties( Point.prototype, { x: { get: function ( ) { return x; }, set: function ( value ) { x = parseInt( value, 10 ); } }, y: { // same here } } ); }

  • have some kind of array where you store values and another array where you store the this and let those arrays be accessible only from the methods needing it. Then you would get the index of the instance by doing indexOf( this ) on the array of instances and then you would search for the value at this index. Sucks too.

jsfiddle.net/xavierm02/UzNK3/15

( function ( ) { var instances = [ ], xs = [ ], ys = [ ]; function Point( x, y ) { instances.push( this ); xs.push( x ); ys.push( y ); } Object.defineProperties( Point.prototype, { x: { get: function ( ) { return xs[ instances.indexOf( this ) ]; }, set: function ( value ) { xs[ instances.indexOf( this ) ] = parseInt( value ); } }, y: { // same here } } ); this.Point = Point; } )( );

I don't know know what you think of this but these methods really aren't convenient from my point of view...

There are potential new features that could allow you to do this too:

  • private properties but then that would mean giving access to private properties to the methods in the prototype, which would mean that adding a method in the prototype would allow to change the values, which isn't what you want... Even though you could freeze the prototype to avoid it...
  • I remember thinking of another one but I can't remember which one atm...

So here'swhat I propose:

  • the getter gets a parameter which is the stored value
  • the setter's return value is set as value for the "private" property

Here's an implementation of what I would like having (It sucks sinces it prevents garbage collecting and could easilly be optimized but still works):

jsfiddle.net/xavierm02/UzNK3/17

Object.defineProperties = ( function ( ) { var defineProperties = Object.defineProperties; return function ( object, propertyDescriptors ) { for ( var name in propertyDescriptors ) { if ( propertyDescriptors.hasOwnProperty( name ) ) { ( function ( propertyDescriptor ) { var get; var set; if ( propertyDescriptor.hasOwnProperty( 'get' ) ) { get = propertyDescriptor.get; propertyDescriptor.get = function ( ) { return get.call( this, propertyDescriptor.value ); }; } if ( propertyDescriptor.hasOwnProperty( 'set' ) ) { set = propertyDescriptor.set; propertyDescriptor.set = function ( value ) { return propertyDescriptor.value = set.call( this, value ); }; } } )( propertyDescriptors[ name ] ); } } defineProperties( object, propertyDescriptors ); }; } )( );

And you would use it that way:

jsfiddle.net/xavierm02/UzNK3/17

function Point( x, y ) { this.x = x; this.y = y; } Object.defineProperties( Point.prototype, { x: { get: function ( value ) { return value; }, set: function ( value ) { value = parseInt( value ); return value; } }, y: { // same here } } );

Of course, if it were implemented, it would probably allow setting a defaut setter that woudl simply assign and a a default getter that would simply return the value by putting true instead of the function. Or somthing similar.

Of course, for this example, implementing type checking would make it useless. But that really was just a example. Something where it would have a real use is, for example:

jsfiddle.net/xavierm02/UzNK3/20

// isn't suposed to work // here just for wyntax coloration function WYSIWYGEditor( ) { Object.defineProperties( this, { iframe: { value: document.createElement( 'iframe' ), configurable: false, enumerable: true, writable: false }, textarea: { value: document.createElement( 'textarea' ), configurable: false, enumerable: true, writable: false }, container: { value: document.createElement( 'div' ), configurable: false, enumerable: true, writable: false } } ); Object.defineProperty( this, 'wysiwygField', { value: this.iframe./* get the container */, configurable: false, enumerable: true, writable: false } ) this.container.appendChild( iframe ); } Object.defineProperties( WYSIWYGEditor.prototype, { wysiwyg: { value: true, configurable: false, enumerable: true, get: function ( value ) {// or simply true or somethign similar is shorcuts are implemented return value }, set: function ( value ) { value = !!value; if ( value ) { this.wysiwygField.innerHTML = this.textarea.value; this.container.removeChild( this.textarea ); this.container.appendChild( this.iframe ); } else { this.textarea.value = this.wysiwygField.innerHTML; this.container.removeChild( this.iframe ); this.container.appendChild( this.textarea ); } return value; } } } );

What do you think?

See: harmony:private_name_objects

# Xavier MONTILLET (13 years ago)

module name from "@name"; let key = name.create(); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function() { ... this[key] ... } };

All it does is make the key a special string... If you get the same string, you can still access the property... Plus, either you make it non-enumerable so that noone can find the key which is annoying, or you can find the key with a simple for in loop...

I'm not sure I understand this proposal...

# Dean Landolt (13 years ago)

On Fri, Sep 30, 2011 at 10:14 AM, Xavier MONTILLET <xavierm02.net at gmail.com>wrote:

module name from "@name"; let key = name.create(); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function() { ... this[key] ... } };

All it does is make the key a special string...

No, the key is a special object...

If you get the same string, you can still access the property...

Because it's an object this is impossible, and it's guaranteed to be unforgeable.

Plus, either you make it non-enumerable so that noone can find the key which is annoying, or you can find the key with a simple for in loop...

There's a note about a possible "visibility flag" extension in there, but as it stands, no, it's not enumerable. The creator of the private name object is the one that doles out the capability to get to the value.

I'm not sure I understand this proposal...

Maybe it could use more example use cases. What you're proposing above could also be done by keeping the private value in a closure, but if you need to hang it off the object this gives you a way to do that safely.

# Xavier MONTILLET (13 years ago)

Maybe it could use more example use cases. What you're proposing above could also be done by keeping the private value in a closure, but if you > need to hang it off the object this gives you a way to do that safely.

Well yes a closure works if you want a value for an object... but as soon as you start having several instances, you need to either not use the prototype or do some some arrays and store values in them... and it kind of sucks...

I still don't understand how the thing in the link you psoted works... var s = {}; undefined var o = {}; undefined var o2 = {}; undefined s[ o ] = 1; 1 s[ o2 ]; 1

# Dean Landolt (13 years ago)

On Fri, Sep 30, 2011 at 12:26 PM, Xavier MONTILLET <xavierm02.net at gmail.com>wrote:

Maybe it could use more example use cases. What you're proposing above could also be done by keeping the private value in a closure, but if you > need to hang it off the object this gives you a way to do that safely. Well yes a closure works if you want a value for an object... but as soon as you start having several instances, you need to either not use the prototype or do some some arrays and store values in them... and it kind of sucks...

Sure. That's when it helps to be able to hang any old key off the proto in a way that's guaranteed not to collide. This is what private names give you. You only need to generate one name object per class, and you can keep this name object anywhere you'd like, so long as your getter and setter have access. They interact with this by way of the name object, and your private state is stored on the instance, completely silently (unless some kind of "visibility flag" came to pass -- then you could choose to expose it to all but still gain the benefits of name collision avoidance).

I still don't understand how the thing in the link you psoted works... var s = {}; undefined var o = {}; undefined var o2 = {}; undefined s[ o ] = 1; 1 s[ o2 ]; 1

It certainly won't work in an es-current shell. What we've got here is a coercion fail -- you're using the string key '[object Object]' in s. You'd need to use an actual private name object -- and AFAIK no such implementation exists.

Does anyone know of one in the works? I suspect it's blocked by a harmony-modules implementation. And this is a real shame -- like proxies and weak maps this shouldn't need to wait on modules -- otherwise it's just new semantics, not syntax. Anyone have ideas on how to get around this? Would it be so bad to piss a little more in the Object.* namespace pool? It's already pretty yellow, and the chances of an Object.PrivateName collision are remote.

# Xavier MONTILLET (13 years ago)

---------- Forwarded message ---------- From: Xavier MONTILLET <xavierm02.net at gmail.com>

Date: Fri, Sep 30, 2011 at 7:20 PM Subject: Re: Proposal: Storage for getters and setters To: Dean Landolt <dean at deanlandolt.com>

Alright... So it'll store it with the object as key... It's really a good idea since you could create a polyfill that just generates an object with a toString returning some random string and therefore making collision nearly impossible. I just tryed implementing it with proxies but when the traps are called, the object has already turned into a string :/