带有 getter 和 setter 的 JavaScript 类会导致 RangeError:超出最大调用堆栈大小
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31484535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded
提问by Cludch
I am currently experimenting with ECMA6 classes. My current class looks like the following
我目前正在试验 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
}
};
When I am now creating a new Object by calling let playerObject = new Player(1);
I receive the following error
当我现在通过调用创建一个新对象时,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
What does this have to do with the mysql library? Why does the error is multiple times in the same line? I am only calling it once.
这与 mysql 库有什么关系?为什么错误在同一行中多次出现?我只调用一次。
回答by Greg Burghardt
Your "cash" setter calls the "cash" setter, which calls the "cash" setter, which calls the "cash" setter...
你的“现金”设置器调用“现金”设置器,它调用“现金”设置器,它调用“现金”设置器......
Accessing the property setter by its own name inside the setter creates an infinite recursive function call.
在 setter 中通过它自己的名称访问属性 setter 会创建一个无限递归函数调用。
回答by Matheus Dal'Pizzol
I know I'm late, but I think I can clarify one or two points here:
我知道我迟到了,但我想我可以在这里澄清一两点:
First, there is the question of privacy, which is a long term discussion in the JavaScript community.
首先是隐私问题,这是 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();
In this case, this.cash is a public property, so you don't really need a getter and a setter method to handle it, because you can get it with player1.cashand set it with player1.cash = newCash; and it is throwing the error because the getter and the setter are being called recursively, as mentioned by others.
在这种情况下,this.cash 是一个公共属性,所以你真的不需要一个 getter 和一个 setter 方法来处理它,因为你可以用player1.cash获取它并用player1.cash = newCash设置它;正如其他人所提到的,它正在抛出错误,因为 getter 和 setter 被递归调用。
However, if you simply rename the property to this._cash, you have to understand that this IS NOT A PRIVATE PROPERTY. If you try to access player1._cash, you will have access to the property value in the same way you will have with player1.cash.
但是,如果您只是将属性重命名为this._cash,您必须明白这不是私有财产。如果您尝试访问player1._cash,你将有你有同样的方式访问属性值player1.cash。
So, how do we get privacy poperly implemented?
那么,我们如何让隐私得到普遍实施呢?
There are 2 main ways of doing this with ES6/ES2015: Using the new primitive type Symbolor using WeakMaps. I'm not going into details about this two new features of the language, but I'll show how this would be implemented in this case.
ES6/ES2015 有两种主要方法可以做到这一点:使用新的原始类型Symbol或使用WeakMaps。我不会详细介绍该语言的这两个新特性,但我将展示在这种情况下将如何实现。
Using Symbols:
使用符号:
const CASH = Symbol();
class Player {
constructor () {
this[CASH] = 350;
}
get cash(){
return this[CASH];
}
set cash(cash) {
this[CASH] = cash;
}
}
Using 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;
}
}
IMPORTANT
重要的
While the syntax for Symbols are better, it requires native support of the browser to actually work. You can write it with a transpiler but, under the hood, it will mock it to old ES5 standards. The native support for WeakMaps are better and, on the other hand, this feature just plays with the GC and with the enumerable option of the objects properties. So, in the end, it's your choice.
虽然 Symbols 的语法更好,但它需要浏览器的本机支持才能实际工作。您可以使用转译器编写它,但在幕后,它会根据旧的 ES5 标准模拟它。对 WeakMaps 的原生支持更好,另一方面,此功能仅与 GC 和对象属性的可枚举选项一起使用。所以,最终,这是你的选择。
回答by htatche
回答by skypHyman
You are calling recursively your getter.
您正在递归调用您的吸气剂。
It follows a possible alternative:
它遵循一个可能的替代方案:
class Player {
constructor(id) {
this.id = id;
this._cash = 350;
}
get cash() {
return this._cash;
}
set cash(value) {
this._cash = value;
}
};
Another one using Object.defineProperty
:
另一个使用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;
}
});
}
};
回答by d.danailov
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
Get & Set ES6 类为对象属性的 getter 和 setter 带来了新的语法。Get 和 set 允许我们在读取或写入属性时运行代码。ES5 也有 getter 和 setter,但由于较旧的 IE 浏览器而没有被广泛使用。ES5 的 getter 和 setter 没有 ES6 给我们带来的那么好的语法。所以让我们为我们的 name 属性创建一个 get 和 set 。
Example:
示例:
// 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'