JavaScript中调用方法后全局变量仅修改一次且后续不再变化的异常问题排查
问题根源:基本类型的按值传递陷阱
嘿,这个坑我踩过好多次!你遇到的核心问题是:JavaScript的基本类型(比如数字)是按值传递的,你的attack方法根本没真正修改到全局的enemyHealth变量,只是在方法内部改了个临时副本而已。
具体来说,当你调用archer.attack(enemyHealth)时,JavaScript会把全局变量enemyHealth的当前值(200)复制一份,传给方法里的enemyHealth参数。你在方法里修改这个参数的值,只会影响这个局部副本,完全碰不到外面的全局变量。所以第一次调用后,全局的enemyHealth还是200——你看到DOM更新只是因为你把局部副本的值渲染了出来。第二次调用时,你又把全局的200传进去,计算出来的结果自然和第一次一样,看起来就像变量没变化。
三种解决方案,按需选择
方案1:直接引用全局变量(最简单,但耦合度高)
既然你本来就想修改全局的enemyHealth,那干脆不要把它作为参数传递,直接在方法里引用全局变量就行:
class Human { constructor(kind, name, agility, mana, weapon, health) { this.kind = kind; this.name = name; this.agility = agility; this.mana = mana; this.weapon = weapon; this.health = health; } attack() { // 去掉参数,直接用全局变量 const neprijateljhp = document.querySelector(".enemyhealth"); let damageh, numberofattacksh; if (this.kind === "archer") { damageh = 30; numberofattacksh = 2; } else if (this.kind === "witch") { damageh = 30; numberofattacksh = 0.5; } else if (this.kind === "swordswomen") { damageh = 80; numberofattacksh = 1.2; } // 直接修改全局的enemyHealth enemyHealth = enemyHealth - damageh * numberofattacksh; neprijateljhp.innerHTML = "Health:" + enemyHealth; } } let enemyHealth = 200; const archer = new Human("archer", "Anabelle", "60", "20", "bow and arrow", "200"); archer.attack(); // 调用时不用传参数
这种方式最直接,但缺点是依赖全局变量,代码耦合度高,后续如果有多个敌人或者复杂逻辑,容易出问题。
方案2:用对象存储敌人状态(更面向对象,推荐)
游戏里通常会把敌人的属性封装成对象,对象是按引用传递的——修改对象的属性会直接影响外部的原对象,完美解决这个问题:
class Human { constructor(kind, name, agility, mana, weapon, health) { this.kind = kind; this.name = name; this.agility = agility; this.mana = mana; this.weapon = weapon; this.health = health; } attack(enemy) { // 传入敌人对象 const neprijateljhp = document.querySelector(".enemyhealth"); let damageh, numberofattacksh; if (this.kind === "archer") { damageh = 30; numberofattacksh = 2; } else if (this.kind === "witch") { damageh = 30; numberofattacksh = 0.5; } else if (this.kind === "swordswomen") { damageh = 80; numberofattacksh = 1.2; } // 修改敌人对象的health属性 enemy.health = enemy.health - damageh * numberofattacksh; neprijateljhp.innerHTML = "Health:" + enemy.health; } } // 用对象存储敌人的生命值(以后还能加其他属性,比如名字、攻击力) const enemy = { health: 200 }; const archer = new Human("archer", "Anabelle", "60", "20", "bow and arrow", "200"); archer.attack(enemy); // 传入敌人对象
这种方式更符合游戏的面向对象设计,以后扩展敌人的其他属性也很方便,还能避免全局变量的滥用。
方案3:让方法返回新生命值(函数式风格,副作用小)
如果你不想依赖全局变量或对象,也可以让attack方法计算出新的生命值,然后在外部赋值给全局变量:
class Human { constructor(kind, name, agility, mana, weapon, health) { this.kind = kind; this.name = name; this.agility = agility; this.mana = mana; this.weapon = weapon; this.health = health; } attack(currentHealth) { const neprijateljhp = document.querySelector(".enemyhealth"); let damageh, numberofattacksh; if (this.kind === "archer") { damageh = 30; numberofattacksh = 2; } else if (this.kind === "witch") { damageh = 30; numberofattacksh = 0.5; } else if (this.kind === "swordswomen") { damageh = 80; numberofattacksh = 1.2; } const newHealth = currentHealth - damageh * numberofattacksh; neprijateljhp.innerHTML = "Health:" + newHealth; return newHealth; // 返回新的生命值 } } let enemyHealth = 200; const archer = new Human("archer", "Anabelle", "60", "20", "bow and arrow", "200"); // 接收方法返回的新值,赋值给全局变量 enemyHealth = archer.attack(enemyHealth);
这种方式的副作用最小,方法本身只负责计算和渲染,不直接修改外部状态,适合需要更可控逻辑的场景。
内容的提问来源于stack exchange,提问作者tyume
相关产品推荐
相关产品推荐

