3

我需要设置一些动态设置器并想使用defineProperty。

但是我不知道为什么这不起作用。请注意,有人在另一个问题中写道,他们得到了它的工作,但他们的例子对我也不起作用......

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

它必须以某种方式递归。

编辑:我尝试实现的功能是 2 个使属性保持同步的对象。所以我想创建一个通用设置器,如果没有相同的引用/值,它会更新姐妹对象。

我需要符合 ES5 标准的解决方案,而且似乎大多数替代 setter/getter 都是依赖于实现的 hack。

4

3 回答 3

4

set在你做的函数里面:

that[ name ] = value

这将再次触发该set功能,因为您将其绑定到name. set您必须在函数内使用不同的属性名称。例如:

that[ '__' + name ] = value
于 2013-08-04T12:10:42.360 回答
1

您的set:函数导致递归,因为它导致写入该set:函数已注册的相同属性,即

that[name] = value

将导致set调用该函数,然后执行以下操作:

that[name] = value

将导致set函数被调用...

于 2013-08-04T12:10:54.017 回答
0

我对 defineProperty 的理解是错误的。我认为它可以让您在现有属性上设置 getter/setter。然后常识会遵守 setter 要么只返回新值,要么如果用户必须在 setter 中设置实际属性,至少分配给该属性不会再次触发 setter。

但是,它不是使用 defineProperty 创建的那种功能。相反,它用于创建接口。您创建了一个访问器属性,它本身不包含任何数据,但会从数据结构中公开一些其他数据,这些数据可能通过接口对客户端隐藏。这是一个强大的功能,尽管如果您只是想快速将验证添加到您的某个属性,这会有点麻烦。

MDN对此不是很清楚,因此可能会造成混淆,特别是因为 defineProperty 的“其他”用途是在包含值的属性上设置属性。getter/setter 不是此类属性的属性,但与它分离。请注意,您的 setter 不会收到正在访问的属性的名称。因此,如果您想制作通用代码,则必须将其包装在匿名函数中以传递名称。如果您正在制作通用代码,则可能无论如何都在循环中分配属性,因此您将不得不使用匿名无论如何处理“关闭效应”的功能。

我想用它来为私有对象创建一个公共接口。问题在于,如果客户端将某些内容分配给接口上的属性,他们会将其与私有对应项断开连接,并且私有和公共代码将不再作用于相同的值。事实证明,在这种情况下,defineProperty 的工作方式是正确的,因为我已经有一个私有(引用)对象,并且我想在另一个对象上创建访问器,以允许访问此数据而无需访问私有对象。

示例代码: 如果您想要一个通用的 setter/getter(例如,在循环中分配并且实际代码存在于其他地方,因此所有对象上的每个属性都不需要函数的完整副本),您将必须:

  1. 为属性的实际值创建辅助存储。

  2. 将您的 defineProperty 调用包装在一个匿名函数中以处理闭包,并创建一个内联 getter/setter,它将使用属性名称调用您的实际 getter/setter。

这将按预期工作:

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!
于 2013-08-04T14:06:47.650 回答