困惑
在练习数组操作的时候,希望实现删除数组中特定元素(比如删除字符“1”)的功能
很容易地就想到了利用for循环和splice方法
for (let i = 0; i < hobbies.length; i++) {
if (hobbies[i] == "1") {
hobbies.splice(i, 1);
i--;
}
}
测试结果
"1", "2", "3","1", "1", "4", "1"
"2", "3", "4"
如愿达成目标,不过想起来还有for in这个东西,似乎写起来更简洁
那用for in行不行呢
for(var i in hobbies)
{
if(hobbies[i]=="1")
{
hobbies.splice(i,1);
i--;
}
}
测试结果
"1", "2", "3", "1", "1","4"
"2", "3", "1", "4"
为什么中间多了一个"1"出来?发生了什么?
测试
添加console.log语句观测i值的变化
for(var i in hobbies)
{
console.log("判断前"+i);
if(hobbies[i]=="1")
{
hobbies.splice(i,1);
i=i+1;
}
console.log("判断后"+i)
}
结果
判断前0
判断后-1
判断前1
判断后1
判断前2
判断后1
判断前3
判断后3
可以看到,在第一次删去1后,i--由0变成了-1
但下一次判断的时候,i直接由-1跳到了1
隐藏的陷阱
在查询资料之后,才发现问题出在了i的类型上
标准的for循环中的i是number类型,表示的是数组的下标
但是for in循环中的i, 表示的是数组的key是string类型!
由于隐式类型转换的机制,这个区别被隐藏了起来
因为我测试的时候,用的是i--
而字符串,在运用递增递减符号的时候,会把它转换成number类型
因此虽然i是字符”1“,但也变成了0
如果我用的是i=i+1
字符串加数字,会把数字转化成字符串拼接,出现01这样的结果
那么这个区别就会被我监测到
另外,在splice函数中,参数用了字符串也并没有影响最后的结果,因为也进行了隐式转换
实际发生的事
在先前,我们删除了元素后,给i--,想当然地觉得下一次变量的值会+1,变回i,就可以对删除的元素后面的一个元素进行判断(因为它的角标数值由i+1,变成了i)
在for循环中是没事的,但在for in里面
i--这个操作其实没有任何意义,即便给i随便赋一个值,它的下一次取值依然是i+1
因为它是根据数组的元素键值(索引)来进行遍历的,string类型的0,1,2,3,4...arr.length-1已经排好了,并不会因为中途i的值有所变化而改变
除此之外,for in还有一些坑
•作用于数组的for-in循环体除了遍历数组元素外,还会遍历自定义属性。举个例子,如果你的数组中有一个可枚举属性myArray.name,循环将额外执行一次,遍历到名为“name”的索引。就连数组原型链上的属性都能被访问到。
•某些情况下,for in可能按照随机顺序遍历数组元素。
因此不适合用for in来遍历数组
for in适合用来遍历对象
P.S
如果用forEach呢
结果如下
hobbies.forEach(function(value,index,hobbies){
console.log("判断前"+index);
if(value=="1")
{
hobbies.splice(index,1);
console.log(typeof index);
index=index-1;
// console.log("二"+i);
}
console.log("判断后"+index);
});
["1", "2", "3", "1", "1", "4", "1"]
判断前0
number
判断后-1
判断前1
判断后1
判断前2
number
判断后1
判断前3
判断后3
判断前4
number
判断后3
["2", "3", "1", "4"]
数组中有几项,那么传递进去的匿名回调函数就执行几次
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。