常见的 **for… ** 循环,是一种最简单的迭代,循环是迭代机制的基础,
目录
一、可迭代对象有哪些?
- Array 数组
- Map
- Set 集合(内容不重复)
- 类似数组 arguments
- 字符串 类型
- NodeList
我的理解、我猜:
早期的数据结构比较简单,如数组[1,2,3,4],字符串“qwdafa”等,还没有(map/set)较复杂的数据类型,所以遍历这些简单的数据结构用for…循环 就够了
1.1 怎么看该对象是否为可迭代对象呢?
不靠谱的办法:console.log().(某些浏览器是不显示对象的__proto___属性)
例子(Chrome):
var obj1 = {
age:20,
country:'China',
addr: '广东'
}
var arr = [2,323,124,45,323,6];
console.log("obj1:",obj1.__proto__);
console.log("arr:",arr.__proto__);
红线部分:实现了iterator接口,arr为可迭代对象。
二、简单的迭代(for循环)的对象,它有什么特点?
换句话说,就是数组、字符串、类数组、NodeList,它们有什么共同点:
- 可以明确知道对象的长度
- 每一项都可以通过索引(下标)获取
三、为什么说forEach仅限数组使用?
前期学习的时候,遇到遍历数组,查询资料就有好多for…in、 for…of 、forEach区别类的文章,我就直接硬背没深究也没细心看书,现在算是懂了一点。
凭什么Array这么豪横独自拥有 **forEach()**方法 呢 ?
来看看定义Array.Array.prototype.forEach(),,Array. …所以懂了吧!害
来看看 for… 、 forEach() 遍历:
var arr = [1, 234,45,3,4,56,2,10];
var str = "dfadbam";
// 已知长度
for(var i = 0; i<arr.length; i++){
//下标索引
console.log(arr[i]);
// 除了打印,还可以对数组进行
}
arr.forEach(function(value,index){
console.log(value);
});
// 已知长度
for(var i = 0; i<str.length; i++){
console.log(str[i]);
}
//字符串
//报错!!!
str.forEach(function(value,index){
console.log(value);
});
// 类数组
function foo (a, b, c){
console.log(arguments);
arguments.forEach((value,index)=>console.log(value)); //报错!!!
}
foo(1,2,3,4,5)
3.1为什么说Array.prototype.forEach()方法向通用迭代需求迈进了一步,但仍不够理想?
我个人硬是觉得是,因为它是Array独有享有的,但是需要遍历的对象数据结构多了(也就是需求多了),所以不顾理想。(手动狗头)
四、那来讲讲 for…in
接下来又是瞎猜(知道官方解答可以评论砸我):
数组有一个除了 for 循环----还有forEach遍历,那其他数据结构类型(这里指对象类型)有没有呢?有,那就是for … in了,数组Array也是对象类型,所以for … in 对数组也适用。
对于for…in,官方描述:
官方:for…in语句遍历由字符串设置键控的对象的所有可枚举属性(忽略由符号设置键控的属性),包括继承的可枚举属性。
for…in循环只对可枚举的非符号属性进行迭代。从内置构造函数(如Array和Object)创建的对象继承了对象.原型以及字符串.原型,例如String的indexOf()方法或Object的toString()方法。循环将迭代对象本身的所有可枚举属性以及对象从其原型链继承的属性(较近原型的属性优先于其原型链中距离对象较远的原型的属性)。
4.1 对象类型的数据结构是怎样的?
ECMA-262 将对象定义为一组属性的无序集合。内容就是一组 属性名/值 对,值可以是数据或者函数。
就形如:
// 属性名:name,值:Jack
var obj = {
name:'Jack',
age:20,
sex:'female',
eat:function(){
console.log('eat apple');
}
}
4.2 for…in 遍历的是什么?
对象的属性,以及对象原型链上可枚举的属性,这很重要!
for…in遍历对象:
var obj = {
name: 'Jack',
age: 20,
7 :'意外',
sex: 'female',
eat: function () {
console.log('eat apple');
}
}
console.log(obj);
for(let key in obj){
console.log(key + ":" +obj[key]);
}
可以看到obj 原型上的这些函数没有打印出来,因为这些属性是不可枚举的!
举例证实for…in遍历的是对象的可枚举非符号属性:
var obj = {
name: 'Jack',
age: 20,
7: '意外',
sex: 'female',
eat: function () {
console.log('eat apple');
}
}
/*默认添加属性的它的数据类型是
{
configurable:true, //是否可配置
enumerable:true, //是否可枚举
writable:true, //是否可修改
value: undefined //这个属性对应的值
}*/
//添加可枚举的属性 an
Object.prototype.an = '惊喜';
// 给对象原型上添加不可枚举属性:bn,
Object.defineProperty(Object.prototype,"bn",{
configurable:true,
enumerable:false,
writable:true,
value:'你好'
})
console.log(obj);
for (let key in obj) {
console.log(key + ":" + obj[key]);
}
"你好“ 没有被打印出来,(bn 和它下面的属性亮度是灰色的,。。。)
4.3 for…in 遍历顺序
ECMA第三版说是按属性定义的顺序,第五版说顺序是没有被规定的
//Chrome 浏览器
var obj = {
name: 'Jack',
age: 20,
7 :'意外',
sex: 'female',
eat: function () {
console.log('eat apple');
}
}
for(let key in obj){
console.log(key + ":" +obj[key]);
}
Chrome 浏览器上的结果:
Chrome 、Opera,它们会先提取所有 key 的 parseFloat 值为非负整数的属性, 然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。
4.4 for…in 遍历对象 – Array
其实到这就不用硬背,就知道for … in 不适用于Array 了吧。假如 Array.prototype 上没有你自定义的可枚举属性,用for … in 是没问题(下标就是Array的属性);但是你不确定的话,所以才会有这句话:推荐你用for … of ·····
五、最后,for…of(ES6)
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
5.1 劳动节? 可迭代对象。
什么是可迭代对象?(第一点是官方解释)
- 实现了iterator接口的对象都可以作为迭代器使用,接口的实现可以自定义遍历访问到对象的value;
- 实现这个接口需要遵守迭代器协议;
- 然后迭代器模式就是:开发者无需知道是如何迭代的就能实现迭代操作。
我们不需要知道它是怎么从找到下一个要遍历的对象以及你脑子的疑惑,
只需要消费(next)就好了,就像你买衣服不需要知道衣服尽力了什么到你手里,只要付钱消费,有货就货到手,没货就提示无货。。。
有自带迭代器的数据类型
- Array 数组
- Map
- Set 集合(内容不重复)
- 类似数组 arguments
- 字符串 类型
- NodeList
5.2 for…of 遍历 Array
记住,实现了iterator接口的对象,才能用for…of 遍历。
那我们使用for … of 遍历Array时,具体Array上的这个接口是如何实现的,我还没看,通过实践可知它返回的是下标索引对应的值,所以呢,就推荐你用它。
关于如何实现iterator接口,写个自定义的迭代器,可以参考其他文章。
(https://blog.csdn.net/Lhy_JL/article/details/115457380)
六、总结
目前,是不是觉得遍历的方法有点杂,不统一?对于使用者来说,还需先判断数据的类型,考虑下使用哪个方法遍历?
那有没有统一的遍历方法呢,适用于任何结构的数据?
答案:有,那就是实现iterator接口(迭代器)。