【问题标题】:Should I declare a variable using LET globally for a function that runs constantly?我应该为一个不断运行的函数全局使用 LET 声明一个变量吗?
【发布时间】:2018-10-12 16:51:38
【问题描述】:

这里最相关的答案不是指块作用域的let,而是提升的var。我似乎无法得到一个明确的答案。

我有一个全局声明的变量,它初始化一次:

let firePaused = false;

然后是每次我按下按钮时都会运行的键盘处理程序中的一个函数:

function actOnKeyPress() {
  if (rightPressed) {
    game.hero.rotate(game.hero.speed);
  } else if (leftPressed) {
    game.hero.rotate(-game.hero.speed);
  }
  if (!firePressed) {
    firePaused = false;
  }
  if (firePressed && options.numberOfBullets > 0) {
    if (!firePaused) {
      fireBullet();
      firePaused = true;
    }
  }
}

(与问题无关,但其目的是只允许玩家开火一次,需要有一个 keyup 事件才能再次开火)

根据干净代码的规则,我应该在函数顶部声明变量...但这意味着每次我按下按钮时都会重新声明它。

这里写着https://www.sitepoint.com/how-to-declare-variables-javascript/

初始化:当你声明一个变量时,它会自动 初始化,这意味着内存被分配给变量 JavaScript 引擎。

所以每次使用 let 关键字时,我都会创建一个全新的变量。

我是否应该在函数的开头写一个条件来检查firePaused是否已经声明,如果没有声明它?这似乎太过分了。

【问题讨论】:

  • 您似乎有一个全局变量gamegame.state.firePaused 呢?这样,如果您将所有与游戏状态相关的变量都放在那里,您甚至可以轻松保存游戏。
  • 是否可以返回 true 或 false 而不是设置变量?
  • 那个 Sitepoint 注释充其量是误导,在我看来它完全是错误的。

标签: javascript ecmascript-6


【解决方案1】:

如果您的变量是在全局范围内声明的,那么使用letvar 并不重要。

这些在功能上是相同的:

let myVar = 123;
function doStuff() {
  console.log(myVar);
}
doStuff();

var myVar = 123;
function doStuff() {
  console.log(myVar);
}
doStuff();

varlet 在块中声明它们时,它们之间的区别会变得很明显:

if(true) {
  var foo = 1;
}
if(true) {
  let bar = 2;
}

console.log("var foo:", foo);
console.log("let bar:", bar);

如您所见,let 声明仅限于其包装块范围。 var 声明忽略块作用域。

【讨论】:

【解决方案2】:

不,您不应该创建全局变量(无论如何也不应该使用let)。
是的,如果你想在调用之间共享它,你应该在函数之外声明它。

您可以为此使用闭包,使用任何类型的模块模式——从 ES6 模块到 IIFE 再到简单的块作用域。

// ES6 module
let firePaused = false;
export function actOnKeyPress() {
  // use `firePaused`
}

// IIFE
var actOnKeyPress = (function() {
  let firePaused = false;
  return function actOnKeyPress() {
    // use `firePaused`
  };
}());

// block scope
var actOnKeyPress;
{
  let firePaused = false;
  actOnKeyPress = function actOnKeyPress() {
    // use `firePaused`
  };
}

【讨论】:

    【解决方案3】:

    这个问题实际上与 letvar 本身无关 - 它与一般范围有关。

    变量应该在保持程序功能的最小范围内声明。全局变量应该是最后的手段。

    因此,在您的情况下,您不需要全局变量来实现在每次函数调用时不重新声明变量的目标。您只需要创建另一个范围。由于首先所有代码都应该被排除在全局范围之外,因此您的代码应该已经至少有一个子范围,这通常通过 Immediately Invoked Function Expression 来实现,它创建了 "Module Pattern"

    (function(){
      let firePaused = false; // This is scoped to the entire module, but not Global
    
      function actOnKeyPress() {
        if (rightPressed) {
          game.hero.rotate(game.hero.speed);
        } else if (leftPressed) {
          game.hero.rotate(-game.hero.speed);
        }
        if (!firePressed) {
          firePaused = false;
        }
        if (firePressed && options.numberOfBullets > 0) {
          if (!firePaused) {
            fireBullet();
            firePaused = true;
          }
        }
      }
    })();
    

    【讨论】:

    • 对于这个问题,编码约定是不是有点超出了“范围”?
    • @Cerbrus 不会在那些“模式”(“约定”完全是另一回事)修改范围时。
    • 吹毛求疵:“所有代码都应保持在全局范围之外” 如何做到这一点,是通过实现某种模式。
    • @Cerbrus(更挑剔)这是“最佳实践”。 “约定”是一种普遍接受的做事方式(即命名约定)。有很多方法可以避免全局范围。
    • 你,先生,只是挑剔了我。
    【解决方案4】:

    看起来您正试图在多个位置保持角色(英雄)的状态。这将变得越来越难以在全局范围内维护,因为每个角色的动作/状态都会添加到全局变量中。

    根据@jeff-huijsmans 的建议,我认为您应该保持game 对象内的状态。

    这可以通过几种方式定义:

    1. game.state.firePaused - 这会将您的游戏状态锁定为单个角色,但会更好地包含角色射击的状态。
    2. game.hero.firePaused - 这允许每个角色保持自己的射击状态。这还有一个额外的好处,那就是能够添加更多具有射击状态的角色。

    顺便说一句,这里的大多数答案似乎都试图解决范围问题。全局定义变量并尝试在函数之外维护状态变得非常难以理解/阅读/测试。关于这个话题会有很多意见。幸运的是,对于您的根本问题,您可以通过使用预先存在的状态对象来避免这种情况。

    【讨论】:

      猜你喜欢
      • 2010-10-01
      • 2015-12-14
      • 2018-09-18
      • 2012-05-23
      • 2021-12-14
      • 1970-01-01
      • 2017-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多