合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
[TOC] # for循环的基本用法 要计算1+2+3,我们可以直接写表达式: ~~~ 1 + 2 + 3; // 6 ~~~ 要计算1+2+3+...+10,勉强也能写出来。 但是,要计算1+2+3+...+10000,直接写表达式就不可能了。 为了让计算机能计算成千上万次的重复运算,我们就需要循环语句。 JavaScript的循环有两种,一种是`for`循环,通过初始条件、结束条件和递增条件来循环执行语句块: ~~~js let x = 0; for (let i=1; i<=10000; i++) { x = x + i; } x; // 50005000 ~~~ 让我们来分析一下`for`循环的控制条件: * let i=1 这是初始条件,将定义局部变量i,并将变量i置为1; * i<=10000 这是判断条件,满足时就继续循环,不满足就退出循环; * i++ 这是每次循环后的递增条件,由于每次循环后变量i都会加1,因此它终将在若干次循环后不满足判断条件`i<=10000`而退出循环。 ## 练习 利用`for`循环计算`1 * 2 * 3 * ... * 10`的结果: ~~~ 'use strict'; let x = ?; let i; for ... if (x === 3628800) { alert('1 x 2 x 3 x ... x 10 = ' + x); } else { alert('计算错误'); } ~~~ `for`循环最常用的地方是利用索引来遍历数组: ~~~ const arr = ['Apple', 'Google', 'Microsoft']; for (let i=0; i<arr.length; i++) { const x = arr[i]; alert(x); } ~~~ `for`循环的3个条件都是可以省略的,如果没有退出循环的判断条件,就必须使用`break`语句退出循环,否则就是死循环: ~~~ let x = 0; for (;;) { // 将无限循环下去 if (x > 100) { break; // 通过if判断来退出循环 } x ++; } ~~~ # forEach 在ES5中引入。给定一个数组,您可以使用list.forEach()迭代其属性: ~~~ const list = ['a', 'b', 'c'] list.forEach((item, index) =>{ console.log(item) //value console.log(index) //index }) //index is optional list.forEach(item =&gt; console.log(item)) ~~~ > 不过需要注意的是你无法摆脱这个循环。 直接使用`iterable`内置的`forEach`方法,它接收一个函数,每次迭代就自动回调该函数。以`Array`为例: ```js var a = ['A', 'B', 'C']; a.forEach(function (element, index, array) { // element: 指向当前元素的值 // index: 指向当前索引 // array: 指向Array对象本身 alert(element); }); ``` *注意*,`forEach()`方法是ES5.1标准引入的,你需要测试浏览器是否支持。 `Set`与`Array`类似,但`Set`没有索引,因此回调函数的前两个参数都是元素本身: ~~~ var s = new Set(['A', 'B', 'C']); s.forEach(function (element, sameElement, set) { alert(element); }); ~~~ `Map`的回调函数参数依次为`value`、`key`和`map`本身: ~~~ var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); m.forEach(function (value, key, map) { alert(value); }); ~~~ 如果对某些参数不感兴趣,由于JavaScript的函数调用不要求参数必须一致,因此可以忽略它们。例如,只需要获得`Array`的`element`: ~~~ var a = ['A', 'B', 'C']; a.forEach(function (element) { alert(element); }); ~~~ # for ... in `for`循环的一个变体是`for ... in`循环,它可以把一个对象的所有属性依次循环出来: ~~~ var o = { name: 'Jack', age: 20, city: 'Beijing' }; for (var key in o) { alert(key); // 'name', 'age', 'city' } ~~~ 要过滤掉对象继承的属性,用`hasOwnProperty()`来实现: ~~~ const obj = { name: 'Jack', age: 20, city: 'Beijing' }; for (const key in obj) { if (obj.hasOwnProperty(key)) { console.log(key); // 'name', 'age', 'city' } } ~~~ 由于`Array`也是对象,而它的每个元素的索引被视为对象的属性,因此,`for ... in`循环可以直接循环出`Array`的索引: ~~~ var a = ['A', 'B', 'C']; for (var i in a) { alert(i); // '0', '1', '2' alert(a[i]); // 'A', 'B', 'C' } ~~~ *请注意*,`for ... in`对`Array`的循环得到的是`String`而不是`Number`。 # for...of语句 for...of语句在可迭代对象(包括 Array, Map, Set, String, TypedArray,arguments 对象等等)上创建一个迭代循环,对每个不同属性的属性值,调用一个自定义的有执行语句的迭代挂钩. for...of语法是为各种collection对象专门定制的,并不适用于所有的object.它会以这种方式迭代出任何拥有`[Symbol.iterator]` 属性的collection对象的每个元素。 ~~~ //variable:每一次迭代,不同属性的属性值会被赋值给该变量. //object:一个可迭代对象. for (variable of object) { statement } ~~~ 遍历 Array: ~~~ let iterable = [10, 20, 30]; for (let value of iterable) { console.log(value); } // 10 // 20 // 30 ~~~ 如果你不修改语句块中的变量 , 也可以使用 const 代替 let。注意不要用var,会导致变量提升副作用。 ~~~ let iterable = [10, 20, 30]; for (const value of iterable) { console.log(value); } // 10 // 20 // 30 遍历Map: let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]); for (let entry of iterable) { console.log(entry); } // [a, 1] // [b, 2] // [c, 3] for (let [key, value] of iterable) { console.log(value); } // 1 // 2 // 3 遍历 Set: let iterable = new Set([1, 1, 2, 2, 3, 3]); //集合是不包含重复元素的 for (let value of iterable) { console.log(value); } // 1 // 2 // 3 ~~~ # for...of与for...in的区别 for...in循环会遍历一个object所有的可枚举属性。 for...of语法是为各种collection对象专门定制的,并不适用于所有的object.它会以这种方式迭代出任何拥有`[Symbol.iterator] `属性的collection对象的每个元素。 下面的例子演示了for...of 循环和 for...in 循环的区别。for...in 遍历(当前对象及其原型上的)每一个属性名称,而 for...of遍历(当前对象上的)每一个属性值: ~~~ Object.prototype.objCustom = function () {}; Array.prototype.arrCustom = function () {}; let iterable = [3, 5, 7]; iterable.foo = "hello"; for (let i in iterable) { console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" } for (let i of iterable) { console.log(i); // logs 3, 5, 7 } ~~~ > for .. of与for ... in 语句一定注意弄清楚使用的环境,否则会得到意想不到的结果。. 你可能会有疑问,`for ... of`循环和`for ... in`循环有何区别? `for ... in`循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个`Array`数组实际上也是一个对象,它的每个元素的索引被视为一个属性。 当我们手动给`Array`对象添加了额外的属性后,`for ... in`循环将带来意想不到的意外效果: ```js var a = ['A', 'B', 'C']; a.name = 'Hello'; for (var x in a) { alert(x); // '0', '1', '2', 'name' } ``` `for ... in`循环将把`name`包括在内,但`Array`的`length`属性却不包括在内。 `for ... of`循环则完全修复了这些问题,它只循环集合本身的元素: ```js var a = ['A', 'B', 'C']; a.name = 'Hello'; for (var x of a) { alert(x); 'A', 'B', 'C' } ``` 这就是为什么要引入新的`for ... of`循环。 # 小结 循环是让计算机做重复任务的有效的方法,有些时候,如果代码写得有问题,会让程序陷入“死循环”,也就是永远循环下去。JavaScript的死循环会让浏览器无法正常显示或执行当前页面的逻辑,有的浏览器会直接挂掉,有的浏览器会在一段时间后提示你强行终止JavaScript的执行,因此,要特别注意死循环的问题。