felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

JavaScript 循环遍历 #145

Open felix-cao opened 5 years ago

felix-cao commented 5 years ago

循环 (loop) 或者叫遍历 (loop through),还有一种说法叫迭代 (Iterate), 是让计算机做重复任务的有效的方法。循环是所有语言最基础的语法,

跳出循环

JavaScript 中的遍历主要是对“集合”的遍历,JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又新增了 MapSet 。本文重点讨论 JavaScript 原有数据“集合”的遍历,即 ArrayObject

Array 提供了一种顺序存储一组元素的功能,并可以按索引来访问。 Object 是一个属性和方法的集合,它是无序的,通过属性名或方法名去访问。

JavaScript 的循环遍历语法如下:

命令 数组 对象 Notes
1. for x 通过初始条件结束条件递增条件来循环执行语句块
2. for ...in 把一个对象的所有属性依次循环出来
3. while x 只指定循环条件为 true
4. do ...while x 循环完成时判断循环条件是否进入下一次循环
5. forEach x 调用数组的每个元素,并将元素传递给回调函数
6. for ...of ES6 新增的,用来弥补 forEach 和 for...in 的短板

本文主要介绍前 5 个常见的循环语句,此外ES5 还给我们提供了 map、reduce、filter、some、every 等常见的高阶函数,请移步《JavaScript 高阶函数(Higher-order function) 》, 关于 for ...of,因为是 ES6 中新增的语法,回头会在 ES6 中讲解。

一、for 循环

for 循环,通过 初始条件结束条件递增条件 来循环执行语句块:

var x = 0;
for ( var i=1; i<=100; i++) {
    x = x + i;
}
x; // 5050

让我们来分析一下 for 循环的控制条件: 🔢

👍 for 循环不能遍历对象,这是因为 递增条件 决定着循环对象必须是有序的,而 Array 是有序,Object 是无序的。

for 循环最常用的地方是利用索引来遍历数组或类数组:

var arr = ['Apple', 'Google', 'Microsoft'];

for ( var i=0; i<arr.length; i++) {
    console.log(arr[i]);
}

👍 for 循环的 3 个条件都是可以省略的,如果没有退出循环的判断条件,就必须使用 break 语句终止循环,否则就是死循环:

var x = 0;
for (;;) { // 将无限循环下去
    if (x > 15) {
        console.log('x > 15: break');
        break; // 通过if判断来终止循环
    }
    x ++;
    console.log('x: ', x);
}

👍 也可以使用 continue 跳出本次循环,进入下一个循环:

var x = 0;
for (;;) { // 将无限循环下去
    if (x > 15) {
        console.log('x > 15: break');
        break; // 通过if判断来终止循环
    }
    x ++;
    if( x === 2){
      console.log('x === 2: continue');
      continue;
    }
    console.log('x: ', x);
}

二、for ...in 遍历

for 循环的一个变体是 for ... in 循环,它可以把一个对象的所有属性依次循环出来:

var o = {
    name: 'Felix',
    age: 18,
    city: 'Hefei'
};
for (var key in o) {
    if(key === 'age') {
      break;
    }
    console.log(key); // 'name', 'age', 'city'
}

由于 Array 也是对象,其元素的索引被视为对象的属性,因此,for ... in 循环可以直接循环出 Array 的索引:

var a = ['Apple', 'Banala', 'Cat'];
for (var i in a) {
    console.log(i); // '0', '1', '2'
    console.log(a[i]); // 'Apple', 'Banala', 'Cat'
}

请注意,for ... inArray 的循环得到的是 String 类型的值而不是 Number

👍 不过,ECMAScript 5.1 (ECMA-262) 给我们提供了更加简洁优雅的提取对象属性的方式: Object.keys()

var o = {
    name: 'Felix',
    age: 18,
    city: 'Hefei'
};
var a = ['Apple', 'Banala', 'Cat'];

console.log(Object.keys(o)); // ["name", "age", "city"]
console.log(Object.keys(a)); // ["0", "1", "2"]

三、while 循环

for 循环在明确循环的初始和结束条件时非常有用。而忽略了条件的 for 循环容易让人看不清循环的逻辑,这个时候推荐使用 while 循环

while 循环里只要指定条件为 true,循环就可以一直执行代码块。把本篇的第一段演示代码改写如下

var x = 0;
var i = 0;
while( i <= 100 ) {
  x = x + i;
  i ++;
}
x; // 5050
i; // 101

在循环体内部变量 i 不断自增,直到变为 101 时,不再满足 while 条件,退出循环。

也可以在循环体内使用 break 终止循环或 continue 跳出循环。

四、do ...while 循环

do { ... } while() 循环,它和 while 循环的唯一区别在于,不是在每次循环开始的时候判断条件,而是在每次循环完成的时候判断条件:

var x = 0;
var i = 0;
do{
  x = x + i;
  i ++;
} while( i <= 100 ) 
x; // 5050
i; // 101

👍 需要注意的是: 用 do { ... } while() 循环要小心,循环体至少执行1次,而 forwhile 循环则可能一次都不执行。

五、forEach() 遍历数组

forEach() 方法用于遍历数组的每个元素,并将元素传递给回调函数。

array.forEach(function(currentValue, [index], [arr]){
  // 回调函数的函数体
}, [thisValue])

例如:

var arr = ['Felix', 'Lucy', 'Bindy']
arr.forEach(console.log)

得到:

Felix 0  ["Felix", "Lucy", "Bindy"]
Lucy 1 ["Felix", "Lucy", "Bindy"]
Bindy 2 ["Felix", "Lucy", "Bindy"]

👍 注意: forEach() 对于空数组是不会执行回调函数的。

👍 跳出本次循环,不能使用 continue, 而是用 return false

var arr = ['Felix', 'Lucy', 'Bindy']
arr.forEach(function(val) {
  if( val === 'Lucy' ) {
    console.log('continue: Lucy');
    return false;
  }
  console.log(val);
})

上面的代码,遍历数组 arr , 当遇到一个元素的值完全等于字符串 Lucy 即跳出本次循环,进入下一次 Bindy 元素的循环。

👍 终止循环,使用 try catch:

var arr = ['Felix', 'Bindy', 3, 4, 5, 6, 'Lucy', 8, 9, 10];
try{
    arr.forEach(function(val) {
      if( val === 'Lucy' ) {
        throw new Error('exist from Lucy')
      }
      console.log(val);
    })
} catch (e) {
  console.log(e.message);
  if(e.message=='exist') throw e
} 

上面的代码,遍历数组 arr , 当遇到一个元素的值完全等于字符串 Lucy 时抛出一个 error 给 catch 捕捉到, 然后在 catch 里面 return,这样就能中止循环了。

felix-cao commented 2 years ago

Jquery 中的 $.each

$.each = function( obj, callback ) { 
   var value, 
   i = 0, 
   length = obj.length, 
   isArray = isArraylike( obj ); 
   if ( isArray ) { // 迭代类数组
      for ( ; i < length; i++ ) { 
         value = callback.call( obj[ i ], i, obj[ i ] ); 
         if ( value === false ) { 
            break; 
         } 
      } 
   } else { 
      for ( i in obj ) { // 迭代 object 对象
         value = callback.call( obj[ i ], i, obj[ i ] ); 
         if ( value === false ) { 
            break; 
         } 
      } 
    } 
    return obj; 
};

上面代码的 value 值可以理解 return false 时, 停止当前循环跳到下一个循环。