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 標準に準拠する必要があります。ほとんどの代替セッター/ゲッターは実装依存のハックのようです。

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 の私の理解は間違っていました。既存のプロパティにゲッター/セッターを設定できると思いました。その場合、常識は、セッターが新しい値を返すだけであるか、ユーザーがセッター内で実際のプロパティを設定する必要がある場合、少なくともそのプロパティに割り当ててもセッターを再度トリガーしないことを順守します。

ただし、defineProperty で作成されたのはそのような機能ではありません。むしろ、インターフェースを作成するためのものです。それ自体はデータを保持しないが、インターフェイスを介してクライアントから隠されている可能性のあるデータ構造から他のデータを公開するアクセサー プロパティを作成します。これは強力な機能ですが、プロパティの 1 つにバリデーションをすばやく追加しようと考えた場合には少し厄介です。

MDNはこれについてあまり明確ではないため、特に defineProperty の「その他の」使用法は、値を保持するプロパティに属性を設定することであるため、混乱を招く可能性があります。ゲッター/セッターはそのようなプロパティの属性ではなく、そこから分離されています。セッターは、アクセスされているプロパティの名前を受け取らないことに注意してください。したがって、汎用コードを作成する場合は、名前を渡すために匿名関数でラップする必要があります。汎用コードを作成している場合は、とにかくループでプロパティを割り当てる可能性があるため、匿名関数を使用する必要があります。とにかく「閉鎖効果」に対処するための関数。

これを使用したかったのは、プライベート オブジェクトへのパブリック インターフェイスを作成することです。問題は、クライアントがインターフェイスのプロパティに何かを割り当てると、それをプライベートな対応物から切断し、プライベート コードとパブリック コードが同じ値で動作しなくなることです。この場合、defineProperty の動作が適切であることがわかります。これは、プライベート (参照) オブジェクトが既にあり、別のオブジェクトにアクセサーを作成して、プライベート オブジェクトにアクセスせずにこのデータにアクセスできるようにしたいからです。

コード例: 一般的なセッター/ゲッターが必要な場合 (たとえば、ループで割り当てられ、実際のコードが他の場所に存在するため、すべてのオブジェクトのすべてのプロパティが関数の完全なコピーを必要としません)、次のことを行う必要があります。

  1. プロパティの実際の値のセカンダリ ストレージを作成します。

  2. defineProperty 呼び出しを匿名関数でラップしてクロージャを処理し、プロパティの名前で実際のゲッター/セッターを呼び出すインライン ゲッター/セッターを作成します。

これは期待どおりに機能します。

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 に答える