for...of,for...in,forEach和map的区别

1. for…of循环

具有 iterator 接口,就可以用for...of循环遍历它的成员(属性值value)。for...of循环可以使用的范围包括数组、SetMap 结构、某些类似数组的对象、Generator 对象,以及字符串。for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。可以中断循环。

一个对象如果要具备可被 for...of 循环调用的 Iterator 接口,就必须在其 Symbol.iterator 的属性上部署遍历器生成方法(或者原型链上的对象具有该方法)

PS: 遍历器对象根本特征就是具有next方法。每次调用next方法,都会返回一个代表当前成员的信息对象,具有valuedone两个属性。

普通对象是不具备遍历器接口,例如:

let obj = {
  name: "zl",
  age: 21,
  job: "coder"
};

for(let item of obj) {
  console.log(item);  //TypeError: obj is not iterable
}

为其部署遍历器生成方法:

let obj = {
  name: "zl",
  age: 21,
  job: "coder",
  [Symbol.iterator]() {
    const self = this;
    const keys = Object.keys(self);  // [ 'name', 'age', 'job' ]
    let index = 0;
    return {
      next() {
        if(index

PSObject.keys():返回给定对象所有可枚举属性的字符串数组。

因为generator函数返回的就是迭代器,迭代器具有next()方法,所以使用 Generator 函数(遍历器对象生成函数)简写 Symbol.iterator 方法,可以简写如下:

let obj = {
  name: "zl",
  age: 21,
  job: "coder",
  * [Symbol.iterator]() {
    const self = this;
    const keys = Object.keys(self);
    for(let index=0; index

原生具备 Iterator 接口的数据结构有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象
  • ES6 的数组、Set、Map 都部署了以下三个方法: entries() / keys() / values(),调用后都返回遍历器对象。

扩展:类数组及转为数组的方法

两者的区别:

⑴ 都拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理);
⑵ 类数组不具有数组所具有的方法;
⑶ 类数组是一个普通对象,而真实的数组是Array类型。

常见的类数组有: 函数的参数 arguments, DOM 对象列表Nodelist(比如通过 document.querySelectorAll 得到的列表), jQuery 对象 (比如 $(“div”))。

类数组可以转换为数组:

//第一种方法
Array.prototype.slice.call(arrayLike, start);
//第二种方法
[...arrayLike];
//第三种方法:
Array.from(arrayLike);

PS:任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。

2. for…in循环

遍历对象自身的和继承的可枚举的属性,也就是说会包括那些原型链上的属性。如果想要仅迭代自身的属性,那么在使用 for...in 的同时还需要配合 getOwnPropertyNames()hasOwnProperty()。可以中断循环。

3. forEach

只能遍历数组,不能中断,没有返回值(或认为返回值是undefined,故无法链式调用)。传递的函数作为forEach()的第一个参数,然后forEach()使用三个参数调用该函数:数组元素、元素的索引和数组本身。看个例子:

//只传入了一个参数,数组元素的值。
var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function (value) {
  sum += value;
});
console.log(sum); //15
console.log(data)  // [ 1, 2, 3, 4, 5 ]
// 传入三个参数
var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function (v, i, a) {
  a[i] = v + 1;
});
console.log(data); // [ 2, 3, 4, 5, 6 ]

由上面两个例子可见对元素进行操作的话是不会改变原数组的,但是直接对数组进行操作的话是会改变原数组的。

当数组项是复杂数据类型时,操作元素也会改变原数组:

let arr = [
  {name: "zl"},
  {age: 21}
];
arr.forEach((item) => {
  item.job = "coder"
});
console.log(arr);  //[ { name: 'zl', job: 'coder' }, { age: 21, job: 'coder' } ]

不能通过break中断循环,但是用try{}catch(){}可以通过抛出异常跳出循环。捕获异常机制本身的功能就是在出现异常的时候跳出try的代码块到catch里处理异常。我们可以用throw方法手动抛出一个异常,这样就跳出了forEach循环。

    let arr = [0, 1, 2, 3, 4, 5, 6]
try{
    arr.forEach((item) => {
        if (item === 3) {
            throw 'Jump out now!'//在这里抛出异常
        }
        console.log(item)
    })
} catch (e) {
    console.log(e)
}

运行结果:

0
1
2
Jump out now!

4. map

只能遍历数组,不能中断,返回值是修改后的数组。传递给map的函数的调用方式和传递给和forEach()的函数的调用方式是一样的。但是传递给map()的函数应该有返回值。注意,map()返回的是新数组,对于原数组的改变与否参考forEach()。看一个简单的例子:

var a = [1,2,3,4,5];
var b = a.map(function (x) {
  return x * x
});
console.log(b);
console.log(a);

参考:
《JavaScript权威指南》
眷你:https://juejin.im/post/5bcb249a6fb9a05d212ed038#heading-0
刘小夕:https://juejin.im/post/5cab0c45f265da2513734390#heading-4

你可能感兴趣的:(JavaScript,foreach,map,for...in,for...of)