JS中的循环遍历与控制循环(终止,跳过)

688 阅读3分钟

问题:

  1. 如何跳出forEach循环
  2. for...of怎么获取数组的index
  3. Object.keys()与Object.getOwnPropertyNames()的区别
  • for:最古老,也一直被认为性能最好的方法

但是要注意for (let i = 0, length = arr.length; i < length; i++) {}arr.length写在申明变量里,如果i<arr.length会每次都重新获取数组的length属性而耗费了一些性能

  • for...in:任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性,由于它迭代对象的整个原型链,很少场景需要用到,所以不建议使用,通常用于调试
  • for...of 可以遍历任何具有 iterator 接口的数据结构,比如 ArrayMapSetStringTypedArrayarguments 对象等等),稀疏数组的空值也会遍历,由于底层是依赖于 Symbol,需要包 regenerator-runtime 的支持,通常@babel/runtime替你做了这个事情

遍历的方法有很多,以下会记录比较常用的,但是不管是什么遍历方法,通常,不要在遍历的过程中对遍历对象进行添加,移除的操作(使代码变得不可预测与难以理解)

遍历数组

如果需要控制循环(终止,跳过)用for...of代替, 其他可以根据场景选择便利的数组内置的方法

for...of

const array1 = ['a', 'b', ,'c'];
// 只需要value
for(const item of array1){console.log(item)}
// a b undefined c

for...of循环中获取数组的index

const array1 = ['a', 'b', ,'c'];
for (const [index, value] of array1.entries()) {
  console.log(index, value);
}
// 0 'a'
// 1 'b'
// 2 undefined
// 3 'c'

forEach

Array.prototype.forEach((element, index, array) => { /* … */ }): 为数组中含有效值的每一项执行一次 callbackFn 函数,那些已删除或者未初始化的项将被跳过

const array1 = ['a', 'b', , 'c'];

array1.forEach((element, index, array) => console.log(element));
// a b c

map

Array.prototype.map(): 创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成. 很适合react中list的渲染React List Components by Example

const array1 = [1, 4, 9, , 16];

// pass a function to map
const map1 = array1.map((element, inddex, array) => element * 2);

console.log(map1);
// expected output: Array [2, 8, 18, empty,32]

遍历对象

key,value都一步到位(最常用)

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`)); // "foo: bar", "baz: 42"
// 等同于
for(const [key, value] of Object.entries(obj)){
    console.log(`${key}: ${value}`)
}

只遍历keys

const obj = { foo: 'bar', baz: 42, [Symbol()]:'symbol' };
Object.defineProperty(obj, 'property1', {
  value: 42,
  enumerable: false
});

Object.keys(obj).forEach(item=>console.log(item))
// foo baz
Object.getOwnPropertyNames(obj).forEach(item=>console.log(item))
// foo baz property1

只遍历values

const obj = { foo: 'bar', baz: 42, [Symbol()]:'symbol' };
Object.defineProperty(obj, 'property1', {
  value: 42,
  enumerable: false
});

Object.values(obj).forEach(item=>console.log(item))
// bar 42
  • object对象转map对象 new Map(Object.entries({ foo: 'bar', baz: 42 }))

遍历Map对象

const mapObj = new Map([['foo', 3], ['bar', {}], ['baz', undefined]]) 
mapObj.forEach((value, key, map)=>{
  console.log(`m[${key}] = ${value}`);
});
// 等同于
for (const [key, value] of mapObj) {
   console.log(`m[${key}] = ${value}`);
}

// 只遍历key
for(const key of mapObj.keys()){
    console.log(key)
}
// foo bar baz

// 只遍历value
for(const key of mapObj.values()){
    console.log(key)
}
// 3 {} undefined
  • map对象转数组 [...mapObj.entries()] [...mapObj.values()] [...mapObj.keys()]

控制循环终止

  • break:终止整个循环,break语句应嵌套在要中断的当前循环、switch 或 label 语句中,不能在 function 函数体中直接使用
  • continue: 只跳过此次循环,并在下一次迭代时继续执行循环,在循环以及label语句中使用
for(const item of [1,2,3,4,5,6]){
    if(item === 1){
        continue;
    }
    if(item === 3){
        break;
    }
    console.log(item);
}
// 2

跳出forEach循环

forEach需要抛出异常才可以终止循环,当你需要终止循环,不应该使用forEach()方法,请用for...of代替

var array1 = ['a', 'b', 'c','d'];

array1.forEach(element =>{
     if(element === "a"){
        return; // 跳过当前循环,相当于continue
    }
    if(element === "c"){
        throw Error("test") //终止循环
    }
     console.log(element)
});
// b
// Uncaught Error: test

那些有结果会提前结束遍历的原生数组方法: