【问题标题】:JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded带有 getter 和 setter 的 JavaScript 类导致 RangeError:超出最大调用堆栈大小
【发布时间】:2015-07-17 20:52:41
【问题描述】:

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

【问题讨论】:

  • 我对 ECMA6 类的了解不够,但您似乎在递归调用您的 setter。
  • 递归调用是什么意思?
  • 表示它在调用自己。尝试将您的 setter 方法重命名为现金以外的其他名称。

标签: javascript node.js node-mysql


【解决方案1】:

您的“cash”setter 调用“cash”setter,后者调用“cash”setter,后者调用“cash”setter...

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

【讨论】:

  • 重命名“内部”变量。我将this.cash 更改为this.playerCash。谢谢大家。
  • @Cludch 没问题。您应该养成不重复使用名称的习惯,或者如果您真的想使用该名称,请在前面添加“get”或“set”之类的内容,即“getCash”或“setCash”。
【解决方案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 是一个公共属性,因此您实际上不需要 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;
   }

}

重要

虽然符号的语法更好,但它需要浏览器的原生支持才能真正工作。您可以使用转译器编写它,但在底层,它将模拟旧的 ES5 标准。对 WeakMaps 的原生支持更好,另一方面,此功能仅与 GC 和对象属性的可枚举选项一起使用。所以,最终,这是你的选择。

【讨论】:

  • 真的很烦人。如果我这样做,当我想序列化/反序列化对象时会发生什么?我相信它将被序列化为"{'_cash': 350}",这对于通过网络发送并不理想。你必须编写一个自定义的toString() 函数来处理这个问题。
【解决方案3】:

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

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

请查看this page 以获得明确的示例。

【讨论】:

    【解决方案4】:

    你正在递归调用你的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;
                }
            });
        }
    };
    

    【讨论】:

      【解决方案5】:

      Get & Set ES6 类为 getter 和 setter 带来了新的语法 对象属性。 Get 和 set 允许我们在读取时运行代码或 写一个属性。 ES5 也有 getter 和 setter,但没有 由于较旧的 IE 浏览器而被广泛使用。 ES5 getter 和 setter 做到了 没有 ES6 给我们带来的好语法。所以让我们创建一个 get 并为我们的 name 属性设置。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-26
        • 2021-09-21
        • 2018-02-06
        • 1970-01-01
        • 2021-05-30
        相关资源
        最近更新 更多