《JavaScript 闖關記》之數組

數組是值的有序集合。每一個值叫作一個元素,而每一個元素在數組中有一個位置,以數字表示,稱爲索引。javascript

JavaScript 數組是無類型的,數組元素能夠是任意類型,而且同一個數組中的不一樣元素也可能有不一樣的類型。數組的元素甚至也多是對象或其餘數組。前端

JavaScript數組是動態的,根據須要它們會增加或縮減,而且在建立數組時無須聲明一個固定的大小或者在數組大小變化時無須從新分配空間。java

JavaScript 數組多是稀疏的,數組元素的索引不必定要連續的,它們之間能夠有空缺。每一個JavaScript數組都有一個length屬性。針對非稀疏數組,該屬性就是數組元素的個數。針對稀疏數組,length比全部元素的索引要大。ios

JavaScript 數組是 JavaScript 對象的特殊形式,數組索引實際上和碰巧是整數的屬性名差很少。一般,數組的實現是通過優化的,用數字索引來訪問數組元素通常來講比訪問常規的對象屬性要快不少。git

數組繼承自 Array.prototype 中的屬性,它定義了一套豐富的數組操做方法。github

建立數組

可使用數組字面量和 new 關鍵字來建立數組。算法

使用數組字面量建立數組(推薦)

var empty = [];                 // 沒有元素的數組
var primes = [2, 3, 5, 7, 11];  // 有5個數值的數組
var misc = [1.1, true, "a"];    // 3個不一樣類型的元素

// 數組直接量中的值不必定要是常量,能夠是任意的表達式
var base = 1024;
var table = [base, base+1, base+2, base+3];

// 也能夠包含對象直接量或其餘數組直接量
var b = [[1, {x:1, y:2}], [2, {x:3, y:4}]];複製代碼

注意,不要忽略數組字面量的最後一個元素,僅以逗號結尾。下面幾個案例,在不一樣的瀏覽器下,可能會被識別成2個元素,也有可能識別成3個元素,而形成程序bug。例如:數組

var nums = [,,,];               // 很差的寫法
var names = ["stone",,];        // 很差的寫法
var colors = ["red","green",];  // 很差的寫法複製代碼

使用 new 關鍵字建立數組

使用 new 關鍵字調用構造函數 Array() 是建立數組的另外一種方法,能夠用三種方式調用構造函數。例如:瀏覽器

// 調用時沒有參數
var a = new Array();

// 調用時有一個數值參數,它指定長度
var a = new Array(10); 

// 顯式指定多個數組元素或者數組的一個非數值元素
var a = new Array(5, 4, 3, 2, 1, "testing");複製代碼

數組元素的讀和寫

使用 [] 操做符來訪問數組中的一個元素。數組的引用位於方括號的左邊。方括號中是一個返回非負整數值的任意表達式。使用該語法既能夠讀又能夠寫數組的一個元素。例如:微信

var a = ["world"];     // 從一個元素的數組開始
var value = a[0];      // 讀第0個元素
a[1] = 3.14;           // 寫第1個元素
var i = 2; 
a[i] = 3;              // 寫第2個元素
a[i + 1] = "hello";    // 寫第3個元素
a[a[i]] = a[0];        // 讀第0個和第2個元素,寫第3個元素複製代碼

請記住,數組是對象的特殊形式,能夠爲其建立任意名字的屬性。但若是使用的屬性是數組的索引,數組的特殊行爲就是將根據須要更新它們的length屬性值。

注意,可使用負數或非整數來索引數組。這種狀況下,數值轉換爲字符串,字符串做爲屬性名來用。既然名字不是非負整數,它就只能當作常規的對象屬性,而非數組的索引。一樣,若是湊巧使用了是非負整數的字符串,它就當作數組索引,而非對象屬性。當使用的一個浮點數和一個整數相等時狀況也是同樣的。例如:

a[-1.23] = true;  // 這將建立一個名爲"-1.23"的屬性
a["1000"] = 0;    // 這是數組的第1001個元素
a[1.000]          // 和 a[1] 相等複製代碼

