回顾for-in遍历

在js里,for-in遍历的是可迭代对象的key,这点需要特别注意:

"use strcit";
var x=['lz','h','is','a sb']; //Array对象是可迭代的
for(let i in x){
    console.log(i); //用for-in遍历输出看一下
}

输出

0
1
2
3
不方便之处

如果要获取它的值,就需要再去查询一次这个key所对应的值:

"use strcit";
var x=['lz','h','is','a sb']; //Array对象是可迭代的
for(let i in x){
    console.log(x[i]); //i是key,而x[i]就是i在x中对应的value
}

输出

lz
h
is
a sb

这种方法显然有些低效,而且语义上不是那么直观,我想要遍历的只是数组里的各个元素,为什么要使用key呢。

尝试遍历Set时存在的问题

如果尝试用这种方式去遍历Set,前面学了Set可以看成只存储key的键值对系,但是却获取不到东西:

"use strcit";
var x=new Set(['lz','h','is','a sb']); //Set对象是可迭代的
for(let i in x){
    console.log(i); //如果i视为object的key,尝试访问key去遍历Set
}

输出(没东西)

我认为这可能体现了Set的无序性,总之没能遍历它。

尝试遍历Map时存在的问题

Map底层基于树来实现,它的key大抵也是无序的,没法遍历:

"use strcit";
var x=new Map([[6,'lz'],[7,'h'],[7.5,'is'],[9.2,'a sb']]); //Map对象是可迭代的
for(let i in x){
    console.log(i); //如果i视为object的key,尝试访问key去遍历Map的每个键
}

输出(没东西)

for-of遍历

用于Set

for-of遍历对于Set遍历的是集合中的值:

"use strcit";
var x=new Set(['lz','h','is','a sb']);
for(let i of x){
    console.log(i);
}

输出

lz
h
is
a sb
用于Map

for-of遍历对于Map遍历的是键值对的Array形式:

"use strcit";
var x=new Map([[6,'lz'],[7,'h'],[7.5,'is'],[9.2,'a sb']]);
for(let i of x){
    console.log(i);
}

输出

Array [ 6, "lz" ]
Array [ 7, "h" ]
Array [ 7.5, "is" ]
Array [ 9.2, "a sb" ]
用于Array

for-of遍历对于Arrayt遍历的是其每个元素的值:

"use strcit";
var x=new Array('lz','h','is','a sb');
for(let i of x){
    console.log(i);
}

输出

lz
h
is
a sb

还有一些差异

在处理Object对象时的差异

for-of是ES6标准下的,在这之前就存在的for-in将Object也视为可迭代对象,遍历出的是对象的各个属性和方法的名称:

"use strcit";
//为这个Object对象添加了4个属性2个方法
var ok={
    sb1:'lz',
    sb2:'h',
    sb3:'is',
    sb4:'a sb',
    myfun1:function(x,y){
        return x+y;
    },
    myfun2:function(x,y){
        return x-y;
    }   
};
for(let i in ok){
    console.log(i);
}

输出

sb1
sb2
sb3
sb4
myfun1
myfun2

ES6标准不认为Object对象是可迭代的,只认Map,Set,Array,所以不能用for-of遍历之:

"use strcit";
//Object对象
var ok={
    sb1:'lz',
    sb2:'h',
    sb3:'is',
    sb4:'a sb',
    myfun1:function(x,y){
        return x+y;
    },
    myfun2:function(x,y){
        return x-y;
    }   
};
for(let i of ok){
    console.log(i); //尝试用for-of遍历之
}

输出(报错)

TypeError: ok is not iterable
为可迭代对象添加成员时的差异

从for-in的角度来看,Map和Set遍历不了,Object本来就是由各个成员变量和成员方法组成的,而for-in又认为Object可迭代,添加成员能遍历是理所当然的。

所以添加成员时的差异只要去看Array对象。

为Array对象添加成员时,for-in会把这个成员和其它下标同一地视为key去遍历到:

