Modern JavaScript Tutorial 数组实战:用 sumInput() 实现 prompt 循环输入求和

原创2026-10-02 15:23:131,604 阅读
文章标签:文档/教程前端

Modern JavaScript Tutorial 数组实战:用 sumInput() 实现 prompt 循环输入求和

导读

sumInput() 是 Modern JavaScript Tutorial 中《Arrays(数组)》章节的核心练习,它要求我们用 prompt 循环收集用户输入的数字存入数组,在遇到非数字、空字符串或取消时停止,最后返回所有元素之和。它同时锤炼了四个基础能力:数组的 push 追加与 for..of 遍历、prompt 返回值的类型判断、一元加号 + 的显式数值转换,以及 isFinite 的输入合法性校验。读完本文,你将掌握一段可直接运行的、对空字符串与零做了精确区分的数组求和参考实现,并理解其背后"延迟类型转换"的精妙设计。

任务回顾:需要满足哪些约束

原任务定义在 task.md 中,要求实现函数 sumInput(),行为如下:

  • 使用 prompt 向用户询问数值,并把得到的值存入数组;
  • 当用户输入非数值、空字符串或按下 "Cancel" 时结束输入;
  • 计算并返回数组各项之和。

其中有一句特别提示(P.S.):零 0 是合法数字,请不要在遇到零时停止输入。这句话是整个解法设计的出发点,也是最容易写错的地方——如果像下面这样写,就会出现问题:

let value = +prompt("A number please?", 0);
if (value === 0) break; // 错误:会把合法的 0 误判为"停止信号"

因为空字符串在数值转换后恰好也是 0(详见下文),一旦在读取后立刻转换,就无法区分"用户按了确定但没输入"和"用户真的输入了 0"。

参考解法:完整代码

仓库给出的参考实现位于 solution.md,代码如下:

function sumInput() {

  let numbers = [];

  while (true) {

    let value = prompt("A number please?", 0);

    // should we cancel?
    if (value === "" || value === null || !isFinite(value)) break;

    numbers.push(+value);
  }

  let sum = 0;
  for (let number of numbers) {
    sum += number;
  }
  return sum;
}

alert( sumInput() );

运行流程:

  1. 创建空数组 numbers 用于暂存输入;
  2. 进入 while (true) 无限循环,每次用 prompt("A number please?", 0) 询问用户;
  3. 命中任一"停止条件"立即 break:输入为空字符串、用户按 Cancel(得到 null)、或输入不是有限数字(!isFinite(value));
  4. 通过校验后,用 numbers.push(+value) 把值转成数字追加到数组末尾;
  5. 循环结束后用 for..of 遍历求和,返回 sum。

关键细节:为什么不能立刻把 value 转成数字

原解法开头有一段专门说明("Please note the subtle, but important detail..."):不要在 prompt 之后立刻执行 value = +value 的转换。

原因在于:执行 value = +value 之后,空字符串会被转换成数字 0(空串在数值转换规则下结果为 0),于是我们就再也无法区分下面两种情况了:

  • 空字符串(用户没输入内容,应视为"停止信号");
  • 数字 0(用户输入了合法的零,应当继续收集)。

这正是任务 P.S. 强调"不要在零上停止输入"的原因。参考解法的策略是:先用原始字符串形态完成全部停止条件的判断,再在 push 时用一元加号 +value 一次性完成转换,从而既保留了"空串即停止"的语义,又让 0 得以作为合法数字正常入数组。

这个思路与同仓库中另一个练习 3-repeat-until-number/solution.md 形成鲜明对照:那个 readNumber() 练习是先循环等输入变成"常规数字",再在停止后单独把 null 和空串特判为返回 null,否则转成数字会得到 0。两题处理顺序不同,但本质都在应对"空串/取消在数值上等价于 0"这一陷阱。

深挖实现细节:prompt、isFinite 与 +value 各司其职

prompt 永远返回字符串