事實上數組索引僅僅是對象屬性名的一種特殊類型,這意味着 JavaScript 數組沒有「越界」錯誤的概念。當試圖查詢任何對象中不存在的屬性時,不會報錯,只會獲得 undefined 值。

稀疏數組

稀疏數組就是包含從0開始的不連續索引的數組。一般,數組的 length 屬性值表明數組中元素的個數。若是數組是稀疏的,length 屬性值大於元素的個數。能夠用 Array() 構造函數或簡單地指定數組的索引值大於當前的數組長度來建立稀疏數組。

a = new Array(5);   // 數組沒有元素,可是 a.length = 5
a = [];             // 建立一個空數組,a.length = 0
a[1000] = 0;        // 添加一個元素,a.length 被自動更新爲1001複製代碼

足夠稀疏的數組一般在實現上比稠密的數組更慢、內存利用率更高,在這樣的數組中查找元素的時間與常規對象屬性的查找時間同樣長。

須要注意的是,當省略數組直接量中的值時(使用連續的逗號,好比 [1,,3] ),這時所獲得的數組也是稀疏數組,省略掉的值是不存在的:

var a1 = [,'1','2'];    // 此數組長度是3 
var a2 = [undefined];   // 此數組包含一個值爲 undefined 的元素 
console.log(0 in a1);   // false,a1 在索引0處沒有元素
console.log(0 in a2);   // true,a2 在索引0處有一個值爲 undefined 的元素複製代碼

瞭解稀疏數組是瞭解 JavaScript 數組的真實本質的一部分。儘管如此,實際上你所碰到的絕大多數 JavaScript 數組不是稀疏數組。而且,若是你確實碰到了稀疏數組,你的代碼極可能像對待非稀疏數組同樣來對待它們,只不過它們包含一些 undefined 值。

數組長度

每一個數組有一個 length 屬性,就是這個屬性使其區別於常規的 JavaScript 對象。針對稠密(也就是非稀疏)數組,length 屬性值表明數組中元素的個數。其值比數組中最大的索引大1。例如:

[].length             // 0,數組沒有元素
['a','b','c'].length  // 3,最大的索引爲2,length 爲3複製代碼

當數組是稀疏的時,length 屬性值大於元素的個數。並且關於此咱們能夠說的一切也就是數組長度保證大於它每一個元素的索引值。或者,換一種說法,在數組中(不管稀疏與否)確定找不到一個元素的索引值大於或等於它的長度。爲了維持此規則不變化,數組有兩個特殊的行爲。

  • 第一個如同上面的描述:若是爲一個數組元素賦值,它的索引 i 大於或等於現有數組的長度時,length 屬性的值將設置爲 i+1
  • 第二個特殊的行爲就是設置 length 屬性爲一個小於當前長度的非負整數 n 時,當前數組中那些索引值大於或等於 n 的元素將從中刪除。例如:
a = [1,2,3,4,5];     // 從5個元素的數組開始
a.length = 3;        // 如今 a 爲[1,2,3]
a.length = 0;        // 刪除全部的元素。a 爲[ ]
a.length = 5;        // 長度爲5,可是沒有元素,就像 new Array(5)複製代碼

還能夠將數組的 length 屬性值設置爲大於其當前的長度。實際上這不會向數組中添加新的元素,它只是在數組尾部建立一個空的區域。

在 ECMAScript 5中,能夠用 Object.defineProperty() 讓數組的 length 屬性變成只讀的。例如:

a = [1,2,3];                                            // 從3個元素的數組開始
Object.defineProperty(a, "length", {writable: false});  // 讓 length 屬性只讀
a.length = 0;                                           // a 不會改變複製代碼

數組元素的添加和刪除

咱們已經見過添加數組元素最簡單的方法,爲新索引賦值。例如:

a = []           // 開始是一個空數組
a[0] = "zero";   // 而後向其中添加元素
a[1] = "one";複製代碼

也可使用 push() 方法在數組末尾增長一個或多個元素。例如:

