循环遍历数组的次数会受数组变化的影响吗?
2018-03-21 本文已影响55人
Gaarahan
在查看MDN文档中Array.prototype.filter()时,看到这样一段话:
The range of elements processed by filter() is set before the first invocation of callback. Elements which are appended to the array after the call to filter() begins will not be visited by callback. If existing elements of the array are changed, or deleted, their value as passed to callback will be the value at the time filter() visits them; elements that are deleted are not visited.
- 提供给filter的元素范围在第一次调用callback函数时已经给定,调用filter之后添加的数组元素不会被callback函数访问
- 如果数组现存的元素被改变,传给callback的值将是filter访问到该元素时的值(改变后的)
- 如果数组现存的元素被删除,它将不会被访问
测试 1
var arr = [1,2,3];
var flag = 0;
arr.filter(function(val){
if(flag == 0){ /*只在第一次循环时push一个新值 4 */
arr.push(4);
flag = 1;
}
console.log(val); /*每次输出传给callback函数的元素*/
console.log('当前数组:' + arr); /*每次输出当前的数组*/
});
输出
image.png可以看出,4虽然被加入到了数组中,但并未被访问
测试2
var arr = [1,2,3,4,5,6];
arr.filter(function(val,index){
if(index == 3){ /*当访问到第4个元素时*/
arr[4] = 10; /*修改数组的第5个元素*/
arr.pop(); /*删除数组的最后一个元素*/
}
console.log('参数:' +val);
console.log('当前数组:' + arr);
});
image.png
arr[4]被改变,访问到的值为改变后的值;
arr[5]被删除,因此没有被callback访问只进行了5次输出
验证后,突然想到,在循环遍历数组过程中,如果数组改变或增加,循环会不会遍历新加入的元素?
先看看简单的for循环
var arr = [1,2,3,4];
var flag = 0;
for(var i = 0; i < arr.length; i ++ ){
if(flag == 0){
arr.push(5);
flag = 1;
}
console.log( arr[i] )
}
image.png
-
看到,在arr新加入了5后,5也被遍历到了
这个很容易理解,循环的结束条件是 i < arr.length,在数组改变后,它的length也同时增加,循环继续执行下去
试试forEach()循环
var arr = [1,2,3,4];
var flag = 0;
arr.forEach(function(val){
if(flag == 0){
arr.push(5);
flag = 1;
}
console.log( val )
});
console.log("arr : " + arr);
image.png
-
可以看出,新加入的元素5并未被遍历到
原因同样在MDN中有说明:
*The range of elements processed byforEach()
is set before the first invocation ofcallback
. Elements that are appended to the array after the call toforEach()
begins will not be visited bycallback
. If the values of existing elements of the array are changed, the value passed tocallback
will be the value at the timeforEach()
visits them; elements that are deleted before being visited are not visited. If elements that are already visited are removed (e.g. usingshift()
) during the iteration, later elements will be skipped - see example below.
与filter()的描述相同,不过最后一句提到:
- 如果一个已经被访问过的元素被移除,在迭代过程中,后面的元素会被跳过
自己测试一下
MDN例子
var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
console.log(word);
if (word === 'two') {
words.shift();
}
});
console.log(words);
image.png
删除两个元素
var words = ['one', 'two', 'three', 'four','five'];
words.forEach(function(word) {
console.log(word);
if (word === 'two') {
words.shift();
words.shift();
}
});
console.log(words);
image.png
简单对比可看出,删除了几个元素,就有几个当前元素后的元素被跳过,可以简单的理解为遍历时的参照为元素的下标,无论元素是否移动,下一个始终访问的是当前元素下标+1的元素