如何让JavaScript按顺序遍历一组预定义的响应?

前端开发 2026-07-11

我想创建一个设置,让用户可以和一个聊天机器人对话,机器人会按顺序依次遍历一个预设的回答列表。机器人每次接收到用户输入时都会回应,但输入值并不决定回应内容。本质上,每次用户点击“发送”按钮,机器人应该给出列表中的下一个单词。

当前机器人每次都回应列表中的最后一项。我认为需要解决的问题基本是:

  • 让函数能够连续遍历列表,并跟踪上一次使用的是哪一项

  • 让函数在用户点击提交时生成一个新的单词,而无需重新加载页面(这是我在过去创建随机单词生成器时也遇到的问题)

以下是我的代码,我希望机器人按顺序一次给出一个水果列表中的项。

<div class="chatbox">
  <div class="chat-window" id="chat-window">

    <!-- Messages displayed here -->

  </div>
  <div class="chat-input">
    <input type="text" id="message-input" placeholder="Type a message...">
    <button id="send-button">Send</button>
  </div>
</div>
document.getElementById('send-button').addEventListener('click', sendMessage);
document.getElementById('message-input').addEventListener('keypress', function (e) {
  if (e.key === 'Enter') {
    sendMessage();
  }
});


function sendMessage() {
  var input   = document.getElementById('message-input');
  var message = input.value.trim();

  if (message === '') return;

  appendMessage('user', message);
  input.value = '';

  setTimeout(() => {
    appendMessage('bot', botMessage);
  }, 1000);
}

var words = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew"]

for (var index = 0, len = words.length; index < len; ++index) {
  var botMessage = words[index];
  console.log(botMessage);
}

function appendMessage(sender, message) {
  var chatWindow     = document.getElementById('chat-window');
  var messageElement = document.createElement('div');
  messageElement.classList.add('message', sender);
  messageElement.textContent = message;
  chatWindow.appendChild(messageElement);
  chatWindow.scrollTop = chatWindow.scrollHeight;
}

解决方案

你说:

当前机器人每次都回应列表中的最后一个项。我认为需要解决的问题基本是:

  1. 让函数能够连续遍历列表,并跟踪上一次使用的是哪一项
  2. 在用户点击提交时运行函数以生成一个新的单词,而无需重新加载页面(这是我在过去创建随机单词生成器时也遇到的问题)

由于你没有展示与第2点相关的任何代码(因为它实际上是一个完全独立的问题,应该放在一个单独的提问中),所以我将忽略它,专注于第1点。


在函数 sendMessage 中,你使用定时器来创建「机器人回应」:

  setTimeout(() => {
    appendMessage("bot", botMessage);
  }, 1000);

这个 botMessage 是对外部作用域中的一个变量的引用。它是通过遍历数组 words 来创建的:

var words = [
  "apple",
  "banana",
  "cherry",
  "date",
  "elderberry",
  "fig",
  "grape",
  "honeydew",
];

for (var index = 0, len = words.length; index < len; ++index) {
  var botMessage = words[index];
  console.log(botMessage);
}

循环每次迭代时,botMessage 的值都会被更新(被覆盖),因此当循环执行到最后一次时,botMessage 的值等于数组中的最后一个元素("honeydew")。因此,每次创建「机器人回应」时,值总是 "honeydew"

这与你的观察结果一致:

当前机器人每次都回应列表中的最后一项。

为了修复这个问题,你需要在列表中跟踪“当前的位置”,以便每次需要创建机器人消息时,可以使用数组中的相应单词,而不是始终引用一个静态变量。

因为数组是按整数索引的,你可以只需要跟踪索引并在每次增加——当你到达数组的最后一个索引时,将其设回 0 以实现循环。你可以这样写,使用取模运算符 %(链接保留原样):

const words = [
  "apple",
  "banana",
  "cherry",
  "date",
  "elderberry",
  "fig",
  "grape",
  "honeydew",
];

let wordIndex = 0;

function getNextWord() {
  const word = words[wordIndex];
  wordIndex = (wordIndex + 1) % words.length;
  return word;
}

然后,在函数 sendMessage 中,你可以在 setTimeout 之前像这样初始化 botMessage

  const botMessage = getNextWord();
  setTimeout(() => appendMessage("bot", botMessage), 1000);