a = [];                 // 開始是一個空數組
a.push("zero");         // 在末尾添加一個元素。a = ["zero"]
a.push("one", "two");   // 再添加兩個元素。a = ["zero", "one", "two"]複製代碼

能夠像刪除對象屬性同樣使用 delete 運算符來刪除數組元素。例如:

a = [1,2,3]; 
delete a[1];   // a在索引1的位置再也不有元素
1 in a         // => false: 數組索引1並未在數組中定義
a.length       // => 3: delete操做並不影響數組長度複製代碼

注意,對一個數組元素使用 delete 不會修改數組的 length 屬性,也不會將元素從高索引處移下來填充已刪除屬性留下的空白。若是從數組中刪除一個元素,它就變成稀疏數組。

數組遍歷

使用 for 循環是遍歷數組元素最多見的方法。例如:

var keys = Object.keys(o);   // 得到 o 對象屬性名組成的數組
var values = []              // 在數組中存儲匹配屬性的值
for(var i = 0; i < keys.length; i++) {  // 對於數組中每一個索引
    var key = keys[i];                  // 得到索引處的鍵值
    values[i] = o[key];                 // 在 values 數組中保存屬性值
}複製代碼

在嵌套循環或其餘性能很是重要的上下文中,能夠看到這種基本的數組遍歷須要優化,數組的長度應該只查詢一次而非每次循環都要查詢。例如:

for(var i = 0, len = keys.length; i < len; i++) {
   // 循環體仍然不變
}複製代碼

這些例子假設數組是稠密的,而且全部的元素都是合法數據。不然,使用數組元素以前應該先檢測它們。例如:

for(var i = 0; i < a.length; i++) {
    if (!a[i]) continue;    // 跳過 null、undefined 和不存在的元素
    if (!(i in a)) continue ;   // 跳過不存在的元素
    if (a[i] === undefined) continue;   // 跳過 undefined 和不存在的元素
    // 循環體
}複製代碼

還可使用 for-in 循環處理稀疏數組。循環每次將一個可枚舉的屬性名(包括數組索引)賦值給循環變量,不存在的索引將不會遍歷到。例如:

for(var index in sparseArray) {
   var value = sparseArray[index];
   // 此處可使用索引和值作一些事情
}複製代碼

但因爲 for-in 循環可以枚舉繼承的屬性名,如添加到 Array.prototype 中的方法。基於這個緣由,在數組上不該該使用 for-in 循環,除非使用額外的檢測方法來過濾不想要的屬性。例如:

for(var i in a) {
    // 跳過繼承的屬性
    if (!a.hasOwnProperty(i)) continue;

    // 跳過不是非負整數的 i
    if (String(Math.floor(Math.abs(Number(i)))) !== i) continue;
}複製代碼

JavaScript 規範容許 for-in 循環以不一樣的順序遍歷對象的屬性。一般數組元素的遍歷實現是升序的,但不能保證必定是這樣的。若是數組同時擁有對象屬性和數組元素,返回的屬性名極可能是按照建立的順序而非數值的大小順序。如何處理這個問題的實現,各個瀏覽器都不相同,若是算法依賴於遍歷的順序,那麼最好不要使用 for-in 而用常規的 for 循環。

ECMAScript 5定義了一些遍歷數組元素的新方法,按照索引的順序按個傳遞給定義的一個函數。這些方法中最經常使用的就是 forEach() 方法。例如:

var data = [1,2,3,4,5];     // 這是須要遍歷的數組
var sumOfSquares = 0;       // 要獲得數據的平方和
data.forEach(function(x) {  // 把每一個元素傳遞給此函數
    sumOfSquares += x*x;    // 平方相加
});
console.log(sumOfSquares);  // 55,1 + 4 + 9 + 16 + 25複製代碼

數組檢測

給定一個未知的對象,斷定它是否爲數組一般很是有用。在 ECMAScript 5中,可使用 Array.isArray() 函數來作這件事情。例如:

Array.isArray([])   // true
Array.isArray({})   // false複製代碼

可是,在 ECMAScript 5之前,要區分數組和非數組對象很困難。typeof 運算符對數組返回 "object"(而且對於除了函數之外的全部對象都是如此)。instanceof 操做符也只能用於簡單的情形。例如:

[] instanceof Array     // true
({}) instanceof Array   // false複製代碼

使用 instanceof 的問題是在 Web 瀏覽器中有可能有多個窗體存在。每一個窗體都有本身的 JavaScript 環境,有本身的全局對象。而且,每一個全局對象有本身的一組構造函數。所以一個窗體中的對象將不多是另外窗體中的構造函數的實例。窗體之間的混淆不常發生,但這個問題足已證實 instanceof 操做符不能視爲一個可靠的數組檢測方法。

解決方案是檢查對象的類屬性,對數組而言該屬性的值老是 "Array",所以在 ECMAScript 3中 isArray() 函數的代碼能夠這樣書寫。例如:

var isArray = Array.isArray || function(o) {
    return typeof o === "object" && Object.prototype.toString.call(o) === "[object Array]";
};複製代碼

數組方法

ECMAScript 3和 ECMAScript 5在 Array.prototype 中定義了一些頗有用的操做數組的方法。

轉換方法

全部對象都具備 toLocaleString()toString()valueOf() 方法。其中,調用數組的 toString()valueOf() 方法會返回相同的值,即由數組中每一個值的字符串形式拼接而成的一個以逗號分隔的字符串。實際上,爲了建立這個字符串會調用數組每一項的 toString() 方法。例如:

var colors = ["red", "blue", "green"];  // 建立一個包含3個字符串的數組
alert(colors.toString()); // red,blue,green
alert(colors.valueOf());  // red,blue,green
alert(colors);            // red,blue,green複製代碼

在這裏,咱們首先顯式地調用了 toString()valueOf() 方法,以便返回數組的字符串表示,每一個值的字符串表示拼接成了一個字符串,中間以逗號分隔。最後一行代碼直接將數組傳遞給了 alert()。因爲 alert()要接收字符串參數,因此它會在後臺調用 toString() 方法,由此會獲得與直接調用 toString() 方法相同的結果。

另外,toLocaleString() 方法常常也會返回與 toString()valueOf() 方法相同的值,但也不老是如此。當調用數組的 toLocaleString() 方法時,它也會建立一個數組值的以逗號分隔的字符串。而與前兩個方法惟一的不一樣之處在於,這一次爲了取得每一項的值,調用的是每一項的 toLocaleString() 方法,而不是 toString() 方法。例如:

var person1 = {
    toLocaleString : function () {
        return "Nikolaos";
    },
    toString : function() {
        return "Nicholas";
    }
};

var person2 = {
    toLocaleString : function () {
        return "Grigorios";
    },
    toString : function() {
        return "Greg";
    }
};

var people = [person1, person2];
alert(people);                           // Nicholas,Greg
alert(people.toString());                // Nicholas,Greg
alert(people.toLocaleString());          // Nikolaos,Grigorios複製代碼

數組繼承的 toLocaleString()toString()valueOf()方法,在默認狀況下都會以逗號分隔的字符串的形式返回數組項。而若是使用 join() 方法,則可使用不一樣的分隔符來構建這個字符串。join() 方法只接收一個參數,即用做分隔符的字符串,而後返回包含全部數組項的字符串。例如:

var colors = ["red", "green", "blue"];
console.log(colors.join(","));    // red,green,blue
console.log(colors.join("||"));   // red||green||blue複製代碼

若是數組中的某一項的值是 null 或者 undefined,那麼該值在 join()toLocaleString()toString()valueOf() 方法返回的結果中以空字符串表示。

棧方法

棧是一種 LIFO(Last-In-First-Out,後進先出)的數據結構,也就是最新添加的項最先被移除。push() 方法能夠接收任意數量的參數,把它們逐個添加到數組末尾,並返回修改後數組的長度。而 pop() 方法則從數組末尾移除最後一項,減小數組的 length 值,而後返回移除的項。結合 push()pop() 方法,就能夠像棧同樣使用數組。例如:

var colors = [];                            // 建立一個數組
var count = colors.push("red", "green");    // 推入兩項
console.log(count);                         // 2,數組的長度

