这个老虎机练习主要考察JavaScript中的原型链(prototype
)和this
关键字的使用。
主要思路
-
创建三个轮盘(reels)实例:我们需要创建3个独立的轮盘对象,它们都委托(delegate)到基础的
reel
对象。这可以通过Object.create(reel)
来实现,创建新对象并将其原型指向reel
。 -
实现
display()
方法:这是最复杂的部分,需要显示3×3的网格:- 每个轮盘需要显示三个位置:当前位置、上方位置和下方位置
- 行显示(水平方向):以"|"分隔的三个轮盘符号
- 列显示(垂直方向):每个轮盘的三个位置(上中下)
实现代码
function randMax(max) {return Math.trunc(1E9 * Math.random()) % max;
}var reel = {symbols: ["♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"],spin() {if (this.position == null) {this.position = randMax(this.symbols.length); }this.position = (this.position + 100 + randMax(100)) % this.symbols.length;},display() {if (this.position == null) {this.position = randMax(this.symbols.length);}return this.symbols[this.position];}
};var slotMachine = {reels: [Object.create(reel),Object.create(reel),Object.create(reel)],spin() {this.reels.forEach(reel => {reel.spin();});},display() {// 获取每个轮盘上方、当前和下方的符号var lines = [];// 创建三行(上、中、下)for (let linePos = -1; linePos <= 1; linePos++) {let line = this.reels.map(reel => {// 创建一个临时对象,委托到reel,但拥有自己的positionvar slot = Object.create(reel);// 调整position以显示上方/当前/下方的符号slot.position = ((reel.position + linePos) + reel.symbols.length) % reel.symbols.length;return slot.display();});lines.push(line.join(" | "));}// 打印结果console.log(lines.join("\n"));}
};slotMachine.spin();
slotMachine.display();slotMachine.spin();
slotMachine.display();
代码解析
-
循环创建三行:使用
-1
、0
、1
作为偏移量,分别表示上、中、下行 -
显示每个位置的符号:
- 为每个位置创建一个临时对象(使用
Object.create(reel)
) - 调整临时对象的
position
属性,考虑到循环(使用模运算) - 调用临时对象的
display()
方法获取符号
- 为每个位置创建一个临时对象(使用
-
处理循环边界问题:当计算上方位置(特别是当前位置为0时),需要加上
reel.symbols.length
来确保结果为正,然后再取模 -
格式化输出:组合所有符号,map生成的行数组用"|“分隔转换成字符串后加入lines数组,lines数组用换行符”\n"分隔转换成字符串。
-
spin()
方法有两个主要作用:-
初始化位置:如果轮盘的
position
属性为null
(比如第一次使用时),它会为轮盘设置一个初始随机位置。 -
改变轮盘位置:每次调用
spin()
都会使轮盘旋转一定数量的位置(至少100个位置加上0-99的随机数),然后通过取模确保最终位置在有效范围内。在示例代码中:
slotMachine.spin(); slotMachine.display(); // 显示第一组结果slotMachine.spin(); slotMachine.display(); // 显示第二组结果
两次结果不同,正是因为中间调用了
spin()
方法,改变了每个轮盘的位置。如果删除第二个spin()
调用,两次display()
会显示完全相同的结果。所以
spin()
方法是整个老虎机机制的核心部分之一,它模拟了真实老虎机拉杆后轮盘旋转的过程,而这个旋转直接决定了最终显示的符号组合。
-
-
reel
原型对象-
reels
数组中的对象:reels: [Object.create(reel),Object.create(reel),Object.create(reel) ]
这里创建了三个独立的对象,每个都作为
slotMachine
的一个轮盘。这些对象在整个slotMachine
的生命周期中持续存在,并且每个都维护自己的position
属性状态。当我们调用slotMachine.spin()
时,这三个对象的position
属性会被更新。 -
display()
方法中的临时对象:var slot = Object.create(reel); // 这个是个临时对象,不是reels中的元素
这是在
display()
方法内部创建的临时对象,它在每次显示时创建,用完即丢弃。这个临时对象不是用来替代轮盘的,而是用来临时保存修改后的位置,以便显示上方或下方的符号,而不影响原始轮盘的position
属性。理解这一点很关键:
slot
是通过原型链委托到reel
对象的,而reels
数组中的每个元素是通过原型链委托到同一个reel
对象的独立对象。因此,它们在内存中是完全不同的对象,只是共享相同的原型。这种结构让我们能够在不改变原始轮盘位置的情况下,显示每个轮盘的上方、当前和下方位置的符号。
-
-
理解参数
reel
的来源:-
this.reels.map(function getSlot(reel){...})
中的reel
参数是map
方法传入的,它代表的是this.reels
数组中的每个元素,也就是我们在slotMachine
初始化时创建的那三个轮盘对象。 -
然后,我们基于这个轮盘对象创建一个临时对象:
var slot = Object.create(reel)
,这个临时对象的原型是当前遍历到的轮盘对象,而不是原始的reel
对象。 -
这样,我们可以通过
reel.position
访问到当前轮盘的位置,并在临时对象中设置调整后的位置:slot.position = ...
。
简单来说,这里存在三层委托关系:
slot
对象委托到reels
数组中的特定轮盘对象- 那个轮盘对象又委托到原始的
reel
对象 - 当我们调用
slot.display()
时,会使用slot
自己的position
,但方法本身是从reel
原型继承的
-