JavaScript的for從懵懂到辨明

前言

初學JavaScript的時候,知道有各類for的時候,懵懵懂懂,也許是由於沒有系統學習的緣故。如今咱們把各類for都挨個辨明。express

1、for

建立一個循環,包含三個可選表達式。三個可選表達式在圓括號中,由分號分隔。後跟一個循環中執行的語句或塊語句。

語法

for ([initialization]; [condition]; [final-expression])
    statement

initialization

初始化語句。可寫表達式、賦值語句、變量聲明。數組

condition

循環條件表達式。若是表達式結果爲truestatement會被執行。若是表達式結果爲false,那麼執行流程跳到for語句結構後面的第一條語句。不寫表達式,就是永遠爲true函數

final-expression

每次循環的最後都要執行的表達式。執行時機是在下一次condition的計算以前。學習

statement

只要condition的結果爲true就會被執行的語句。多條語句使用塊語句({...})來包含。沒有語句執行,使用空語句(;)。this

示例

我想輸出五個數字。spa

for (let i = 0; i < 5; i++)
    console.log(i);
/*
0
1
2
3
4
*/

另外一種寫法輸出五個數字。可選的三個表達式,多行語句,須要使用{}包含起來。prototype

for (let i = 0; ; i++) {
    if (i >= 5)
        break;
    console.log(i);
}
/*
0
1
2
3
4
*/

注意,若是不寫條件表達式,就要確保循環體內可以跳出,防止死循環。break能夠跳出循環。code

2、for...in

以任意順序遍歷一個對象的可枚舉屬性。對於每一個枚舉的屬性, ...部分都會被執行。

語法

for (variable in object) {...}

variable

每次迭代的時候,將對象的屬性名分配給變量。對象

object

被迭代枚舉的對象。blog

示例

我想輸出對象裏全部的屬性和值。

let o = {
    a: 1,
    b: 2,
    c: 3
};
for (const v in o) {
    console.log(`o.${v} = ${o[v]}`);
}
/*
o.a = 1
o.b = 2
o.c = 3
*/

能夠看見for...in把全部的可枚舉屬性都枚舉了出來,v的類型是String,因此訪問當前遍歷到的屬性值使用了關聯數組o[v]的方式。

for...in在遍歷的時候,是以任意順序遍歷的。

let o = [];
o[0] = 1;
o['one'] = 2;
o[2] = 3;
for (const v in o) {
    console.log(`o[${v}] = ${o[v]}`);
}
/*
o[0] = 1
o[2] = 3
o[one] = 2
*/

所以當遇到對迭代訪問順序很重要的數組時,最好用整數索引。

我想累加數組全部的成員。

Array.prototype.age = 97;
let o = [1,2];
let sum = 0;
for (const v in o) {
    sum += o[v];
    console.log(`o[${v}] = ${o[v]}`);
}
console.log(`sum = ${sum}`);
/*
o[0] = 1
o[1] = 2
o[age] = 97
sum = 100
*/

很顯然這裏不符合咱們的預期,由於for...in循環語句將返回全部可枚舉屬性,包括非整數類型的名稱和繼承的那些。還會獲取到原型鏈上的可枚舉屬性。

我只想累加自身全部屬性。

Array.prototype.age = 97;
let arr = [1, 2];
let sum = 0;
for (const v in arr) {
    if (arr.hasOwnProperty(v)) {
        sum += arr[v];
    }
    console.log(`arr[${v}] = ${arr[v]}`);
}
console.log(`sum = ${sum}`);
/*
o[0] = 1
o[1] = 2
o[age] = 97
sum = 3
*/

若是你只要考慮對象自己的屬性,而不是它的原型,那麼使用Object.getOwnPropertyNames()或執行Object.prototype.hasOwnProperty()來肯定某屬性是不是對象自己的屬性(也能使用propertyIsEnumerable)。

3、Array.prototype.forEach()

對數組的每一個元素執行一次提供的函數。返回值爲 undefined

語法

