JS基础:for...in、for...of、forEach(详解)

122 篇文章 11 订阅

 

 

for in循环

for...in 语句以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性。
for...in 循环只遍历可枚举属性。像 Array和 Object 使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf()  方法或 Object的toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性
语法:for (variable in object)    variable:在每次迭代时,variable会被赋值为不同的属性名。 object:非Symbol类型的可枚举属性被迭代的对象。
最好不要用for...in去迭代一个数组。

var obj={a:1,b:2,c:3};
for(var prop in obj){
  console.log(prop+" "+obj[prop]);
}

 可以循环遍历数组

var arr = ['nick','freddy','mike','james'];
 
for(var index in arr){	
    console.log(index +'. ' + arr[index]);
}
// 0. nick
// 1. freddy
// 2. mike
// 3. james

for in 与 for of 它们之间的主要区别在于它们的迭代方式

for...in 语句以任意顺序迭代对象的可枚举属性,会遍历手动添加的其他键,甚至包括原型链上的键,只能获得对象的键名,不能直接获取键,为遍历对象而设计。
for...of 语句遍历可迭代对象定义要迭代的数据,允许遍历获得键值,数组的遍历器接口只返回具有数字索引的属性,不同于forEach方法,它可以与break、continue和return配合使用。提供了遍历所有数据结构的统一操作接口。
 

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {}; 
 
let iterable = [3, 5, 7]; 
iterable.foo = 'hello';

每个对象将继承objCustom属性,并且作为Array的每个对象将继承arrCustom属性,因为将这些属性添加到Object.prototype和Array.prototype。由于继承和原型链,对象iterable继承属性objCustom和arrCustom。

for (let i in iterable) {
  console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom" 
}

此循环仅以原始插入顺序记录iterable 对象的可枚举属性。它不记录数组元素3, 5, 7 或hello,因为这些不是枚举属性。但是它记录了数组索引以及arrCustom和objCustom。

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // 0, 1, 2, "foo"
  }
}

这个循环类似于第一个,但是它使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是0, 1, 2和foo,因为它们是自身的属性(不是继承的)。属性arrCustom和objCustom不会被记录,因为它们是继承的。

for (let i of iterable) {
  console.log(i); // 3, 5, 7 
}

for of循环迭代并记录iterable作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性。

对于对象

对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,for...in循环依然可以用来遍历键名。

 

let es6 = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};
 
for (let e in es6) {
  console.log(e);
}
// edition
// committee
// standard
 
