Damon的es6学习之路 -- 循环的学习(day 2)

先总结下es6常用的几种循环:
forEachmapfiltersomeeveryindexOf,lastIndexOf,reduce,reduceRight
其中,粗体标记的是我目前认为会比较常用的方法。
特别声明,学习的文章以及部分内容摘抄自ImpulsionAndpower的博客文章:

https://blog.csdn.net/pupilxiaoming/article/details/78315419

以下是我对ImpulsionAndpower文章阅读后的理解:

forEach循环

forEach循环似乎以前就已经有过了,不过在es6中,应该出现了一些变化。
如文章所说最基础的示例是:

[1, 2 ,3, 4].forEach(console.log);

// 结果: 

// 1, 0, [1, 2, 3, 4]
// 2, 1, [1, 2, 3, 4]
// 3, 2, [1, 2, 3, 4]
// 4, 3, [1, 2, 3, 4]

这是forEach最简单的示例,事实上,forEach的完全体应该是酱紫:

[].forEach(function(value, index, array) {
    // ...
});

由上方方法的参数可以看出:
forEach方法中的function回调支持3个参数,第1个是遍历的数组内容;第2个是对应的数组索引,第3个是数组本身。
这里涉及到一个以前老是模糊理解的概念:回调函数
回调函数即是指:主函数运行完成后,再回过头去调用的函数,即使主函数在一开始就调用了回调函数,它也会在当前主体函数运行到最后时才进入回调函数内部。
关于forEach的例子,直接使用文章中的例子,就已经是最简单易懂的了。

var sum = 0;

[1, 2, 3, 4].forEach(function (item, index, array) {
  sum += item;
});

alert(sum); // 10

forEach的进一步使用

原文

forEach除了接受一个必须的回调函数参数,还可以接受一个可选的上下文参数(改变回调函数里面的this指向)
array.forEach(callback,[ thisObject])

var database = {
  users: ["张含韵", "江一燕", "李小璐"],
  sendEmail: function (user) {
    if (this.isValidUser(user)) {
      console.log("你好," + user);
    } else {
      console.log("抱歉,"+ user +",你不是本家人");	
    }
  },
  isValidUser: function (user) {
    return /^张/.test(user);
  }
};

// 给每个人法邮件
database.users.forEach(  // database.users中人遍历
  database.sendEmail,    // 发送邮件
  database               // 使用database代替上面标红的this
);

// 结果:
// 你好,张含韵
// 抱歉,江一燕,你不是本家人
// 抱歉,李小璐,你不是本家

如果这第2个可选参数不指定,则使用全局对象代替(在浏览器是为window),严格模式下甚至是undefined.

关于这个例子有两个需要注意的地方:

  1. 一开始提到的“forEach除了接受一个必须的回调函数参数”
  2. 还可以接受一个可选的上下文参数(改变回调函数里面的this指向)

这也是为什么一开始要明确理解回调函数的原因,且注意里面的“接受”被我加粗了。因为之前的简单的forEach是可以直接进行操作的,forEach的function自带有三个回调参数的,一个值,一个索引,一个原数组。而如果需要,我们还可以传入一个自写的回调函数。文中提到的“必须”,应该是指,如果要给forEach传入非默认的三个回调参数以外的东西,就必须以传入回调函数的方式进行。

关于上下文参数,我理解的是“作用域”。例子中可以看到,对于database下的users数组调用forEach,回调函数使用的是database下的sendEmail方法,因为该回调函数中使用到了database下的另一个方法isValidUser,而forEach的调用是在外面,即是说,作用域并不在database内部了,this的指向将是全局。第二个参数是database本身,代表的是改变作用域。那么回调函数启用时的this指向的就是database。

另外, forEach不会遍历纯粹“占着官位吃空饷”的元素的,例如下面这个例子:

var array = [1, 2, 3];

delete array[1]; // 移除 2
alert(array); // "1,,3"

alert(array.length); // but the length is still 3

array.forEach(alert); // 弹出的仅仅是1和3

这里其实存在一定疑问,当delete被删除后,数组被for循环遍历的话,索引1(即被delete的2)的位置打印出来的是undefined

这里是引用

