我需要设置一些动态setter并想使用defineProperty。
然而,我无法弄清楚为什么这不起作用。请注意,有人在this other question写道,他们认为它有效,但他们的例子对我来说也不起作用......
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个对象,可以使属性保持同步。所以我想创建一个通用的setter来更新姐妹对象,如果它没有相同的引用/值。
我需要解决方案符合ES5标准,似乎大多数替代的setter / getter都是依赖于实现的黑客。
答案 0 :(得分:4)
在set
函数内部执行:
that[ name ] = value
会再次触发set
函数,因为您将其绑定到name
。您必须在set
函数中使用不同的属性名称。例如:
that[ '__' + name ] = value
答案 1 :(得分:1)
您的set:
函数导致递归,因为它导致写入set:
函数已注册的同一属性,即
that[name] = value
将导致调用set
函数,然后执行:
that[name] = value
将导致set
函数被调用...
答案 2 :(得分:0)
我对defineProperty的理解是错误的。我以为它会让你在现有的属性上设置一个getter / setter。然后常识会认为setter只返回新值,或者如果用户必须在setter中设置实际属性,至少分配给该属性不会再次触发setter。
然而,并非使用defineProperty创建的那种功能。而是用于创建接口。您创建一个访问者属性,该属性本身不包含任何数据,但会从数据结构中公开某些其他数据,这些数据可能通过接口从客户端隐藏。这是一个强大的功能,如果您只是想快速添加验证到您的某个属性,这有点令人讨厌。
MDN对此并不十分清楚,因此可能会让人感到困惑,特别是因为defineProperty的“其他”用法是在包含值的属性上设置属性。 getter / setter不是这种属性的属性,而是与它分离。请注意,您的setter不会收到正在访问的属性的名称。因此,如果你想制作通用代码,你必须将它包装在匿名函数中以传递名称。如果你正在制作通用代码,你可能无论如何都要在循环中分配属性,所以你必须使用匿名功能是为了处理“封闭效应”无论如何。
我想用它来创建一个私有对象的公共接口。问题在于,如果客户端向接口上的属性分配内容,则会将其与私有对象断开连接,并且私有和公共代码将不再对相同的值执行操作。事实证明,在这种情况下,defineProperty工作的方式是点,因为我已经有一个私有(引用)对象,我想在另一个对象上创建访问器,允许访问这些数据而无需访问私有对象。
示例代码: 如果你想要一个通用的setter / getter(例如,在循环中分配并且实际代码存在于其他地方,所以你所有对象上的每个属性都不需要该函数的完整副本),你将不得不:
为属性的实际值创建辅助存储。
将一个匿名函数中的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!