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. jamesfor 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
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
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也更适用