如果这是你反复遇到的场景,你可以创建一个在数组上循环的抽象,使用一个实现了 迭代协议,来维护索引状态——例如:

class CyclingIterator {
  #index;

  constructor(array) {
    this.array = [...array];
    this.#index = 0;
  }

  [Symbol.iterator]() {
    return this;
  }

  next() {
    const value = this.array[this.#index];
    this.#index = (this.#index + 1) % this.array.length;
    return { value };
  }
}

const cycleWords = new CyclingIterator([
  "apple",
  "banana",
  "cherry",
  "date",
  "elderberry",
  "fig",
  "grape",
  "honeydew",
]);

……并在函数 sendMessage 中,你可以通过从迭代器中获取下一个值来创建 botMessage

  const botMessage = cycleWords.next().value;
  setTimeout(() => appendMessage("bot", botMessage), 1000);

下面是一个包含你问题中的代码以及上述修改的代码片段:

document.getElementById('send-button').addEventListener('click', sendMessage);
document.getElementById('message-input').addEventListener('keypress', function (e) {
  if (e.key === 'Enter') {
    sendMessage();
  }
});


class CyclingIterator {
    #index;

    constructor(array) {
        this.array = [...array];
        this.#index = 0;
    }

    [Symbol.iterator]() {
        return this;
    }

    next() {
        const value = this.array[this.#index];
        this.#index = (this.#index + 1) % this.array.length;
        return { value };
    }
}

const cycleWords = new CyclingIterator([
    "apple",
    "banana",
    "cherry",
    "date",
    "elderberry",
    "fig",
    "grape",
    "honeydew",
]);

function sendMessage() {
    var input = document.getElementById("message-input");
    var message = input.value.trim();

    if (message === "") return;

    appendMessage("user", message);
    input.value = "";

    const botMessage = cycleWords.next().value;
    setTimeout(() => appendMessage("bot", botMessage), 1000);
}


function appendMessage(sender, message) {
  var chatWindow     = document.getElementById('chat-window');
  var messageElement = document.createElement('div');
  messageElement.classList.add('message', sender);
  messageElement.textContent = message;
  chatWindow.appendChild(messageElement);
  chatWindow.scrollTop = chatWindow.scrollHeight;
}
<div class="chatbox">
  <div class="chat-window" id="chat-window">

    <!-- Messages displayed here -->

  </div>
  <div class="chat-input">
    <input type="text" id="message-input" placeholder="Type a message...">
    <button id="send-button">Send</button>
  </div>
</div>

备选方案

  1. 捕捉输入和回车键的正确方式,是使用一个表单并阻止 submit 事件。
  2. 你需要一个全局变量来记住上次使用的单词。

Notes:

  1. 你的机器人回应与消息并不完全同步。例如,用户可能在收到意外答案前提问两次。我已经通过使用IIFE修复了这一点。
  2. 如果你的页面重新加载,你仍然可以在开发者工具中持久化日志时看到控制台中的错误。
var currentIndex = 0;

var words = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew"]


document.getElementById('frm').addEventListener('submit', function(ev) {
  ev.preventDefault();
  sendMessage()
});



function sendMessage() {
  var input = document.getElementById('message-input');
  var message = input.value.trim();
  if (message === '') return;
  appendMessage('user', message);

  input.value = '';
  input.focus();

  botMessage = words[currentIndex];
  currentIndex = (currentIndex + 1) % words.length;

  // copy the botMessage into a function that would remember it
  (function(botMessage) {
    setTimeout(() => {
      appendMessage('bot', botMessage);
    }, 1000);
  })(botMessage);
}


function appendMessage(sender, message) {
  var chatWindow = document.getElementById('chat-window');
  var messageElement = document.createElement('div');
  messageElement.classList.add('message', sender);
  messageElement.textContent = message;
  chatWindow.appendChild(messageElement);
  chatWindow.scrollTop = chatWindow.scrollHeight;
}
.bot {
  color: green;
}

.user {
  color: blue;
}
<div class="chatbox">
  <div class="chat-window" id="chat-window">

    <!-- Messages displayed here -->

  </div>
  <div class="chat-input">
    <form id="frm">
      <input type="text" id="message-input" placeholder="Type a message...">
      <button>Send</button>
    </form>
  </div>
</div>
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章