數組是編程世界裏最多見的數據結構。任何一種編程語言都包含數組,只是形式稍微有差別。數組是編程語言中的內建類型,一般效率都很高。能夠知足不一樣需求的數據存儲,本章將探索javascript中的數組工做原理,以及它們的使用場合。javascript
一:javascript中對數組的定義html
數組的標準定義是:一個存儲元素的線性集合(collection),元素能夠經過索引來任意存儲,索引一般是數字,用於計算元素之間存儲位置的偏移量。幾乎全部的編程語言都有相似的數據結構。然而javascript確略有不一樣。java
javascript中數組是一種特殊的對象,用來表示偏移量的索引是該對象的屬性,索引多是整數。然而這些數字索引在內部被轉換爲字符串類型,這是由於javascript對象中的屬性名必須是字符串。數組在javascript又是一種特殊的對象,因此效率上不如其餘語言中的高。程序員
javascript中的數組,嚴格的來講應該稱做爲對象,是特殊的javascript對象,在內部被歸類爲數組。因爲Array在javascript中被當作對象,所以它有不少屬性和方法在編程中使用。算法
二:使用數組編程
javascript中的數組很是靈活。可是建立數組和存取元素的方法就有好多種。也能夠經過關不一樣的方式對數組進行進行查找和排序。javascript1.5還提供了一些函數,讓程序員再處理數組時可使用函數式編程技巧。數組
1.建立數組數據結構
最簡單的方式就是經過[]操做符聲明一個數組變量。dom
var numbers = [];
使用這樣的方式建立數組,將獲得一個長度爲0的空數組。也能夠經過內建的length來證實這一點。編程語言
console.log(numbers.length); //0
另一種方式是在聲明數組時,直接在[]操做符內放入一組元素。
var numbers = [1,2,3,4,5] console.log(numbers.length); //5
也能夠經過調用Array的構造函數進行建立數組
var numbers = new Array(); console.log(numbers.length);//0
一樣能夠爲構造函數傳入一組元素做爲數組的初始值
var numbers = new Array(1,2,3,4,5) console.log(numbers.length)
最後,在調用構造函數時,能夠只傳入一個參數,來指定數組的長度。
var numbers = new Array(10); console.log(numbers.length);//10 console.log(numbers);//[]
在腳本語言中很常見的一個特性是,數組中的元素沒必要是同一種數據類型,這一點和其它編程語言不一樣,以下所示:
var objects = [1,"joe",true,null];
能夠調用Array.isArray()來判斷一個對象是否數組。以下所示範
var numbers = 3; var arr = [7,4,123]; console.log(Array.isArray(numbers));//false console.log(Array.isArray(arr)) //true
本節咱們討論的數組的建立,那種方式最好,大多數javascript專家推薦使用[],和Array的構造函數相比,這種方式被認爲效率更高。
2.讀寫數組
在一條賦值語句中,可使用[]操做符將數據賦值給數組。好比如下循環,將1-100的數字賦予給一個數組。
var num = []; for (var i = 0; i < 100; ++i) { num[i] = i+1 }; console.log(num)
還可使用[]操做符讀取數組中的元素,以下所示:
var numbers = [1,2,3,4,5] var sum = numbers[0]+ numbers[1] + numbers[2] + numbers[3] + numbers[4]; console.log(sum) //15
要依次取得數組中的全部元素,使用for循環更加簡單。
var numbers = [1,2,3,5,8,13,21]; var sum = 0; for (var i = 0; i < numbers.length; i++) { sum += numbers[i] } console.log(sum);//53
3.由字符串生成數組
調用字符串對象split()方法也能夠生成數組。該方法經過一些常見的分割符,好比分割單詞的空格,將一個字符串分爲幾部分,並將每一個部分做爲一個元素保存在一個新建的數組中。
下面的一段代碼演示了split()的使用方法。
var sent = "the quik brown fox jumped over zhe lazy dog"; var word = sent.split(" ") console.log(word) ;//["the", "quik", "brown", "fox", "jumped", "over", "zhe", "lazy", "dog"] for (var i = 0; i < word.length; ++i){ word[i] console.log("word " + i + ":" + word[i]) }
4.對數組的總體性操做
有幾個操做做爲一個總體進行的。首先,能夠將一個數組賦值給另一個數組:
var nums = []; for (var i = 0; i < 10 ; ++i){ nums[i] = i + 1; } var sam = nums; console.log(sam)
可是,當把一個數組賦值給另一個數組時,只是爲被賦值的數組增長了一個新的引用。當你經過原引用修改了數組的值,另一個引用也會感知到這個變化。下面的代碼展現了這個狀況。
var nums = []; for (var i = 0; i < 100; +i){ nums[i] = i + 1; } var sament = nums; nums[0] = 400; console.log(sament[0])//400
這種複製被稱爲淺複製,新數組任然會指向原來的數組,一個更好的方案是深複製,將原數組的每個元素都複製到新數組中,能夠寫一個方法來複制函數來作這件事情
function arr_copy(arr1, arr2){ for (var i = 0; i < arr1.length; i++){ arr2[i] = arr1[i] } } var nums = []; for (i = 0; i < 100; i++) { nums[i] = i + 1; } var sament = []; arr_copy(nums,sament); nums[0] = 400; console.log(sament[0])//1
三:存取函數
javascript提供了一組用來訪問數組元素的函數,叫存取函數,這些函數返回目標數組的某種變體。
1.查找元素
indexof()函數是最經常使用的存取函數之一,用來查找傳進來的參數在目標數組中是否存在。若是目標函數中存在該參數,就返回該元素在數組中索引。若是不包含,就返回-1.indexof()對大小寫敏感
若是數組中包含多個相同的元素,則indexof()則返回第一個與參數相同的元素索引。有另一個功能相似的函數,lastIndexOf().該函數返回相同元素的最後一個元素的索引,若是沒有找到相同元素,則返回-1.以下.indexof()對大小寫敏感
var str="Hello world!" console.log(str.indexOf("Hello")) console.log(str.indexOf("World")) console.log(str.indexOf("world"))
若是數組中包含多個相同的元素,則indexof()則返回第一個與參數相同的元素索引。有另一個功能相似的函數,lastIndexOf().該函數返回相同元素的最後一個元素的索引,若是沒有找到相同元素,則返回-1.以下
var names = ["David", "Mike", "Cynthia", "Mike", "Jennifer"]; var name = "Mike"; var firstPos = names.indexOf(name); console.log(firstPos)//1 var lastPos = names.lastIndexOf(name) console.log(lastPos)//3
2.數組的字符串表示
有兩個方法能夠將數組轉化爲字符串:join()和toString(),這兩個方法都返回一個包含數組全部元素的字符串,各元素之間用逗號隔開,下面是一些例子
var names = ["David", "Mike", "Cynthia", "Mike", "Jennifer"]; var namestr = names.join(); namestr.toString(); console.log(namestr);//David,Mike,Cynthia,Mike,Jennifer
3.由已有數組建立新數組
concat()和splice()方法容許經過已經有的數組建立新數組。concat方法能夠合併多個數組建立一個新數組,splice()方法截取一個數組的子集建立一個新數組
咱們先來看看,concat方法的工做原理。該方法發起者是一個數組,參數是另外一個數組。做爲參數的數組,其中全部的元素都被鏈接到調用concat()方法的數組後面,下面展現了concat()方法的工做原理
var cisDept = ["Mike", "Clayton", "Terrill", "Danny", "Jennifer"]; var dmpDept = ["Ramondy", "Cynthia", "Bryan"]; var itDiv = cisDept.concat(dmpDept); console.log(itDiv) //["Mike", "Clayton", "Terrill", "Danny", "Jennifer", "Ramondy", "Cynthia", "Bryan"] itDiv = dmpDept.concat(cisDept) console.log(itDiv) ;//["Ramondy", "Cynthia", "Bryan", "Mike", "Clayton", "Terrill", "Danny", "Jennifer"]
輸出爲
Mike,Clayton,Terrill,Danny,Jennifer,Ramondy,Cynthia,Bryan
Ramondy,Cynthia,Bryan,Mike,Clayton,Terrill,Danny,Jennifer
splice()方法從現有數組裏截取一個新數組。該方法的第一個參數是是截取的起始索引,第二個參數是截取的長度。下面的程序展現了splice()方法工做原理:
var itDiv = ["Mike", "Clayton", "Terrill", "Danny", "Jennifer", "Cynthia", "Bryan"]; var dmpDept = itDiv.splice(3,3) //["Danny", "Jennifer", "Cynthia"] var cisDept = itDiv; //["Mike", "Clayton", "Terrill", "Bryan"]
splice()方法還有其它的用法,好比爲一個數組增長或移除元素,具體請參考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
4.可變函數
javascript中有一組可變函數,使用它們,能夠沒必要引用數組中某個元素,就能改變數組內容。這些函數一般化簡繁。讓困難的事情變得容易。
爲數組添加元素
有兩個方法 能夠爲數組添加元素,push()和unshift()。push()方法會將一個元素添加到數組末尾。
var nums = [1,2,3,4,5]; nums.push(6) console.log(nums);//[1, 2, 3, 4, 5, 6]
也可用使用數組的length屬性爲數組添加元素,但push()看起來更直觀。
var nums = [1,2,3,4,5]; nums[nums.length] = 6; console.log(nums)
和在數組的末尾添加元素比起來,在數組的開頭添加元素更難,若是不利於數組提供的可變函數,則新的元素添加進來後,須要把後面的每一個元素都相應的向後移一個位置。下面的代碼展現了這個過程。
var nums = [111,2,3,4,5,22,2] var newnum = 1; var _n = nums.length; for (var i = _n; i >= 0; --i){ nums[i] = nums[i-1] } nums[0] = newnum; console.log(nums) //[1, 111, 2, 3, 4, 5, 22, 2]
不過隨着數組中儲存的元素愈來愈多,上述代碼會變得原來越低效。
unshift()方法能夠將元素添加在數組的開頭,下面代碼展現改方法的用法。
var nums = [2,3,4,5,6] var newnum = 1; nums.unshift(newnum); var nums2 = [3,4,5]; nums2.unshift(newnum,1,2) console.log(nums2) ;//[1, 1, 2, 3, 4, 5]
其中第二次出現的unshift(),展現了能夠一次調用,爲數組添加多個元素。
2. 從數組中刪除元素
使用pop()方法能夠刪除數組末尾元素:
var nums = [1,2,3,4,5,9] nums.pop() console.log(nums)
若是沒有可變函數,從數組中刪除第一個元素須要將後續元素各自向前移動一個位置
var nums = [9,1,2,3,4,5,6]; for (var i = 0; i < nums.length; ++i) { nums[i] = nums[i+1] }
除了要將後續的元素向前移動一位,還多出了一個元素。
shift()方法能夠刪除數組中的第一個元素,下面代碼展現其用法。
var nums = [11,1,2,3,4,5,6,7]; nums.shift(); console.log(nums)
這組數組末尾那個多餘的逗號消失了。pop()和shift()方法都將刪掉的元素做爲方法返回值返回,所以,可使用一個變量來保存刪除的元素。
var nums = [7,1,2,3,4,5,6]; var _d = nums.shift(); nums.push(_d) console.log(nums)//[1, 2, 3, 4, 5, 6, 7]
3.從數組的中間位置添加和刪除元素
刪除數組的第一個元素和在數組開頭添加一個元素存在一樣的問題 ,兩種操做都須要將數組中的剩餘元素向前或向後移,然而splice()方法能夠幫助咱們執行其中的任何一種操做。
使用splice()方法爲數組添加元素,須要提供如下參數
var nums = [1,2,3,7,8,9] var newElement = [4,5,6] nums.splice(3,0,newElement)
要插入的數組沒必要組織成一個數組,能夠是任意元素序列
var nums = [1,2,3,7,8,9] nums.splice(3,0,11,22,3,4,5) console.log(nums);//[1, 2, 3, 11, 22, 3, 4, 5, 7, 8, 9]
下面是splice()方法從數組中刪除元素的例子
var nums = [1, 2, 3, 11, 22, 3, 4, 5, 7, 8, 9] nums.splice(3,3) console.log(nums);//[1, 2, 3, 4, 5, 7, 8, 9]
4.爲數組排序。
剩下的兩個方法是爲數組排序。第一個方法是reverse(),該方法將數組數組中的元素進行反翻轉。下面的例子展現了此用法。
var nums = [1,2,3,4,5,6] nums.reverse(); console.log(nums);//[6, 5, 4, 3, 2, 1]
對數組的排序是常常要遇到的要求,若是元素是字符串類型,那麼數組的可變方法sort()就變得很是好使。
var names = ["David", "Mike", "Cynthia", "Mike", "Jennifer"]; names.sort(); console.log(names);//["Cynthia", "David", "Jennifer", "Mike", "Mike"]
可是,若是數組元素是數字類型,那麼sort()方法的排序就不那麼讓人滿意了。
var nums = [3,1,2,100,4,200] nums.sort() console.log(nums);//[1, 100, 2, 200, 3, 4]
sort()方法是按照字典排序時對元素進行排序的,所以,它將定元素都是字符串類型,在上一個例子中,即便元素是數字類型,也是會被認爲爲字符串類型。爲了讓sort()方法也能排序數字類型的元素,能夠在調用方法時傳入一個比較大小的函數。排序時,sort()方法將會比較兩個數組元素的大小。
對於數字類型,該函數能夠是一個簡單的相減操做,從一個數字減去另一個數字。搞清楚這些後咱們將以下操做:
function compare(num1, num2){ return num1 - num2; } var nums = [3,1,2,100,4,200] nums.sort(compare); console.log(nums);//[1, 2, 3, 4, 100, 200]
5.迭代器方法
最後是迭代器方法,這些方法對數組中的每個元素應用一個函數,能夠返回一個值,一組值或一個新數組。
1.不生成新數組的迭代器方法。
咱們討論的第一組迭代器不產生任何新數組,相反,它們要麼對於數組中的每一個元素執行某種操做,要麼返回一個值。
這組中第一個方法是forEach(),該方法接受一個函數做爲參數,對數組中的每一個元素使用該函數。
function square(num){ console.log(num,num * num); } var nums = [1,2,3,4,5,6,7,8,9,10] nums.forEach(square);
另一個迭代器方法是every(),該方法接受一個返回值爲布爾類型的函數,對數組中的每一個元素使用該函數。若是對於全部的元素,該函數均返回true,則該方法返回true,下面是一個例子:
function isEven(num){ return num % 2 == 0; } var nums = [2,4,6,8,10]; var even = nums.every(isEven); if (even) { console.log("全是偶數") } else { console.log("不全是偶數") }
some()方法也接受一個返回值爲布爾類型的函數,只要一個元素使得該函數返回true,該方法就返回true.
function isEven(num){ return num % 2 == 0; } var nums = [1,2,4,5,6,7,8,9,10] var someEven = nums.some(isEven); console.log(someEven)
reduce()方法接受一個函數,返回一個值。該方法會從一個累加值開始,不斷對累加值和數組中的後續元素調用該函數,直到數組中的最後一個元素,最後返回獲得的累加值。下面這個例子展現瞭如何使用reduce()
function add(num1, num2){ return num1 + num2; } var nums = [1,2,3,4,5,6,7,8,9,10]; var sum = nums.reduce(add); console.log(sum);//55
reduce()方法和add()函數一塊兒,從左至右,依次對數組中的元素求和,其執行過程以下:
add(1,2) -> 3 add(3,3) -> 6 add(6,4) -> 10 add(10,5) -> 15 add(15,6) -> 21 add(21,7) -> 28 add(28,8) -> 36 add(36,9) -> 45 add(45,10) -> 55
reduce()方法也能夠用來將數組中的元素連成一個長的字符串。
function concat(accString, item){ return accString + item; } var words = ['the ', 'quick ', 'brown ', 'fox ']; var sentence = words.reduce(concat) console.log(sentence) ;//the quick brown fox
javascript還提供了reduceRight()方法,和reduce()不一樣,它是從右至左執行。下面的程序使用reduceRight()方法將數組中的元素進行翻轉。
function concat(accString, item){ return accString + item; } var words = ['the ', 'quick ', 'brown ', 'fox ']; var sentence = words.reduceRight(concat) console.log(sentence) ;//fox brown quick the
2.生成新數組的迭代器方法
有兩個迭代器方法能夠產生新數組:map()和filter()。map()和forEach()有點兒像。對數組中的每一個元素使用某個函數。二者的區別是map()返回一個新的數組,該數組的元素是對原有元素應用某個函數獲得的結果。下面給出一個例子:
function curve(grade){ return grade += 5; } var grades = [1,3,5,8,10]; var newgrades = grades.map(curve); console.log(newgrades)
下面是對一個字符串數組使用map()方法的例子:
function first(word){ return word[0] } var words = ['for', 'your', 'information']; var acronym = words.map(first); console.log(acronym.join(""));//fyi
filter()和every()相似,傳入一個返回值爲布爾類型的函數。和every()方法不一樣的是,當對數組中全部的元素應用該函數,當結果爲true時,該方法不返回true,而是返回一個新數組。該組包含該函數結果爲true的元素。下面是一個例子。
function isEven(num){ return num % 2 == 0; } function isOdd(num){ return num % 2 != 0; } var nums = []; for (var i = 0; i < 20; ++i){ nums[i] = i + 1; } console.log(nums) var events = nums.filter(isEven) console.log(events);//[2, 4, 6, 8, 10, 12, 14, 16, 18, 20] var odds = nums.filter(isOdd); console.log(odds);//[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
下面是使用filter()方法的有趣案例
function passing(num){ return num >= 60; } var grades = []; for (var i = 0; i < 20; ++i){ grades[i] = Math.floor(Math.random()*101); } console.log(grades);//[4, 56, 87, 62, 24, 54, 80, 7, 95, 98, 40, 43, 56, 79, 84, 52, 87, 23, 43, 18] var passGardes = grades.filter(passing); console.log(passGardes);//[87, 62, 80, 95, 98, 79, 84, 87]隨機
固然,還可使用filter()過濾字符串數組,下面的這個例子展現了那些不包含"cie"的單詞:
function after(str) { if (str.indexOf("cie") > -1) { return true } return false; } var words = ["recieve", "deceive" , "percieve", "deceit", "concieven"]; var nocie = words.filter(after); console.log(nocie);//["recieve", "percieve", "concieven"]
六,二維和多維數組
javascript只支持一維數組,可是經過在數組裏保存數組元素的方式,能夠輕鬆建立多維數組。本節將討論如何在javascript中建立二維數組。
1.建立二維數組
二維數組相似一種由行和列構成的數據表格。在javascript建立二維數組,須要建立一個數組,而後讓數組的每一個元素也是一個數組。最起碼,咱們須要知道二維數組要包含多少行,有了這個信息,就能夠建立一個n行1列的二維數組了。
var twod = []; var row = 5; for (var i = 0; i < row; ++i){ twod[i] = []; } console.log(twod);//[Array[0], Array[0], Array[0], Array[0], Array[0]]
這樣作的問題是,數組中的每一個元素都是undefined。更好的方式是經過擴展javascript數組對象,爲其增長一個新方法。該方法根據傳入的參數,設定了數組的行數,列數和初始值。下面是這個方法的定義;
Array.matrix = function(numrows, numcols, initial) { var arr = []; for (var i = 0; i < numrows; ++i){ var columns = []; for (var j = 0; j < numcols; ++j) { columns[j] = initial; } arr[i] = columns } return arr; }
下面是該測試方法的一些測試代碼:
var nums = Array.matrix(5,5,0); console.log(nums[1][1]);//0 var names = Array.matrix(3,3,""); console.log(names) names[1][2] = "Joe";
還可使用使用一行代碼和使用一組初始值來初始化一個二維數組
var grades = [[1,35,1],[123,52,14],[123,12,5,21,5,2]]
對於小規模數據,這是建立二維數組的最簡單的方式。
2.處理二維數組的元素
處理二維數組中的元素,有兩種最基本的方式:按列訪問和按行訪問。
對於這兩種方式,咱們均使用一組嵌入式的for循環。對於按列訪問,外層循環對於行,內層循環對應列。
以數組grades爲例,每一行對於一個學生的成績記錄。咱們能夠將該學生的全部成績相加,而後除以科目數獲得該學生的平均成績。下面的代碼展現了這個過程。
var grades = [[89,77,78],[76,62,81],[91,94,89]]; var total = 0; var average = 0.0; for (var row = 0; row < grades.length; ++i) { for (var col = 0; col < grades[row].length; ++col){ total += grades[row][col] } average = total / grades[row].length; console.log("Student" + parseInt(row + 1) + " average: " + average.toFixed(2)); total = 0; average = 0.0;
內層循環由這個表達式控制;
col < grades[row].length
這個表達式之因此多行,是由於每一行都是一個數組,咱們可使用數組的length屬性判斷每行包含多少行。
Student1 average: 81.33 Student2 average: 73.00 Student3 average: 91.33
對於按行訪問,只須要稍微調整for循環的順序,使外層循環對應列,內存循環對應行便可。下面的程序計算了一個學生的各科成績。
var grades = [[89,77,78],[76,62,81],[91,94,89]]; var total = 0; var average = 0.0; for (var col = 0; col < grades.length; ++col) { for(var row = 0; row < grades[col].length; ++row){ total += grades[row][col] } average = total / grades[col].length; console.log("Test " + parseInt(col+1) + "average " + average.toFixed(2)); total = 0; average = 0.0; }
輸出
Test 1average 85.33 Test 2average 77.67 Test 3average 82.67
3.層次不齊的數組
層次不齊的數組是指數組中每行的元素個數彼此不一樣。不少編程語言在處理這樣的數組時表現的都不是很好,但javascript表現良好,是由於每一行的長度能夠經過計算獲得。
爲了給個示例,假設數組grades中,每一個學生的成績記錄個數是不同的,不用修改代碼,依然能夠正確計算出正確的平均分。
var grades = [[89,77],[76,82,81],[91,94,89,99]]; var total = 0; var average = 0.0; for (var row = 0; row < grades.length; ++row){ for (var col = 0; col < grades[row].length; ++col){ total += grades[row][col]; } average = total / grades[row].length; console.log("Student" + parseInt(row + 1) + " average " + average.toFixed(2)); total = 0; average = 0.0; }
注意,第一名同窗有兩門課成績,第二名有三門課成績,第三名有4門課成績。由於程序在內層for循環計算了每一個數組的長度,即便數組中每一行的長度不一,程序依然不會出現什麼問題。該段程序輸出爲:
Student1 average 83.00 Student2 average 79.67 Student3 average 93.25
七,對象數組
到目前爲止,本章討論的數組都只包含基本數據類型的元素,好比數字和字符串。數組還能夠包含對象,數組的方法和屬性對對象依然適用。
請看下面的例子:
function Point(x,y) { this.x = x; this.y = y; } function displayPts(arr) { for (var i = 0; i < arr.length; ++i) { console.log(arr[i].x + ", " + arr[i].y); } } var p1 = new Point(1,2); var p2 = new Point(3,5); var p3 = new Point(2,8); var p4 = new Point(4,4); var points = [p1,p2,p3,p4] for (var i = 0 ; i < points.length; ++i) { console.log("Point " + parseInt(i+1) + ": " + points[i].x + ", " + points[i].y) } var p5 = new Point(12,-3); points.push(p5) displayPts(points); points.shift(); console.log(points) displayPts(points)
適用push()方法將點(12,-3)添加進數組,適用shift()方法將(1,2)從數組移除。
八,對象中的數組。
在對象中,可使用數組存儲複雜的數據。不少數據都能被實現成一個對象,對象內部使用數組保存數據。
下面的例子,咱們建立了一個對象,用於保存觀測到的最高氣溫數據,該對象有兩個方法,一個方法用來增長一條新的氣溫記錄,另一個方法用來計算存儲在對象中的平均氣溫。代碼以下
function weekTemps() { this.dataStore = []; this.add = add; this.average = average; } function add(temp) { this.dataStore.push(temp) } function average() { var total = 0; for (var i = 0; i < this.dataStore.length; ++i) { total += this.dataStore[i]; } return total / this.dataStore.length; } var thisWeek = new weekTemps(); thisWeek.add(52); thisWeek.add(55); thisWeek.add(61); thisWeek.add(65); thisWeek.add(55); thisWeek.add(50); thisWeek.add(52); thisWeek.add(49); console.log(thisWeek.average());//54.875
add()方法中用到了數組的push()方法,將元素添加到數組dataStore中,爲何這個方法名叫add()而不是叫push()?這是由於在自定義方法時,一個直觀的名字是很是有用的技巧(不是全部人知道push一個元素是什麼意思,可是全部人add一個元素是什麼意思。)
(本章完結)