JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded

JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded

我目前正在试验 ECMA6 classes。 我当前的 class 如下所示

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 库有什么关系?为什么错误在同一行中出现多次?我只调用了一次。

你的"cash"setter调用了"cash"setter,调用了"cash"setter,调用了"cash" ] setter...

在 setter 中通过其自己的名称访问 属性 setter 会创建无限递归函数调用。

cash代表getter/setter,_cash代表'private'属性.

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

查看 this page 以获得清晰的示例。

您正在递归调用您的 getter。

它遵循一个可能的替代方案:

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

    get cash() {
        return this._cash;
    }

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

另一个使用 Object.defineProperty:

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

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

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

Get & Set ES6 classes brings a new syntax for getters and setters on object properties. Get and set allows us to run code on the reading or writing of a property. ES5 had getters and setters as well but was not widely used because of older IE browsers. ES5 getters and setters did not have as nice of a syntax that ES6 brings us. So lets create a get and set for our name property.

Source: JavaScript ES6 Class Syntax

示例:

// 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'

我知道我迟到了,但我想我可以在这里澄清一两点:

首先是隐私问题,这是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 是 public 属性,因此您实际上不需要 getter和一个 setter 方法来处理它,因为你可以用 player1.cash 得到它并用 player1.cash = newCash;它正在抛出错误,因为 getter 和 setter 被递归调用,正如其他人提到的那样。

但是,如果您只是将 属性 重命名为 this._cash,您必须明白这 不是私有的 属性。如果您尝试访问 player1._cash,您将可以像访问 player1.cash 一样访问 属性 值].

那么,我们如何切实实施隐私保护?

使用 ES6/ES2015 有两种主要方法:使用新的原始类型 Symbol or using WeakMaps。我不会详细介绍该语言的这两个新功能,但我会展示在这种情况下如何实现。

使用符号:

const CASH = Symbol();

class Player {

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

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

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

}

使用 Wea​​kMaps

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;
   }

}

重要

虽然 Symbols 的语法更好,但它需要浏览器的本机支持才能实际工作。您可以使用转译器编写它,但在幕后,它会将其模拟为旧的 ES5 标准。对 WeakMaps 的原生支持更好,另一方面,此功能仅与 GC 和对象属性的可枚举选项一起使用。所以,最后,这是你的选择。