[TOC]
## 數組的定義
數組(array)是按次序排列的一組值,單個值稱為元素,它們的位置都有編號(從0開始)。整個數組用方括號表示。
~~~
var arr = ['a', 'b', 'c'];
~~~
上面代碼中的a、b、c就構成一個數組,兩端的方括號是數組的標志,a是0號位置,b是1號位置,c是2號位置。
除了在定義時賦值,數組也可以先定義后賦值。
~~~
var arr = [];
arr[0] = 'a';
arr[1] = 'b';
arr[2] = 'c';
~~~
任意一種類型的數據,都可以放入數組。
~~~
var arr = [{a:1}, [1,2,3],function (){return true;}];
arr[0] // Object {a: 1}
arr[1] // [1, 2, 3]
arr[2] // function (){return true;}
~~~
上面數組的3個成員分別是對象、數組、函數。
如果數組的元素還是數組,就形成了多維數組。
~~~
var a = [[1,2],[3,4]];
a[0][1] // 2
a[1][1] // 4
~~~
## 數組與對象的關系
本質上,數組也屬于對象,是字典結構(dictionary)的一個變種。
~~~
typeof [1,2,3] // "object"
~~~
上面代碼表明,數組只是一種特殊的對象,所以typeof運算符返回數組的類型是object。
數組的特殊性體現在,它的鍵默認是按次序排列的整數(0,1,2...),所以數組不用為每個元素指定鍵名,而對象的每個成員都必須指定鍵名。此外,對象以字符串來識別鍵名,非字符串的鍵名會被轉為字符串,所以使用數值或字符串作為鍵名,都能讀取數組的成員。
~~~
var arr = ['a', 'b', 'c'];
arr['0'] // 'a'
arr[0] // 'a'
~~~
上面代碼分別用數值和字符串作為鍵名,結果都能讀取數組。
需要注意的是,這一條在賦值時也成立,即如果一個值可以被轉換為整數,則以該值為鍵名,等于以對應的整數為鍵名。
~~~
var a = [];
a['1000'] = 'abc';
a[1000] // 'abc'
a[1.00] = 6;
a[1] // 1
~~~
上面代碼表明,由于字符串“1000”和浮點數1.00都可以轉換為整數,則視同為整數鍵賦值。
上一節說過,對象有兩種讀取成員的方法:“點”結構(object.key)和方括號結構(object[key])。但是,對于數字的鍵名,不能使用點結構,arr.0的寫法不合法,因為單獨的數字不能作為標識符(identifier)。所以,數組成員只能用方括號arr[0]表示(方括號是運算符,可以接受數值)。
## length屬性
數組的length屬性,返回數組的成員數量。
~~~
['a', 'b', 'c'].length // 3
~~~
JavaScript使用一個32位整數,保存數組的元素個數。這意味著,數組成員最多只有4294967295個(232-1)個,也就是說length屬性的最大值就是4294967295。
數組的length屬性與對象的length屬性有區別,只要是數組,就一定有length屬性,而對象不一定有。而且,數組的length屬性是一個動態的值,等于鍵名中的最大整數加上1。
~~~
var arr = ['a', 'b'];
arr.length // 2
arr[2] = 'c';
arr.length // 3
arr[9] = 'd';
arr.length // 10
arr[1000] = 'e';
arr.length // 1001
~~~
上面代碼表示,數組的數字鍵不需要連續,length屬性的值總是比最大的那個整數鍵大1。另外,這也表明數組是一種動態的數據結構,可以隨時增減數組的成員。
length屬性是可寫的。如果人為設置一個小于當前成員個數的值,該數組的成員會自動減少到length設置的值。
~~~
var arr = [ 'a', 'b', 'c' ];
arr.length // 3
arr.length = 2;
arr // ["a", "b"]
~~~
上面代碼表示,當數組的length屬性設為2,即最大的整數鍵只能是1,那么整數鍵2(值為c)就已經不在數組中了,被自動刪除了。
將數組清空的一個有效方法,就是將length屬性設為0。
~~~
var arr = [ 'a', 'b', 'c' ];
arr.length = 0;
arr // []
~~~
如果人為設置length大于當前元素個數,則數組的成員數量會增加到這個值,新增的位置填入空元素。
~~~
var a = ['a'];
a.length = 3;
a // ["a", undefined × 2]
~~~
上面代碼表示,當length屬性設為大于數組個數時,新增的位置都填充為undefined。
如果人為設置length為不合法的值,JavaScript會報錯。
~~~
// 設置負值
[].length = -1
// RangeError: Invalid array length
// 數組元素個數大于等于2的32次方
[].length = Math.pow(2,32)
// RangeError: Invalid array length
// 設置字符串
[].length = 'abc'
// RangeError: Invalid array length
~~~
值得注意的是,由于數組本質上是對象的一種,所以我們可以為數組添加屬性,但是這不影響length屬性的值。
~~~
var a = [];
a["p"] = "abc";
a.length // 0
a[2.1] = "abc";
a.length // 0
~~~
上面代碼將數組的鍵分別設為字符串和小數,結果都不影響length屬性。因為,length屬性的值就是等于最大的數字鍵加1,而這個數組沒有整數鍵,所以length屬性保持為0。
## 數組的空位
當數組的某個位置是空元素(比如兩個逗號之間沒有任何值,或者值為undefined),我們稱該數組存在空位(hole)。
~~~
var a = [1,,1];
a // [1, undefined, 1]
a.length // 3
~~~
需要注意的是,如果最后一個元素后面有逗號,并不會產生空位。也就是說,有沒有這個逗號,結果都是一樣的。不過,IE 8及以下版本不支持這條語法規則,會報錯。
~~~
var a = [1,2,3,];
a.length // 3
a // [1, 2, 3]
~~~
上面代碼中,數組最后一個成員后面有一個逗號,這不影響length屬性的值,與沒有這個逗號時效果一樣。
使用delete命令刪除一個值,會形成空位。
~~~
var a = [1,2,3];
delete a[1];
a // [1, undefined, 3]
~~~
需要注意的是,如果使用delete命令刪除一個值,不影響length屬性。
~~~
var a = [1,2,3];
delete a[1];
delete a[2];
a // [1, undefined, undefined]
a.length // 3
~~~
上面代碼用delete命令刪除了兩個鍵,對length屬性沒有影響。因為這兩個鍵還在,只是值變為了undefined。也就是說,length屬性不過濾undefined的值。所以,使用length屬性進行數組遍歷,一定要非常小心。
空位通過空值生成,還是通過顯式設為undefined生成,有一個細微的差別。如果通過空值生成,使用數組的forEach方法或者for...in結構進行遍歷,空位就會被跳過。
~~~
var a = [,,,];
a.forEach(function (x, i) { console.log(i+". "+x) })
// 不產生任何輸出
for (var i in a){console.log(i)}
// 不產生任何輸出
~~~
如果空位是通過顯式定義undefined生成,遍歷的時候就不會被跳過。
~~~
var a = [undefined,undefined,undefined];
a.forEach(function (x, i) { console.log(i+". "+x) });
// 0\. undefined
// 1\. undefined
// 2\. undefined
for (var i in a){console.log(i)}
// 0
// 1
// 2
~~~
## in運算符,for...in循環
檢查某個鍵是否存在的運算符in,適用于對象,也適用于數組。
~~~
2 in [ 'a', 'b', 'c' ]
// true
'2' in [ 'a', 'b', 'c' ]
// true
~~~
使用for-in循環,可以遍歷數組的所有元素。
~~~
var a = [1,2,3];
for (var i in a){
console.log(a[i]);
}
// 1
// 2
// 3
~~~
需要注意的是,for-in會遍歷數組所有的鍵,即使是非數字鍵。
~~~
var a = [1,2,3];
a.foo = true;
for (var key in a) {
console.log(key);
}
// 0
// 1
// 2
// foo
~~~
上面代碼在遍歷數組時,也遍歷到了非整數鍵foo。所以,使用for-in遍歷數組的時候,一定要小心。
另一種遍歷的做法是用for循環或者while循環結合length屬性。
~~~
var a = [1,2,3];
for(var i = 0; i < a.length; i++){
console.log(a[i]);
}
// or
var i = 0;
while (i< a.length){
console.log(a[i]);
i++;
}
// or
var l = a.length;
while (l--){
console.log(a[l]);
}
~~~
上面代碼是三種遍歷數組的寫法。最后一種寫法是逆向遍歷,即從最后一個元素向第一個元素遍歷。
## Array構造函數
除了直接使用方括號創建,數組還可以使用JavaScript內置的Array構造函數創建。
~~~
var a = new Array();
a // []
a.length // 0
var a = new Array(1);
a // [undefined × 1]
a.length // 1
var a = new Array(2);
a // [undefined × 2]
a.length // 2
var a = new Array(1,2);
a // [1,2]
a.length // 2
~~~
上面代碼說明,Array構造函數的用法不符合直覺。沒有參數時,返回一個空數組;使用一個參數時,返回一個指定長度的空數組;使用多個參數,返回一個指定成員的數組。所以,建議總是直接采用方括號創建數組。Array構造函數的詳細介紹,參見《標準庫》一章的《Array對象》。
## 參考鏈接
* Axel Rauschmayer,?[Arrays in JavaScript](http://www.2ality.com/2012/12/arrays.html)
* Axel Rauschmayer,?[JavaScript: sparse arrays vs. dense arrays](http://www.2ality.com/2012/06/dense-arrays.html)
* Felix Bohm,?[What They Didn’t Tell You About ES5′s Array Extras](http://net.tutsplus.com/tutorials/javascript-ajax/what-they-didnt-tell-you-about-es5s-array-extras/)
* Juriy Zaytsev,?[How ECMAScript 5 still does not allow to subclass an array](http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/)
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架