开发者问题收集

如何在另一个函数中获取一个函数内的 var?

2020-08-28
162

在我的游戏中,玩家可以选择投掷 2、3 或 4 个骰子,然后投掷所选数量的骰子,数字最大的获胜。但是,当用户选择投掷 3 或 4 个骰子时,我无法在按下提交按钮时触发的事件侦听器内获取 var randomNumber1 - var randomNumber4 。我需要这些 var 才能继续编码。有人可以帮忙吗,谢谢。

在我的 javascript 代码的倒数第四行中, if (noOfChoices === "3") , console.log(userChoices); 未打印,即使上面的 if (noOfChoices === "2") 的情况有效。 chrome dev tools 上显示的错误消息是:

index.js:91 Uncaught ReferenceError: randomNumber1 is not defined
    at HTMLAnchorElement.<anonymous> (index.js:91)

有人可以帮忙吗,谢谢。

这是我的 javascript 代码:

function diceRoll() {
  var randomNumber1 = Math.floor(Math.random() * 6 + 1);
  var Image1 = "dice" + randomNumber1 + ".png";
  document.querySelectorAll("img")[1].setAttribute("src", Image1);

  var randomNumber2 = Math.floor(Math.random() * 6 + 1);
  var Image2 = "dice" + randomNumber2 + ".png";
  document.querySelectorAll("img")[2].setAttribute("src", Image2);

  var randomNumber3 = Math.floor(Math.random() * 6 + 1);
  var Image3 = "dice" + randomNumber3 + ".png";
  document.querySelectorAll("img")[3].setAttribute("src", Image3);

  var randomNumber3 = Math.floor(Math.random() * 6 + 1);
  var Image4 = "dice" + randomNumber4 + ".png";
  document.querySelectorAll("img")[4].setAttribute("src", Image4);
}

// Storing user noOfChoices
let links = document.querySelectorAll('#list li')
links.forEach((el) => {
  el.addEventListener('click', (event) => {
    let numberOfChoices = event.target.innerText
    document.getElementById('dropdownMenu').innerHTML = `${numberOfChoices}<span class="caret"></span>`)})

// Responding to Submit
document.getElementById("submit").addEventListener("click", function(e) {
  e.preventDefault();

// Storing Data into variables
  var choice1 = $("#choice1").val();
  var choice2 = $("#choice2").val();
  var choice3 = $("#choice3").val();
  var choice4 = $("#choice4").val();
  var noOfChoices = $("#dropdownMenu").text();
  var userChoices = [];

// Displaying no. of dices that user chose
    if (noOfChoices === "2") {
      $("#caption1, #caption2").removeClass("invisible");
      $("#caption3, #caption4").addClass("invisible");
    }

    if (noOfChoices === "3") {
      $("#caption1, #caption2, #caption3").removeClass("invisible");
      $("#caption4").addClass("invisible");
    }

    if (noOfChoices === "4") {
      $(".caption").removeClass("invisible");
    }

$("#submit").html("Again");

// Rolling Dice
diceRoll();

// Determining Winner
if (noOfChoices === "2") {if (randomNumber1 > randomNumber2) {$("#title").html(choice1 + " wins! 🏆");}
else if (randomNumber2 > randomNumber1) {$("#title").html(choice2 + " wins! 🏆");}
else if (randomNumber2 = randomNumber1){$("#title").html("Oops, try again!");}
}

if (noOfChoices === "3") {userChoices.push(randomNumber1, randomNumber2,randomNumber3);
console.log(userChoices);
}
})}

这是我的 html:

<head>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</head>

<body>
<div class="container-of-images">
      <img src="chick2.png">

      <figure>
        <img id="img1" class="dice" src="dice6.png">
        <figcaption class="caption" id="caption1">Choice 1</figcaption>
      </figure>

      <figure>
        <img id="img2" class="dice" src="dice6.png">
        <figcaption class="caption" id="caption2">Choice 2</figcaption>
      </figure>

      <figure class="threeChoices">
        <img id="img3" class="dice" src="dice6.png">
        <figcaption class="caption" id="caption3">Choice 3</figcaption>
      </figure>

      <figure class="fourChoices">
        <img id="img4" class="dice" src="dice6.png">
        <figcaption class="caption" id="caption4">Choice 4</figcaption>
      </figure>
      <img src="chick1.png">
    </div>

  <div class="container-of-forms">

    <!-- Dropdown Button -->
    <div class="dropdown">
      <button class="btn btn-info dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
        0
        <span class="caret"></span>
      </button>
      <ul id="list" class="dropdown-menu dropdown-info" aria-labelledby="dropdownMenu">
        <li><a href="#">2</a></li>
        <li><a href="#">3</a></li>
        <li><a href="#">4</a></li>
      </ul>

      <!-- Input Text Fields -->
      <div class="container-inner">  <br>
        <input class="text-input-box" id="choice1" type="text" name="" value="" placeholder="Choice 1"> <br>
        <input class="text-input-box" id="choice2" type="text" name="" value="" placeholder="Choice 2"> <br>
        <input class="text-input-box invisible threeChoices" id="choice3" type="text" name="" value="" placeholder="Choice 3"> <br>
        <input class="text-input-box invisible fourChoices" id="choice4" type="text" name="" value="" placeholder="Choice 4">
      </div>
    </div>

  </div>

  <h5>The bigger number wins!</h5>
  <!-- Submit Button -->
  <a href="" id="submit" class="btn btn-info btn-lg" role="button">Go</a>