count = colors.push("black");               // 推入另外一項
console.log(count);                         // 3,數組的長度

var item = colors.pop();                    // 取得最後一項
console.log(item);                          // "black"
console.log(colors.length);                 // 2,數組的長度複製代碼

隊列方法

隊列是一種 FIFO(First-In-First-Out,先進先出)的數據結構,隊列在列表的末端添加項,從列表的前端移除項。shift() 方法則從數組前端移除第一項,減小數組的 length 值,而後返回移除的項。結合 push()shift() 方法,就能夠像隊列同樣使用數組。例如:

var colors = [];                            // 建立一個數組
var count = colors.push("red", "green");    // 推入兩項
console.log(count);                         // 2,數組的長度

count = colors.push("black");               // 推入另外一項
console.log(count);                         // 3,數組的長度

var item = colors.shift();                  // 取得第一項
console.log(item);                          // "red"
console.log(colors.length);                 // 2,數組的長度複製代碼

JavaScipt 還爲數組提供了一個 unshift() 方法。顧名思義,unshift()shift() 的用途相反,它能在數組前端添加任意個項並返回新數組的長度。所以,同時使用 unshift()pop() 方法,能夠從相反的方向來模擬隊列,即在數組的前端添加項,從數組末端移除項。

重排序方法

數組中有兩個重排序的方法:reverse()sort()reverse() 方法能夠反轉數組元素的順序。例如:

var values = [1, 2, 3, 4, 5];
values.reverse();
console.log(values);  // 5,4,3,2,1複製代碼

sort() 方法能夠按升序排列數組元素(即最小的值位於最前面,最大的值排在最後面)。sort() 方法在排序的過程當中會調用每一個數組元素的 toString(),而後比較獲得的字符串,以肯定如何排序。即便數組中的每一項都是數值,sort() 方法比較的也是字符串,例如:

var values = [0, 1, 5, 10, 15];
values.sort();
console.log(values);     // 0,1,10,15,5複製代碼

這種排序方式在不少狀況下都不是最佳方案,所以 sort() 方法能夠接收一個比較函數做爲參數,以便咱們指定哪一個值位於哪一個值的前面,如下就是一個簡單的比較函數。例如:

function compare(value1, value2) {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}複製代碼

這個比較函數接收兩個參數,若是第一個參數應該位於第二個以前則返回一個負數,若是兩個參數相等則返回0,若是第一個參數應該位於第二個以後則返回一個正數。它能夠適用於大多數狀況,只要將其做爲參數傳遞給 sort() 方法便可。例如:

var values = [10, 5, 1, 0, 15];
values.sort(compare);
console.log(values);   // 0,1,5,10,15複製代碼

對於數值類型或者其 valueOf() 方法會返回數值類型的對象類型,可使用一個更簡單的比較函數。這個函數只要用第二個值減第一個值便可。例如:

function compare(value1, value2){
    return value2 - value1;
}複製代碼

因爲比較函數經過返回一個小於零、等於零或大於零的值來影響排序結果,所以減法操做就能夠適當地處理全部這些狀況。

操做方法

JavaScript 爲操做已經包含在數組中的元素提供了不少方法。其中,concat() 方法能夠基於當前數組中的全部項建立一個新數組。具體來講,這個方法會先建立當前數組一個副本,而後將接收到的參數添加到這個副本的末尾,最後返回新構建的數組。在沒有給 concat() 方法傳遞參數的狀況下,它只是複製當前數組並返回副本。若是傳遞給 concat() 方法的是一或多個數組,則該方法會將這些數組中的每一項都添加到結果數組中。若是傳遞的值不是數組,這些值就會被簡單地添加到結果數組的末尾。例如:

var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);

console.log(colors);     // red,green,blue
console.log(colors2);    // red,green,blue,yellow,black,brown複製代碼

