JavaScript--ES5数组新增方法(forEach、filter、every、some、reduce、reduceRight)的用法与原码实现

ES5数组方法(forEach、filter、every、some、map、reduce、reduceRight)的用法

在讲原码实现之前,先将各个API的用法。这里我先放一个数组,以便后续使用~~
<script>
    var dataArr = [
            {name: "John", des: '全网最帅', sex: 'boy', age: 18, grade: 80},
            {name: "sunny", des: '比刘宝宝丑那么点', sex: 'boy', age: 30, grade: 100},
            {name: "MrDeng", des: '比成哥丑那么点', sex: 'boy', age: 45, grade: 100},
            {name: "cst", des: '全网最丑', sex: 'boy', age: 26, grade: 100},
            {name: "chang", des: '全网最矮', sex: 'girl', age: 18, grade: 80},
            {name: "Monikatx", des: '全网最美', sex: 'girl', age: 20, grade: 100},
        ];
script>

1.forEach

    dataArr.forEach(function (item, index, self){
           this[index].innerText = item.name;
    }, dom);

点击这里(forEach详细API)

forEach必须要传一个匿名函数作为实参,第二个参数是可选参数,讲用作改变匿名函数的this。
forEach的作用,可以看做是加强的for循环,用来遍历数组
forEach之所以是看做是加强版的for循环,就是因为他也是一个循环。所发挥的作用也for相差无几,底层也是利用的for循环。那他到底是怎么循环的呢?遍历数组length长度多长,这里的第一个实参,匿名函数就执行几次,而形参也会跟着被遍历。

这里我先说下匿名函数形参(形参是可以随意命名的,可以选择自己喜欢的名字):

    形参一: item  代表的所遍历数组的value值, 如:arr[0] = "刘宝宝" ,"刘宝宝"是value值。
    形参二: index 代表的所遍历数组的key值(索引), 如:arr[0] = "刘宝宝" ,[0]是key值。
    形参二: self 代表的所遍历数组自己, 如:arr[0] = "刘宝宝" ,arr是self值。

forEach传的匿名函数会自己调用,而第二个实参,将被作为匿名函数的this传入 ,如:

JavaScript--ES5数组新增方法(forEach、filter、every、some、reduce、reduceRight)的用法与原码实现_第1张图片
之后6个方法原理是一样的,之后我就不在重复述说了

2.filter

  // filter
   var younger = dataArr.filter(function (item, index, self){
       let flag = item.age < 25;
       this[index].innerText += ( flag  ?  " baby" : " old");
       return flag;
   }, dom)
   console.log(younger);//输出[{name: "John"...}, {name: "chang"}, {name: "Monikatx"}]

filter与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。

这里主要说说filter的功能:执行完成返回一个数组,我这里用younger接收。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给filter,filter自己将做隐式转化为布尔值判断真假。当返回值return 为真(true),将该次遍历的数组添加到younger。反之false舍弃,不添加。

3.every

 // every 当遇上false停止遍历
    var flagEvery = dataArr.every(function (item, index, self){
        console.log(item.grade);
        return item.grade > 90 ? true : false;
    }, dom)
    console.log(flagEvery);

every与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。

every的功能:执行完成返回一个布尔值。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给every。当所有返回值return 为真,返回真(true),只要有一个不为真(true)返回假(false)。

4.some

// some 遇上true停止遍历
var flagSome = dataArr.some(function (item, index, self){
   console.log(item.grade);
   return item.grade > 90 ? true : false;
}, dom)
console.log(flagSome);

some与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。

some的功能:执行完成返回一个布尔值。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给some。当所有返回值return 有一个为真(true),返回true,全为假(false)返回假(false)。

5.map

// map 映射
var newAge = dataArr.map(function (item, index, self){
   // return item.age + 10;
   item.age += 10;
   return item;
}, dom)
console.log(newAge);

map与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。

map的功能:执行完成返回一个新的数组。执行匿名函数内的代码时,将该次遍历的返回值添加到新数组中。这段代码中我写了两段return,第一段return改变的是对象里的属性,并返回该属性值,这里返回的是原始值,和之前的的数组将没有联系。第二段return改变的也是对象里的属性,但返回的是对象本身,对象是引用值。如果你对新数组里的对象进行操作,原素组里的对象也将发生改变

6.reduce

// reduce
var allName = dataArr.reduce(function (prev, current, index, self) {
    return prev + current.name;
}, '+');
console.log(allName)

