[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 => 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的執行,因此,要特別注意死循環的問題。
- 內容介紹
- EcmaScript基礎
- 快速入門
- 常量與變量
- 字符串
- 函數的基本概念
- 條件判斷
- 數組
- 循環
- while循環
- for循環
- 函數基礎
- 對象
- 對象的方法
- 函數
- 變量作用域
- 箭頭函數
- 閉包
- 高階函數
- map/reduce
- filter
- sort
- Promise
- 基本對象
- Arguments 對象
- 剩余參數
- Map和Set
- Json基礎
- RegExp
- Date
- async
- callback
- promise基礎
- promise-api
- promise鏈
- async-await
- 項目實踐
- 標簽系統
- 遠程API請求
- 面向對象編程
- 創建對象
- 原型繼承
- 項目實踐
- Classes
- 構造函數
- extends
- static
- 項目實踐
- 模塊
- import
- export
- 項目實踐
- 第三方擴展庫
- immutable
- Vue快速入門
- 理解MVVM
- Vue中的MVVM模型
- Webpack+Vue快速入門
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 混入
- 過濾器
- 項目實踐
- 標簽編輯
- iView
- iView快速入門
- 課程講座
- 環境配置
- 第3周 Javascript快速入門