JavaScript中的數組創建
數組是一個包含了對象或原始類型的有序集合。很難想象一個不使用數組的程序會是什么樣。
以下是幾種操作數組的方式:
-
初始化數組並設置初始值
-
通過索引訪問數組元素
-
添加新元素
-
刪除現有元素
本文涵蓋了數組的初始化以及設置初始值的操作。在JavaScript中要做到這一點的基本方法是使用數組字面量,例如[1, 5, 8]
或是數組構造器new Array (1, 5, 8)
。
除了手動枚舉之外,JavaScript還提供了更有趣更直接的數組創建方式。讓我一起看看在JavaScript中初始化數組的一般場景和高級場景吧。
1. 數組字面量
數組字面量由一組包裹在方括號[
]
之間的逗號分隔的元素element1, element2, ..., elementN
組成。
讓我們看幾個數組字面量的例子:
let numbers = [1, 5, 7, 8];
let planets = ['Earth', 'Mercury', 'Jupiter'];
數組字面量可以包含任意類型的元素,包括null
, undefined
, 原始類型以及對象:
let mixed = [1, 'Earth', null, NaN, undefined, ['Mars']];
1.1 數組字面量中的逗號
逗號,
用來分隔數組字面量中的元素。基於逗號的位置或是逗號之間元素的缺失的情況,不同結構的數組會被創建。
讓我們詳細看一看現有的三種情況。
第一種情況:普通的數組字面量
通常情況是在任何一對逗號之間都有一個元素並且數組字面量不以逗號開始或結尾。這是推薦的使用逗號分隔手動初始化數組的方式:
let items = ['first', 'second', 'third'];
items; // => ['first', 'second', 'third']
items
是由2個逗號分隔的3個元素創建的。
在這個例子中item
是一個密集數組,因為它的元素有着連續的索引(或者簡單來說數組中沒有空洞)。
大多數時候,你會使用這種方式初始化數組。
第二種情況: 在數組末尾的一個無用逗號
第二種情況和第一種情況類似,只不過在最后一個逗號之后沒有指定元素。這種情況中,最后一個逗號會被JavaScript忽略:
let items = ['first', 'second', 'third', ];
items; // => ['first', 'second', 'third']
在元素'third'
之后指定的一個逗號,它是數組中的最后一個逗號並且在那之后沒有任何元素。這個末尾的逗號是無用的,意味着它對新創建的數組沒有任何影響。
這種情況下JavaScript也會創建一個密集數組。
第三種情況: 逗號之間沒有元素
第三種情況發生在當一對逗號之間沒有指定元素或是數組字面量以一個逗號開始時。
這會創建一個稀疏數組:一個其元素索引不連續的集合(換句話說數組中存在空洞)。
下面的數組字面量以逗號開始,創建了一個稀疏數組:
let items = [, 'first', 'second', 'third'];
items; // => [<1 empty slot>, 'first', 'second', 'third']
items[0]; // => undefined
items[1]; // => 'first'
items.length; // => 4
數組字面量[, ...]
以逗號開始。結果是items
是一個稀疏數組,在索引0
的位置是一個空slot。訪問空slot items[0]
會得到undefined
。
區分一個空slot和一個值是undefined
的元素是很重要的。通過索引訪問這種類型的元素時都會得到undefined
,這使得區分它們變得很棘手。
空slot意味着數組在某個索引位置上沒有元素(index in array
返回false
),這與一個值是undefined
的元素(index in array
返回true
)是不同的。
需要注意的是空slot在Firefox的控制台會被顯示為<1 empty slot>
,這是展示空slot的正確方法。Chrome的控制台會展示undefined x 1
。其它瀏覽器的控制台只會簡單的展示undefined
。
當數組字面量的兩個逗號之間沒有元素時也會創建一個稀疏數組:
let items = ['first', , 'second', 'third'];
items; // => ['first', <1 empty slot> ,'second', 'third']
items[0]; // => 'first'
items[1]; // => undefined
items.length; // => 4
數組字面量包含了中間沒有元素的逗號:[... , , ...]
。這樣item
成了一個索引1
處是一個空slot的稀疏數組。訪問空slot items[1]
會得到undefined
。
通常你應該避免這種會創建稀疏數組的使用方式。同時你也應該盡可能的不去操作稀疏數組。
在一個數組字面量中刪除或是添加元素時你可能會在不經意間創建一個稀疏數組。因此在修改之后切記仔細檢查。
1.2 spread運算符帶來的改善
ECMAScript 6中引入的spread運算符改善了使用其它數組中的元素初始新數組這一操作。
在很多場景下spread運算符都可以使數組創建變得更簡單。方法就是在數組字面量中把...
作為源數組的前綴,然后源數組中的元素就被包括到新創建的數組中了。就這么簡單。
下面的數組字面量在創建時使用了spread運算符:
let source = ['second', 'third'];
let items = ['first', ...source];
items; // => ['first', 'second', 'third']
數組字面量['First', ...source]
表示'First'
會被作為數組中的第一個元素。剩余的元素則是通過spread運算符從source
數組取得。
常規的元素枚舉方式可以和spread運算符可以不受限制的組合在一起。
let odds = [1, 3, 5];
let evens = [4, 6];
let zero = 0;
let negative = -1;
let items = [...odds, zero, ...evens, negative];
items; // => [1, 3, 5, 0, 4, 6, -1]
創建items
時使用一個組合了普通變量zero
和negative
以及前置spread運算符的源數組...odds
和...evens
的集合。
由於spread運算符接收的是普通的可迭代對象(數組默認就是可迭代的),這使得自定義的初始化成為可能。
一個生成器函數也會返回一個可迭代的生成器對象,因此你可以利用生成器的靈活性來創建數組。
讓我們創建一個第一個參數代表元素值第二個參數代表元素數量的生成器函數。然后使用它和spread運算符以及數組字面量來初始化新數組:
function* elements(element, length) {
let index = 0;
while (length > index++) {
yield element;
}
}
[...elements(0, 5)]; // => [0, 0, 0, 0, 0]
[...elements('hi', 2)]; // => ['hi', 'hi']
每次執行elements(element, length)
時都會創建一個生成器對象。spread運算符會利用該生成器對象來初始化數組。
[...elements(0, 5)]
會創建一個有5個0的數組。而[...elements('hi', 2)]
會創建一個有兩個字符串'h1'
的數組。
2. 數組構造器
JavaScript中的數組是一個對象。和任何對象一樣,它有一個可以用來創建新實例的構造器函數Array
。讓我們看一個例子:
// 構造器調用
let arrayConstr = new Array(1, 5);
arrayConstr; // => [1, 5]
typeof arrayConstr; // => 'object'
arrayConstr.constructor === Array; // => true
// 數組字面量
let arrayLiteral = [1, 5];
arrayLiteral; // => [1, 5]
typeof arrayLiteral; // => 'object'
arrayLiteral.constructor === Array; // => true
arrayConstr
和arrayLiteral
都是數組實例,它們的構造器都是Array
。對象arrayConstr
是通過構造器調用創建的:new Array(1, 5)
。
你也可以像調用普通函數那樣通過Array來創建數組實例:Array(1, 5)
。
你應該更傾向於使用字面量[item1, item2, ..., itemN]
而不是構造器new Array(item1, item2, ..., itemN)
來創建數組。主要原因是數組字面量的寫法更短,更簡單。還有一個原因就是數組構造器在第一個參數是不同類型的值時,產生的怪異行為。
讓我們看看Array
使如何根據第一個參數的類型以及參數的個數來創建數組實例的吧。
2.1 數值類型的參數下創建稀疏數組
當數組構造器new Array(numberArg)
以一個單一的數值類型的參數調用時,JavaScript會創建一個帶有參數指定的個數的空slot的稀疏數組。
看一個例子:
let items = new Array(3);
items; // => [<3 empty slots>]
items.length; // => 3
new Array(3)
是一個帶有單一參數3
的構造器調用。一個長度為3
的稀疏數組items
被創建了,但實際上它並不包含任何元素而只是有幾個空slot。
這種創建數組的方式本身並沒有什么價值。然而把它和一些靜態方法組合起來用於創建指定長度的數組並填充生成的元素時卻是有用的。
2.2 枚舉元素
如果調用Array
構造器時傳入了一個參數列表而不是單個數字,那么這些參數就會成為數組的元素。
這種方式和數組字面量的方式幾乎一樣,只不過是在一個構造器調用中而已。
下面的例子創建了一個數組:
let items = new Array('first', 'second', 'third');
items; // => ['first', 'second', 'third']
new Array('first', 'second', 'third')
使用參數中的元素創建了一個數組。
由於spread運算符的靈活性,在構造器調用中使用來自其它數組的元素也是可行的:
let source = new Array('second', 'third');
let items = new Array('first', ...source);
items; // => ['first', 'second', 'third']
new Array('First', ...source)
創建數組時使用了'First'
元素以及source
數組中的所有元素。
無論哪種方式,你都應該傾向於使用數組字面量,因為它更簡單直接。
2.3 有用的靜態方法
當讀到關於通過在構造器調用中傳入一個數字來創建稀疏數組的部分時你可能好奇這有什么實際的用處。
ECMAScript 6增加了一些有用的方法如Array.prototype.fill()
和Array.from()
。這兩個方法都可以用來填充一個稀疏數組中的空slot。
讓我使用fill()
方法來創建一個包含5個0的數組:
let zeros = new Array(5).fill(0);
zeros; // => [0, 0, 0, 0, 0]
new Array(5)
創建了一個有5個空slot的稀疏數組。接着fill(0)
方法用0
填充了空slot。
靜態方法Array.from()
則有着更寬的使用場景。像上邊的例子一樣,讓我們創建一個包含5個0的數組:
let zeros = Array.from(new Array(5), () => 0);
zeros; // => [0, 0, 0, 0, 0]
一個通過new Array(5)
創建的長度為5
的稀疏組數作為參數被傳遞給Array.from()
。第二個參數作為一個返回0
的映射函數。
共執行了5
次迭代,每次迭代中箭頭函數的返回值被用作數組的元素。
由於在每次迭代中都會執行映射函數,因此動態創建數組元素是可行的。讓我們創建一個包含1
到5
的數組:
let items = Array.from(new Array(5), (item, index) => index + 1);
items; // => [1, 2, 3, 4, 5]
映射函數被調用時會傳入兩個參數:當前的item
以及當前迭代的index
。索引參數被用來生成元素:index + 1
。
Array.from()
的第一個參數可以接受任何可迭代對象,這使得它更有價值。
讓我們使用一個生成器對象創建一個遞增的數字列表:
function* generate(max) {
let count = 0;
while (max > count++) {
yield count;
}
}
let items = Array.from(generate(5));
items; // => [1, 2, 3, 4, 5]
let itemsSpread = [...generate(5)];
itemsSpread; // => [1, 2, 3, 4, 5]
generate(max)
是一個生成器函數,它會生成從一串從1
到max
的數字。
Array.from(generate(5))
使用一個生成器對象作為參數創建了一個包含1
到5
數字的數組。
使用spread運算符[...generate(5)]
和數組字面量可以達到同樣的目的。
3. 總結
數組初始化是操作集合時的常見操作。JavaScript提供了多種方法以及靈活性來實現該目的。
數組構造器的行為在很多情況下會讓你感到意外。因此數組字面量是初始化數組實例更好,更簡單的方式。
當數組需要根據基於每個迭代元素的計算進行初始化時,Array.from()
是一個不錯的選擇。
如果數組元素需要被填充為同一個值,使用Array.prototype.fill()
和new Array(length)
的組合。
不要低估可迭代對象和生成器函數的能力,它們可以和spread運算符組合起來使用在數組字面量或是Array.from()
中。
本文轉載自:眾成翻譯
譯者:loveky
鏈接:http://www.zcfy.cc/article/713
原文:http://rainsoft.io/power-up-the-array-creation-in-javascript/