Array.forEach(callback[, thisArg])

callback

爲數組每一個元素執行的函數,這個函數接受三個參數。

currentValue

數組中正在處理的當前元素值。

index

數組中正在處理的當前元素的索引。

array

forEach()方法正在操做的數組。

thisArg

可選參數。當執行回調 函數時用做this的值(參考對象)。

示例

我想輸出全部元素。

function logArrayElements(element, index, array) {
    console.log(`a[${index}] = ${element}`);
}
[4, 2, 3].forEach(logArrayElements);
/*
a[0] = 4
a[1] = 2
a[2] = 3
*/

forEcah()會跳過已經刪除或者爲初始化的項(但不包括那些值爲undefined的項,例如在稀疏數組上)。

function logArrayElements(element, index, array) {
    console.log(`a[${index}] = ${element}`);
}
[4, , 3].forEach(logArrayElements);
[1, undefined, 3].forEach(logArrayElements);
/*
a[0] = 4
a[2] = 3
a[0] = 1
a[1] = undefined
a[2] = 3
*/

沒有辦法終止會跳出forEcah()循環,除了拋出一個異常。

function logArrayElements(element, index, array) {
    console.log(`a[${index}] = ${element}`);
    break;
}
[1, 2, 3].forEach(logArrayElements);
/*
Uncaught SyntaxError: Illegal break statement
    at Array.forEach (<anonymous>)
    at <anonymous>:5:11
*/

使用return也沒法停止循環。

使用thisArg,舉個勉強的例子。經過自定義的add()方法,計算所添加數組的和sum和成員數count

function Counter() {
    this.sum = 0;
    this.count = 0;
}
Counter.prototype.add = function(array) {
    array.forEach(function(element) {
        this.sum += element;
        ++this.count;
    }, this);
};
let obj = new Counter();
obj.add([1, 3, 5, 7]);
console.log(obj.count);  // 4 === (1+1+1+1)
console.log(obj.sum);  // 16 === (1+3+5+7)
/*
4
16
*/

注意:若是使用箭頭函數表達式傳入函數參數,thisArg參數會被忽略,由於箭頭函數在詞法上綁定了this值。

若是數組在迭代時被修改了,則其餘元素會被跳過。

let words = ["one", "two", "three", "four"];
words.forEach(function(word) {
  console.log(word);
  if (word === "two") {
    words.shift();
  }
});
/*
one
two
four
*/

當到達包含值"two"的項時,整個數組的第一個項被移除了,這致使全部剩下的項前移了一個位置。由於元素"four"如今在數組更前的位置,"three"會被跳過。forEach()不會在迭代以前建立數組的副本。

4、for...of

for...of語句在能夠迭代的對象( ArrayMapSetStringTypedArrayarguments對象等等)上建立一個迭代循環,調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句。

語法

for (variable of iterable) {
    ...
}

variable

在每次迭代中,將不一樣屬性的值分配給變量。

iterable

被迭代枚舉其屬性的對象。

示例

迭代Array

let a = [10, 20, 30];
for (let v of a) {
    console.log(v);
}
/*
10
20
30
*/

迭代String

let s = 'Tang';
for (let v of s) {
    console.log(v);
}
/*
T
a
n
g
*/

迭代arguments

(function() {
    for (let v of arguments) {
        console.log(v);
    }
}
)(1, 2, 3);
/*
1
2
3
*/

區別

不管是 for...in仍是 for...of語句都是迭代一些東西。它們之間的主要區別在於它們的迭代方式。

for...in語句以原始插入順序迭代對象的可枚舉屬性。

for...of語句遍歷可迭代對象定義要迭代的數據。

如下示例顯示了與Array一塊兒使用時,for...of循環和for...in循環之間的區別。

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i);
}
/*
0
1
2
foo
arrCustom
objCustom
*/

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i);
  }
}
/*
0
1
2
foo
*/

for (let i of iterable) {
  console.log(i);
}
/*
3
5
7
*/
相關文章
相關標籤/搜索