Failed to create Object.defineProperty object set in loop

I need to install some dynamic setters and would like to use defineProperty.

However, I cannot understand why this is not working. Please note that someone writes to this other question that they worked for him, but their examples do not work for me either ...

var a = { q:1, b:2, c:3, d:4, e:5, f:9 } var iFaces = [ {}, {}, {}, {} ] for( key in a ) { console.log( key ); for( var l = iFaces.length - 1; l >= 0; --l ) { (function( that, name ) { Object.defineProperty ( that , name , { enumerable : true , configurable: true , set : function( value ){ that[ name ] = value } } ) } )( iFaces[ l ], key ) iFaces[ l ][ key ] = a[ key ] } } // output: RangeError: Maximum call stack size exceeded 

It must be somehow recursive.

edit . The function that I am trying to implement is 2 objects that store properties there in synchronization. So I wanted to create a universal setter that updates the sister object if it does not have the same reference / value.

I need the solution to be standard for ES5, and it seems that most of the alternative solutions / getters are implementation dependent hacks.

+3
javascript
Aug 04 '13 at 12:03
source share
3 answers

Inside the set function, you do:

 that[ name ] = value 

which will run the set function again since you bound it to name . You must use a different property name inside the set function. For example:

 that[ '__' + name ] = value 
+4
Aug 04 '13 at 12:10
source share

Your set: function calls recursion because it calls a record of the same property as the set: function, i.e.

 that[name] = value 

calls the set function, which then executes:

 that[name] = value 

will call the set function ...

+1
Aug 04 '13 at 12:10
source share

My understanding of defineProperty was wrong. I thought this would allow you to set the getter / setter to an existing property. Then common sense will adhere to the fact that the setter either simply returns a new value, or if the user needs to set the actual property inside the setter, at least assigning this property will not lead to a restart of the setter.

However, this is not the functionality that was created using defineProperty. Rather, it is designed to create interfaces. You create an accessor property, which in itself does not store any data, but which can display some other data from the data structure, possibly hidden from the client through the interface. This is a powerful feature, albeit a bit unpleasant, if you just decided to quickly add a check on one of your properties.

MDN is not very clear on this, and therefore it can be confusing, especially since the “other” use of defineProperty is to set attributes on a property that matters. A getter / setter are not attributes of this property, but are separate from it. Note that your setter will not receive the name of the property being accessed. Therefore, if you want to create a common code, you will need to wrap it with an anonymous function in order to pass the name. If you are doing generic code, you probably assign properties in a loop anyway, so you will have to use anonymous to deal with the “closing effect” anyway.

What I wanted to use for this is to make a public interface for a private object. The problem is that if the client assigns something to a property on the interface, it will disconnect it from the private partner, and closed and open code will no longer act on the same value. It turns out that in this case, the defineProperty method works because I already have a private (reference) object, and I want to create accessors on another object that allows me to access this data without having access to the private object.

Code example: If you need a common set / receiver (for example, assigned in a loop and actual code existing elsewhere, so that each property on all your objects does not need a full copy of the function), you will need:

  • Create secondary storage for the actual property value.

  • wrap your call to defineProperty with an anonymous function to work with closure and create a built-in getter / setter that will call your actual getter / setter with the property name.

This will work as expected:

 var properties = { q:1, b:2, c:3 } var backends = [ {}, {} ] var iFaces = [ {}, {} ] for( var key in properties ) { for( var l = iFaces.length - 1; l >= 0; --l ) { ;(function( iFace, backend, name ) { Object.defineProperty ( iFace , name , { enumerable : true , configurable: false , set: function setter( value ){ _setter( backend, name, value ) } , get: function getter( ){ return _getter( backend, name ) } } ) })( iFaces[ l ], backends[ l ], key ) iFaces[ l ][ key ] = properties[ key ] } } function _setter( backend, name, value ) { // do some validation //... backend[ name ] = value } function _getter( backend, name ) { // keep access logs for example //... return backend[ name ] } iFaces[ 1 ].b = "Bananas, it works!" console.log( backends ); console.log( iFaces[ 1 ].b ); // ouput: // [ { q: 1, b: 2, c: 3 }, // { q: 1, b: 'Bananas, it works!', c: 3 } ] // Bananas, it works! 
0
Aug 04
source share



All Articles