然而我亲自尝试过,如果array直接赋值为[1,undefined,3]或者[1,null,3]或者[1,0,3]或者[1,false,3],forEach都是可以打印出来的。所以文章所指的“不会遍历纯粹‘占着官位吃空饷’的元素”,应该是指被delete的情况,不会进行判断。

forEach的总结

简单来说,我感觉forEach其实就是个简化的for循环,它依旧具有for循环的一切功能,基础来说,它的value、index、array可操作性可能更强。值得注意的是,forEach循环判断结束,是在遍历完成后undefined的情况才停止的。
在这里插入图片描述
通过这个验证可以发现,forEach打印出来的undefined,只是超过数组长度时,arr数组的索引为5的对象,为undefined。而哪怕值是undefined也可以放心打印出来。

map循环

Map跟forEach很相似

array.map(callback,[ thisObject]);

而默认的回调函数跟forEach也是一样的

[].map(function(value, index, array) {
    // ...
});

区别在于,forEach只是直接遍历数组,而map是映射。即:会将原数组遍历,操作后映射到新数组里,相当于一次数组深复制。
值得注意的是,callback需要有return值

var data = [1, 2, 3, 4];

var arrayOfSquares = data.map(function (item) {
  return item * item;
});

alert(arrayOfSquares); // 1, 4, 9, 16

如果callback没有return值,所有值就会被映射成undefined:

var data = [1, 2, 3, 4];
var arrayOfSquares = data.map(function() {});

arrayOfSquares.forEach(console.log);

以下是我实验得到的数据截图

这里是引用

实际使用中,map方法可以直接使用到对象数组中的特指参数,使用方式有点类似于vue中的v-for,也是js中for循环的功能,这里直接引用的文章中的例子:

var users = [
  {name: "张含韵", "email": "zhang@email.com"},
  {name: "江一燕",   "email": "jiang@email.com"},
  {name: "李小璐",  "email": "li@email.com"}
];

var emails = users.map(function (item) { return item.email; });

console.log(emails.join(", ")); // zhang@email.com, jiang@email.com, li@email.com
map总结

事实上,map跟forEach最大的区别就在于,forEach本身只是遍历数组,是在原数组基础上进行操作的。而map是将原数组操作后映射到一个新的数组。

filter循环

filter的主要作用在于“筛选”、“过滤”,最终也会和map一样,映射进一个新的数组里。区别是filter的return不是必须的

array.filter(callback,[ thisObject]);

它的callback同样需要有return值,不同之处在于,它return的时候是会判断一次的,只有return的true才会被返回,如果return的false则会被遗弃。
原文示例:

var data = [0, 1, 2, 3];
var arrayFilter = data.filter(function(item) {
    return item;
});
console.log(arrayFilter); // [1, 2, 3]

我自写的示例

这里是引用

值得一提的是,返回值只要是弱等于== true/false就可以了,而非非得返回 === true/false.

filter总结

在某些需要遍历数组,且有判断的时候,filter就特别有用,它的判断机制甚至可以写成一个函数进行判断,相比之下就比原来的for循环节约很多效率。
实验如下:
1:利用callback进行简单的筛选

这里是引用

2:利用callback进行多级筛选

这里是引用

由此可见,filter本身的return自己就是一个if的判断,可以节约一步判断逻辑的书写。

some循环

some的意义是“某些”,只要某些项满足即会返回true,在我看来,很大程度是,它是类似indexOf的。同样的,它的用法类似于forEach

array.some(callback,[ thisObject]);

以下是原文中的例子

var scores = [5, 8, 3, 10];
var current = 7;

function higherThanCurrent(score) {
  return score > current;
}

if (scores.some(higherThanCurrent)) {
  alert("朕准了!");
}

文中例子声明了一个数组,里面是无序的四个数字,然后声明一个current为数字7。最后利用callback进行some的遍历,当发现数组中某一个数字大于7时,循环便会停止,返回true,否则会返回false,这个过程非常类似indexOf,只是indexOf返回的是数组下标。

some总结

some可以用于简单的判断搜寻,主要用于判断“是否存在”,而不能判断“在哪儿”。使用局限性比较大。不过如果只是查找是否存在,是很简便的方法。

every循环

every循环类似some循环,按原文的意思,是一对好基友,事实上它的作用就在于,数组中必须每一项都为true才会返回true,否则会返回false。而不是some那样,一旦遇到true即停止循环返回true。