浏览器 prompt 的返回值只有两种情况:用户点确定时返回字符串(即使只输入了数字,得到的也是 "5" 这样的字符串;如果什么都没输入,则是空字符串 ""),用户点取消时返回 null。因此,value 在整个判断阶段都是一个字符串或 null,而不是数字——这是后面所有判断成立的前提。

三个停止条件的含义

if (value === "" || value === null || !isFinite(value)) break;
条件 触发场景 说明
value === "" 用户点确定但未输入任何内容 空字符串,视为停止信号
value === null 用户点击 Cancel 明确表示放弃输入
!isFinite(value) 输入了非数字内容,如 "abc"、"12px" 无法解析为有限数字,视为非法输入

第三项 isFinite 是关键的兜底校验。根据 数字类型章节的 Tests: isFinite and isNaN 小节,isFinite(value) 会先把参数转成数字,再判断它是否为"常规数字"(不是 NaN、Infinity、-Infinity):

alert( isFinite("15") );     // true,字符串可以转成数字
alert( isFinite("str") );    // false,转换结果是 NaN
alert( isFinite(Infinity) ); // false,无穷大不是有限数字

所以 "abc"、"12px" 这类输入会得到 false,!isFinite 为真,循环终止。同时注意 isFinite 是宽松校验:它接受字符串并自动转换,正因如此 prompt 返回的字符串可以直接交给它判断;如果改用 Number.isFinite 这种严格版本,字符串一律返回 false,反而会让任何正常输入都触发停止。

push(+value):入数组时的显式数值转换

通过校验后,numbers.push(+value) 中的一元加号把字符串显式转成数字再入数组。根据 类型转换章节的 Numeric Conversion 小节,数值转换规则是:字符串首尾空白被去除,剩余为空串则结果为 0,能完整读出数字就转成对应数字,否则得到 NaN。由于非法字符串已经被 isFinite 拦下,此刻转换必然成功——例如 "5" 变成 5、"0" 变成 0,存入 numbers 的都是真正的 number 类型,后续求和才是纯数字运算,而不是字符串拼接。

for..of 遍历求和

求和阶段使用 for..of 而非 for..in:

let sum = 0;
for (let number of numbers) {
  sum += number;
}

依据 数组章节的 Loops 小节,for..of 是遍历数组元素值的现代简洁语法,只关心值、不涉及索引;而 for..in 会遍历包括非数字属性在内的所有属性、且性能更差,数组场景应避免使用。这里每个元素都是数字,sum += number 就是普通的数值累加。

边界情况验证:这份解法是否完备

用户输入 判定 结果
"5"、"10" isFinite 为 true,push 转换后入数组 继续收集
"0" 空串判断 value === "" 为 false("0" 非空),isFinite("0") 为 true 合法数字,入数组继续
""(确定但留空) 命中 value === "" 停止
点击 Cancel 命中 value === null 停止
"abc"、"3.14abc" isFinite 返回 false 停止

输入 ["5", "0", "10"] 时,最终返回 15,其中 0 被完整计入,不会中断流程——这正是任务 P.S. 要求的行为。

一处可以进一步加固的地方

参考解法用 for..of 求和,简单直接。如果希望更函数式,也可以改用数组内置的 reduce,二者结果等价:

return numbers.reduce((sum, number) => sum + number, 0);

(reduce 的完整介绍见 array-methods 章节。)注意:本任务的解法主体是原文档的参考实现,for..of 与 push 组合更贴近前面数组章节的教学主线,直接复刻原解法即可通过全部约束。

小结

sumInput() 是一个小而有代表性的综合练习,它的价值不在于代码量,而在于一个容易被忽视的细节:判断输入是否该停止,必须在类型转换之前基于原始字符串完成。延后到 push 阶段再执行 +value,才能在"空字符串"与"数字 0"之间保留清晰边界。配合 isFinite 的宽松校验与 for..of 的遍历求和,这套参考实现完整覆盖了任务的全部约束,也是阅读 数组章节、数字类型校验 与 类型转换 三章内容后的理想串联练习。

登录后查看全文
en.javascript.info