web-dev-qa-db-ja.com

getterおよびsetterを含むJavaScriptクラスはRangeErrorを引き起こします:呼び出しスタックの最大サイズを超えました

私は現在ECMA6クラスを実験しています。私の現在のクラスは次のようになります

_class Player {
  constructor(id) {
    this.id = id;
    this.cash = 350;
  }

  get cash() {
    return this.cash;
  }

  set cash(value) { // line 19
    this.cash = value; // line 20
  }
};
_

let playerObject = new Player(1);を呼び出して新しいオブジェクトを作成すると、次のエラーが表示されます

_...\node_modules\mysql\lib\protocol\Parser.js:82
        throw err;
              ^
RangeError: Maximum call stack size exceeded
    at Player.cash (player.js:19:11)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
Press enter to exit
_

これはmysqlライブラリと何が関係していますか?エラーが同じ行に複数回あるのはなぜですか?私はそれを一度だけ呼びます。

25
Cludch

"cash"セッターは "cash"セッターを呼び出し、 "cash"セッターは "cash"セッターを呼び出し、 "cash"セッターは "cash"セッターを呼び出します...

セッター内の独自の名前でプロパティセッターにアクセスすると、無限再帰関数呼び出しが作成されます。

26
Greg Burghardt

遅れていることはわかっていますが、ここで1つまたは2つのポイントを明確にできると思います。

まず、プライバシーの問題があります。これは、JavaScriptコミュニティでの長期的な議論です。

class Player {
   constructor(id) {
      this.cash = 350; // this._cash, alternatively
   }

   get cash() {
      return this.cash;
   }

   set cash(value) {
      this.cash = value;
   }
};

let player1 = new Player();

この場合、this.cashはパブリックプロパティであるため、player1.cashで取得できるため、実際にそれを処理するためのゲッターおよびセッターメソッドは必要ありません。player1.cash = newCashで設定します。そして、他の人が述べたように、ゲッターとセッターが再帰的に呼び出されているため、エラーがスローされます。

ただし、プロパティの名前を単にthis._cashに変更する場合は、これがIS NOT A PRIVATE PROPERTYであることを理解する必要があります。アクセスplayer1._cashplayer1.cashと同じようにプロパティ値にアクセスできます。

では、プライバシーを適切に実装するにはどうすればよいですか?

ES6/ES2015でこれを行うには、主に2つの方法があります。新しいプリミティブタイプSymbolを使用するか、WeakMapsを使用します。この言語の2つの新機能については詳しく説明しませんが、この場合にこれがどのように実装されるかを示します。

記号の使用:

const CASH = Symbol();

class Player {

   constructor () {
      this[CASH] = 350;
   }

   get cash(){
      return this[CASH];
   }

   set cash(cash) {
      this[CASH] = cash;
   }

}

WeakMapsの使用

let map =  new WeakMap();

class Player {

   constructor () {
      map.set(this, {
         cash: 350
      });    
   }

   get cash(){
      return map.get(this).cash;
   }

   set cash(cash) {
      map.get(this).cash = cash;
   }

}

重要

シンボルの構文は優れていますが、実際に機能するにはブラウザのネイティブサポートが必要です。トランスパイラーで作成することもできますが、内部的には古いES5標準に模倣します。 WeakMapsのネイティブサポートの方が優れていますが、一方で、この機能はGCとオブジェクトプロパティの列挙可能なオプションでのみ機能します。結局のところ、それはあなたの選択です。

18

現金はゲッター/セッターを表し、_cashは「プライベート」プロパティです。

  set cash(value) { // line 19
      this._cash = value; // line 20
  }

明確な例については このページ をご覧ください。

4
htatche

あなたは再帰的にゲッターを呼び出しています。

それは可能な代替案に従います:

class Player {
    constructor(id) {
        this.id = id;
        this._cash = 350;
    }

    get cash() {
        return this._cash;
    }

    set cash(value) {
        this._cash = value;
    }
};

Object.definePropertyを使用するもう1つ:

class Player {
    constructor(id) {
        this.id = id;

        var _cash = 350;
        Object.defineProperty(this, 'cash', {
            get: function() {
                return _cash;
            }

            set: function(v) {
                _cash = v;
            }
        });
    }
};
4
skypjack

Get&Set ES6クラスは、オブジェクトプロパティのゲッターとセッターに新しい構文をもたらします。取得と設定により、プロパティの読み取りまたは書き込み時にコードを実行できます。 ES5にもゲッターとセッターがありましたが、古いIEブラウザーのため、広く使用されていませんでした。ES5ゲッターとセッターには、ES6がもたらす構文のナイスがありませんでした。 nameプロパティに設定します。

ソース: JavaScript ES6クラスの構文

// ES6 get and set
class Person {
    constructor(name) {
        this._name = name;
    }

    get name() {
        return this._name.toUpperCase();
    }

    set name(newName) {
        this._name = newName;   // validation could be checked here such as only allowing non numerical values
    }

    walk() {
        console.log(this._name + ' is walking.');
    }
}

let bob = new Person('Bob');
console.log(bob.name);  // Outputs 'BOB'
0
d.danailov