如何解决如何避免副作用 (Javascript)
我目前正在构建一个二十一点游戏,我的一个课程叫做“Deck”。这套牌需要做两件事:
- 返回一组其他对象可以使用的卡片(这相当于将这些卡片发出)
- 从牌组中取出这些牌(一旦发了牌,就应该将其取出)。
目前,我通过创建两个方法实现了这两个功能:selectCards()
和 removeIndexFromDeck
。我首先调用 selectCards()
返回选定的卡片,但也调用 removeIndexFromDeck()
。
这种方法是否违反了最佳做法?看起来我的 selectCards()
函数既有返回值又有副作用。
如果确实违反了最佳实践,我将如何更改这些方法,但要确保我仍然能够返回选定的卡片并将它们从牌组中移除。
谢谢!
class Deck {
constructor() {
this.deck = [];
['♦','♣','♥','♠'].forEach(suit => {
['A','2','3','4','5','6','7','8','9','10','J','Q','K'].forEach(value => {
this.deck.push(`${value}${suit}`);
});
});
}
selectCards(numCards) {
let selectedCards = [];
while (numCards > 0) {
let randIndex = Math.floor(Math.random() * this.deck.length);
selectedCards.push(this.deck[randIndex]);
this.removeIndexFromDeck(randIndex);
numCards -= 1;
}
return selectedCards;
}
removeIndexFromDeck(index) {
this.deck.splice(index,1);
}
}
解决方法
确实,您通常应该避免同时改变和返回值,但好的规则总是有例外的。即使是原生 Java Script 也有例外,而 Array#pop
可能是最著名的一个。然而,许多人会同意 pop
非常有用,因为它目前有效。此外,您在脚本中调用的 Array#splice
会改变并返回信息。
除非你想放弃 OOP 并转向函数式编程,否则这种模式很好。我只是确保方法的名称尽可能少地怀疑这种双重效果。出于这个原因,我会称 selectCards
而不是 extractCards
或 pullCards
。这给出了一个更强烈的暗示,表明套牌发生了变异。
我还建议实施 shuffle
方法,而不是在选择卡片时使用随机索引。如果您支持私有属性,则将 deck
数组定义为私有,以便向外界隐藏混洗后的内容。
这就是我的意思(没有私人):
class Deck {
constructor() {
this.deck = Array.from('♦♣♥♠',suit =>
['A','2','3','4','5','6','7','8','9','10','J','Q','K'].map(
value => `${value}${suit}`
)
).flat();
}
shuffle() { // mutates the deck,much like Array#sort mutates an array
let deck = this.deck;
for (let i = deck.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
}
extractCards(numCards) { // Better name. Mutates & returns.
// Perform a controlled Array#splice
if (typeof numCards !== "number" || numCards <= 0) throw "Invalid argument";
if (this.deck.length < numCards) throw "Deck does not have enough cards for this operation";
return this.deck.splice(-numCards);
}
}
let deck = new Deck();
deck.shuffle();
console.log(...deck.extractCards(4));
如果您使用类,这通常意味着存在副作用,因为数据是类的一部分。也就是你得到的方法没有返回值(通常意味着它们有副作用),这些方法会改变类状态。您可以做的最接近的事情是制作尽可能多的方法返回新的套牌值,而不是使用变异/副作用数组方法,然后在您的父方法中执行 this.deck = removeIndexFromDeck(index)
来限制有副作用的地方。
如果你放弃类,而是创建操作数据的函数,并将卡片组视为仅数据而不是智能类,则函数本身将不会产生副作用。
您还可以使用在进行更改时返回新的 Deck
类实例的模式,这意味着 Deck
中的数据将始终是不可变的。不过,在这种情况下,我没有看到这样做的好处。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。