标签是自 JavaScript 创建以来就存在的功能。它们并不新鲜!我认为并没有那么多人了解它们,我甚至认为它们有点令人困惑。但是,正如我们将看到的,标签在非常特定的情况下可能很有用。
但首先:不要将 JavaScript 标签与 HTML 的 <label>
混淆,它们是完全不同的东西!
JavaScript 标签是一种为语句或代码块命名的方法。通常:循环和条件语句。这允许您从内部 break
或 continue
带标签的语句。要将标签应用于语句,请以 label:
开头,您作为“label”输入的内容将成为您稍后可以引用的标签。
以下是标签的基本语法
let x = 0;
// Label a loop as "myLoop"
myLoop:
while (true) {
if (x >= 10) {
// This will cause "myLoop" to end.
break myLoop;
}
x++;
}
标签只是对语句的内部引用,不能被查找、导出或存储在值中。它们也不会与变量名冲突,因此如果您真的想让人困惑,您可以让循环和变量具有相同的名称!请不要这样做——未来的您和任何必须阅读您代码的人都会感激不尽。标签的使用案例有限,但在正确的人手中却非常强大。
break
和 continue
的简要介绍
让我们稍微退一步,谈谈 break
和 continue
。break
语句将终止当前正在运行的循环或条件语句。它最常用于 switch
语句以结束 case
,但它也可用于提前结束 if
语句,或导致 for
或 while
循环结束并停止循环。这是退出条件语句或提前结束循环的好方法。
以下是用法的基本示例break
const x = 1;
switch(x) {
case 1:
console.log('On your mark!');
break; // Doesn't check the rest of the switch statement if 1 is true
case 2:
console.log('Get set!');
break; // Doesn't check the rest of the switch statement if 2 is true
case 3:
console.log('GO!');
break; // Doesn't check the rest of the switch statement if 3 is true
}
// logs: 'On your mark!'
类似地,continue
语句可用于循环以提前结束当前迭代,并开始循环的下一轮运行。但是,这仅在循环内部有效。
以下是continue
的使用方法
for (let x = 0; x &< 10; x++) {
if (x !== 5) continue; // If the number isn't five, go to the next pass of the loop.
console.log(x);
}
// logs: 5
break
使用标签与通常,标签的使用案例出现在您遇到任何类型的嵌套语句时。将它们与break
一起使用可以停止深度嵌套的循环或条件,并使其立即停止。
让我们回到这篇博文标题!
// Our outer if statement
outerIf:
if (true) {
// Our inner if statement
innerIf:
if (true) {
break outerIf; // Immediately skips to the end of the outer if statement
}
console.log('This never logs!');
}
就是这样,您可以为if
语句添加标签。
continue
使用标签与有时我会创建一个嵌套循环,并希望在内部循环中跳过外部循环的一些迭代。我通常会中断内部循环,然后检查我是否处于想要跳过的状态,然后继续外部循环。能够将该代码简化为更易于阅读的语句非常棒!
let x = 0;
outerLoop:
while (x < 10) {
x++;
for (let y = 0; y < x; y++) {
// This will jump back to the top of outerLoop
if (y === 5) continue outerLoop;
console.log(x,y);
}
console.log('----'); // This will only happen if x < 6
}
块语句和标签
块语句 在 JavaScript 中是一种将 const
和 let
变量的作用域限制到代码一部分的方法。如果您想本地化一些变量而不必创建函数,这可能很有用。这方面的一个(重大)警告是,块语句在 严格模式 中是无效的,而 ES 模块默认就是严格模式。
这是一个带标签的块语句
// This example throws a syntax error in an ES module
const myElement = document.createElement('p');
myConditionalBlock: {
const myHash = window.location.hash;
// escape the block if there is not a hash.
if (!myHash) break myConditionalBlock;
myElement.innerText = myHash;
}
console.log(myHash); // undefined
document.body.appendChild(myElement);
实际使用
我花了一段时间才想出一个在日常生产代码中使用标签的理由。这可能有点牵强,但在 JavaScript 中标签可能派上用场的地方是从 switch
语句内的循环中提前退出。由于您可以在 switch
中使用 break
,因此能够为提前结束循环的循环应用标签可能会使您的代码更有效率。
以下是如何在计算器应用程序中使用它
const calculatorActions = [
{ action: "ADD", amount: 1 },
{ action: "SUB", amount: 5 },
{ action: "EQ" },
{ action: "ADD", amount: 10 }
];
let el = {};
let amount = 0;
calculate: while (el) {
// Remove the first element of the calculatorActions array
el = calculatorActions.shift();
switch (el.action) {
case "ADD":
amount += el.amount;
break; // Breaks the switch
case "SUB":
amount -= el.amount;
break; // Breaks the switch
case "EQ":
break calculate; // Breaks the loop
default:
continue calculate; // If we have an action we don't know, skip it.
}
}
这样,当条件匹配时,我们可以退出 calculate
循环,而不是让脚本继续执行!
结论
您很少需要使用 JavaScript 标签。事实上,您可以在完全不知道它的情况下过上非常充实的事业。但是,如果您碰巧找到一个可以使用此语法的地方,那么您现在就可以使用它了。
所以基本上就像其他语言中的 goto 语句……
也许吧?除了它没有去任何地方?
真是一个有趣的功能。从不知道这个。
此外,这段代码
break outerIf; // 立即跳到外部 if 语句的末尾
表明它确实去了某个地方。break
将goto
向前,continue
将goto
向后。它不像真正的goto
语句那样灵活,但我可以看到依赖它们会导致意大利面条代码。这是一个相当有限且专门的
goto
,它仍然与逻辑代码结构相关联,不像某些其他语言的无限goto
。在
switch
语句中使用break
在一些语言中是标准的,那里没问题。break
在 JS 中的另一个“正常”用途,当然也是我唯一使用过的非switch
用途(而且只非常偶尔),是让嵌套的内部循环break
或continue
外部循环——这可能比尝试使用更多if
/else
结构或其他任何东西来实现相同逻辑产生更清晰、更容易理解的代码。我从未想过或需要像文章中建议的那样
break
出if
块。这个特性当然是一个你可以使用但可能不值得引起其他开发人员困惑和/或恐惧的功能!
标签和“goto”是相同的。他们的目标是告诉运行时引擎它必须跳过其线性流程。当您尝试在运行时跟踪代码时,此行为可能很危险。它会带来奇怪的错误和问题。
而且,在我看来,使用标签(或 goto)代码的可读性降低了。
几乎所有语言都鼓励避免使用。
没有理由重新使用这种类型的语句。
在所有现代高级语言中,内部都对这一点进行了抽象,但如果你对低级语言有扎实的理解,这种方法并没有什么问题。
这是在
switch
语句中的标准用法。在某些情况下,嵌套的内循环可以使用
break
或continue
跳出外循环是可以的——这可能比尝试使用更多if
/else
结构或嵌套带有条件return
等的函数调用来实现相同逻辑,从而产生更清晰、更容易理解的代码。但是,
break
的其他用法只会使代码更混乱。(跳出if
块?请不要这样做。)你可以这样做,但请不要这样做!
作者应该更深入地探讨嵌套循环中“continue”的工作原理。
如果在内循环中使用外层标签的“continue”,它实际上会跳出所有内循环,直到到达与标签匹配的外层循环,然后继续执行该外层循环的下一轮迭代。因此,对于那些与标签不匹配的循环,“continue”实际上等同于“break”。
例如:
这里,
continue labelA
将跳出labelB循环,但继续执行labelA循环的下一轮迭代。哈?我刚刚在控制台中检查了一下——在两种模式下,无标签和有标签的块语句都可以正常工作。在MDN上也没有看到相关说明。
(而且,几乎每个let/const教程都使用块语句来演示块级作用域)
哦,你好!你说得对。我以为我测试过这个,但显然我搞错了。MDN文档中提到的是
有趣。在我看来,
if ()
语句和块语句中的break
确实有一些有效的用途。不过,我认为你关于带标签的块语句在严格模式下无效的说法是不正确的。这在NodeJS(包括ESM)中按预期工作。
不错!还有一个问题.. 主题和字体?Vscode?
最后一个例子中有一个错误。当calculatedActions为空并且el calculatorActions.shift();返回undefined时,下一行将抛出异常。
这看起来太奇怪了…
在PHP中,你只需使用break n(其中n是自然数)即可跳出嵌套循环或循环内的switch语句。
我们不要忽视JavaScript中现在存在的标签的主要用例——Svelte使用
$
标签来实现其反应式逻辑!这完全是错误的。我们可以更新这篇文章吗?