开发者问题收集

随机值不在数组/字典的范围内

2021-05-14
102

我本周刚开始学习 Javascript,想写一个文字游戏来帮助我的孩子。我的想法是,它应该显示并播放来自 dictionaryrandom 单词,她写下它,检查 entry 并从 dictionary 中删除 random 。游戏继续,直到字典 length===0 ,如果有错误的单词,则将其总结出来。不知何故,该程序不可预测,它实际上在 7 次中只有 1 次有效,我不明白为什么。给出以下错误:

Uncaught (in promise) TypeError: Cannot read property 'word' of undefined

我确实认为它与我删除 random 的方式有关,或者检查 dictionary 是否为空。代码下方粘贴了指向 console.log 屏幕截图的链接; 1 是程序完全完成的结果,另一个是没有完成的结果。有趣的是,错误也是不可预测的,有时只出现 1 个字,有时出现 2 个字。我唯一做的就是刷新页面,程序的行为就不同了。我也尝试在不同的浏览器上运行它。

作为一个完全的菜鸟,我很惊讶在尝试做同样的事情时得到了不同的结果。发现发生了什么事真是令人沮丧 :-)

<html>
<head>
    <title>Aliyah's dictee spel</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div id="header">
    <h1>Hej! Velkommen til Aliyahs diktee!</h1>
    </div>
    <div id="Random_word">
        <h2 id="Empty">Click start to start</h2>
        <button id="startGame">Start</button>
        <button id="editList">Edit word list</button>
        <h3 id="correctWord"></h3>

    </div>
    <script>
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    var dictionary = [
        {   word: "apple",
            audio: 'apple.mp3',
        },
        {
            word: "baby",
            audio: 'baby.mp3',
        },
        {
            word: "car",
            audio: 'car.mp3'
        }

    ];

    var wordsWrong = [];
    var wordsCorrectCounter = 0;
    var wordsWrongCounter = 0;
    //var cheer = new Audio('correct.mp3');
    //var boo = new Audio('wrong.mp3');

    function editWords(){
        console.log("under construction");
    };

    function startGame(){
        document.getElementById("startGame").remove();
        document.getElementById("editList").remove();
        newWord(dictionary);
    };
    
    async function newWord(dictionary)
    {
        if (Object.entries(dictionary).length === 0){
            endGame();
        }
            else {
                var random = Math.floor(Math.random() * dictionary.length);
                document.getElementById("Empty").innerHTML = dictionary[random].word;
                console.log(dictionary[random].word);
                console.log(dictionary);
                await sleep(2000);
                document.getElementById("Empty").innerHTML = "       ";
                createInputField(random);
            }
    };

    function createInputField(random)
    {
        var entry = document.createElement("input");
        entry.setAttribute("type", "text");
        entry.id = "inputfield";
        document.body.appendChild(entry);
        let btn = document.createElement("button");
        btn.id = "okBtn";
        btn.innerHTML = "ok";
        btn.type = "submit";
        btn.name = "answerBtn";
        document.body.appendChild(btn);
        document.getElementById("okBtn").addEventListener("click", () => checkAnswer(random, entry.value));
    };

    function checkAnswer(random, entry)
        {var answer = entry.toLowerCase();
    
        if (dictionary[random].word == answer){
            //cheer.play();
            wordsCorrectCounter += 1;
            document.getElementById("okBtn").remove();
            document.getElementById("inputfield").remove();
            delete dictionary[random];
            console.log(dictionary);
            newWord(dictionary);
        }
            else{
                wordsWrong.push(dictionary[random].word);
                wordsWrongCounter += 1;
                document.getElementById("okBtn").remove();
                document.getElementById("inputfield").remove();
                //boo.play();
                document.body.style.backgroundColor = "red";
                document.getElementById("correctWord").innerHTML = dictionary[random].word;
                let btn = document.createElement("button");
                btn.id = "contBtn";
                btn.innerHTML = "Continue";
                btn.type = "submit";
                btn.name = "continueBtn";
                document.body.appendChild(btn);
                document.getElementById("contBtn").addEventListener("click", () => wrongAnswer(random));
            }
    };

    function wrongAnswer(random){
        document.getElementById("contBtn").remove();
        document.getElementById("correctWord").innerHTML = "    "
        delete dictionary[random];
        newWord(dictionary);
    };

    function endGame()
    {
        document.getElementById("Empty").innerHTML = "you are done!" + "Correct: " + wordsCorrectCounter + "Wrong: " + wordsWrongCounter 
        + "These words were wrong: " + wordsWrong;
        

    };

    function Refresh() {
        window.parent.location = window.parent.location.href;
    };
 
    document.getElementById("startGame").addEventListener("click", () => startGame());



    

    </script>