for (let e of es6) {
  console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function

上面代码表示,对于普通的对象,for...in循环可以遍历键名,for...of循环会报错。

一种解决方法是,使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组。

for (var key of Object.keys(someObject)) {
  console.log(key + ': ' + someObject[key]);
}

在js中一般使用的循环有两种

1.常规的for(var i=0;i<length;i++)

2.for-in:for(var item in list)

但是个人更喜欢使用第一种循环,而不喜欢几乎从来不使用for-in这种写法,原因如下:

1.第一种写法能够很好的控制循环何时结束,以及对应的索引;而第二种循环无法控制

2.第二种写法在某种情况下,可能会导致一些奇怪的bug

针对原因2参见下面简单的案例:

 

以上是在jsBin中编辑的代码,在第一段代码中,对js的数组Array添加了一个新的方法(prototype 属性使您有能力向对象添加属性和方法),为了在某些时候能够方便使用。此时,如果用for-in循环出数组arr的内容,会发现在本来想要得到的0,1,2,3的后面会多一个function(){alert("myfunction");},这应该不是我们想要得到的结果。

注意:for..in循环会把某个类型的原型(prototype)中方法与属性给遍历出来,所以这可能会导致代码中出现意外的错误

上面只是简单的遍历一个数组,可能出现的问题还不够明显,如下所示

 

如果我们遍历的数组中存放的是业务对象,此时会发现,最后一次输出的是空,在被循环的数组中不存在这个名称为空的人员,此时出现的错误就导致某些业务无法正常使用或显示。

对于使用for-in可能导出的bug,有两种方式避免

1.在循环数组集合时,不使用for-in,统一使用for(var i=0;i<length;i++)这种形式;

2.在for-in循环中增加一个hasOwnProperty的判断;

hasOwnProperty函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false

该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。

 for in , for of 和forEach三者都是循环时经常使用的,但是每个使用场景都是有轻微不同,接下来就进行一个对比

for…in…的作用

枚举对象

const person = {
  name: 'Lydia',
  age: 21,
};

for (const item in person) {
  console.log(item);
}

这个输出结果是: name age

对于这个结果可以简单理解为,对于对象object,使用for…in…循环是对对象的key值进行循环

但是使用for…of…结果就不相同

const person = {
  name: 'Lydia',
  age: 21,
};

for (const item of person) {
    console.log(item);
  }

这个输出结果TypeError: person is not iterable

这个结果可以看出for…of…不能对对象进行循环

再看看forEach能怎么样?

const person = {
  name: 'Lydia',
  age: 21,
}

person.forEach((i) => {
  console.log(i)
})

可枚举数组

这个输出结果为 0 ‘a’ 1 ‘b’ 2 ‘c’

这个结果看出使用for…in…是输出索引值,通过索引值能拿到数组数据

但是使用for…of…结果就不相同

 const arr = ['a','b','c']
 
 for (const item of arr) {
    console.log(item);
  }

这个输出结果为 ‘a’ ‘b’ ‘c’

这个结果看出使用for…of…是输出数组值

 const arr = ['a','b','c']
 
 for (const item of arr) {
    console.log(item);
  }

可枚举数组的原型对象

Array.prototype.sayHello = function(){
  console.log("Hello")
}
Array.prototype.str = 'world';
var myArray = [1,2,3];
myArray.name='数组';

for(let index in myArray){
  console.log(index);
}

这个输出结果是: 0 1 2 name sayHello str

这个结果看出for in 不仅返回的是数组的下标,而且将数组的原型对象以及数组对象本身属性值都会返回。但是这也存在一个问题,在实际工作开发中,这些对象很可能是不需要的,全部列举出来可能会产生新的问题。

为了解决原型对象这个问题,可以使用hasOwnProperty
 

Array.prototype.sayHello = function(){
  console.log("Hello")
}
Array.prototype.str = 'world';
var myArray = [1,2,3];
myArray.name='数组';

for(let index in myArray){
  if(myArray.hasOwnProperty(index)) {
    console.log(index);
  }
}

forEach的作用

可遍历数组

Array.prototype.sayHello = function(){
  console.log("Hello")
}
Array.prototype.str = 'world';
var myArray = ['a','b','c'];
myArray.name='数组';

myArray.forEach((value,i) => {
  console.log(value)
  console.log(i)
})

无法break

forEach有个问题就是不能中断执行

var arr = [3, 5, 7];

arr.forEach(function (value) {
  console.log(value);
  if (value === 5) {
    return false;
  }
});

输出的结果是 3 5 7

从结果可以看出,return false没有执行,他会一直运行到底

但是 for in 可以用break :

var arr = [3, 5, 7];

for (let index in arr) {
  console.log(arr[index]);
  if (arr[index] == 5) {
    break;
  }
}

输出的结果是 3 5

从结果可以看出,for in 可以使用break

for…of…的作用

可遍历数组

针对上面1.1.3中提及的问题,除了可用forEach,还可以使用for of进行处理

Array.prototype.sayHello = function(){
  console.log("Hello")
}
Array.prototype.str = 'world';
var myArray = ['a','b','c'];
myArray.name='数组';

for(let index of myArray) {
  console.log(index)
}

输出结果是 a b c

使用for of无法输出索引值,但也不会输出数组的原型对象。

可中断

针对上面1.2.2中提及的问题,for of可以使用break进行解决

var arr = [3, 5, 7];

for (let value of arr) {
  console.log(value);
  if (value == 5) {
    break;
  }
}

可迭代字符串

let str = 'hello';

for (let value of str) {
  console.log(value);
}

输出结果是 ‘h’ ‘e’ ‘l’ ‘l’ ‘o’

可迭代arguments类数组对象

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

输出结果是 1 2 3

可迭代map和set

let mapData = new Map([['a', 1], ['b', 2], ['c', 3]]);

for (let [key, value] of mapData) {
  console.log(value);
}


let setData = new Set([['a', 1], ['b', 2], ['c', 3]]);

for (let [key, value] of setData) {
  console.log(value);
}

for in 适用于纯对象的遍历,并且只能输出可枚举属性

forEach适用于需要知道索引值的数组遍历,但是不能中断

for of适用于无需知道索引值的数组遍历,因为可以中断。另外对于其他字符串,类数组,类型数组的迭代,for of也更适用

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值