例子就不举了,跟some几乎一样。

every总结

雷同some的使用方式,区别于indexOf,更偏向于整体判断数组。应用面同样不大,但应用场景其实很多。也是值得使用的方法之一。

reduce循环

如原文所说,我比较赞同reduce更偏向于“迭代”而不是“减少”或则“简约”的意思。它的主要作用就是前后数据的操作,个人感觉通常用于简单的统计算法,例如求和之类的地方。
对于reduce的理解,配合例子比较好理解点,以下是原文的介绍和例子:

callback函数接受4个参数:之前值、当前值、索引值以及数组本身。initialValue参数可选,表示初始值。若指定,则当作最初使用的previous值;如果缺省,则使用数组的第一个元素作为previous初始值,同时current往后排一位,相比有initialValue值少一次迭代。

var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {
  return previous + current;
});

console.log(sum); // 10

一眼可以看出来,1+2=3,3+3=6,6+4=10,所以最后sum=10。reduce的作用不严谨地直观理解就可以是:
前一个值(对象)跟下一个值(对象)进行操作,得到的值(对象)再跟下一个值(对象)进行操作。
以下是原文中的说明:

这里是引用

由于原文并没有解释initialValue这个可选参数该怎么使用,于是查阅了下文档,并进行试验,同样的例子:

这里是引用

由此可见,initialValue是写在最后的而不是作为参数写在方法体内的。这是需要注意的地方。
另外,原文中对于二维数组的扁平化也有介绍:

var matrix = [
  [1, 2],
  [3, 4],
  [5, 6]
];

// 二维数组扁平化
var flatten = matrix.reduce(function (previous, current) {
  return previous.concat(current);
});

console.log(flatten); // [1, 2, 3, 4, 5, 6]

例子中只是单纯的数组拼接,事实上如果三个数组内的对象都为键值对,结合map应该会有更好的操作方式,可惜在试验过程中失败了。遇到的主要问题在于:

  1. reduce循环最后会undefined一次,这时候map的调用会报错。
  2. reduce循环时,各自可以map出previous和current的子元素(因为是二维数组,所以previous和current自身也是个数组),然而暂时没想到好的办法进行操作和拼接。本次学习重点是基础运用,暂时就不深入探究了。

贴出失败的代码,它是由上面的例子改变的,希望不使用concat拼接的情况下,就能够将字符串拼接在一起。然而就遇到了如上两个问题,以下是实验使用到的被终止的代码:

var matrix = [
      [{name:'a'}, {name:'b'}],
      [{name:'c'}, {name:'d'}],
      [{name:'e'}, {name:'f'}],
    ];

    // 二维数组扁平化
    var flatten = matrix.reduce(function (previous, current){

      let previousName = previous.map((item)=>{
        return item.name + item.name
      })
      let currentName = current.map((item)=>{
        return item.name + item.name
      })
      console.log(previousName,'previousName')
      console.log(currentName,'currentName')
      // return previousName + currentName
    });

    // console.log(flatten);

以下是打印出的内容,进行到这里就中断了试验,等以后有更好的例子或者感悟时,会回来进行补充和修改(暂时的模糊想法是reduce的嵌套。不过没有太大必要进行试验,不是本次学习的重点)

这里是引用

reduce总结

reduce更偏向于前后数据的对比统计操作,类似“求和”之类的操作最是方便。如我试验中的情况,或许会用到reduce的嵌套,相比之下的实用度和性价比有待商榷。不可否认的是,一维数组简单对比统计,reduce是特别方便的。

学习总结

indexOf、lastIndexOf以及reduceRight我没有去写,indexOf是曾经就掌握的东西,也是比较简单的东西,就没有去赘述,lastIndexOf是反向使用的indexOf。而reduceRight是反向的reduce,也就没有详细写了。
其实以上方法对于一维数组是最高效的,多维数组或复杂的逻辑判断,只用单一某一种循环进行判断,我认为是性价比较低的,应该组合使用,大可以使用map将原始数据映射成简单的一维数组,再由reduce进行统计计算。最后甚至可以使用some或者every进行判断之类的,我想效率会高很多。
es6循环的学习暂时就告一段落,更多的是需要在实际生产学习中多使用才能有更多的感悟和熟练度。加油~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值