前言
这是我梳理JavaScript基础系列文章中的一篇,这里没有谈及各类循环的效率,主要是总结一下使用方式和了解一些注意事项,点击二三级标题可以打开MDN相应文档,日常博客记录在语雀,欢迎关注 语雀文档。
如果文章对你有帮助的话,欢迎点赞评论收藏加转发。有问题和疑惑的话也可以在评论区留言,我会第一时间回复大家,如果觉得我的文章哪里有知识点错误的话,也恳请能够告知,把错的东西理解成对的,无论在什么行业,都是致命的。
JavaScript中循环
for
for...of
for...in
for await...of
forEach
这个其实不能划为此类while
do...while
先创建基础数据,全文通用。
const string='12345'
const array = [1, 2, 3, 4, 5]
const uint8Array=new Uint8Array([0, 1, 2, 3])
const searchParams = new URLSearchParams("key1=value1&key2=value2");
const obj = {
'obj0': 1,
'obj1': 2,
'obj2': 3,
'obj3': 4,
'obj4': 5
}
const obj2 = Object.defineProperties({
'obj0': 1
}, {
'obj1': {
value: 2,
enumerable: true
},
'obj2': {
value: 3,
enumerable: true
},
'obj3': {
value: 4,
enumerable: false
},
});
obj2.__proto__.obj4=5
const map = new Map([[0,1],[1,2],[2,3],[3,4],[4,5]])
const set = new Set([1,2,3,4,5])
for
纯粹的for
是JavaScript
中最基础的循环语句
for (let i = 0; i < array.length; i++) {
console.log(map.get(i)); //循环输出1 2 3 4 5
}
需要知道的是,for
的三个表达式都是可选的,也就是上面的let i = 0; i < array.length; i++
这部分,但是,虽然表达式是可选的,但三个;
是强制要求要有的。
for (let i = 0,j=0; ; i++,j++) {
//这里不给他中止,会gg的
if (i > array.length) break;
console.log(i,j);
}
// 0 0
// 1 1
// 2 2
// 3 3
// 4 4
// 5 5
如果你有需要的话,你甚至可以声明一个无语句的for
循环,这样也是可以的。
let j=[]
for (let i = 0; i < array.length; i++,j.push(i)); //这个分号不能少
console.log(j) //[ 1, 2, 3, 4, 5 ]
for...of
for...of
语句循环可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments
对象等等),循环出来的的对象的值。
for (const element of array) {
console.log(element);//循环输出1 2 3 4 5
}
这里要注意,普通的object
对象不是可迭代对象,无法使用for...of
,
for (let element of obj) {
console.log(element); // TypeError: obj is not iterable
}
甚至可以循环字符串
for (let element of string) {
console.log(element); //循环输出1 2 3 4 5
}
也能循环Map和Set
for (let element of map) {
//是个数组哟
console.log(element[0],element[1]);
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
}
for (let element of set) {
console.log(element); //循环输出1 2 3 4 5
}
对于for...of
的循环,可以由break、throw、continue、return
终止。
for (let element of set) {
if(element>3) break
console.log(element); //循环输出1 2 3
}
对于不支持的普通object
,我们可以为他创建一个迭代器,这样也能实现for...of
循环。
obj[Symbol.iterator] = function*(){
var keys = Object.keys(obj);
for(var key of keys){
yield [key,obj[key]]
}
};
for(var [key,value] of obj){
console.log(key,value);
// obj0 1
// obj1 2
// obj2 3
// obj3 4
// obj4 5
}
也能借助Object.entries 实现遍历,Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组。
for (const [key, value] of Object.entries(obj2)) {
console.log(`${key}: ${value}`);
//obj0: 1
//obj1: 2
//obj2: 3
}
还有一些其他可迭代对象可以使用for...of
,这里就不一一举例了。
for...in
for...in
语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性,注意:语句无法保证遍历顺序(各浏览器有点区别,也可以去了解下常规属性和排序属性)。
for (let prop in obj) {
console.log(prop + " = " + obj[prop]);
// obj0 = 1
// obj1 = 2
// obj2 = 3
// obj3 = 4
// obj4 = 5
}
只会遍历出可迭代的属性,obj2['obj3']
的enumerable
为false
所有没有被遍历出来,obj2['obj4']
是继承过来的属性(__proto__
上面的),也是能遍历出来的。
for (let prop in obj2) {
console.log(prop + " = " + obj2[prop]);
// obj0 = 1
// obj1 = 2
// obj2 = 3
// obj4 = 5
}
如果仅需自身属性,可以使用hasOwnProperty判断
for (let prop in obj2) {
if (obj2.hasOwnProperty(prop)) {
console.log(prop + " = " + obj[prop]);
}
// obj0 = 1
// obj1 = 2
// obj2 = 3
}
for await...of
和for...fo
一样,但它只适用于异步可迭代的异步迭代器。
function getRes(){
return fetch('http://whois.pconline.com.cn/ip.jsp')
.then(response => 'res')
}
async function test(){
let i=0
let arr = [getRes(),getRes(),getRes()]
for await (let x of arr){
i++
console.log(x+'---'+i); //按顺序循环出 res---1 res---2 res---3
}
}
test()
异步生成器已经实现了异步迭代器协议, 所以可以用 for await...of
循环。
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
yield i++;
}
}
async function test(){
for await (num of asyncGenerator()) {
if (num>1) break; //也能使用break中止 ,完全和for...of一样
console.log(num); //按顺序循环出 0 0
}
}
test()
while
while
语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。
let n=0
while (n < array.length) {
console.log(array[n]); //循环输出1 2 3 4 5
n++;
}
do...while
do...while
语句创建一个执行指定语句的循环,直到条件值为 false
。在执行后do
内的代码后,检测while
内的条件,所以do
内代码至少会执行一次。
let n=0
do {
console.log(array[n]); //循环输出1 2 3 4 5
n++;
}while (n < array.length)
do {
console.log(array[n]); //1 至少会循环一次
n++;
}while (n < 0)
forEach
JavaScript
中Array、Map、Set、NodeList、TypedArray、URLSearchParams
都存在forEach
方法,对上述类型的每个元素执行一次给定的函数,无返回值。除URLSearchParams
外,其他都有第二个thisArg可选参数,在Map.prototype.forEach
中有示例。
Map.prototype.forEach
map.forEach((value,key,myMap)=>{
console.log(`${key}-${value}-${myMap.get(key)}`)
// 0-1-1
// 1-2-2
// 2-3-3
// 3-4-4
// 4-5-5
})
//第二个参数,本文后续类型的不再举例,这里不能使用箭头函数
//如果使用箭头函数表达式来传入函数参数, thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。
map.forEach(function(value,key,myMap){
console.log(`${key}-${value}-${myMap.get(key)}-${this.exp}`)
// 0-1-1-exp
// 1-2-2-exp
// 2-3-3-exp
// 3-4-4-exp
// 4-5-5-exp
},{exp:'exp'})
Array.prototype.forEach
forEach
方法按升序为数组中含有效值的每一项执行一次callback
函数;forEach
不会改变原数组,但是可以被callback 修改;forEach
不可链式调用;forEach
遍历的范围在第一次调用callback
前就会确定;- 除了抛出异常以外,没有办法中止或跳出
forEach
循环; - 如果已经存在的值被改变,
callback
拿到的是最新的值; - 调用
forEach
后添加到数组中的项不会被callback
访问到; - 已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用
shift
),之后的元素将被跳过。
array.forEach((currentValue, index ,myArray)=> {
console.log(`${currentValue}-${index}-${myArray[index]}`);
// 1-0-1
// 2-1-2
// 3-2-3
// 4-3-4
// 5-4-5
});
forEach
遍历的范围在第一次调用 callback
前就会确定。调用 forEach
后添加到数组中的项不会被 callback
访问到。如果已经存在的值被改变,则传递给 callback
的值是 forEach
(遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift
),之后的元素将被跳过。
array.forEach((currentValue) => {
if (currentValue === 1) {
//已经存在的值被改变,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值
array[4] = 666
//不会遍历出来,forEach遍历的范围在第一次调用 callback 前就会确定
array.push(777)
}
console.log(currentValue)
// 1 2 3 4 666
});
array.forEach((currentValue) => {
if (currentValue === 1) {
array.shift();
}
//执行到1的时候,此时currentValue已经是1了,所以可以打印出来,把数组第一个删除,导致
//所有数组向前后退了一步[2,3,4,5],但是此时,索引已经是1了,所以跳过了2 打印3 4 5
console.log(currentValue)
// 1
// 3
// 4
// 5
});
除了抛出异常以外,没有办法中止或跳出 forEach()
循环
array.forEach((currentValue)=> {
if(currentValue>3) break; // Uncaught SyntaxError: Illegal break statement
console.log(currentValue)
});
Set.prototype.forEach
set.forEach((value,mySet)=>{
console.log(`${value}-${mySet}`)
// 1-1
// 2-2
// 3-3
// 4-4
// 5-5
})
NodeList.prototype.forEach
let node = document.createElement("div");
let kid1 = document.createElement("p");
let kid2 = document.createTextNode("hey");
let kid3 = document.createElement("span");
node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);
let list = node.childNodes;
list.forEach((currentValue, currentIndex, listObj) => {
console.log(`${currentValue}-${currentIndex}-${listObj[currentIndex]}`)
// [object HTMLParagraphElement]-0-[object HTMLParagraphElement]
// [object Text]-1- [object Text]
// [object HTMLSpanElement]-2- [object HTMLSpanElement]
}
);
TypedArray.prototype.forEach
TypedArray一个类型化数组
uint8Array.forEach((element, index, array)=>{
console.log(`${element}-${index}-${array[index]}`)
// 0-0-0
// 1-1-1
// 2-2-2
// 3-3-3
});
URLSearchParams.forEach
searchParams.forEach((value, key,searchParams)=> {
console.log(value, key,searchParams.get(key));
// value1 key1 value1
// value2 key2 value2
});
for...of和for...in异同
相同点
for...in
和for...of
语句都是循环(迭代)一些东西;- 都可以
break,continue
和return
中断遍历。
不同点
for...in
语句以任意顺序遍历一个对象的除Symbol
以外的可枚举属性,包括继承的可枚举属性,注意:语句无法保证遍历顺序;for...in
迭代的是对象的键,而for...of
则迭代对象的键对应的值;for...in
可以迭代任何对象,但for...of
则需要对象是可迭代对象;;
do...while和while异同
相同点:都是循环语句
不同点:do...while
至少会循环一次
JavaScript中的break,continue和return
return
return语句终止函数执行( return
是函数返回语句,但是返回的同时也将函数停止),并返回一个指定的值给函数调用者,如果忽略,则返回 undefined
。
array.forEach
是f()
内部的一个方法,在array.forEach
内return
和在f()
方法内return
是不一样的。
// 在函数中
function f() {
array.forEach((item)=>{
console.log(item) // return无效 连续打印 1 2 3 4 5
return item+1 //array.forEach是无返回值的
})
console.log(2) //打印2
}
console.log(f()) //undefined
//在全局作用域
array.forEach((item)=>{
console.log(item)
return item+1 // return无效 连续打印 1 2 3 4 5
})
console.log(3) //打印3
在函数内部或全局作用域使用for...in
,return
能正常中断for...in
语句执行
// 在函数中
function f() {
for (const objElement in array) {
console.log(array[objElement]) //1
return array[objElement]
}
console.log(2) //无法打印,已经中止函数执行
}
console.log(f()) //1
// 在全局作用域
for (const objElement in array) {
console.log(array[objElement]) //1
return array[objElement]
}
console.log(3) //无法打印,已经中止
在函数内部或全局作用域使用for...of
,return
能正常中断for...of
语句执行
// 在函数中
function f() {
for (const objElement of array) {
console.log(objElement) //1
return objElement
}
console.log(2) //无法打印,已经中止函数执行
}
console.log(f()) //1
// 在全局作用域
for (const objElement of array) {
console.log(objElement) //1
return objElement
}
console.log(3) //无法打印,已经中止
在函数内部或全局作用域使用for
,return
能正常中断for
语句执行
// 在函数中
function f() {
for (let i = 0; i < array.length; i++) {
console.log(array[i]) // 1
return array[i]
}
console.log(2) //无法打印,已经中止函数执行
}
console.log(f()) //1
// 在全局作用域
for (let i = 0; i < array.length; i++) {
console.log(array[i]) // node环境和v8 表现不一致 Illegal return statement
return array[i]
}
console.log(3) //无法打印,已经中止执行
在函数内部或全局作用域使用while
和do...while
,return
能正常中断while
和do...while
语句执行
// 在函数中
function f() {
let n=0
while (n < array.length) {
n++;
if (n === 3) {
return 'f';
}
console.log(n) // 1 2
}
console.log(2) //2
}
console.log(f()) //f
//在全局作用域
let n=0
while (n < array.length) {
n++;
if (n === 3) {
return 'f'; // Illegal return statement
}
console.log(n)
}
console.log(3)
小结:
return
会将此时进行的语句停止,但是return
更加适用于函数语句;- 对于
forEach
方法,除抛出异常外无法中断,本节后面不再对forEach
进行举例。
break
break语句中止当前循环、switch语句或label 语句( label
语句,后面专门写一篇文章),并把程序控制流转到紧接着被中止语句后面的语句。
在函数内部或全局作用域使用for...in
,break
能正常中断当前循环,但不会阻止后续程序执行。
// 在函数中
function f() {
for (const objElement in array) {
console.log(array[objElement]) //1
break
}
console.log(2) //2
}
//
console.log(f()) //undefined
// // 在全局作用域
for (const objElement in array) {
console.log(array[objElement]) //1
break
}
console.log(3) //3
在函数内部或全局作用域使用for...of
,break
能正常中断当前循环,但不会阻止后续程序执行。
// 在函数中
function f() {
for (const objElement of array) {
console.log(objElement) //1
break
}
console.log(2) //2
}
//
console.log(f()) //undefined
// // 在全局作用域
for (const objElement of array) {
console.log(objElement) //1
break
}
console.log(3) //3
在函数内部或全局作用域使用for
,break
能正常中断当前循环,但不会阻止后续程序执行。
// 在函数中
function f() {
for (let i = 0; i < array.length; i++) {
console.log(array[i]) // 1
break;
}
console.log(2) //2
}
//
console.log(f()) //undefined
// // 在全局作用域
for (let i = 0; i < array.length; i++) {
console.log(array[i]) // 1
break;
}
console.log(3) //3
在函数内部或全局作用域使用while
和do...while
,break
能正常中断当前循环,但不会阻止后续程序执行。
// 在函数中
function f() {
let n=0
while (n < array.length) {
n++;
if (n === 3) {
break;
}
console.log(n) // 1 2
}
console.log(2) //2
}
console.log(f()) //undefined
//在全局作用域
let n=0
while (n < array.length) {
n++;
if (n === 3) {
break;
}
console.log(n) // 1 2
}
console.log(3) //3
小结:break
终止当前循环,但不会阻止后续程序执行;
continue
continue
跳过本次循环,继续下一次循环。
// 在函数中
function f() {
for (let i = 0; i < array.length; i++) {
if(array[i] === 1) continue;
console.log(array[i]) // 2 3 4 5
}
console.log(2) //2
}
console.log(f()) //undefined
//在全局作用域
for (let i = 0; i < array.length; i++) {
if(array[i] === 1) continue;
console.log(array[i]) // 2 3 4 5
}
console.log(3) //3
在函数内部或全局作用域使用for...in
,continue
能正常跳过本次循环,继续下一次循环。
// 在函数中
function f() {
for (const objElement in array) {
if(array[objElement] === 1) continue;
console.log(array[objElement]) // 2 3 4 5
}
console.log(2) //2
}
console.log(f()) //undefined
//在全局作用域
for (const objElement in array) {
if(array[objElement] === 1) continue;
console.log(array[objElement]) // 2 3 4 5
}
console.log(3) //3
在函数内部或全局作用域使用for...of
,continue
正常跳过本次循环,继续下一次循环。
// 在函数中
function f() {
for (const objElement of array) {
if(objElement === 1) continue;
console.log(objElement) // 2 3 4 5
}
console.log(2) //2
}
console.log(f()) //undefined
//在全局作用域
for (const objElement of array) {
if(objElement === 1) continue;
console.log(objElement) // 2 3 4 5
}
console.log(3) //3
在函数内部或全局作用域使用while
和do...while
,continue
跳过本次循环,继续下一次循环。
// 在函数中
function f() {
let n=0
while (n < array.length) {
n++;
if (n === 3) {
continue;
}
console.log(n) // 1 2 4 5
}
console.log(2) //2
}
console.log(f()) //undefined
//在全局作用域
let n=0
while (n < array.length) {
n++;
if (n === 3) {
continue;
}
console.log(n) // 1 2 4 5
}
console.log(3) //3
小结:continue
用于跳过本次循环,继续下一次循环。
总结
forEach
除了抛出异常,否则无法中断;- 三者都会将此时进行的语句停止/跳过;
break
中断当前循环,但不会阻止后续程序执行;continue
跳过本次循环,继续下一次循环;return
是函数返回语句,但是返回的同时也将函数停止;- 使用的语句环境不一样,
break
和continue
是用在循环或switch
语句中,return
是用在函数语句中。
引用
首发于语雀文档@is_tao
MDN
js中的break,continue和return到底怎么用?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。