【问题标题】:Javascript oop undefined reading length in loop循环中的Javascript oop未定义读取长度
【发布时间】:2022-01-04 13:36:31
【问题描述】:

我正在创建一个类中具有多个功能的 oop 项目。我有一段代码,但是由于我使用的是 bind 它不再起作用了。

    document.getElementById("finalLink").innerHTML +=
        "<a id='FLink' href='https://www.voetbalshop.nl/voetbalschoenen.html#' onclick='location.href=this.href+getLink(url);return false;'>Result</a>";

    class QuestionControl{
        /**
         * @param {QuizPart[]} quiz
         */
        constructor(quiz) {
            this.quiz = quiz;
            this.url = [];
            this.questionNumber = -1;
            this.button = document.getElementById('answer');
            this.questionName = document.getElementById('question');
            this.nextbtn = document.getElementById('nextbtn');
            this.prevbtn = document.getElementById('prevbtn')
            this.resultbtn = document.getElementById('FLink');
        }

        Initialize(){
            this.NextQuestion();
        }

        /**
         *
         * @param {int} question
         * @returns {QuizPart}
         */
        SetQuestion(question){
            if (this.questionNumber >= 0){
                let oldAnswerButton = document.querySelectorAll('.filter_anwser');

                // Deletes old question when the next question is clicked
                for (let answerButton of oldAnswerButton) {
                    answerButton.style.display = 'none';
                }
            }

            this.questionNumber = question;

            let q = this.quiz[question];
            // Check if your at the last question so the next button will stop being displayed.
            if (this.questionNumber === Quiz.length-1) {
                this.nextbtn.style.display = 'none';
                this.prevbtn.style.display = 'block';
                this.resultbtn.style.display = 'grid';
            } else if (this.questionNumber === 0 ) {
                this.nextbtn.style.display = 'block';
                this.prevbtn.style.display = 'none';
                this.resultbtn.style.display = 'none';
            } else{
                this.nextbtn.style.display = 'block';
                this.prevbtn.style.display = 'block';
                this.resultbtn.style.display = 'none';
            }

            // Displays Question
            this.questionName.textContent = q.questionDescription;
            this.questionName.id = "questionID";

            return q;
        }

        NextQuestion() {
            let question = this.SetQuestion.bind(this.questionNumber + 1);

            // Displays answers of the questions
            for (let y = 0; y < question.chosenAnswer.length; y++) {
                let item = question.chosenAnswer[y];
                // Display answer buttons
                let btn = document.createElement('button');
                btn.value = item.id;
                btn.className = "filter_anwser";
                btn.textContent = item.name;
                this.button.appendChild(btn);
            }
        }

        PrevQuestion() {
            let question = this.SetQuestion(this.questionNumber - 1);

            // Displays answers of the questions
            for (let y = 0; y < question.chosenAnswer.length; y++) {
                let item = question.chosenAnswer[y];
                // Display answer buttons

                let btn = document.querySelector('button[value="' + item.id + '"]');
                btn.style.display = 'block';
                console.log(btn);
            }
        }

        /**
         * Returns the parameters for the URL.
         *
         * @returns {string}
         */
        getLink() {
            let tmp = [];
            for (let i = 0; i < this.url.length; i++) {
                // Check if question is from the same quiz part and adds a , between chosen answers and add the right prefix at the beginning
                if (this.url[i].length > 0){
                    tmp.push("" + Quiz[i].prefix + this.url[i].join(","))
                }
            }
            /// If answers are from different quiz parts add a & between answers.
            console.log(this.url, this.questionNumber);
            return "" + tmp.join("&");
        };
    }

    class QuizPart{
        constructor(questionDescription, chosenAnswer, prefix){
            this.questionDescription = questionDescription;
            this.chosenAnswer = chosenAnswer;
            this.prefix = prefix;
        }
    }

    class ChosenAnswer{
        constructor(id, name){
            this.id = id;
            this.name = name;
        }
    }

    let Quiz = [
        new QuizPart('Whats your size?', [
            new ChosenAnswer('6595', '41'),
            new ChosenAnswer('6598', '42'),
            new ChosenAnswer('6601', '43'),
        ], 'bd_shoe_size_ids='),

        new QuizPart('What color would you like?', [
            new ChosenAnswer('6053', 'Red'),
            new ChosenAnswer('6044', 'Blue'),
            new ChosenAnswer('6056', 'Yellow'),
            new ChosenAnswer('6048', 'Green'),
        ], 'color_ids='),

        new QuizPart('What brand would you like?', [
            new ChosenAnswer('5805', 'Adidas'),
            new ChosenAnswer('5866', 'Nike'),
            new ChosenAnswer('5875', 'Puma'),
        ], 'manufacturer_ids='),
    ]

    let control = new QuestionControl(Quiz);
    control.Initialize();

    document.getElementById('nextbtn').addEventListener("click", control.NextQuestion);
    document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion);

    control.button.addEventListener("click", function (e) {
        const tgt = e.target;

        // clear the url array if there's nothing clicked
        if (control.url.length === control.questionNumber) {
            control.url.push([]);
        }

        let quizUrl = control.url[control.questionNumber];

        // Check if a button is clicked. Changes color and adds value to the url array.
        if (quizUrl.indexOf(tgt.value) === -1) {
            quizUrl.push(tgt.value);
            e.target.style.backgroundColor = "orange";
            // Check if a button is clicked again. If clicked again changes color back and deletes value in the url array.
        } else {
            quizUrl.splice(quizUrl.indexOf(tgt.value), 1);
            e.target.style.backgroundColor = "white";
        }
        console.log(control.getLink());
    })

