遍历时删除会出现什么问题
迭代器是什么,for,forEach,for in,for of都是什么
遍历时删除会出现什么问题
先看这个例子,遍历数组时删除
1 | var arr = [1,2,3] |
发现结果和期望不一样,应该是个空数组
因为当修改原数组时,下一轮用的是这个新的数组,所以第二轮实际上是[2,3]的arr[1]
1 | // 正序删除,需要更新下标 |
另外,java中有iterator.remove()方法进行删除
for
,forEach
,for in
,for of
差异
通过MDN来简单
回顾一下它们
for
常见的处理数组
的循环嘛
for (let i = 0; i < 9; i++) {}
特点
- 提前结束
- break退出循环
- continue提前结束本次循环,开始下一轮
- 是所有循环语法糖的基础
- 作用域
- 使用 var 声明的变量与 for 循环处在同样的作用域中。用 let 声明的变量是语句的局部变量。
forEach
依旧是处理数组
的循环,对每个元素依次执行一次指定函数
1 | arr.forEach((item,index,_arr)=>{ |
那些已删除或者未初始化的项将被跳过(例如稀疏数组[1,,3,,5])
(callback,thisArg) 还接收第二参数this,用法参照函数的this规则,默认指向window,见文末1
特点
- 提前结束
- throw抛出异常
- 实现浅拷贝,和循环类似
1
2
3
4
5
6
7
8
9function copy(obj){
const copy = Object.create(Object.getPrototypeOf(obj)) // 返回指定对象的原型
const propNames = Object.getOwnPropertyNames(obj); // 返回属性的属性名
propNames.forEach(function(name) {
const desc = Object.getOwnPropertyDescriptor(obj, name); // 返回属性名对应的四大属性描述符configurable,enumerable,value,writable
Object.defineProperty(copy, name, desc); // 更新对象的现有属性
});
return copy;
} - 在遍历时删除会遇到同样的问题,因为每次都是以当前arr执行
- 扁平化数组,建议使用flat()
1
2
3
4
5
6
7
8
9
10
11
12function flatten(arr) {
const result = [];
arr.forEach((i) => {
if (Array.isArray(i))
result.push(...flatten(i));
else
result.push(i);
})
return result;
}
const problem = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
flatten(problem); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
map
和forEach一样都是遍历数组的每一项
map()返回新数组,forEach()返回undefined
map不会改变原数组,forEach会改变原数组
for in
以任意顺序
遍历一个对象
的除Symbol以外的可枚举属性enumerable
(包括它的原型链上的可枚举属性)
用于调试,可以更方便的去检查对象属性
1 | for (var prop in obj) { |
特点
- 迭代过程中不要进行该
对象
的增删改- 不保证新属性会被访问到
- 不建议用于
数组
- 因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素
- 只迭代对象自身的属性
- getOwnPropertyNames() 或 hasOwnProperty() 或 propertyIsEnumerable 确定某属性是否是对象本身的属性
1 | // 这个例子可以看到由于存在原型链继承规则 |
for of
在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环
1 | for (let item of arr) { |
特点
- 弥补 forEach 和 for…in 的缺点
- 提前结束
- break, continue, throw
1 | // Array |
for in/of区别
迭代方式
for…in 语句以任意顺序迭代对象的可枚举属性
。
for…of 语句遍历可迭代对象
定义要迭代的数据。
迭代器是什么
迭代器是包含next方法的对象
{done:是否结束,产生下一个值,value:返回值}
本质上,迭代器会不断调用其 next() 方法直到返回 done: true
Array 或 Map 等内置可迭代对象
有默认的迭代行为
Object 则没有
所以不能直接用for…of ,可以用Object.keys(obj),Object.entries(obj)
1 | for..of |
附录
这里来枚举一下forEach中的this情况
1 | var arr = [1,2,3] |