<JavaScript深入>for-of、for-in、forEach 和map的区别

JavaScript 深入系列 #9

<JavaScript深入>for-of、for-in、forEach 和map的区别
Photo by Ferenc Almasi / Unsplash
  1. for...of 语句可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
  2. for...in 语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
  3. Array​.prototype.forEach() 方法对数组的每个元素执行一次提供的函数。
  4. Array​.prototype​.map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

区别1: for...in 、for...of 可以中断,forEach 、map 不可以中断

var array = ['a', 'b', 'c', 'd', 'e'];

// for...of 
for(const value of array) {
  // if (value === 'b') countinue; for...of 没有 countinue
  if (value === 'e') break;
  console.log(value); // a b c d
}

// for...in
for(const value in array) {
  // if (value === '1') countinue; for...in 没有 countinue
  // typeof value === string
  if (value === 4) break; // false 
  if (value === '4') break; // true 
  console.log(value); // 0 1 2 3
}

// forEach
array.forEach((value, index) => {
  // forEach() 没有办法中止或者跳出循环, 除了抛出一个异常
  if (value === 'e') throw 'forEach Error';
  console.log(value, index); // a 0 b 1 c 2 d 3
})

// map
array.map((value, index) => {
  // map() 没有办法中止或者跳出循环, 除了抛出一个异常
  if (value === 'e') throw 'map Error';
  console.log(value, index); // a 0 b 1 c 2 d 3
})

区别2: for...of 具有 iterator 接口,for...inforEachmap 没有

var array = ['a', 'b', 'c', 'd', 'e'];
// for...of
for(const value of array) {
  // 如果不中断一直循环,卡死
  if (array.length > 12) break;
  array.push(value);
  console.log(value); // a b c d e a b c ...如果一直循环,则一直渲染
}

// for...in
array = ['a', 'b', 'c', 'd', 'e'];
for(const key in array) {
  array.push(key);
  console.log(key); // 0 1 2 3 4
}

// forEach
array = ['a', 'b', 'c', 'd', 'e'];
array.forEach(value => {
  array.push(value);
  console.log(value); // a b c d e
})

// map
array = ['a', 'b', 'c', 'd', 'e'];
array.map(value => {
  array.push(value);
  console.log(value); // a b c d e
})

// 如果是删除数组
// for...of
array = ['a', 'b', 'c', 'd', 'e'];
for(const value of array) {
  array.pop();
  console.log(value); // a b c
}

// for...in
array = ['a', 'b', 'c', 'd', 'e'];
for(const key in array) {
  array.pop();
  console.log(key); // 0 1 2
}

// forEach
array = ['a', 'b', 'c', 'd', 'e'];
array.forEach(value => {
  array.pop();
  console.log('forEach', value); // a b c
})

// map
array = ['a', 'b', 'c', 'd', 'e'];
array.map(value => {
  array.pop();
  console.log(value); // a b c
})

区别3: for...in 可以遍历 Array Map Set Object String, for...of 可以直接遍历 String Set,forEach 能遍历 Array Map Set, map 只能遍历 Array

var string = 'abcde';

// for...of
for(const value of string) {
  console.log(value); // a b c d e
}

// for...in 
for(const key in string) {
  console.log(key); // 0 1 2 3 4
}

var json = {a: 'a', b: 'b', c: 'c', d: 'd', e: 'e'};

// for...in 不需要声明遍历器
for(const key in json) {
  console.log(key); // a b c d e
}

// for...of 直接调用会报错,object is not iterable
var keys = Object.keys(json);
json[Symbol.iterator] = keys[Symbol.iterator].bind(keys);
// for...of
for(const value of json) {
  console.log(value); // a b c d e
}

var object = function() {
  this.keya = 'a';
  this.keyb = 'b';
  this.keyc = 'c';
}
var newObject = new object();

// for...in 不需要声明遍历器
for(const key in newObject) {
  console.log(key); // keya keyb keyc
}

// for...of 直接调用会报错,object is not iterable
var keys = Object.getOwnPropertyNames(newObject);
newObject[Symbol.iterator] = keys[Symbol.iterator].bind(keys);
// for...of 
for(const value of newObject) {
  console.log(value); // a b c
}

var map = new Map();

扩展

forEachmap 为什么不能 return 或者 break 跳出循环 (猜测)

Array.prototype.forEach = function(callback) {
  for (var i = 0; i < this.length; i++) {
    callback(this[i], i, this);
  }
};
      
array = ['a', 'b', 'c', 'd', 'e'];
array.forEach((value, index, _array) => {
  // break; function 没有 break
  // return; 也只是跳过当前 function
  console.log(value);
})                               

forEachmapfor...in 在删除数组的时候为什么没有抛出错误

forEach 遍历的范围在第一次调用 callback 前就会确定。调用 forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()),之后的元素将被跳过  - 摘取自 MSD

参考文献

  1. Array.prototype.forEach()
  2. Array.prototype.map()
  3. for...in
  4. for...of
  5. Map
  6. Iterator 遍历器
  7. for...in for...of forEach map