本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!
前言
本系列将分多篇文章介绍 js 相关的“冷门知识”,这里的“冷门知识”是指大部分开发者都不曾留意过的知识点,亦或者是意料不到的知识点。这些知识点对现实中的面试、开发也许没有很大的帮助,甚至压根没任何用处,但希望以此博得大家一乐,并额外拓展下知识面。
本系列文章的大部分知识参考于阮一峰写的“网道 - 互联网开发文档 (wangdoc.com)”,强烈建议 web 前端开发者都看下这份文档,既是入门的好帮手,也是拓展知识面的利器。
本系列文章的任何参考,均会在文末标出。
文章示例均是经过本人验证,如有错漏,请在评论区指正。
跳出循环
我们都知道 js 的“跳出循环”都是使用关键字 break
或 continue
,单独的 break
用来跳出当前循环,单独的 continue
用来跳出一次循环(这里强调“单独”是有原因的,往下看即可明白)。
假设我们现在有两个循环体,如下:
for (var i=0; i<2; i++) {
for (var j=0; j<2; j++) {
console.log(i, j)
}
}
现有要求在第二个循环体里判断,当 j===1
时,不允许输出 i
和 j
的值并直接跳出所有循环体,注意是所有循环体。
你也许会这样写,添加一个变量标识是否需要跳转出循环,再在顶层循环里做一次判断。
var flag = false
for (var i=0; i<2; i++) {
for (var j=0; j<2; j++) {
if (j === 1) {
flag = true
break
}
console.log(i, j)
}
if (flag) {
break
}
}
控制台正常输出的结果是
0 0
这种方法是没问题的,但是如果这里不单止有一个内嵌循环体,而是有一百个内嵌循环体且带有参数j
的循环体是最里面一个,是不是就要写一百次个判断?那岂不是很麻烦。
当然,事实上如果代码是放在函数体里面的,可以用 return
来解决,但这里不考虑这种情况。
break + 标签
其实 es5 里面有一种更简便的跳出指定循环体的写法,那就是使用 es5 的标签(label),如下:
top:for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
if (j === 1) {
break top
}
console.log(i, j)
}
}
上面的代码在原来的代码里有两个变化:
-
一个变化是在第一个
for
前面添加了代码top:
,这个冒号前面的top
就是 es5 的标签,标签名可以自定义(top
、myTop
等等),但不能是 js 的关键字。这里的意思是将第一个循环体外的环境标识为top
标签。 -
另一个变化是当
j === 1
时,关键字break
不再是单独使用,而是配合标签top
使用,break top
意思是指直接跳出中间的循环体到标签top
所在的环境,也就是说,程序会直接跳出第一个循环体和第二个循环体。
这样,不管里面内嵌了多少个循环体,都可以用简单的 break top
解决。
我们也可以将标签 top
放到 for
上面的代码行
top:
for (var i = 0; i < 2; i++) {
...
}
也可以加一个代码块,这样可能更加好理解一点。
top: {
for (var i = 0; i < 2; i++) {
...
}
}
continue + 标签
当然,continue
也是可以配合标签使用,比如:
top2:
for (var i = 0; i < 2; i++) {
console.log('i:', i)
for (var j = 0; j < 2; j++) {
if (j == 0) {
continue top2
}
console.log('i, j:', i, j)
}
}
控制台输出:
i: 0
i: 1
当代码执行到 continue top2
,内层循环体直接跳出(并非跳出一次)到外层循环,因此 console.log('i, j:', i, j)
的代码根本没有执行,continue top2
这里的作用是不是很像单独的 break
的作用?这完全可以使用 break
来代替,因此 continue + 标签
的用法没什么使用价值。
标签的错误理解
在“网道”文档里,是这样介绍“标签”:
JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置...
我当时看到这句话,就产生了一个错觉,“可以通过标签跳转到程序的任意位置”,这么逆天......
然后,我试了一下这样写。
top:console.log(123)
for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
if (j == 1) {
break top
}
console.log(i, j)
}
}
按照逻辑来说,这里应该死循环,当 j===1
时,跳转到 top
,top
标记于 console.log(123)
,因此再次打印了 123
,并且继续再次进行下面的循环......
但这个代码运行是直接报错的。
这说明标签可能只允许标记在最近的循环体上,标记在其他代码上会报错,并且并非我想象中的“可以通过标签跳转到程序的任意位置”。
然后我又换了一个写法。
top: {
console.log(123)
for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
if (j == 1) {
break top
}
console.log(i, j)
}
}
}
这次不会报错了,但并没有预期出现死循环,这也很符合预期,因为 top
是标记在代码块上的。
标签的正确理解
经过上一章节的试验,我们并不可以通过“标签”跳转到程序的任意位置,我们只可以通过“标签”跳出循环体或代码块,跳出后,继续往下面执行代码。
标签的正确用法如下:
标签: for (...) {
...
break 标签
...
}
或
标签:
for (...) {
...
break 标签
...
}
或
标签: {
...
break 标签
...
}