reduce这里需要注意,和forEach有点传参有些不一样了,第一个实参也是匿名函数,自行调用。这第二个实参也是可选参数,可传可不传,但是作用不一样。forEach是传一个实参作为this,而reduce的第二个实参是初始值。具体看下面~~

reduce的匿名函数形参:

  1. 形参一: prev 代表的所遍历数组索引位的上一个索引位匿名函数的返回值。若当前索引位为0,先看reduce有没有第二实参,也就是我上面所说的初始值。有,将初始值给prev。无,prev为空。
  2. 形参二: current 代表的所遍历数组的value值, 如:arr[0] = “刘宝宝” ,”刘宝宝”是value值。
  3. 形参三: index 代表的所遍历数组的key值(索引), 如:arr[0] = “刘宝宝” ,[0]是key值。
  4. 形参四: self 代表的所遍历数组自己, 如:arr[0] = “刘宝宝” ,arr是self值。

reduce的功能:执行完成返回一个返回值。执行匿名函数内的代码时,执行匿名函数内的代码块。返回一个返回值,但这个返回值不是给reduce自己用的,而是给自己下一个遍历数组索引位下一个匿名函数形参prev赋值的。最后一个索引位匿名函数的返回值才是reduce的返回值。

7.reduceRight

// reduceRight
 var allNameBack = dataArr.reduceRight(function (prev, current, index, self) {
     return prev + current.name;
 }, '-')
 console.log(allNameBack)

reduceRight与reduce其实差不多,区别在于他的遍历顺序是反过来的。细节没什么好说的,不懂的可以参考reduce

reduceRight的功能:执行完成返回一个返回值。执行匿名函数内的代码时,执行匿名函数内的代码块。返回一个返回值。reduceRight遍历是从数组最后一位开始遍历,初始也是给的最后数组索引位匿名函数的形参prev。返回值是索引位 [0] 匿名函数的返回值。


好了,本文到此就结束了~~
期待下一次更新吧!!

好吧,我是真想睡了。写到这已经是将近凌晨4点半了。但我还是坚持写完,整篇没看到我皮,是不是不适应??不是不想皮,是实在太困了。

接下来是用js原码自己封装7种ES5数组方法——–


ES5数组方法(forEach、filter、every、some、map、reduce、reduceRight)的原码实现

1.myForEach

Array.prototype.myForEach = function (fn) {
    var i, _self = this, len = _self.length, target = arguments[1];
    for(i = 0; i < len; i++) {
        fn.apply(target, [_self[i], i, _self]);
    }
}

既然是数组方法,那就要封装在Array.prototype上。Array.prototype.myForEach = function () {fn},这里的函数表达式执行就是arr.myForEach ()。按道理这么基础的东西,我本不应该再讲,但考虑到,我同学在听课的时候已经被绕晕了,所以是讲一下。这里的 fn 是形参,那执行就是arr.myForEach (function () {}), fn 就是这个里面的实参(匿名函数了)。可能你们会有疑惑,那dom作为第二个实参,在这怎么没看到与之对应的形参呢?我只看到一个形参!!!来来,接下来给你看一下第二个形参在哪里。不应该叫形参了,得叫实参列表JavaScript--ES5数组新增方法(forEach、filter、every、some、reduce、reduceRight)的用法与原码实现_第2张图片

这里通过argument[1]来获取第二个实参,也就是dom。而之前说的foreach第二个实参将成为匿名函数的this,就是在这了apply将匿名函数fn的this指向dom,并传入三个形参,变量名i, _self, len, 分别是指遍历的索引,被遍历的数组本身也就是arr, len是数组的长度arr.length,而这里面的for相信你们也能看懂,让匿名函数fn执行len-1次。希望你们不要再被 myForEachfn 给绕晕,我已经尽我最大的能力来讲了。下面的我就不再这么讲了

2.myFilter

 // 封装myFilter
Array.prototype.myFilter = function (fn) {
    var i, _self = this, len = _self.length, target = arguments[1], newArr = [];
    for(i = 0; i < len; i++) {
        fn.apply(target, [_self[i], i, _self]) ? newArr.push(_self[i]) : '像刘宝宝这么帅的人不多了';
    }
    return newArr;
}

有没有发现myFilter 和myForEach长竟然如此相像,先不要惊讶,因为在后面你会发现,这7个方法都长的差不多。

