JavaScript中对象的字段实例无需绑定父引用,如何调用所属上层对象的方法与属性
解答
你可以通过以下两种符合要求的方案实现需求,都不需要在FarklePlayer中存储FarkleGame实例引用,也不需要给roll()方法传递骰子实例参数:
方案1:事件驱动模式(最符合解耦要求)
JS原生支持自定义事件,你可以让FarklePlayer触发自定义事件,FarkleGame监听对应事件后执行掷骰、算分逻辑,完全解耦父子实例的直接依赖:
// 先让FarklePlayer继承EventTarget支持事件 class FarklePlayer extends EventTarget { name; constructor(name_of_player) { super(); this.name = name_of_player; } roll() { // 仅触发事件,不需要感知上层实例存在 this.dispatchEvent(new CustomEvent('rollRequest')); } // 提供接收得分的方法,上层算完分后调用 setCurrentScore(score) { // 这里处理玩家自身的得分逻辑 console.log(`${this.name}本次掷骰得分:${score}`); } } class FarkleGame { player_one; player_two; die; currentPlayer; // 新增字段标记当前回合玩家 constructor(name_of_player_one, name_of_player_two) { this.player_one = new FarklePlayer(name_of_player_one); this.player_two = new FarklePlayer(name_of_player_two); this.die = new FarkleDie(); this.currentPlayer = this.player_one; // 监听两个玩家的掷骰请求事件 this.player_one.addEventListener('rollRequest', () => this.handlePlayerRoll()); this.player_two.addEventListener('rollRequest', () => this.handlePlayerRoll()); } // 统一处理玩家掷骰请求 handlePlayerRoll() { this.die.rolled(); const score = this.score_die(); this.currentPlayer.setCurrentScore(score); } // 修复原代码语法错误,补充返回值 score_die() { let die_value = this.get_die().get_value(); let score = ((die_value * die_value) * 10) - 1; return score; } get_player_one() { return this.player_one; } get_player_two() { return this.player_two; } get_die() { return this.die; } } class FarkleDie { value; constructor() { this.value = null; } rolled() { this.value = Math.floor(Math.random() * 6) + 1; } get_value() { return this.value; } // 补充原代码缺失的取值方法 }
使用示例:
const game = new FarkleGame('张三', '李四'); // 当前玩家发起掷骰请求,自动触发上层的掷骰、算分逻辑 game.get_player_one().roll();
方案2:构造期注入绑定(轻量化实现)
如果你不想用事件,也可以在FarkleGame构造实例时,给FarklePlayer绑定定制化的roll方法实现,不需要Player类感知上层实例:
class FarklePlayer { name; roll; // 预留roll方法位,由上层实例注入实现 constructor(name_of_player) { this.name = name_of_player; } } class FarkleGame { player_one; player_two; die; constructor(name_of_player_one, name_of_player_two) { this.die = new FarkleDie(); // 构造Player时直接注入roll方法的实现,绑定当前game实例上下文 this.player_one = new FarklePlayer(name_of_player_one); this.player_one.roll = () => { this.die.rolled(); const score = this.score_die(); console.log(`${this.player_one.name}得分:${score}`); }; this.player_two = new FarklePlayer(name_of_player_two); this.player_two.roll = () => { this.die.rolled(); const score = this.score_die(); console.log(`${this.player_two.name}得分:${score}`); }; } // 其余方法和FarkleDie类代码同方案1,补充修复后的score_die、get_value方法即可 get_player_one() { return this.player_one; } get_player_two() { return this.player_two; } get_die() { return this.die; } score_die() { let die_value = this.get_die().get_value(); let score = ((die_value * die_value) * 10) - 1; return score; } }
说明
两种方案都满足你要求的不在FarklePlayer中存储FarkleGame实例引用、不给roll方法传骰子参数的前提,事件驱动方案更适合复杂的多交互场景,注入方案更适合简单的轻量化场景。
内容的提问来源于stack exchange,提问作者Rar
相关产品推荐
相关产品推荐

