为什么如果我不在当前函数中实例化我的变量,它会在下一个函数中被忽略?

Why is it that if I don't instantiate my variable in the current function, it's ignored in the next function?

我有以下代码片段:

init: function () {
  var $mainSection = $('.main-section'),
      cardId = '',
      cardTitle = '',
      cardSelected = '';

  $('.large-card', $mainSection).each(function () {
    cardId = getCardId(this);
    cardTitle = $('.card-title', this).text().trim();

    cardsModel[cardId] = { title: cardTitle, id: cardId, selected: false };

    $('.checkbox', this).on('click', function () {
      cardSelected = cardsModel[cardId].selected;

      cardsModel[cardId].selected = !cardSelected;
    });
  });
},
getCardId: function (card) {
  var prop, value;

  for (prop in card.classList) {
    if (card.classList.hasOwnProperty(prop)) {
      value = card.classList[prop];
      if (value.startsWith('product-id--')) {
        return value.replace('product-id--', '');
      }
    }
  }
},
cardsModel: []

我习惯于尽可能将所有 var 定义写在我的函数的顶部,正如一些同事的建议,他告诉我这对 minifiers 等更好。

问题是,如果我不在第 8 行使用 var,例如,每个 .checkbox 元素上的 click 事件总是得到 cardId 作为最后一个 .large-card ID。因此,假设,如果我有 5 张 ID 分别为 1、2、3、4 和 5 的卡片,如果我单击它们的任何复选框,它总是会更新 ID 5。

但是,如果我使用 var cardId = getCardId(this);,代码会按预期运行。

我可能遗漏了一些非常基本的东西,但有人能解释一下为什么会这样吗?

老实说,这让我害怕再次写下这样的 var 定义,哈哈。

是的,我搜索过类似的问题,如果这是一个骗局,我可能不知道如何搜索它,如果是,我深表歉意。

谢谢!

这与 JavaScript 处理 scopehoisting 的方式有关。 This page 对这些概念进行了很好的总结:

JavaScript has two scopes – global and local. Any variable declared outside of a function belongs to the global scope, and is therefore accessible from anywhere in your code. Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions.

注意:上面强调的是我的。从 EcmaScript 2015 开始,这并不完全正确。 let 运算符的使用也允许 block 作用域。更多内容见下文。

在您上面的示例中,您首先在 init 函数中对 cardId 变量进行 decalre。这意味着 cardId 的范围对于 init 函数 任何嵌套函数都是局部的。由于您没有在 .each() 回调中重新声明 cardId,因此 JavaScript 引擎将在创建它之前爬上作用域链以查看它是否存在于另一个可访问作用域或全局作用域中在全局范围内(如果尚不存在)。

重要的是要注意,在分配时,如果以前没有定义变量,将在全局范围级别自动创建变量(除了可能在严格模式下,我不确定)。但是,尝试为操作引用 undefined 变量,例如比较将抛出 ReferenceError 指出变量未定义。

最后,请注意,与某些其他语言不同,varfunction 范围内运行,而不是在 block 范围内运行。要在 JavaScript 中使用 block 范围,您可以使用新的 let 运算符。 Here 是一篇很好的文章,描述了 JavaScript 中 varlet 的范围和正确用法。

正如War10ck上面提到的,它是关于作用域和提升的。这不仅与 Javascript 相关,而且大多数编程语言中都存在全局和局部作用域。可能有多种解决方案来解决这个问题,但我自己通常会初始化我知道我将在文件顶部的多个本地范围内使用的变量,例如:

let exampleVariable;

但是要小心。你应该只在你真的需要的时候使用它,因为太多的全局变量会让你的代码很快变得混乱。另外,你应该尽量不要使用 var,因为它也会在你的代码中为你的全局范围带来一些“问题”,而是使用 ES6 的 let & const。