问题是当我开始使用 bind 将函数 SetQuestion 连接到 NextQuestion 时,它给了我以下错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'length')

在 nextQuestion 的 for 循环中,但如果我停止使用 bind,它将无法识别函数 SetQuestion。有人知道为什么会这样吗?

【问题讨论】:

  • "使用bind将函数SetQuestion连接到NextQuestion":什么意思?我在您的代码中没有看到任何bind?通常bind不用于将函数绑定到另一个函数,而是绑定到类实例对象。
  • 你在哪里使用bind?我在您的代码中的任何地方都找不到它。
  • 你的代码不是客户端javascript,不是吗?
  • 在使用它的地方不需要bind。只需致电this.SetQuestion(...)。您需要 bind 的地方是添加事件侦听器时。
  • @trincot 我添加了绑定,我不小心添加了旧代码。我在 NextQuestion 函数的第一行中使用了 bind,但我不太了解 JavaScript 面向对象。我不知道如何正确地做到这一点。

标签: javascript loops oop


【解决方案1】:

这不是bind的正确用法:

let question = this.SetQuestion.bind(this.questionNumber + 1);

bind 返回一个函数;它不执行它。当最终调用返回的函数时,您传递给 bind 的参数应该是用于 this 的值。

由于这不是这里的意图,你应该在这里删除.bind,然后:

let question = this.SetQuestion(this.questionNumber + 1);

问题真的出在这里:

document.getElementById('nextbtn').addEventListener("click", control.NextQuestion);
document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion);

这些点击处理函数将在没有特定this 值的情况下被调用(在严格模式下它将是undefined,否则是全局对象)。这不是您想要的,因为您的代码期望 this 代表 QuestionControl 的一个实例。

这可以通过bind 解决,如下:

document.getElementById('nextbtn').addEventListener("click", control.NextQuestion.bind(control));
document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion.bind(control));

所以不是你写的:

使用bind将函数SetQuestion连接到NextQuestion

bind 用于将函数连接到在函数执行期间必须充当this 值的对象。在你的情况下是control

也可以显式提供绑定函数:

document.getElementById('nextbtn').addEventListener("click", () => control.NextQuestion());
document.getElementById('prevbtn').addEventListener("click", () => control.PrevQuestion());

【讨论】:

  • 谢谢你!我仍然不太擅长 javascript,所以你真的帮助了我!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
相关资源
最近更新 更多