</div>
</body>
3个回答

您不能引用这些值,但可以返回这些值并使用它们。简单的方法是使用数组。清理后的代码如下所示

function rollResult () {
  return Math.floor(Math.random() * 6 + 1);
}

function diceRoll() {

  var images = document.querySelectorAll("img");

  const dice = [];
  for (var i = 0; i<4; i++) {
    var number = rollResult();
    dice[i] = number;
    images[i+1].src = "dice" + number + ".png";
  }

  return dice;

}

var diceResult = diceRoll();
console.log(1, diceResult[0]);
console.log(2, diceResult[1]);
console.log(3, diceResult[2]);
console.log(4, diceResult[3]);
epascarello
2020-08-28

您可能不知道,但您问的是一个非常非常重要的 Javascript 概念,称为“作用域”。

变量具有“作用域”。将作用域视为一组可以访问变量的上下文。

变量对于其“当前作用域”及其下面的所有作用域都是可见的。始终存在“全局”作用域,即最外层的运行时环境。如果您查看 diceRoll 函数定义之后的第一行可执行代码,您将看到一个全局作用域变量的示例: links 。只要您不使用某种模块捆绑器来修改作用域(看起来您在这里进行了一些非常基本的编程,所以现在让我们忘掉它),变量 links 将从 程序中的任何位置 访问。

除了全局作用域之外,我们还有其他作用域。其中有一点对您来说非常重要,那就是函数作用域。

函数会创建自己的作用域。这意味着函数之外的任何东西都无法访问函数内部的内容。它是函数自己定义的小作用域。您在其中定义的任何变量都无法在该函数之外访问。如果您思考一下原因,就会明白:函数是临时代码。它们运行,然后就完成了。每次调用函数时,都会创建一个新作用域,处理函数逻辑,然后销毁所有变量引用,只要它们没有传递回调用上下文。这是设计使然。

那么您在这里所做的就是创建变量,例如 randomNumber1 ,这些变量在函数 diceRoll 作用域之外根本不存在。

现在,更高级的编程技术会主张您 永远不要创建全局变量 ,我保证您最终会学会坚持这一点。但是现在,您可以开始了解范围,解决问题的最简单方法是 定义 diceRoll 函数 外部 的变量,然后从 内部 为它们 分配值 。基本上,这将解决您的问题:

// Here, you **define** your variables. This "creates" them, but does not assign them values.
var randomNumber1;
var randomNumber2;
var randomNumber3;
var randomNumber4;
var image1;
var image2;
var image3;
var image4;

// When code execution reaches this point, you could do something like this:
console.log(randomNumber1);
// ^ the above line would log `undefined` to the console, because the variable exists but has no value assigned to it. Great! We're all set, now all your global scope stuff further down can access these variables!

function diceRoll() {
  // Now in here, you simply remove the *var* keyword, since you don't want to *create new variables*, you want to *assign values to the existing ones*. If you use the *var* keyword here, it will create a *new* variable with the same name, but only within the function scope. You will still have the same problem.
  randomNumber1 = Math.floor(Math.random() * 6 + 1);
  image1 = "dice" + randomNumber1 + ".png";
  document.querySelectorAll("img")[1].setAttribute("src", Image1);

  randomNumber2 = Math.floor(Math.random() * 6 + 1);
  image2 = "dice" + randomNumber2 + ".png";
  document.querySelectorAll("img")[2].setAttribute("src", Image2);

  randomNumber3 = Math.floor(Math.random() * 6 + 1);
  image3 = "dice" + randomNumber3 + ".png";
  document.querySelectorAll("img")[3].setAttribute("src", Image3);

  randomNumber3 = Math.floor(Math.random() * 6 + 1);
  image4 = "dice" + randomNumber3 + ".png";
  document.querySelectorAll("img")[4].setAttribute("src", Image4);
}

请注意,作为一个小的语义选择,我以驼峰式命名法(首字母小写)声明了您的 Image[x] 变量名,这是行业标准惯例。

最后但并非最不重要的一点是,让我们回过头来重新阅读这里的重要内容,以帮助您找出问题:错误本身:

index.js:91 Uncaught ReferenceError: randomNumber1 is not defined
  at HTMLAnchorElement.<anonymous> (index.js:91)

让我们将其翻译成英文:

Uncaught ReferenceError

