JavaScript中的for循环

本文详细介绍了JavaScript中的普通for循环、for...in、for...of、forEach以及forawait...of等不同类型的for循环,强调了它们的语法、适用场景和注意事项,帮助读者掌握在不同情况下的选择和使用。
摘要由CSDN通过智能技术生成

前情提要:

主要讲讲为什么会跟大家聊聊js中的for循环问题,在大部分情况下,可能作为初学者的我们,会比较习惯使用与其他语言类似的像while、普通for循环。

但是呢,在很多情况下,在我们编程js的时候,我们还会看到其他类的for循环,比如for...in,for...of这样的,而且,这类for循环,在某些场景下,会显得格外好用!所以笔者打算做一期总结,总结下JavaScript下的for循环们。由于笔者能力有限,也还望大家多多指教!那我们直接开启正片吧!


普通for循环

语法

for ([①初始化]; [②条件]; [④最后执行])
   ③循环体

//执行顺序是:① -> ② -> ③ -> ④

示例:

let str = '';
for (let i = 0; i < 9; i++) {
  str = str + i;
}

说明

1. 在普通for循环中,它总共有三个可选的表达式,他们一般为:

  • [①初始化]:一般是用于初始化一个计数器
  • [②条件]:根据其表达式计算结果,用于确定每一次循环是否能被执行
  • [④最后执行]:每次循环的最后都要执行的表达式

当然啦,你可能会问,我一个tip里面能不能写两个嘞。

回答是:当然可以啦,你中间只需要加上逗号就好,毕竟逗号表达式也算表达式嘞。同样,是表达式的话,你也可以写函数调用,但如果用函数作为条件的话,请注意函数的返回值是否会导致死循环。

这些表达式在普通for循环的应用中,都是能够进行忽略选填的,如果忽略只需要不填写任何东西就行,但是中间的分号一定不能省略,以下是示例:

var i = 0;

for (;;) {
  if (i > 3) break;
  console.log(i);
  i++;
}

2. 还有一种比较特殊,就是循环体也是能够省略的。诶,你先别奇怪,还真有这样的需求在呢!

以下 for 循环计算 final-expression 部分中节点的偏移位置,它不需要执行一个 statement 或者一组 block statement ,因此使用了空语句。

function showOffsetPos(sId) {
  var nLeft = 0,
    nTop = 0;

  for (
    var oItNode = document.getElementById(sId) /* initialization */;
    oItNode /* condition */;
    nLeft += oItNode.offsetLeft,
      nTop += oItNode.offsetTop,
      oItNode = oItNode.offsetParent /* final-expression */
  ); /* 分号 semicolon */

  console.log(
    "Offset position of '" +
      sId +
      "' element:\n left: " +
      nLeft +
      "px;\n top: " +
      nTop +
      "px;",
  );
}

/* Example call: */

showOffsetPos("content");

// Output:
// "Offset position of "content" element:
// left: 0px;
// top: 153px;"

🤩好啦,我们来开始下一个for循环 ---> for...in


for...in

语法

for (属性 in 可枚举属性被迭代的对象)
  循环体
//迭代的属性中,Symbol、以及继承的可枚举属性不能迭代

示例:

var obj = { a: 1, b: 2, c: 3 };
for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

说明

1. for..in循环遍历数组、字符串,是会输出其索引值但注意,它不适合遍历数组,因为for..in 循环的遍历顺序是不确定的,可能会出错。

诶,你可能会经常听说这句话吧,但又为什么说它不太适合遍历数组?

首先,我们要明确的是,这类循环,并不专门设计用于数组,而是为对象属性遍历而存在的

其次,for...in接收的是一个对象,对于数组,数组的索引会被视为对象的属性,但是,它不能保证按照数组的索引顺序进行迭代。它可能会按照属性添加的顺序,或者在某些JavaScript引擎中,按照内部哈希表的顺序

因此,我们还是要尽可能避免这种不确定性的发生,它不应该用来关注于数组的索引

//不推荐
let arr = [3, 1, 4];
arr.foo = 'bar';
for (let key in arr) {
  console.log(key); // 输出 0 1 2 foo
}

2. for...in遍历的属性包括原型,如果不想遍历原型方法和属性的话,可以在循环内部判断一下,使用hasOwnProperty()方法可以判断某属性是不是该对象的实例属性

var arr = [1,2,3]
Array.prototype.a = 123
    
for (let index in arr) {
  let res = arr[index]
  console.log(res)
}
//1 2 3 123

for(let index in arr) {
    if(arr.hasOwnProperty(index)){
        let res = arr[index]
  		console.log(res)
    }
}
// 1 2 3

3. for...in中,Symbol、以及继承的可枚举属性不能迭代

// Symbol
let obj = { a: 1, b: 2, [Symbol('c')]: 3 };
for (let key in obj) {
  console.log(key); // 输出: a, b
}

//继承的可枚举属性
let parent = { a: 1, b: 2 };
let child = Object.create(parent);
child.c = 3;

for (let key in child) {
  console.log(key); // 输出: c
}

🤩好啦,我们来开始下一个for循环 ---> for...of


for...of

语法

😎这家伙可就厉害了!!!

for (不同属性的值 of 具有iterator接口的数据结构) {
    循环体
}

示例:

let iterable = [10, 20, 30];
for (let value of iterable) {
  value += 1;
  console.log(value);    //11 21 31
}

说明

1.该语句能在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句!

白话:这家伙能够遍历不同的可迭代对象,并且针对每种不同的迭代对象,它都配备了其独有的输出参数供你使用,这样,我们除了数组啥的,去迭代Map,Set这些东西也不成问题!