下一個方法是 slice(),它可以基於當前數組中的一或多個項建立一個新數組。slice() 方法能夠接受一或兩個參數,即要返回項的起始和結束位置。在只有一個參數的狀況下,slice() 方法返回從該參數指定位置開始到當前數組末尾的全部項。若是有兩個參數,該方法返回起始和結束位置之間的項,但不包括結束位置的項。注意,slice() 方法不會影響原始數組。例如:

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

console.log(colors2);   // green,blue,yellow,purple
console.log(colors3);   // green,blue,yellow複製代碼

若是 slice() 方法的參數中有一個負數,則用數組長度加上該數來肯定相應的位置。例如,在一個包含5項的數組上調用 slice(-2,-1) 與調用 slice(3,4) 獲得的結果相同。若是結束位置小於起始位置,則返回空數組。

下一個方法是 splice(),它的主要用途是向數組的中部插入元素,主要有如下3種使用方式。

  • 刪除:能夠刪除任意數量的項,只需指定2個參數:起始位置和要刪除元素的數量。例如,splice(0,2) 會刪除數組中的前兩項。
  • 插入:能夠向指定位置插入任意數量的項,只需提供3個參數:起始位置、0(要刪除元素的數量)和要插入的元素。若是要插入多個元素,能夠再傳入第4、第五,以致任意多個元素。例如,splice(2,0,"red","green") 會從當前數組的位置2開始插入字符串 "red""green"
  • 替換:能夠向指定位置插入任意數量的項,且同時刪除任意數量的項,只需指定3個參數:起始位置、要刪除元素的數量和要插入的元素。插入的項數沒必要與刪除的項數相等。例如,splice (2,1,"red","green")會刪除當前數組位置2的項,而後再從位置2開始插入字符串 "red""green"

splice() 方法始終都會返回一個數組,該數組中包含從原始數組中刪除的項(若是沒有刪除任何項,則返回一個空數組)。下面的代碼展現了上述3種使用 splice() 方法的方式。例如:

var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1);       // 刪除第一項
console.log(colors);                    // green,blue
console.log(removed);                   // red,返回的數組中只包含一項

removed = colors.splice(1, 0, "yellow", "orange");  // 從位置1開始插入兩項
console.log(colors);                    // green,yellow,orange,blue
console.log(removed);                   // 返回的是一個空數組

removed = colors.splice(1, 1, "red", "purple");     // 插入兩項,刪除一項
console.log(colors);                    // green,red,purple,orange,blue
console.log(removed);                   // yellow,返回的數組中只包含一項複製代碼

位置方法

ECMAScript 5爲數組實例添加了兩個位置方法:indexOf()lastIndexOf()。這兩個方法都接收兩個參數:要查找的項和(可選的)表示查找起點位置的索引。其中,indexOf() 方法從數組的開頭(位置0)開始向後查找,lastIndexOf() 方法則從數組的末尾開始向前查找。

這兩個方法都返回要查找的項在數組中的位置,或者在沒找到的狀況下返回 -1。在比較第一個參數與數組中的每一項時,會使用全等操做符;也就是說,要求查找的項必須嚴格相等(就像使用 === 同樣)。例如:

var numbers = [1,2,3,4,5,4,3,2,1];
console.log(numbers.indexOf(4));          // 3
console.log(numbers.lastIndexOf(4));      // 5
console.log(numbers.indexOf(4, 4));       // 5
console.log(numbers.lastIndexOf(4, 4));   // 3

var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];
console.log(people.indexOf(person));      // -1
console.log(morePeople.indexOf(person));  // 0複製代碼

迭代方法

ECMAScript 5爲數組定義了5個迭代方法。每一個方法都接收兩個參數:要在每一項上運行的函數和(可選的)運行該函數的做用域對象。傳入這些方法中的函數會接收三個參數:數組項的值、該項在數組中的位置和數組對象自己。根據使用的方法不一樣,這個函數執行後的返回值可能會也可能不會影響訪問的返回值。如下是這5個迭代方法的做用。

  • every(),對數組中的每一項運行給定函數,若是該函數對每一項都返回 true ,則返回 true
  • filter(),對數組中的每一項運行給定函數,返回該函數會返回 true 的項組成的數組。
  • forEach(),對數組中的每一項運行給定函數。這個方法沒有返回值。
  • map(),對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。
  • some(),對數組中的每一項運行給定函數,若是該函數對任一項返回 true ,則返回 true

