最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Having trouble making Object.defineProperty set work in a loop - Stack Overflow

programmeradmin2浏览0评论

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

However I can't figure out why this is not working. Note that someone writes in this other question that they got it working, but their examples don't 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 recursing somehow.

edit: The feature that I try to implement is 2 objects that keep there properties in sync. So I wanted to create a generic setter which updates the sister object if that hasn't got the same reference/value.

I need the solution to be ES5 standard pliant, and it seems most alternative setters/getters are implementation dependent hacks.

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

However I can't figure out why this is not working. Note that someone writes in this other question that they got it working, but their examples don't 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 recursing somehow.

edit: The feature that I try to implement is 2 objects that keep there properties in sync. So I wanted to create a generic setter which updates the sister object if that hasn't got the same reference/value.

I need the solution to be ES5 standard pliant, and it seems most alternative setters/getters are implementation dependent hacks.

Share Improve this question edited May 23, 2017 at 10:28 CommunityBot 11 silver badge asked Aug 4, 2013 at 12:03 user1115652user1115652
Add a ment  | 

3 Answers 3

Reset to default 4

Inside the set function you do:

that[ name ] = value

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

that[ '__' + name ] = value

Your set: function is causing recursion because it's causing a write to the very same property that the set: function has been registered on, i.e.

that[name] = value

will cause the set function to be called, which then does:

that[name] = value

will cause the set function to be called...

My understanding of defineProperty was wrong. I thought it would let you set a getter/setter on an existing property. Then mon sense would abide that a setter either just returns the new value, or that if the user has to set the actual property within the setter, at least assigning to that property doesn't trigger the setter again.

However it is not that kind of functionality which has been created with defineProperty. Rather it is meant for creating interfaces. You create an accessor property which in itself doesn't hold any data but which will expose some other data from a data structure maybe hidden from the client through an interface. This is a powerful feature, albeit a bit of a nuisance if you just thought to quickly add validation to one of your properties.

MDN isn't very clear on this and so it might be confusing, especially since the "other" use of defineProperty is to set attributes on a property which holds a value. The getter/setter are not attributes of such a property but are detached from it. Note that your setter will not receive the name of the property that is being accessed. So if you want to make generic code, you will have to wrap it up in an anonymous function to pass the name in. If you are making generic code, you probably assign properties in a loop anyways, so you will have to use an anonymous function in order to deal with the "closure effect" anyways.

What I wanted to use this for is to make a public interface to a private object. The problem being that if a client assigns something to a property on the interface, they will disconnect it from the private counterpart and private and public code will no longer be acting on the same value. It turns out that in this case the way defineProperty works is spot on, because I already have a private (reference) object and I want to create accessors on another object which allow accessing this data without having access to the private object.

Example code: If you want a generic setter/getter (eg. assigned in a loop and the actual code existing elsewhere so every property on all your objects doesn't need a full copy of the function), you will have to:

  1. create a secondary storage for the actual value of the property.

  2. wrap your defineProperty call in a an anonymous function to deal with the closure, and create an inline getter/setter which will call your actual getter/setter with the name of the property.

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)

  1. 暂无评论