JavaScript 基础知识 3
#空值合并运算符 '??'
既不是 null 也不是 undefined 的表达式称为“已定义的(defined)”。
空值合并运算符(nullish coalescing operator)的写法为两个问号 ??。
a ?? b 的结果是:
如果 a 是已定义的,则结果为 a,
如果 a 不是已定义的,则结果为 b。
换句话说,如果第一个参数不是 null/undefined,则 ?? 返回第一个参数。否则,返回第二个参数。
我们还可以使用 ?? 序列从一系列的值中选择出第一个非 null/undefined 的值。
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
// 显示第一个已定义的值:
alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder
与 || 比较
或运算符 || 可以以与 ?? 运算符相同的方式使用。
空值合并运算符 ?? 是最近才被添加到 JavaScript 中的,它的出现是因为人们对 || 不太满意。
它们之间重要的区别是:
|| 返回第一个 真 值。
?? 返回第一个 已定义的 值。
换句话说,|| 无法区分 false、0、空字符串 "" 和 null/undefined。它们都一样 —— 假值(falsy values)。如果其中任何一个是 || 的第一个参数,那么我们将得到第二个参数作为结果。
例如,考虑下面这种情况:
let height = 0;
alert(height || 100); // 100
alert(height ?? 100); // 0
优先级
?? 运算符的优先级相当低:在 MDN table 中为 5。因此,?? 在 = 和 ? 之前计算,但在大多数其他运算符(例如,+ 和 *)之后计算。
因此,如果我们需要在还有其他运算符的表达式中使用 ?? 进行取值,需要考虑加括号:
let height = null;
let width = null;
// 重要:使用括号
let area = (height ?? 100) * (width ?? 50);
alert(area); // 5000
否则,如果我们省略了括号,则由于 * 的优先级比 ?? 高,它会先执行,进而导致错误的结果。
?? 与 && 或 || 一起使用
出于安全原因,JavaScript 禁止将 ?? 运算符与 && 和 || 运算符一起使用,除非使用括号明确指定了优先级。
下面的代码会触发一个语法错误:
let x = 1 && 2 ?? 3; // Syntax error
这个限制无疑是值得商榷的,但它被添加到语言规范中是为了避免人们从 || 切换到 ?? 时的编程错误。
可以明确地使用括号来解决这个问题:
let x = (1 && 2) ?? 3; // 正常工作了
alert(x); // 2
总结
空值合并运算符 ?? 提供了一种从列表中选择第一个“已定义的”值的简便方式。
它被用于为变量分配默认值:
// 当 height 的值为 null 或 undefined 时,将 height 的值设置为 100
height = height ?? 100;
?? 运算符的优先级非常低,仅略高于 ? 和 =,因此在表达式中使用它时请考虑添加括号。
如果没有明确添加括号,不能将其与 || 或 && 一起使用。
#循环:while 和 for
循环 是一种重复运行同一代码的方法。
“while” 循环
while 循环的语法如下:
while (condition) {
// 代码
// 所谓的“循环体”
}
当 condition 为真时,执行循环体的 code。
任何表达式或变量都可以是循环条件,而不仅仅是比较。在 while 中的循环条件会被计算,计算结果会被转化为布尔值。
例如,while (i != 0) 可简写为 while (i):
单行循环体不需要大括号
如果循环体只有一条语句,则可以省略大括号 {…}:
let i = 3;
while (i) alert(i--);
“do…while” 循环
使用 do..while 语法可以将条件检查移至循环体 下面:
do {
// 循环体
} while (condition);
for” 循环
for 循环更加复杂,但它是最常使用的循环形式。
for 循环看起来就像这样:
for (begin; condition; step) {
// ……循环体……
}
我们通过示例来了解一下这三个部分的含义。下述循环从 i 等于 0 到 3(但不包括 3)运行 alert(i):
for (let i = 0; i < 3; i++) { // 结果为 0、1、2
alert(i);
}
跳出循环 break
通常条件为假时,循环会终止。
但我们随时都可以使用 break 指令强制退出。
例如,下面这个循环要求用户输入一系列数字,在输入的内容不是数字时“终止”循环。
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
如果用户输入空行或取消输入,在 (*) 行的 break 指令会被激活。它立刻终止循环,将控制权传递给循环后的第一行,即,alert。
继续下一次迭代 continue
continue 指令是 break 的“轻量版”。它不会停掉整个循环。而是停止当前这一次迭代,并强制启动新一轮循环(如果条件允许的话)。
如果我们完成了当前的迭代,并且希望继续执行下一次迭代,我们就可以使用它。
下面这个循环使用 continue 来只输出奇数:
for (let i = 0; i < 10; i++) {
//如果为真,跳过循环体的剩余部分。
if (i % 2 == 0) continue;
alert(i); // 1,然后 3,5,7,9
}
对于偶数的 i 值,continue 指令会停止本次循环的继续执行,将控制权传递给下一次 for 循环的迭代(使用下一个数字)。因此 alert 仅被奇数值调用。
触发continue后,continue后的代码不会再执行,而是启动下一次for循环。
break/continue 标签
有时候我们需要从一次从多层嵌套的循环中跳出来。
例如,下述代码中我们的循环使用了 i 和 j,从 (0,0) 到 (3,3) 提示坐标 (i, j):
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// 如果我想从这里退出并直接执行 alert('Done!')
}
}
alert('Done!');
我们需要提供一种方法,以在用户取消输入时来停止这个过程。
在 input 之后的普通 break 只会打破内部循环。这还不够 —— 标签可以实现这一功能!
标签 是在循环之前带有冒号的标识符:
labelName: for (...) {
...
}
break <labelName> 语句跳出循环至标签处:
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// 如果是空字符串或被取消,则中断并跳出这两个循环。
if (!input) break outer; // (*)
// 用得到的值做些事……
}
}
alert('Done!');
上述代码中,break outer 向上寻找名为 outer 的标签并跳出当前循环。
因此,控制权直接从 (*) 转至 alert('Done!')。
我们还可以将标签移至单独一行:
outer:
for (let i = 0; i < 3; i++) { ... }
continue 指令也可以与标签一起使用。在这种情况下,执行跳转到标记循环的下一次迭代。
posted on 2021-10-10 23:39 PerfectData 阅读(54) 评论(0) 编辑 收藏 举报