以上方法都不會修改數組中的包含的值。在這些方法中,最類似的是 every()some(),它們都用於查詢數組中的項是否知足某個條件。對 every() 來講,傳入的函數必須對每一項都返回 true,這個方法才返回 true;不然,它就返回 false。而 some()方法則是隻要傳入的函數對數組中的某一項返回 true,就會返回 true。例如:

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item, index, array){
    return (item > 2); 
});
console.log(everyResult);   // false

var someResult = numbers.some(function(item, index, array){
    return (item > 2);
});
console.log(someResult);    // true複製代碼

下面再看一看 filter() 函數,它利用指定的函數肯定是否在返回的數組中包含的某一項。例如:

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item, index, array){
    return (item > 2);
});
console.log(filterResult);  // [3,4,5,4,3]複製代碼

map() 也返回一個數組,而這個數組的每一項都是在原始數組中的對應項上運行傳入函數的結果。例如,能夠給數組中的每一項乘以2,而後返回這些乘積組成的數組。例如:

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item, index, array){
    return item * 2;
});
console.log(mapResult);     // [2,4,6,8,10,8,6,4,2]複製代碼

最後一個方法是 forEach(),它只是對數組中的每一項運行傳入的函數。這個方法沒有返回值,本質上與使用 for 循環迭代數組同樣。例如:

var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item, index, array){
    //執行某些操做 
});複製代碼

### 縮小方法

ECMAScript 5還新增了兩個縮小數組的方法:reduce()reduceRight()。這兩個方法都會迭代數組的全部項,而後構建一個最終返回的值。其中,reduce() 方法從數組的第一項開始,逐個遍歷到最後。而 reduceRight() 則從數組的最後一項開始,向前遍歷到第一項。

這兩個方法都接收兩個參數:一個在每一項上調用的函數和(可選的)做爲縮小基礎的初始值。傳給 reduce()reduceRight() 的函數接收4個參數:前一個值、當前值、項的索引和數組對象。這個函數返回的任何值都會做爲第一個參數自動傳給下一項。第一次迭代發生在數組的第二項上,所以第一個參數是數組的第一項,第二個參數就是數組的第二項。

使用 reduce() 方法能夠執行求數組中全部值之和的操做。例如:

var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
    return prev + cur; 
});
console.log(sum); // 15複製代碼

第一次執行回調函數,prev是1,cur是2。第二次,prev是3(1加2的結果),cur是3(數組的第三項)。這個過程會持續到把數組中的每一項都訪問一遍,最後返回結果。

reduceRight() 的做用相似,只不過方向相反而已。例如:

var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev, cur, index, array){
    return prev + cur;
});
console.log(sum); // 15複製代碼

使用 reduce() 仍是 reduceRight(),主要取決於要從哪頭開始遍歷數組。除此以外,它們徹底相同。

關卡

完成下面3個數組去重方法。

// 挑戰一,一維數組
var arr = [2,3,4,2,3,5,6,4,3,2];
var unique = function(arr){
    // 待實現方法體
}
console.log(unique(arr)); // [2,3,4,5,6]複製代碼
// 挑戰二,二維數組
var arr = [2,3,4,[2,3,4,5],3,5,[2,3,4,2],4,3,6,2];
var unique = function(arr){
    // 待實現方法體
}
console.log(unique(arr)); // [2,3,4,5,6]複製代碼
// 挑戰三,三維數組或 n 維數組
var arr = [2,3,4,[2,3,[2,3,4,2],5],3,5,[2,3,[2,3,4,2],2],4,3,6,2];
var unique = function(arr){
    // 待實現方法體
}
console.log(unique(arr)); // [2,3,4,5,6]複製代碼

更多

關注微信公衆號「劼哥舍」回覆「答案」,獲取關卡詳解。
關注 github.com/stone0090/j…,獲取最新動態。

相關文章
相關標籤/搜索