在 ECMAScript5(简称 ES5)中,有三个循环。在 2015 年 6 月发布的 ECMAScript6(简称 ES6)中,新增了一种循环类型。他们是:
for for in for each for of今天,就让我们一起来看看这4个 for 循环。
1、简单的for循环
我们来看看最常见的写法:
const arr = [1, 2, 3]; for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }当循环中数组的长度没有变化时,我们应该将数组的长度存储在一个变量中,这样效率会更高。下面是改进的写法:
const arr = [1, 2, 3]; for (let i = 0, len = arr.length; i <len; i++) { console.log(arr[i]); }2、for-in
2.1、 使用 for-in
通常,我们可以使用for-in来遍历数组的内容,代码如下:
const arr = [1, 2, 3]; let index ; for ( index in arr) { console.log([arr[[ + index + [] = [ + arr[ index ]); }一般来说,操作的结果如下:
arr[0] = 1 arr[1] = 2 arr[2] = 3但这样做往往会产生问题。
2.2、 for-in的真相
for-in 循环遍历对象的属性,而不是数组的索引。所以for-in遍历的对象不限于数组,也可以遍历对象。示例如下:
const person = { fname: [san], lname: [zhang], age: 99 }; let info; for (info in person) { console.log([person[[ + info + [] = [ + person[info]); }结果如下:
person[fname] = san person[lname] = zhang person[age] = 99需要注意的是for-in遍历属性的顺序是不确定的,即输出结果的顺序与对象中属性的顺序无关,也与属性的字母顺序无关,也没有任何其他顺序。
2.3 、关于数组的真相
数组是Javascript中的一个对象,Array的索引是属性名。事实上,Javascript 中的[数组]有点误导。
Javascript 中的数组与大多数其他语言中的数组不同。首先,Javascript 中的数组在内存中不是连续的。
其次,Array 的索引不是指偏移量。其实Array的索引不是Number类型,而是String类型。之所以能正确使用 arr[0] 之类的写法,是因为语言可以自动改变 Number 类型。0 转换为 String 类型的[0]。
因此,Javascript 中从来没有 Array 索引,只有[0]、[1]等属性。
有趣的是,每个 Array 对象都有一个 length 属性,这使得它的行为更像其他语言中的数组。
但是为什么遍历Array对象的时候不输出length属性呢?那是因为for-in只能遍历[可枚举属性],length是不可枚举属性,实际上Array对象还有很多其他不可枚举属性。
现在,让我们回过头来看看使用 for-in 循环数组的例子。我们修改前面遍历数组的例子:
const arr = [1, 2, 3]; arr. name = [Hello world]; let index ; for ( index in arr) { console.log([arr[[ + index + [] = [+ arr[ index ]); }操作的结果是:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[ name ] = Hello world我们看到 for-in 遍历我们新的[name]属性,因为 for-in 遍历对象的所有属性,而不仅仅是[索引]。
同时,需要注意的是,这里输出的索引值,即[0]、[1]、[2]不是Number类型,而是String类型,因为它们是作为属性输出的,不是索引,这是否意味着我们只能输出数组的内容,而不能向我们的 Array 对象添加新属性?答案是否定的。
因为for-in不仅遍历数组本身的属性,还会遍历数组原型链上的所有可枚举属性。让我们看一个例子:
Array.prototype.fatherName = [Father]; const arr = [1, 2, 3]; arr. name = [Hello world]; let index ; for ( index in arr) { console.log([arr[[ + index + [] = [+ arr[ index ]); }操作的结果是:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[ name ] = Hello world arr[fatherName] = Father至此,我们可以发现for-in并不适合遍历Array中的元素,它更适合遍历对象的属性,这也是它创建的初衷。有一个例外,那就是稀疏数组,阅读以下示例:
let key ; const arr = []; arr[0] = [a]; arr[100] = [b]; arr[10000] = [c]; for ( key in arr) { if(arr.hasOwnProperty( key ) && /?$|^[1–9]\d*$/.test( key ) && key <= 4294967294 ) { console.log(arr[ key ]); } }For-in 仅遍历现有实体。上例中for-in遍历了3次(分别遍历属性为[0]、[100]、[10000]的元素,普通for循环会遍历10001次)。因此,只要处理得当,for-in 也可以在遍历 Array 中的元素方面发挥巨大的作用。
为了避免重复工作,我们可以包装上面的代码:
function arrayHasOwnIndex(array, prop) { return array.hasOwnProperty(prop) && /?$|^[1–9]\d*$/.test(prop) && prop <= 4294967294; // 2³²-2 }用法示例如下:
for (let key in arr) { if (arrayHasOwnIndex(arr, key )) { console.log(arr[ key ]); } }2.4、 for-in性能
如上所述,每次迭代操作都会同时搜索实例或原型属性。for-in 循环的每次迭代都会产生更多的开销,所以它比其他循环类型慢,一般速度是其他循环类型的 1/7。
因此,除非您明确需要迭代具有未知数量属性的对象,否则您应该避免使用 for-in 循环。如果需要遍历有限数量的已知属性列表,使用其他循环会更快,例如以下示例:
const obj = { [prop1]: [value1], [prop2]: [value2] }; const props = [[prop1], [prop2]]; for (let i = 0; i <props.length; i++) { console.log(obj[props[i]]); }在上面的代码中,对象的属性存储在一个数组中。与for-in搜索每个属性相比,代码只关注给定的属性,节省了循环的开销和时间。
3、forEach
在 ES5 中,引入了一个新循环,即 forEach 循环。
const arr = [1, 2, 3]; arr.forEach((data) => { console.log(data); });操作结果:
1 2 3forEach 方法对数组中包含有效值的每一项执行一次回调函数,那些已经被删除(使用delete 方法等)或从未赋值的项将被跳过(不包括那些未定义的项) 或空值)。回调函数会依次传入三个参数:
数组中当前项的值; 当前项在数组中的索引; 数组对象本身;需要注意的是,forEach 遍历的范围会在第一次调用回调之前确定。调用 forEach 后添加到数组的项目不会被回调访问。
如果现有值发生变化,则传递给callback的值就是forEach遍历它们时的值。不会遍历已删除的项目。
const arr = []; arr[0] = [a]; arr[3] = [b]; arr[10] = [c]; arr. name = [Hello world]; arr.forEach((data, index , array) => { console.log(data, index , array); });操作结果:
a 0 [[a], 3: [b], 10: [c], name : [Hello world]] b 3 [[a], 3: [b], 10: [c], name : [Hello world]] c 10 [[a], 3: [b], 10: [c], name : [Hello world]]这里的索引是Number类型的,不会像for-in那样遍历原型链上的属性。
因此,在使用forEach时,我们不需要特别声明索引和要遍历的元素,因为这些都是作为回调函数的参数。
另外,forEach 会遍历数组中的所有元素,但是 ES5 定义了一些其他有用的方法,下面是一部分:
every:循环在第一次返回false后返回 some:循环在第一次返回 true 后返回 filter:返回一个元素满足回调函数的新数组 map:在返回之前处理原始数组中的元素 reduce:依次处理数组中的元素,将上一次处理的结果作为下一次处理的输入,最终得到最终结果。forEach 性能
您可以看看jsPerf。在不同浏览器下测试的结果是forEach没有for快。如果将测试代码放在控制台中,可能会得到不同的结果。主要原因是控制台的执行环境与真实的代码执行环境不同。
4、for-of
我们先来看一个例子:
const arr = [‘a’, ‘b’, ‘c’]; for (let data of arr) { console.log(data); }运行的结果是:
a b c为什么要引入for-of?
要回答这个问题,我们先来看看 ES6 之前的 3 种 for 循环的缺陷:
forEach 不能中断和返回;
for-in 的劣势更加明显。它不仅遍历数组中的元素,还遍历自定义属性,甚至访问原型链上的属性。此外,遍历数组元素的顺序可以是随机的。
所以,针对以上缺点,我们需要对原来的for循环进行改进。但是 ES6 不会破坏您已经编写的 JS 代码。
目前,数以千计的网站依赖于 for-in 循环,其中一些甚至将其用于数组遍历。通过修复 for-in 循环来添加数组遍历支持会使这一切变得更加混乱,因此标准委员会在 ES6 中添加了一个新的循环语法来解决当前的问题 for-of 。
那么 for-of 能做什么呢?
与forEach相比,它可以正确响应break、continue、return。
for-of 循环不仅支持数组,还支持大多数类似数组的对象,例如 DOM 节点列表对象。
for-of 循环还支持字符串遍历,它将字符串作为 Unicode 字符序列进行迭代。
for-of 还支持 Map 和 Set(都是 ES6 中的新功能)对象遍历。
总结一下,for-of 循环具有以下特点:
这是迭代数组元素的最简洁直接的语法。
这种方法避免了 for-in 循环的所有陷阱。
与 forEach 不同,它正确响应 break、continue 和 return 语句。
它不仅可以遍历数组,还可以遍历类数组对象和其他可迭代对象。
然而,应该注意的是,for-of 循环不支持普通对象,但是如果您想遍历一个对象的属性,您可以使用 for-in 循环(它就是这样做的)。
最后,但并非最不重要的是,ES6 引入了另一种方法来迭代数组的值,那就是 Iterator。最后一个例子:
const arr = [‘a’, ‘b’, ‘c’]; const iter = arr[Symbol.iterator](); iter. next () // { value: ‘a’, done: false } iter. next () // { value: ‘b’, done: false } iter. next () // { value: ‘c’, done: false } iter. next () // { value: undefined, done: true }不过,这个内容超出了本文的范围,Iterator 有很多要讲的。
原文链接:https://HdhCmsTesttoutiao测试数据/a7052130003575702029/
查看更多关于四个Javascript 中的 For 循环的详细内容...