前言
ES5和ES6中新增了不少东西,对于数组而言,新增了不少迭代方法,让我们可以抛弃for
循环,更方便的写JS代码。
正文
ES5和ES6中新增的的数组迭代方法如下:
- forEach
- map
- filter
- some
- every
- reduce / reduceRight
- find / findIndex
其中,find / findIndex是ES6新增的,其余都是ES5新增的。所以这些方法对低版本IE都不兼容。
接下来我们看看这些方法如何使用并在低版本IE进行兼容。
forEach
forEach方法是这些方法里面最基本的一个方法,它的作用是对数组的每个元素执行一次提供的函数。
例如:
1 2 3 4 5 6 7 8 9 10
| var arr = [1, 2, 3]; arr.forEach(function (element, index, array) { console.log(element, index, array) }) //output 1 0 [1, 2, 3] 2 1 [1, 2, 3] 3 2 [1, 2, 3]
|
forEach方法中的callback函数会被依次传入三个参数:
forEach方法还可以传入第二个参数,这个参数是可选的。如果给forEach传递了第二个参数,callback函数里的this
将指向这个参数。如果没有传入第二个参数,则this
指向全局对象(在浏览器是为window),严格模式下是undefined
。
1 2 3 4 5 6 7 8 9 10 11
| var arr = [1, 2, 3]; var obj = {name: 'zhang'}; arr.forEach(function (element, index, array) { console.log(element, index, array, this) }, obj) // output 1 0 [1, 2, 3] {name: "zhang"} 2 1 [1, 2, 3] {name: "zhang"} 3 2 [1, 2, 3] {name: "zhang"}
|
下面我们用forEach写一个稍显完整的例子了,数组求和:
1 2 3 4 5 6 7 8
| var sum = 0; [1, 2, 3].forEach(function (element, index, array) { console.log(array[index] == element); // true sum += item; }); console.log(sum); // 6
|
最后我们对低版本IE进行一下拓展,是这个方法就有更好的兼容性,代码如下:
1 2 3 4 5 6 7
| Array.prototype.forEach = Array.prototype.forEach || function(fn, context){ for (var k = 0, length = this.length; k < length; k++) { if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) { fn.call(context, this[k], k, this); } } }
|
map
map方法的作用就是将原数组按照一定的规则映射成一个新的数组。再将其返回,是返回一个新的数组,而不是将原数组直接改变。使用方法和参数都跟forEach相似。
下面是一个数值求平方的例子:
1 2 3 4 5 6 7
| var data = [1, 2, 3]; var arrayOfSquares = data.map(function (element) { return element * element; }); console.log(arrayOfSquares); //[1, 4, 9]
|
callback需要有return值,如果没有,就像下面这样:
1 2 3 4 5 6 7
| var data = [1, 2, 3]; var arrayOfSquares = data.map(function (element) { element * element; }); console.log(arrayOfSquares); // [undefined, undefined, undefined]
|
最后我们对map方法进行一下拓展:
1 2 3 4 5 6 7 8 9
| Array.prototype.map = Array.prototype.map || function (fn, context) { var arr = []; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { arr.push(fn.call(context, this[k], k, this)); } } return arr; };
|
filter
filter为“过滤”、“筛选”的意思。指数组filter后,返回过滤后的新数组。用法和参数跟map差不多。
与map方法不同的是,filter方法的callback函数需要返回弱等于true
或false
的值。如果为true
,则通过,否则,不通过。
例如:
1 2 3 4 5 6 7 8 9 10 11 12
| var arr = [0, 1, 2, 3]; var newArr = arr.filter(function (element, index, array) { return e; }) var newArr2 = arr.filter(function (element, index, array) { return e>=2; }) console.log(newArr); // [1, 2, 3] console.log(newArr2); // [2, 3]
|
下面是对filter方法的拓展:
1 2 3 4 5 6 7 8 9
| Array.prototype.filter = Array.prototype.filter || function (fn, context) { var arr = []; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { fn.call(context, this[k], k, this) && arr.push(this[k]); } } return arr; };
|
some
some方法是只要数组中的某个值,符合你给定的判断条件就返回true
;否则,返回false
。用法和参数跟前面的方法一样。
例如:
1 2 3 4 5 6 7 8
| function isBigEnough(element, index, array) { return element >= 4; } var passed = [1, 2, 3].some(isBigEnough); var passed2 = [1, 2, 3, 4].some(isBigEnough); console.log(passed); // false console.log(passed2); // true
|
下面是some方法的拓展:
1 2 3 4 5 6 7 8 9 10
| Array.prototype.some = Array.prototype.some || function (fn, context) { var passed = false; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (passed === true) break; passed = !!fn.call(context, this[k], k, this); } } return passed; };
|
every
every方法与some方法相对,every方法是数组中的所有值都符合你给定的判断条件的时候才会返回true
,否则就返回false
。
例如:
1 2 3 4 5 6 7 8
| function isBigEnough(element, index, array) { return element >= 3; } var passed = [2, 3, 4].every(isBigEnough); var passed2 = [3, 4, 5].every(isBigEnough); console.log(passed); // false console.log(passed2); // true
|
every方法拓展如下:
1 2 3 4 5 6 7 8 9 10
| Array.prototype.every = Array.prototype.every || function (fn, context) { var passed = true; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (passed === false) break; passed = !!fn.call(context, this[k], k, this); } } return passed; };
|
reduce / reduceRight
reduce / reduceRight 方法比上面的几种方法要复杂一些;它的语法如下:
1
| array.reduce(callback,[initialValue])
|
其中callback
可以依次接受四个参数:
accumulator
上一次调用回调返回的值,或者是提供的初始值(initialValue
)
currentValue
数组中正在处理的元素
currentIndex
数组中正在处理的元素索引,如果提供了initialValue
,从0开始;否则从1开始。
array
数组对象本身
reduce / reduceRight 方法中,第二个参数(initialValue
)是可选的;其值用于第一次调用callback
的第一个参数。
我们先来看一个例子:
1 2 3 4
| var sum = [1, 2, 3].reduce(function(a, b) { return a + b; }); console.log(sum); // 6
|
下面我们来看看reduce是如何运行的
例如执行下面这段代码:
1 2 3 4 5 6 7 8 9 10 11 12
| var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){ console.log(accumulator, currentValue, currentIndex, array) return accumulator + currentValue; }); console.log(sum); // output 0 1 1 [0, 1, 2, 3, 4] 1 2 2 [0, 1, 2, 3, 4] 3 3 3 [0, 1, 2, 3, 4] 6 4 4 [0, 1, 2, 3, 4] 10
|
从上面的输出结果可以看出callback
被调用四次,每次调用的参数和返回值如下表:
| callback | accumulator | currentValue | currentIndex | array | return |
|:—:|:—:|:—:|:—:|:—:|:—:|
|第一次调用|0|1|1|[0, 1, 2, 3, 4]|1|
|第二次调用|1|2|2|[0, 1, 2, 3, 4]|3|
|第三次调用|3|3|3|[0, 1, 2, 3, 4]|6|
|第四次调用|6|4|4|[0, 1, 2, 3, 4]|10|
上面是没有传入第二个参数(initialValue
)的情况,那传入第二个参数又是怎么样的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13
| var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){ console.log(accumulator, currentValue, currentIndex, array) return accumulator + currentValue; }, 10); console.log(sum); // output 10 0 0 [0, 1, 2, 3, 4] 10 1 1 [0, 1, 2, 3, 4] 11 2 2 [0, 1, 2, 3, 4] 13 3 3 [0, 1, 2, 3, 4] 16 4 4 [0, 1, 2, 3, 4] 20
|
传入第二个参数后callback
调用了五次,每次调用的参数和返回值如下表:
| callback | accumulator | currentValue | currentIndex | array | return |
|:—:|:—:|:—:|:—:|:—:|:—:|
|第一次调用|10|0|0|[0, 1, 2, 3, 4]|10|
|第二次调用|10|1|1|[0, 1, 2, 3, 4]|11|
|第三次调用|11|2|2|[0, 1, 2, 3, 4]|13|
|第四次调用|13|3|3|[0, 1, 2, 3, 4]|16|
|第五次调用|16|4|4|[0, 1, 2, 3, 4]|20|
从上面的情况可以看出:不提供initialValue
,reduce方法会从索引1的地方开始执行callback
方法,跳过第一个索引。提供 initialValue
,从索引0开始。
同时,是否提供initialValue
对于回调函数第一次执行时,accumulator
和currentValue
的取值有两种情况:调用reduce时提供initialValue
,accumulator
取值为initialValue
,currentValue
取数组中的第一个值;没有提供initialValue
,accumulator
取数组中的第一个值,currentValue
取数组中的第二个值。
reduceRight与reduce类似,不同之处在于它是从最后一个值开始计算的。
那么我们该如何拓展一个reduce / reduceRight方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| Array.prototype.reduce = Array.prototype.reduce || function (callback, initialValue ) { var previous = initialValue, k = 0, length = this.length; if (typeof initialValue === "undefined") { previous = this[0]; k = 1; } if (typeof callback === "function") { for (k; k < length; k++) { this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this)); } } return previous; }; Array.prototype.reduceRight = Array.prototype.reduceRight || function (callback, initialValue ) { var length = this.length, k = length - 1, previous = initialValue; if (typeof initialValue === "undefined") { previous = this[length - 1]; k--; } if (typeof callback === "function") { for (k; k > -1; k-=1) { this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this)); } } return previous; };
|
find / findIndex
find方法用于找出第一个符合条件的数组成员。它的参数跟forEach方法是一样的;所有数组成员依次执行回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
例如:
1 2 3 4 5 6 7 8 9
| var value = [1, 5, 10, 15].find(function(element, index, array) { return element > 9; }); var value2 = [1, 5, 10, 15].find(function(element, index, array) { return element > 20; }); console.log(value); // 10 console.log(value2); // undefined
|
findIndex方法和find相似;不过它返回数组中符合条件的元素的索引。如果所有成员都不符合条件,则返回-1。
1 2 3 4 5 6 7 8 9
| var value = [1, 5, 10, 15].findIndex(function(element, index, array) { return element > 9; }); var value2 = [1, 5, 10, 15].findIndex(function(element, index, array) { return element > 20; }); console.log(value); // 2 console.log(value2); // -1
|
对于不支持find / findIndex方法的浏览器,我们可以自己实现一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Array.prototype.find = Array.prototype.find || function (fn, context) { if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (fn.call(context, this[k], k, this)) { return this[k]; } } } return undefined; }; Array.prototype.findIndex = Array.prototype.findIndex || function (fn, context) { if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (fn.call(context, this[k], k, this)) { return k; } } } return -1; };
|
最后
上面的兼容实现不知道对不对,欢迎大家指正。
参考资料:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array