“Uncaught”表示抛出了错误(意味着您尝试执行某些由于某种原因不可能完成的事情而导致执行错误)。什么未被捕获?错误(始终是错误),具体类型为 ReferenceError 。 正如我们的好朋友 MDN 告诉我们的那样: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError ReferenceError 对象表示引用不存在的变量时发生的错误。

基本上,您尝试访问当前范围(在您的情况下为全局范围)中尚未定义的变量。

最后: randomNumber1 未定义 <-- 无需翻译。简而言之,尝试引用变量(randomNumber1)时出现错误,因为它未定义。错误的后半部分告诉您 位置

位于 HTMLAnchorElement。<anonymous> (index.js:91) <-- index.js 在第 91 行。您可以使用它来了解您所在的 范围 ,以便正确引用该范围内的变量并解决问题。

HTMLAnchorElement.<anonymous> 处的 指的是调用堆栈中的当前步骤及其上下文 - 但这更复杂,您现在可以忽略它。

因此,总结一下:

我们已将您想要访问的变量的 声明 移到 函数范围 之外,并保留了 对这些变量的赋值 ,以便它仍然发生在 函数内部 。理解这一点非常重要,我强烈建议您阅读一些有关 Javascript、函数范围和闭包的资料。如果你想成为一名入门级 JS 开发人员,你 绝对需要理解这个概念

很高兴回答你可能有的任何问题。祝你好运!

编辑: @Mosia Thabo 的回答也是一个很好的例子,我认为是“更正确的解决方案”。我故意没有提供那个解决方案,那是为了让它保持你的问题的真正内容,即使你不知道。请注意,我提到“全局作用域变量是你想要始终避免的东西”,你无疑会遇到短语“全局变量是邪恶的”。它们确实如此。

我的例子是为了简化范围的概念,我认为这是这里要学习的重要一课。Mosia 的回答是 我将如何处理它 - 该函数实际上变成了你所说的生成器,它 为您提供结果集 作为返回值。但返回值可能会造成混淆,因此我省略了该部分。

范围。了解。关于。范围。

dudewad
2020-08-28

您需要解决语法错误。此答案仅提供了另一种方法来访问那些您稍后需要使用其值的关键 randomNumber

正如我们在评论部分所强调的那样,您无法直接在 Javascript 中访问该函数之外的函数范围变量。您可以采取各种方法来实现这一点……这是其中之一:

让我们让 diceRoll() 返回掷骰结果的 resultObj,如下所示:

function diceRoll() {
  var randomNumber1 = Math.floor(Math.random() * 6 + 1);
  var Image1 = "dice" + randomNumber1 + ".png";
  document.querySelectorAll("img")[1].setAttribute("src", Image1);

  var randomNumber2 = Math.floor(Math.random() * 6 + 1);
  var Image2 = "dice" + randomNumber2 + ".png";
  document.querySelectorAll("img")[2].setAttribute("src", Image2);

  var randomNumber3 = Math.floor(Math.random() * 6 + 1);
  var Image3 = "dice" + randomNumber3 + ".png";
  document.querySelectorAll("img")[3].setAttribute("src", Image3);

  var randomNumber4 = Math.floor(Math.random() * 6 + 1);
  var Image4 = "dice" + randomNumber4 + ".png";
  document.querySelectorAll("img")[4].setAttribute("src", Image4);

  return {
    One: {
      randomNumber: randomNumber1,
      image: Image1
    },
    Two: {
      randomNumber: randomNumber2,
      image: Image2
    },
    Three: {
      randomNumber: randomNumber3,
      image: Image3
    },
    Four: {
      randomNumber: randomNumber4,
      image: Image4
    }
  }
}

您会注意到我返回了一个有 4 个键的对象,每个键代表一个骰子。每个骰子都有属性 randomNumberimage 。这样,您可以调用 diceRoll 并在稍后通过执行以下操作访问结果:

var result = diceRoll();

骰子已掷出,现在我们可以通过使用 result 访问骰子的属性来获取结果……您可以这样做:


// Determining Winner
if (noOfChoices === "2") {if (result.One.randomNumber > result.Two.randomNumber) {$("#title").html(choice1 + " wins! 🏆");}
else if (result.Two.randomNumber > result.One.randomNumber ) {$("#title").html(choice2 + " wins! 🏆");}
else if (result.Two.randomNumber= result.One.randomNumber){$("#title").html("Oops, try again!");}
}

这适用于骰子 3 和 4。


这是另一个更简单的版本:

function diceRoll() {  
  let DiceKeys = ["One","Two","Three","Four"];
  let resultObject = {};
  
  DiceKeys.forEach((keyName, index)=>{
    let diceRandomNum = Math.floor(Math.random() * 6 + 1);
    let diceImageSrc = "dice" + diceRandomNum + ".png";
    document.querySelectorAll("img")[++index].setAttribute("src", diceImageSrc);
    
    resultObject[keyName] = {
      randomNumber: diceRandomNum,
      imageSrc : diceImageSrc
    };
  });
  
  return resultObject;
}
Mosia Thabo
2020-08-28