这里myFilter 比myForEach多了一个newArr数组,和一个return。这是因为filter是有返回值的,这个返回值也得是一个数组。之前说过filter每次遍历的匿名函数fn都会返回一个值给filter判断,这里for循环内fn.apply就是一次执行,执行后的返回值用作三目运算符判断,返回值为true,newArr添加当前数组索引位_self[i]。
然而这里后面这串字符串才是本文的重点。来来,圈起来,这是重点考试肯定是要考的。
emm,既然是筛选,那么为false就不要它执行任何操作就好了,但是三目运算符是你fasle也要有个表达式,那我就填个空就好了,但是这样不能表现出我的文笔功底,才会引出这么有哲学的一句话。令人反思啊。。。
最后遍历完了,就要返回数组,return newArrr。

3.myEvery

// 封装myEvery
 Array.prototype.myEvery = function (fn) {
     var i, _self = this, len = _self.length, target = arguments[1], flag = true;
     for(i = 0; i < len; i++) {
         if(!fn.apply(target, [_self[i], i, _self])){
             flag = false;
             break;
         } 
     }
     return flag;
 }

是的,我是个诚实的宝宝。你看是不是长得相差不大?

myEvery返回的是true或false。所以开头声明一个flag=true,接下来我只需要遍历出匿名函数fn不是true,让flag=false就好了,当然既然只要是有一个不为true,every就要返回false,那你匿名函数fn返回给我false。我干脆就不遍历了,让你出去吧。免得还让我做无用功,break退出块级作用域,块级作用域不知道的我这里也不说了。最后返回flag为false,当然你匿名函数给我的返回值全是true,那我根本没走if里,自然flag就是true了。

4.mySome

// 封装mySome
Array.prototype.mySome = function (fn) {
    var i, _self = this, len = _self.length, target = arguments[1], flag = false;
    for(i = 0; i < len; i++) {
        if(fn.apply(target, [_self[i], i, _self])){
            flag = true;
            break;
        } 
    }
    return flag;
}

这一块呢,我就不讲了。你只要看懂上面的,下面的你一定能看懂。

mySome返回布尔值。是的,就这么简短~~不服?不服你来打我啊!!

5.myMap

// 封装myMap
 Array.prototype.myMap = function (fn) {
       var i, _self = this, len = _self.length, target = arguments[1], newArr = [];
       for(i = 0; i < len; i++) {
           newArr.push(fn.apply(target, [_self[i], i, _self]));
           // 这里可以使用深度克隆,让返回值是引用值也区分开来
       }
       return newArr;
   }

myMap也没什么好讲的,就是声明一个数组,匿名函数操作完添加,返回数组。

myMap返回数组。是的,还是就这么简短~~不服?不服你又能怎样呢!!

6.myReduce

// 封装myReduce
Array.prototype.myReduce = function (fn) {
    var i, _self = this, len = _self.length, initValue = arguments[1];
    for(i = 0; i < len; i++) {
        initValue = fn(initValue, _self[i], i, _self);
    }
    return initValue;
}

myReduce也没什么好讲的。。。。。。但是我的求生欲告诉我这里还是要讲的。

myReduce的实参列表arguments[1]第二个实参不再是改变匿名函数(fn) this指向,而是直接作为匿名函数fn的第一个实参传入,传进去干嘛呢?之前说了reduce索引位是0的时候,他是没有上一返回值的,所以讲第二个实参传入作为初始值,也就是索引位是0的的上一个返回值。 initValue = fn(initValue, _self[i], i, _self);这样写是让初始值initValue(也就是当前索引返回值)赋值为下一个索引位的匿名函数fn使用(prev)。到for循环遍历完initValue也就被赋值成最后一位索引匿名函数fn的返回值;

6.myReduceRight

 // 封装myReduceRight
Array.prototype.myReduceRight = function (fn) {
    var i, _self = this, len = _self.length, initValue = arguments[1];
    for(i = len-1; i >= 0; i--) {
        console.log(i)
        initValue = fn(initValue, _self[i], i, _self);
    }
    return initValue;
}

myReduceRight也讲讲吧。

myReduceRight是反序遍历,那for循环也就要反向遍历,for(i = len-1; i >= 0; i–)。
好了,讲完了~~


最后,本文到这里就结束了。首先,有疑惑或者我哪里讲的不对,麻烦大家指正,我写文章纯属是来了兴趣就写上一篇,担心给大家带来误导。其次,感谢大家的支持,上次文章的阅读量给了我很大的激励,两天就200多阅读量,这使我膨胀了,我膨胀到自己都不认识了,我要飞上天和太阳肩并肩~~

你可能感兴趣的:(JavaScript)