"use strcit";
var ok=["lzh","is","rubbish","sb","big sb dog"];
ok.name="SBLZH"; //为这个Array对象添加一个成员变量
//为这个Array对象添加一个成员方法
ok.myfun=function(x,y){
    return x+y;
};
//尝试用for-in遍历之
for(let i in ok){
    console.log(ok[i]);
}

输出

lzh
is
rubbish
sb
big sb dog
SBLZH
function ok.myfun()

现在,如果只有for-in,被添加了成员的Array对象变得很难用了。

而for-of会正确的区分成员和其内容:

"use strcit";
var ok=["lzh","is","rubbish","sb","big sb dog"];
ok.name="SBLZH"; //为这个Array对象添加一个成员变量
//为这个Array对象添加一个成员方法
ok.myfun=function(x,y){
    return x+y;
};
//尝试用for-of遍历之
for(let i of ok){
    console.log(i); //注意这里不需要ok[i]了,只要用一个i
}

输出

lzh
is
rubbish
sb
big sb dog

可迭代对象的forEach()方法

forEach方法接收一个函数为参数,这样传进来的函数称为callback回调函数

在每次迭代的过程中都会为这个函数传入一些参数来调用它一次,当然形参的名字是任意合法的都一样。用这个forEach()方法就可以把遍历+做事情写的非常简洁了。

对于Object对象,ES6已经明确指出它不是可迭代对象,Object对象自然没有forEach()方法。

属于Array对象的forEach()方法
"use strcit";
var ok=["lzh","is","rubbish","sb","big sb dog"];
var control=false; //用于控制只在第一次输出对象自己
ok.forEach(
    //传进来三个参数依次是(目前的元素值,目前的下标,本数组)
    function(now_element,now_index,thisArray){
        //第三个参数每次迭代都一样的,只输出一次看一下就好
        if(control===false){
            control=true;
            console.log("这个Array是"+thisArray);
            console.log("第3个参数的类型是"+typeof thisArray);
        }
        console.log("ok["+now_index+"]="+now_element);
    }
);

输出

这个Array是lzh,is,rubbish,sb,big sb dog
第3个参数的类型是object
ok[0]=lzh
ok[1]=is
ok[2]=rubbish
ok[3]=sb
ok[4]=big sb dog
属于Set对象的forEach()方法

注意在Set对象上使用时传进来的前两个参数是完全一样的。

"use strcit";
var ok=new Set(["lzh","is","rubbish","sb","big sb dog"]);
var control=false; //用于控制只在第一次输出对象自己
ok.forEach(
    //传进来三个参数依次是(目前的元素值,目前的元素值,本集合)
    //毕竟Set作为集合不涉及下标(无序),传进来的前两个参数都是元素值
    function(now_element,now_elem,thisSet){
        //第三个参数每次迭代都一样的,只输出一次看一下就好
        if(control===false){
            control=true;
            console.log("这个Set是"+thisSet);
            console.log("第3个参数的类型是"+typeof thisSet);
        }
        console.log("第1个参数="+now_element);
        console.log("第2个参数="+now_elem);
    }
);

输出

这个Set是[object Set]
第3个参数的类型是object
第1个参数=lzh
第2个参数=lzh
第1个参数=is
第2个参数=is
第1个参数=rubbish
第2个参数=rubbish
第1个参数=sb
第2个参数=sb
第1个参数=big sb dog
第2个参数=big sb dog
属于Map对象的forEach()方法
"use strcit";
var ok=new Map([[7,"lzh"],[8.5,"is"],['666阿',"rubbish"],[-30,"sb"],[250,"big sb dog"]]);
var control=false; //用于控制只在第一次输出对象自己
ok.forEach(
    //传进来三个参数依次是(目前的value,目前的key,本Map)
    function(now_value,now_key,thisMap){
        //第三个参数每次迭代都一样的,只输出一次看一下就好
        if(control===false){
            control=true;
            console.log("这个Set是"+thisMap);
            console.log("第3个参数的类型是"+typeof thisMap);
        }
        console.log("ok["+now_key+"]="+now_value);
    }
);

输出

这个Set是[object Map]
第3个参数的类型是object
ok[7]=lzh
ok[8.5]=is
ok[666阿]=rubbish
ok[-30]=sb
ok[250]=big sb dog