可这就说明,for...of是不能遍历对象,因为它没有迭代器对象,但如果想遍历对象的属性,你可以用for in循环(这也是它的本职工作)或用内建的Object.keys()Object.values()Object.entries()把一个对象包装成迭代器,使用起来就和Map差不多了。

啊?还不明白,那就直接展示,我将根据不同的迭代对象,来向你分级说明

数组(Array)

针对数组,其value指的是数组元素值,而且for of遍历的只是数组内的元素,不包括原型属性和索引

let iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value); //10 20 30
}

字符串(String)

针对数组,其value指的是各个字符

let iterable = "boo";

for (let value of iterable) {
  console.log(value); // b o o
}

对象(Object)

let obj = { a: 1, b: 2, c: 3 };

for (let value of Object.values(obj)) {
  console.log(value);
}

TypedArray

let iterable = new Uint8Array([0x00, 0xff]);

for (let value of iterable) {
  console.log(value); //0 255
}

Map

let myMap = new Map();
myMap.set('key1', 'value1');

for (let [key, value] of myMap.entries()) {
  console.log(key, value);    //输出 key1 value1 
}
//由于Map 的默认迭代器就是 myMap.entries(),因此这里的.entries()可以去掉

Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable.values()) {
  console.log(value); // 1 2 3
}
//同理,此处的.values()也可以省略

arguments对象

(function () {
  for (let argument of arguments) {
    console.log(argument); //1 2 3
  }
})(1, 2, 3);

DOM集合

//注意:这只能在实现了 NodeList.prototype[Symbol.iterator] 的平台上运行
let articleParagraphs = document.querySelectorAll("article > p");

for (let paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}

这里列出来是,是一些比较常用的类型,当然你也可以自己生成一个迭代器去使用,不过这样可能需要你花费一点功夫去生成一下嘞!

🤩好啦,我们来开始下一个for循环 ---> for await...of


for await...of

该循环能够遍历异步可迭代对象以及同步可迭代对象

语法

for await (属性值 of 被迭代枚举其属性的对象) {
  statement
}

示例:

async function* asyncGenerator() {
  yield new Promise(resolve => setTimeout(() => resolve(1), 1000));
  yield new Promise(resolve => setTimeout(() => resolve(2), 1000));
  yield new Promise(resolve => setTimeout(() => resolve(3), 1000));
}

async function processAsyncGenerator() {
  // 使用 for await...of 遍历异步生成器
  for await (const value of asyncGenerator()) {
    console.log(value);
  }
}

// 调用异步函数
processAsyncGenerator();

说明

1. 在上面的例子中,asyncGenerator 是一个返回 Promise 的异步生成器函数processAsyncGenerator 函数通过 for await...of 循环遍历异步生成器的结果,等待每个 Promise 完成并输出相应的值。这确保了异步生成器的每个值都在适当的时候被处理。

需要注意的是,for await...of 循环用于异步/同步可迭代对象,当然也可以遍历同步可迭代对象,但这样就失去了使用意义!

2. 解析:

看下面的这几行代码,其实它跟上面的思路差不多,只是构建时封装成了一个函数

function createAsyncIterable(delay) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(delay);
    }, delay);
  });
}

// 数组就是一个可迭代对象
const asyncIterable = [createAsyncIterable(2000), createAsyncIterable(1000), createAsyncIterable(3000)];

async function main() {
  for await (const item of asyncIterable) {
    console.log(item);
  }
}

main();

实际上,它的具体思路相当于:

function createAsyncIterable(delay) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(delay);
    }, delay);
  });
}

// 数组就是一个可迭代对象
const asyncIterable = [createAsyncIterable(2000), createAsyncIterable(1000), createAsyncIterable(3000)];

async function main() {
  const p1 = await asyncIterable[0];
  console.log(p1);
  const p2 = await asyncIterable[1];
  console.log(p2);
  const p3 = await asyncIterable[2];
  console.log(p3);
}

main();

这么一解析,想必你一定能对其有更深的理解吧!

🤩好啦,我们来开始下一个for循环 ---> forEach


forEach

 语法

forEach(每个元素执行的函数)
forEach(每个元素执行的函数, thisArg)

示例:

let numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(num, index) {
  console.log(`Element at index ${index} is: ${num}`);
});

说明

1. 实际上,它只是一种类似语法糖的东西,它内部实则调用了其他类型的for循环,我们只是在此基础上进行了封装处理,以下是其原理的部分呈现(以数组为例,这里我们封装了一个数组的forEach):

let arr =[1,2,3,4]
for(let i=0;i<arr.length;i++){
    console.log(arr[i])
}
Array.prototype.forEach= function(fn){
    for(let i=0;i<this.length;i++){
        fn(this[i],i)
    }
}

2.对于forEach,它的执行函数,针对不同的类别,提供了其对应的参数,只是相对比前面的几种,某种意义上简化了代码的量。

🤩结束啦!!! 


总说明

对于以上五种for循环,其中,只有forEach不能正确响应 break、continue 和 return 语句,其他循环均能够适用

总结:

1. for、for...in、forEach在处理数组时都可以获得数组下标,处理数组最好不用for...in

2.处理对象且需要拿到键首选for..in,可以获得键和值

3.只获得值首选for...of,支持类型多,语法简单,比如只要遍历拿数组的值

4.forEach在数组中有,在数组中要获得数组下标和内容且循环为数组长度可选用forEach,forEach 可以通过设定参数来存储索引下标、数据数值.这样在操作上更加的便利

截止现在,JavaScript中的for循环讲解,也到此为止啦,可能过程会比较枯燥,或许大家打开音乐听听歌,再回顾一遍,可能会放松一点呢,感谢大家的观看,也谢谢大家能够看到最后!

创作不易,还望大家多多支持,笔者的能力和精力有限,还望多多包涵指定!

 🎈万事尽心尽力,而后顺其自然,加油!

  • 25
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值