</body>
</html>

程序能够完成时的 Console.log

程序卡住时的 Console.log

2个回答

简要说明

dictionary = [
    { word: "apple", audio: 'apple.mp3', },
    { word: "baby", audio: 'baby.mp3', },
    { word: "car", audio: 'car.mp3' }
];

您生成一个随机索引,假设为 2
您可以使用 2 并通过 dictionary[2].word

如果您传递了 随机索引
在您 从字典中删除项目 后,它可能会 无效
这就是您收到错误的原因

例如:
您有 旧的 随机索引 2
但您已经删除了该项目
当前字典是

dictionary = [
    { word: "apple", audio: 'apple.mp3', },
    { word: "baby", audio: 'baby.mp3', },
];

然后您尝试访问 dictionary[2]
它不再存在

从数组中删除项目

您可以使用 Array.filter()

let dictionary = [
    { word: "apple", audio: 'apple.mp3', },
    { word: "baby", audio: 'baby.mp3', },
    { word: "car", audio: 'car.mp3' }
];
// only the item that item.word != "apple" will pass this
dictionary = dictionary.filter(item => item.word != "apple");

console.log(dictionary);

字符串和变量

模板文字 (模板字符串)
使用它来设置字符串,它更具可读性 &更容易编辑
您可以使用 ${variable_name> 将变量放入其中

示例:

let x = "test";
console.log(`this is a ${x}`);

在数组中查找特定项目

我看到您有音频文件
如果您需要在字典中查找该项目
您可以使用 Array.find()
因为我们现在只传递单词
我们可以用它来找到它
假设您想为 apple
找到音频 它会像这样

let dictionary = [
            { word: "apple", audio: 'apple.mp3', },
            { word: "baby", audio: 'baby.mp3', },
            { word: "car", audio: 'car.mp3' }
        ],
    target = dictionary.find(item => item.word=="apple"),
    audio = false;
if(target) audio = target.audio;
console.log(audio);

完整答案

我已经添加了评论,您可以检查它

<html>

<head>
    <title>Aliyah's dictee spel</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
    <div id="header">
        <h1>Hej! Velkommen til Aliyahs diktee!</h1>
    </div>
    <div id="Random_word">
        <h2 id="Empty">Click start to start</h2>
        <button id="startGame">Start</button>
        <button id="editList">Edit word list</button>
        <h3 id="correctWord"></h3>

    </div>
    <script>
        function sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

        var dictionary = [
            { word: "apple", audio: 'apple.mp3', },
            { word: "baby", audio: 'baby.mp3', },
            { word: "car", audio: 'car.mp3' }
        ];

        var wordsWrong = [];
        var wordsCorrectCounter = 0;
        var wordsWrongCounter = 0;
        //var cheer = new Audio('correct.mp3');
        //var boo = new Audio('wrong.mp3');

        function editWords() {
            console.log("under construction");
        }

        function startGame() {
            document.getElementById("startGame").remove();
            document.getElementById("editList").remove();
            newWord();
        }

        // dictionary is global variable, you don't need to pass it to access it
        async function newWord() {

            // I add this so the color will be reset after click continue
            document.body.style.backgroundColor = "";

            if (Object.entries(dictionary).length === 0) {
                endGame();
            } else {
                var random = Math.floor(Math.random() * dictionary.length),
                    // get the random word here when it still exist in dictionary
                    random_word = dictionary[random].word;

                document.getElementById("Empty").innerHTML = random_word;
                console.log(random_word);
                console.log(dictionary);
                await sleep(2000);
                document.getElementById("Empty").innerHTML = "       ";

                // direct pass the ramdom word, not the ramdom index
                // ramdom index could be invalid after you remove item from dictionary
                // which is why you get the error
                createInputField(random_word);
            }
        }

        function createInputField(random_word) {
            var entry = document.createElement("input");
            entry.setAttribute("type", "text");
            entry.id = "inputfield";
            document.body.appendChild(entry);
            let btn = document.createElement("button");
            btn.id = "okBtn";
            btn.innerHTML = "ok";
            btn.type = "submit";
            btn.name = "answerBtn";
            document.body.appendChild(btn);
            document.getElementById("okBtn").addEventListener("click", () => checkAnswer(random_word, entry.value));
        }

        function checkAnswer(random_word, entry) {
            var answer = entry.toLowerCase();

            if (random_word == answer) {
                //cheer.play();

                // if you only need +1, you can use ++
                wordsCorrectCounter++;

                document.getElementById("okBtn").remove();
                document.getElementById("inputfield").remove();

                // use Array.filter() to remove random_word(answer) from the dictionary
                // only word != random_word will pass
                dictionary = dictionary.filter(item => item.word != random_word);
                console.log(dictionary);
                newWord();
            } else {
                // I didn't see this, so I add it
                // if you only need +1, you can use ++
                wordsWrongCounter++;

                // because we pass the random_word(answer) now, we can just push it
                wordsWrong.push(random_word);

                document.getElementById("okBtn").remove();
                document.getElementById("inputfield").remove();
                //boo.play();
                document.body.style.backgroundColor = "red";
                document.getElementById("correctWord").innerHTML = random_word;
                
                let btn = document.createElement("button");
                btn.id = "contBtn";
                btn.innerHTML = "Continue";
                btn.type = "submit";
                btn.name = "continueBtn";
                document.body.appendChild(btn);

                document.getElementById("contBtn").addEventListener("click", () => wrongAnswer(random_word));
            }
        }

        function wrongAnswer(random_word) {
            document.getElementById("contBtn").remove();
            document.getElementById("correctWord").innerHTML = "    ";
            // same as above
            // use Array.filter() to remove correct_word(answer) from the dictionary
            // only word != correct_word will pass
            dictionary = dictionary.filter(item => item.word != random_word);
            newWord();
        }

        function endGame() {
            /*
            use `` to set string, it more readable & easier to edit
            you can put variable in it by use ${variable_name}

            example: 
            let x = "test";
            console.log(`this is a ${x}`);
            
            result:
            this is a test
            */
            document.getElementById("Empty").innerHTML =
                `you are done! Correct: ${wordsCorrectCounter} Wrong: ${wordsWrongCounter} These words were wrong: ${wordsWrong}`;
        }

        function Refresh() {
            window.parent.location = window.parent.location.href;
        }

        document.getElementById("startGame").addEventListener("click", () => startGame());
    </script>
</body>

</html>
FlYiNGPoTAToChiP
2021-05-14

我刚刚在你的代码中发现了这个问题,这有点概念性,没什么大不了的。

看看这个控制台截图中出了什么问题

你可以看到删除单词(对象)后,字典(数组)的长度仍然相同。 因为您使用了: delete 关键字,该关键字删除一个项目并将其替换为 empty ,并且数组的大小保持不变。
因此,删除第一个单词后的新词典变为: [empty, {...}, {...}] 现在,每当您尝试获取 dictionary[0].word 时, 都会出现错误:无法读取 undefine 的属性,因为它是空的

除了使用 delete 关键字外,您还可以简单地使用 dictionary.splice(random, 1)

查看使用 splice 后的控制台屏幕截图

<html>
<head>
    <title>Aliyah's dictee spel</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div id="header">
    <h1>Hej! Velkommen til Aliyahs diktee!</h1>
  </div>
  <div id="Random_word">
    <h2 id="Empty">Click start to start</h2>
    <button id="startGame">Start</button>
    <button id="editList">Edit word list</button>
    <h3 id="correctWord"></h3>

  </div>
  <script>
    function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    var dictionary = [{
        word: "apple",
        audio: 'apple.mp3',
      },
      {
        word: "baby",
        audio: 'baby.mp3',
      },
      {
        word: "car",
        audio: 'car.mp3'
      }

    ];

    var wordsWrong = [];
    var wordsCorrectCounter = 0;
    var wordsWrongCounter = 0;
    var cheer = new Audio('correct.mp3');
    var boo = new Audio('wrong.mp3');

    function editWords() {
      console.log("under construction");
    };

    function startGame() {
      document.getElementById("startGame").remove();
      document.getElementById("editList").remove();
      newWord(dictionary);
    };

    async function newWord(dictionary) {
      if (Object.entries(dictionary).length === 0) {
        endGame();
      } else {
        var random = Math.floor(Math.random() * dictionary.length);
        console.log(random)
        document.getElementById("Empty").innerHTML = dictionary[random].word;
        console.log(dictionary[random].word);
        console.log(dictionary);
        await sleep(2000);
        document.getElementById("Empty").innerHTML = "       ";
        createInputField(random);
      }
    };

    function createInputField(random) {
      var entry = document.createElement("input");
      entry.setAttribute("type", "text");
      entry.id = "inputfield";
      document.body.appendChild(entry);
      let btn = document.createElement("button");
      btn.id = "okBtn";
      btn.innerHTML = "ok";
      btn.type = "submit";
      btn.name = "answerBtn";
      document.body.appendChild(btn);
      document.getElementById("okBtn").addEventListener("click", () => checkAnswer(random, entry.value));
    };

    function checkAnswer(random, entry) {
      var answer = entry.toLowerCase();

      if (dictionary[random].word == answer) {
        cheer.play();
        wordsCorrectCounter += 1;
        document.getElementById("okBtn").remove();
        document.getElementById("inputfield").remove();
        dictionary.splice(random, 1);
        console.log(dictionary);
        newWord(dictionary);
      } else {
        wordsWrong.push(dictionary[random].word);
        wordsWrongCounter += 1;
        document.getElementById("okBtn").remove();
        document.getElementById("inputfield").remove();
        boo.play();
        document.body.style.backgroundColor = "red";
        document.getElementById("correctWord").innerHTML = dictionary[random].word;
        let btn = document.createElement("button");
        btn.id = "contBtn";
        btn.innerHTML = "Continue";
        btn.type = "submit";
        btn.name = "continueBtn";
        document.body.appendChild(btn);
        document.getElementById("contBtn").addEventListener("click", () => wrongAnswer(random));
      }
    };

    function wrongAnswer(random) {
      document.getElementById("contBtn").remove();
      document.getElementById("correctWord").innerHTML = "    "
      delete dictionary[random];
      newWord(dictionary);
    };

    function endGame() {
      document.getElementById("Empty").innerHTML = "you are done!" + "Correct: " + wordsCorrectCounter + "Wrong: " + wordsWrongCounter +
        "These words were wrong: " + wordsWrong;


    };

    function Refresh() {
      window.parent.location = window.parent.location.href;
    };

    document.getElementById("startGame").addEventListener("click", () => startGame());
  </script>
</body>

</html>
Aman Singour
2021-05-14