带有 getter 和 setter 的 JavaScript 类会导致 RangeError:超出最大调用堆栈大小

IT技术 javascript node.js node-mysql
2021-03-16 20:27:32

我目前正在试验 ECMA6 类。我目前的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 库有什么关系?为什么错误在同一行中多次出现?我只调用一次。

5个回答

你的“现金”设置器调用“现金”设置器,它调用“现金”设置器,它调用“现金”设置器......

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

重命名“内部”变量。我换this.cashthis.playerCash谢谢大家。
2021-05-01 20:27:32
@Cludch 没问题。您应该养成不重复使用名称的习惯,或者如果您真的想使用该名称,请在前面添加诸如“get”或“set”之类的东西,即“getCash”或“setCash”。
2021-05-13 20:27:32

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

首先是隐私问题,这是 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 是一个公共属性,所以你真的不需要一个 getter 和一个 setter 方法来处理它,因为你可以用player1.cash获取它并用player1.cash = newCash设置它正如其他人所提到的,它正在抛出错误,因为 getter 和 setter 被递归调用。

但是,如果您只是将属性重命名为this._cash,您必须明白这不是私有财产如果您尝试访问player1._cash,你将有你有同样的方式访问属性值player1.cash

那么,我们如何让隐私得到普遍实施呢?

ES6/ES2015 有两种主要方法可以做到这一点:使用新的原始类型Symbol或使用WeakMaps我不会详细介绍该语言的这两个新特性,但我将展示在这种情况下将如何实现。

使用符号:

const CASH = Symbol();

class Player {

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

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

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

}

使用弱映射

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 和对象属性的可枚举选项一起使用。所以,最终,这是你的选择。

真的很烦。如果我这样做,当我想序列化/反序列化对象时会发生什么?我相信它会被序列化,因为"{'_cash': 350}"这不适合通过网络发送。您将不得不编写一个自定义toString()函数来处理这个问题。
2021-05-10 20:27:32

cash 代表 getter/setter,_cash 是“私有”属性。

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

查看此页面以获取清晰的示例。

您正在递归调用您的吸气剂。

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

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 类为对象属性的 getter 和 setter 带来了新的语法。Get 和 set 允许我们在读取或写入属性时运行代码。ES5 也有 getter 和 setter,但由于较旧的 IE 浏览器而没有被广泛使用。ES5 的 getter 和 setter 没有 ES6 给我们带来的那么好的语法。所以让我们为我们的 name 属性创建一个 get 和 set 。

来源: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'