defineProperty の私の理解は間違っていました。既存のプロパティにゲッター/セッターを設定できると思いました。その場合、常識は、セッターが新しい値を返すだけであるか、ユーザーがセッター内で実際のプロパティを設定する必要がある場合、少なくともそのプロパティに割り当ててもセッターを再度トリガーしないことを順守します。
ただし、defineProperty で作成されたのはそのような機能ではありません。むしろ、インターフェースを作成するためのものです。それ自体はデータを保持しないが、インターフェイスを介してクライアントから隠されている可能性のあるデータ構造から他のデータを公開するアクセサー プロパティを作成します。これは強力な機能ですが、プロパティの 1 つにバリデーションをすばやく追加しようと考えた場合には少し厄介です。
MDNはこれについてあまり明確ではないため、特に defineProperty の「その他の」使用法は、値を保持するプロパティに属性を設定することであるため、混乱を招く可能性があります。ゲッター/セッターはそのようなプロパティの属性ではなく、そこから分離されています。セッターは、アクセスされているプロパティの名前を受け取らないことに注意してください。したがって、汎用コードを作成する場合は、名前を渡すために匿名関数でラップする必要があります。汎用コードを作成している場合は、とにかくループでプロパティを割り当てる可能性があるため、匿名関数を使用する必要があります。とにかく「閉鎖効果」に対処するための関数。
これを使用したかったのは、プライベート オブジェクトへのパブリック インターフェイスを作成することです。問題は、クライアントがインターフェイスのプロパティに何かを割り当てると、それをプライベートな対応物から切断し、プライベート コードとパブリック コードが同じ値で動作しなくなることです。この場合、defineProperty の動作が適切であることがわかります。これは、プライベート (参照) オブジェクトが既にあり、別のオブジェクトにアクセサーを作成して、プライベート オブジェクトにアクセスせずにこのデータにアクセスできるようにしたいからです。
コード例:
一般的なセッター/ゲッターが必要な場合 (たとえば、ループで割り当てられ、実際のコードが他の場所に存在するため、すべてのオブジェクトのすべてのプロパティが関数の完全なコピーを必要としません)、次のことを行う必要があります。
プロパティの実際の値のセカンダリ ストレージを作成します。
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!