JS 五种循环比较

198 阅读3分钟
  • for
  • for ... in
  • for ... of
  • forEach
  • map

for 循环

for循环是最早出现的,也是应用最为普遍的,能够遍历绝大部份的数据类型,如数组、字符串、数字...

for...in 循环

for...in循环是ES5新增的,主要为了便利对象,获取对象的键值。

缺点:

  • 数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
  • 不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。(以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性)。
  • 某些情况下,for...in循环会以任意顺序遍历键名。
    let arr = [1,2,3];
    arr.foo = "foo";
    for(let key in arr){
        console.log(key)
    }
    // 1 2 3 foo

    // 包括原型链上的键
    let obj1 = {
        bar: "bar"
    };
    let obj2 = {
        foo: "foo"
    };
    let s = Symbol("s");
    obj1[s] = "SSS";
    Object.defineProperty(obj1, "c", {
	configurable: true,
	enumerable: false,
	value: "c",
	writable: true
    })
    Object.setPrototypeOf(obj1, obj2);
    for(let i in  obj1){
        console.log(i);
    }
    // bar foo

for ... of 循环

来源

  • 一个数据结构只要部署了Symbol.interator属性,就被视为具有interator接口,就可以用for...of循环便利他的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.interator方法(对象不行)。
  • for...of可以使用的范围包括数组、SetMap结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象),Cenerator对象,以及字符串。
// 遍历字符串
for(let value of "for...of"){
    console.log(value)
}
//f o r . . . o f

// 遍历数组
for(let value of [1,2,3,4]){
    console.log(value);
}
// 1
// 2
// 3
// 4

// 遍历Map
let map = new Map();
map.set("a", 1)
map.set("b", 1)
map.set("c", 1)
for(let value of map){
    console.log(value);
}
// ['a', 1]
// ['b', 1]
// ['c', 1]

// 遍历Set
let set = new Set();
set.add("a");
set.add("b");
set.add("c");

for(let value of set){
    console.log(value);
}
// a
// b
// c

// 遍历函数入参arguments
function fun(a,b,c,d){
    for(let value of arguments){
       console.log(value)
    }
}
fun(1,2,3,4)

// 1
// 2
// 3
// 4

// 遍历DOM节点
for(let value of document.querySelectorAll("span")){
    console.log(value)
}
//<span class="f6">z</span>
// ...

// 遍历 Generator 函数
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  yield 'ending';
}
var hw = helloWorldGenerator();
for(let value of hw){
    console.log(value)
}
// hello
// world
// ending

forEach 循环

它是Array原型上的方法,按升序为数组中含有效值的每一项执行一次回调函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上),但是不会改变原来的数组。一般会认为他是for循环的加强版。

let arr = [1,2,3];
arr[10] = 10;
let ttt = arr.forEach((item, index)=>{
    console.log(item, index)
})
// 1 0
// 2 1
// 3 2
// 10 10

forEach 跳出循环,return, break 无法跳出循环,可以用try...catch跳出循环

let arr = [1,2,3,4,5,6,7,8,9];
arr[10] = 10;
try{
    arr.forEach((item, index)=>{
        if(item === 5){
            throw("5555")
        }
        console.log(item, index)
    })
}catch(e){
	console.log(e)
}
// 1 0
// 2 1
// 3 2
// 4 3
// 5555

map循环

遍历时可以返回一个新数组,新数组的结果是原数组中每个元素都调用一次提供的函数后返回的值。

var arr = [1, 2, 3]
let newArr = arr.map((item) => item * 2)
console.log(newArr);
//[2,4,6]

性能比较

在测试环境、测试数据条件一致的情况下,性能排序为:

for > for of > forEach > map > for in

  • for 因为没有额外的函数调用和上下文,所以性能是最快的。
  • for ... of 具有 iterator 接口的数据结构,可以使用它来迭代成员,直接读取键值。
  • forEach 是 for 的语法糖,还有许多的参数和上下文,因此会慢一些。
  • map 因为它返回的是一个等长的全新数组,数组创建和赋值产生的性能开销较大。
  • for...in 性能最差,因为需要列举对象的所有属性,有转化过程,开销比较大。