2017-09-27 7 views
0

Object.definePropertyを使ってオブジェクトにいくつかのプロパティを追加していました。これは、オブジェクトのプロパティに追加すると正常に動作します。しかし、オブジェクトのプロトタイプに追加すると、too much recursionというエラーが発生しました。ObjectのプロトタイプにownPropertyオブジェクトを追加する際のエラー

var obj = {}; 
 
Object.defineProperty(obj.__proto__, 'key', { 
 
    enumerable: true, 
 
    get:() => {return key}, 
 
    set: (value) => { 
 
     if (typeof(value) == 'number'){ 
 
      key = 2*value; 
 
     } 
 
    } 
 
}); 
 
obj.key = 3; 
 
console.log(obj.key);

プロトタイプ内のオブジェクトは、この方法を追加することができないのはなぜ?

+0

'key'があるを持っているクラスを使用することができます定義されている? (それは重要です) – dandavis

+0

@dandavis、それは前に定義されていません。私は初期設定としてセッターを使用していました。しかし、たとえそれをプロトタイプで定義しても、同じエラーが発生します。 – TheChetan

+0

あなたはそこに定義することはできませんが、キーと呼ばれる閉鎖されたプライベート変数が必要です(図参照) – dandavis

答えて

2

これは、オブジェクトのプロトタイプにプロパティを定義することによって、すべてのオブジェクトがそのゲッター/セッターを取得するためです。 JS内のすべてがオブジェクトなので、windowにはkeyセッター/ゲッターがあります。

セッター/ゲッター関数の内部では、ちょうどkeyを使用します。変数keyを明示的に定義していないので、windowで定義された変数を使用します。これが再帰の原因となっています。

は、window.key = 2*valueというセッターを呼び出します。セッターはwindow.key = 2*valueです。あなたが最大のコールスタックに達するまで、順番どおりに進みます。

すべてのインスタンスで必要としない限り、プロトタイプに何かを追加しないでください。

矢印機能を使用しているため、セッター/ゲッター呼び出しにはthisがバインドされていないため、どのインスタンスオブジェクトを操作しているのかわからなくなります。

代わりに、オブジェクトのプロトタイプmakeを独自のものに拡張したり、適切な変数/プロパティを設定/取得したりしないでください。

たとえば、実際のコンストラクタ関数を使用し、矢印関数を正規関数式に変更します。これにより、setter/getterがオブジェクトへのインスタンス参照を取得できるようになります。どのキーがどのインスタンスに属しているかを追跡する方法が必要になります。これらの変数を非表示にする必要がない場合は、単に接頭辞付きの名前付きプロパティー、たとえばthis._key = 2*value;を使用することができます。そうでない場合は、非公開にしたい場合はWeakMapを使用できます。

function YourClass(){}; 
 

 
(function(){ 
 
    var keyMap = new WeakMap(); 
 
    Object.defineProperty(YourClass.prototype, 'key', { 
 
    enumerable: true, 
 
    get: function(){ 
 
     return keyMap.get(this); 
 
    }, 
 
    set: function(value){ 
 
     if(typeof(value) == "number"){ 
 
     keyMap.set(this,2*value); 
 
     } 
 
    } 
 
    }); 
 
})(); 
 
obj = new YourClass(); 
 
obj.key = 3; 
 
console.log(obj.key); 
 

 

 
function secondClass(){} 
 

 
//make secondClass inherit from YourClass 
 
secondClass.prototype = Object.create(
 
    YourClass.prototype, 
 
    { 
 
    "constructor": { 
 
    configurable: true, 
 
    enumerable: false, 
 
    writable: true, 
 
    value: secondClass 
 
    } 
 
}); 
 

 
var obj2 = new secondClass(); 
 
obj2.key = 16; 
 
console.log(obj2.key);

また、あなたがES6を使用して、[OK]思えるので、あなただけのセット/構文を取得

class YourClass { 
 
    constructor(){ 
 
     this._key = null; 
 
    } 
 
    set key(value){ 
 
     this._key = 2*value; 
 
    } 
 
    get key(){ 
 
     return this._key; 
 
    } 
 
} 
 

 
class SecondClass extends YourClass { 
 
    constructor(){ 
 
    super(); 
 
    } 
 
} 
 

 
var obj = new SecondClass(); 
 
obj.key = 17; 
 
console.log(obj.key);

+0

これを継承するすべてのオブジェクトに追加する必要があります。この種の再帰を引き起こさずにプロトタイプにどのように追加することができるかの実例を教えてください。 – TheChetan

+0

私は、なぜグローバル変数が再帰問題を引き起こしていたのか、私が「ウィンドウ」の言及に気づくまで疑問に思っていました。それは正直なところ、面白いバグです。なぜ、 'prototype'(または非標準の' __proto__')を手に入れるのが初心者にはお勧めできないのか、という完璧な例です! –

+0

@TheChetan、更新された回答を参照してください。コンストラクタ関数を使用してES5継承を使用する最初の例をいくつか示しました。 2番目はES6クラス構文を使用します。 –

関連する問題