6.1 Object
????????到目前為止,大多數(shù)引用值的示例使用的是Object類型。Object是ECMAScript中最常用的類型之一。雖然Object的實例沒有多少功能,但很適合存儲和在應(yīng)用程序間交換數(shù)據(jù)。
????????顯式地創(chuàng)建Object的實例有兩種方式。第一種是使用new操作符和Object構(gòu)造函數(shù),如下所示:
let person = new Object();
person.name = "Nicholas";
person.age = 29;
????????另一種方式是使用對象字面量(object literal)表示法。對象字面量是對象定義的簡寫形式,目的是為了簡化包含大量屬性的對象的創(chuàng)建。比如,下面的代碼定義了與前面示例相同的person對象,但使用的是對象字面量表示法:
let person = {
name: "Nicholas",
age: 29
};
????????在這個例子中,左大括號({)表示對象字面量開始,因為它出現(xiàn)在一個表達(dá)式上下文(expression context)中。在ECMAScript中,表達(dá)式上下文指的是期待返回值的上下文。賦值操作符表示后面要期待一個值,因此左大括號表示一個表達(dá)式的開始。同樣是左大括號,如果出現(xiàn)在語句上下文(statement context)中,比如if語句的條件后面,則表示一個語句塊的開始。
????????接下來指定了name屬性,后跟一個冒號,然后是屬性的值。逗號用于在對象字面量中分隔屬性,因此字符串"Nicholas"后面有一個逗號,而29后面沒有,因為age是這個對象的最后一個屬性。在最后一個屬性后面加上逗號在非常老的瀏覽器中會導(dǎo)致報錯,但所有現(xiàn)代瀏覽器都支持這
種寫法。
????????在對象字面量表示法中,屬性名可以是字符串或數(shù)值,比如:
let person = {
"name": "Nicholas",
"age": 29,
5: true
};
????????這個例子會得到一個帶有屬性name、age和5的對象。注意,數(shù)值屬性會自動轉(zhuǎn)換為字符串。
?????????當(dāng)然也可以用對象字面量表示法來定義一個只有默認(rèn)屬性和方法的對象,只要使用一對大括號,中間留空就行了:
let person = {}; // 與new Object()相同
person.name = "Nicholas";
person.age = 29;
????????這個例子跟本節(jié)開始的第一個例子是等效的,雖然看起來有點怪。對象字面量表示法通常只在為了讓屬性一目了然時才使用。
注意 ????????在使用對象字面量表示法定義對象時,并不會實際調(diào)用Object構(gòu)造函數(shù)。
????????雖然使用哪種方式創(chuàng)建Object實例都可以,但實際上開發(fā)者更傾向于使用對象字面量表示法。這是因為對象字面量代碼更少,看起來也更有封裝所有相關(guān)數(shù)據(jù)的感覺。事實上,對象字面量已經(jīng)成為給函數(shù)傳遞大量可選參數(shù)的主要方式,比如:
function displayInfo(args) {
let output = "";
if (typeof args.name == "string"){
output += "Name: " + args.name + "\n";
}
if (typeof args.age == "number") {
output += "Age: " + args.age + "\n";
}
alert(output);
}
displayInfo({
name: "Nicholas",
age: 29
});
displayInfo({
name: "Greg"
});
????????這里,函數(shù)displayInfo()接收一個名為args的參數(shù)。這個參數(shù)可能有屬性name或age,也可能兩個屬性都有或者都沒有。函數(shù)內(nèi)部會使用typeof操作符測試每個屬性是否存在,然后根據(jù)屬性有無構(gòu)造并顯示一條消息。然后,這個函數(shù)被調(diào)用了兩次,每次都通過一個對象字面量傳入了不同的數(shù)據(jù)。兩種情況下,函數(shù)都正常運行。
注意 ????????這種模式非常適合函數(shù)有大量可選參數(shù)的情況。一般來說,命名參數(shù)更直觀,但在可選參數(shù)過多的時候就顯得笨拙了。最好的方式是對必選參數(shù)使用命名參數(shù),再通過一個對象字面量來封裝多個可選參數(shù)。
????????雖然屬性一般是通過點語法來存取的,這也是面向?qū)ο笳Z言的慣例,但也可以使用中括號來存取屬性。在使用中括號時,要在括號內(nèi)使用屬性名的字符串形式,比如:
console.log(person["name"]); // "Nicholas"
console.log(person.name); // "Nicholas"
????????從功能上講,這兩種存取屬性的方式?jīng)]有區(qū)別。使用中括號的主要優(yōu)勢就是可以通過變量訪問屬性,就像下面這個例子中一樣:
let propertyName = "name";
console.log(person[propertyName]); // "Nicholas"
????????另外,如果屬性名中包含可能會導(dǎo)致語法錯誤的字符,或者包含關(guān)鍵字/保留字時,也可以使用中括號語法。比如:
person["first name"] = "Nicholas";
????????因為"first name"中包含一個空格,所以不能使用點語法來訪問。不過,屬性名中是可以包含非字母數(shù)字字符的,這時候只要用中括號語法存取它們就行了。
????????通常,點語法是首選的屬性存取方式,除非訪問屬性時必須使用變量。
6.2 Array
????????除了Object,Array應(yīng)該就是ECMAScript中最常用的類型了。ECMAScript數(shù)組跟其他編程語言的數(shù)組有很大區(qū)別。跟其他語言中的數(shù)組一樣,ECMAScript數(shù)組也是一組有序的數(shù)據(jù),但跟其他語言不同的是,數(shù)組中每個槽位可以存儲任意類型的數(shù)據(jù)。這意味著可以創(chuàng)建一個數(shù)組,它的第一個元素是字符串,第二個元素是數(shù)值,第三個是對象。ECMAScript數(shù)組也是動態(tài)大小的,會隨著數(shù)據(jù)添加而自動增長。
6.2.1 創(chuàng)建數(shù)組
????????有幾種基本的方式可以創(chuàng)建數(shù)組。一種是使用Array構(gòu)造函數(shù),比如:
let colors = new Array();
????????如果知道數(shù)組中元素的數(shù)量,那么可以給構(gòu)造函數(shù)傳入一個數(shù)值,然后length屬性就會被自動創(chuàng)建并設(shè)置為這個值。比如,下面的代碼會創(chuàng)建一個初始length為20的數(shù)組:
let colors = new Array(20);
????????也可以給Array構(gòu)造函數(shù)傳入要保存的元素。比如,下面的代碼會創(chuàng)建一個包含3個字符串值的數(shù)組:
let colors = new Array("red", "blue", "green");
????????創(chuàng)建數(shù)組時可以給構(gòu)造函數(shù)傳一個值。這時候就有點問題了,因為如果這個值是數(shù)值,則會創(chuàng)建一個長度為指定數(shù)值的數(shù)組;而如果這個值是其他類型的,則會創(chuàng)建一個只包含該特定值的數(shù)組。下面看一個例子:
let colors = new Array(3); // 創(chuàng)建一個包含3個元素的數(shù)組
let names = new Array("Greg"); // 創(chuàng)建一個只包含一個元素,即字符串"Greg"的數(shù)組
????????在使用Array構(gòu)造函數(shù)時,也可以省略new操作符。結(jié)果是一樣的,比如:
let colors = Array(3); // 創(chuàng)建一個包含3個元素的數(shù)組
let names = Array("Greg"); // 創(chuàng)建一個只包含一個元素,即字符串"Greg"的數(shù)組
????????另一種創(chuàng)建數(shù)組的方式是使用數(shù)組字面量(array literal)表示法。數(shù)組字面量是在中括號中包含以逗號分隔的元素列表,如下面的例子所示:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個元素的數(shù)組
let names = []; // 創(chuàng)建一個空數(shù)組
let values = [1,2,]; // 創(chuàng)建一個包含2個元素的數(shù)組
????????在這個例子中,第一行創(chuàng)建一個包含3個字符串的數(shù)組。第二行用一對空中括號創(chuàng)建了一個空數(shù)組。第三行展示了在數(shù)組最后一個值后面加逗號的效果:values是一個包含兩個值(1和2)的數(shù)組。
注意 ????????與對象一樣,在使用數(shù)組字面量表示法創(chuàng)建數(shù)組不會調(diào)用Array構(gòu)造函數(shù)。
????????Array構(gòu)造函數(shù)還有兩個ES6新增的用于創(chuàng)建數(shù)組的靜態(tài)方法:from()和of()。from()用于將類數(shù)組結(jié)構(gòu)轉(zhuǎn)換為數(shù)組實例,而of()用于將一組參數(shù)轉(zhuǎn)換為數(shù)組實例。
????????Array.from()的第一個參數(shù)是一個類數(shù)組對象,即任何可迭代的結(jié)構(gòu),或者有一個length屬性和可索引元素的結(jié)構(gòu)。這種方式可用于很多場合:
// 字符串會被拆分為單字符數(shù)組
console.log(Array.from("Matt")); // ["M", "a", "t", "t"]
// 可以使用from()將集合和映射轉(zhuǎn)換為一個新數(shù)組
const m = new Map().set(1, 2)
.set(3, 4);
const s = new Set().add(1)
.add(2)
.add(3)
.add(4);
console.log(Array.from(m)); // [[1, 2], [3, 4]]
console.log(Array.from(s)); // [1, 2, 3, 4]
// Array.from()對現(xiàn)有數(shù)組執(zhí)行淺復(fù)制
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
console.log(a1); // [1, 2, 3, 4]
alert(a1 === a2); // false
// 可以使用任何可迭代對象
const iter = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
};
console.log(Array.from(iter)); // [1, 2, 3, 4]
// arguments對象可以被輕松地轉(zhuǎn)換為數(shù)組
function getArgsArray() {
return Array.from(arguments);
}
console.log(getArgsArray(1, 2, 3, 4)); // [1, 2, 3, 4]
// from()也能轉(zhuǎn)換帶有必要屬性的自定義對象
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4
};
console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4]
????????Array.from()還接收第二個可選的映射函數(shù)參數(shù)。這個函數(shù)可以直接增強新數(shù)組的值,而無須像調(diào)用Array.from().map()那樣先創(chuàng)建一個中間數(shù)組。還可以接收第三個可選參數(shù),用于指定映射函數(shù)中this的值。但這個重寫的this值在箭頭函數(shù)中不適用。
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, x => x**2);
const a3 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2});
console.log(a2); // [1, 4, 9, 16]
console.log(a3); // [1, 4, 9, 16]
????????Array.of()可以把一組參數(shù)轉(zhuǎn)換為數(shù)組。這個方法用于替代在ES6之前常用的Array.prototype.slice.call(arguments),一種異常笨拙的將arguments對象轉(zhuǎn)換為數(shù)組的寫法:
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(undefined)); // [undefined]
6.2.2 數(shù)組空位
????????使用數(shù)組字面量初始化數(shù)組時,可以使用一串逗號來創(chuàng)建空位(hole)。ECMAScript會將逗號之間相應(yīng)索引位置的值當(dāng)成空位,ES6規(guī)范重新定義了該如何處理這些空位。
????????可以像下面這樣創(chuàng)建一個空位數(shù)組:
const options = [,,,,,]; // 創(chuàng)建包含5個元素的數(shù)組
console.log(options.length); // 5
console.log(options); // [,,,,,]
????????ES6新增的方法和迭代器與早期ECMAScript版本中存在的方法行為不同。ES6新增方法普遍將這些空位當(dāng)成存在的元素,只不過值為undefined:
const options = [1,,,,5];
for (const option of options) {
console.log(option === undefined);
}
// false
// true
// true
// true
// false
const a = Array.from([,,,]); // 使用ES6的Array.from()創(chuàng)建的包含3個空位的數(shù)組
for (const val of a) {
alert(val === undefined);
}
// true
// true
// true
alert(Array.of(...[,,,])); // [undefined, undefined, undefined]
for (const [index, value] of options.entries()) {
alert(value);
}
// 1
// undefined
// undefined
// undefined
// 5
????????ES6之前的方法則會忽略這個空位,但具體的行為也會因方法而異:
const options = [1,,,,5];
// map()會跳過空位置
console.log(options.map(() => 6)); // [6, undefined, undefined, undefined, 6]
// join()視空位置為空字符串
console.log(options.join('-')); // "1----5"
注意 ????????由于行為不一致和存在性能隱患,因此實踐中要避免使用數(shù)組空位。如果確實需要空位,則可以顯式地用undefined值代替。
6.2.3 數(shù)組索引
????????要取得或設(shè)置數(shù)組的值,需要使用中括號并提供相應(yīng)值的數(shù)字索引,如下所示:
let colors = ["red", "blue", "green"]; // 定義一個字符串?dāng)?shù)組
alert(colors[0]); // 顯示第一項
colors[2] = "black"; // 修改第三項
colors[3] = "brown"; // 添加第四項
????????在中括號中提供的索引表示要訪問的值。如果索引小于數(shù)組包含的元素數(shù),則返回存儲在相應(yīng)位置的元素,就像示例中colors[0]顯示"red"一樣。設(shè)置數(shù)組的值方法也是一樣的,就是替換指定位置的值。如果把一個值設(shè)置給超過數(shù)組最大索引的索引,就像示例中的colors[3],則數(shù)組長度會自動擴(kuò)展到該索引值加1(示例中設(shè)置的索引3,所以數(shù)組長度變成了4)。
????????數(shù)組中元素的數(shù)量保存在length屬性中,這個屬性始終返回0或大于0的值,如下例所示:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
let names = []; // 創(chuàng)建一個空數(shù)組
alert(colors.length); // 3
alert(names.length); // 0
????????數(shù)組length屬性的獨特之處在于,它不是只讀的。通過修改length屬性,可以從數(shù)組末尾刪除或添加元素。來看下面的例子:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
colors.length = 2;
alert(colors[2]); // undefined
????????這里,數(shù)組colors一開始有3個值。將length設(shè)置為2,就刪除了最后一個(位置2的)值,因此colors[2]就沒有值了。如果將length設(shè)置為大于數(shù)組元素數(shù)的值,則新添加的元素都將以undefined填充,如下例所示:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
colors.length = 4;
alert(colors[3]); // undefined
????????這里將數(shù)組colors的length設(shè)置為4,雖然數(shù)組只包含3個元素。位置3在數(shù)組中不存在,因此訪問其值會返回特殊值undefined。
????????使用length屬性可以方便地向數(shù)組末尾添加元素,如下例所示:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
colors[colors.length] = "black"; // 添加一種顏色(位置3)
colors[colors.length] = "brown"; // 再添加一種顏色(位置4)
????????數(shù)組中最后一個元素的索引始終是length - 1,因此下一個新增槽位的索引就是length。每次在數(shù)組最后一個元素后面新增一項,數(shù)組的length屬性都會自動更新,以反映變化。這意味著第二行的colors[colors.length]會在位置3添加一個新元素,下一行則會在位置4添加一個新元素。新的長度會在新增元素被添加到當(dāng)前數(shù)組外部的位置上時自動更新。換句話說,就是length屬性會更新為位置加上1,如下例所示:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
colors[99] = "black"; // 添加一種顏色(位置99)
alert(colors.length); // 100
????????這里,colors數(shù)組有一個值被插入到位置99,結(jié)果新length就變成了100(99 + 1)。這中間的所有元素,即位置3~98,實際上并不存在,因此在訪問時會返回undefined。
注意 ????????數(shù)組最多可以包含4 294 967 295個元素,這對于大多數(shù)編程任務(wù)應(yīng)該足夠了。如果嘗試添加更多項,則會導(dǎo)致拋出錯誤。以這個最大值作為初始值創(chuàng)建數(shù)組,可能導(dǎo)致腳本運行時間過長的錯誤。
6.2.4 檢測數(shù)組
????????一個經(jīng)典的ECMAScript問題是判斷一個對象是不是數(shù)組。在只有一個網(wǎng)頁(因而只有一個全局作用域)的情況下,使用instanceof操作符就足矣:
if (value instanceof Array){
// 操作數(shù)組
}
????????使用instanceof的問題是假定只有一個全局執(zhí)行上下文。如果網(wǎng)頁里有多個框架,則可能涉及兩個不同的全局執(zhí)行上下文,因此就會有兩個不同版本的Array構(gòu)造函數(shù)。如果要把數(shù)組從一個框架傳給另一個框架,則這個數(shù)組的構(gòu)造函數(shù)將有別于在第二個框架內(nèi)本地創(chuàng)建的數(shù)組。
????????為解決這個問題,ECMAScript提供了Array.isArray()方法。這個方法的目的就是確定一個值是否為數(shù)組,而不用管它是在哪個全局執(zhí)行上下文中創(chuàng)建的。來看下面的例子:
if (Array.isArray(value)){
// 操作數(shù)組
}
6.2.5 迭代器方法
????????在ES6中,Array的原型上暴露了3個用于檢索數(shù)組內(nèi)容的方法:keys()、values()和entries()。keys()返回數(shù)組索引的迭代器,values()返回數(shù)組元素的迭代器,而entries()返回索引/值對的迭代器:
const a = ["foo", "bar", "baz", "qux"];
// 因為這些方法都返回迭代器,所以可以將它們的內(nèi)容
// 通過Array.from()直接轉(zhuǎn)換為數(shù)組實例
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries());
console.log(aKeys); // [0, 1, 2, 3]
console.log(aValues); // ["foo", "bar", "baz", "qux"]
console.log(aEntries); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]
????????使用ES6的解構(gòu)可以非常容易地在循環(huán)中拆分鍵/值對:
const a = ["foo", "bar", "baz", "qux"];
for (const [idx, element] of a.entries()) {
alert(idx);
alert(element);
}
// 0
// foo
// 1
// bar
// 2
// baz
// 3
// qux
6.2.6 復(fù)制和填充方法
????????ES6新增了兩個方法:批量復(fù)制方法copyWithin(),以及填充數(shù)組方法fill()。這兩個方法的函數(shù)簽名類似,都需要指定既有數(shù)組實例上的一個范圍,包含開始索引,不包含結(jié)束索引。使用這個方法不會改變數(shù)組的大小。
????????使用fill()方法可以向一個已有的數(shù)組中插入全部或部分相同的值。開始索引用于指定開始填充的位置,它是可選的。如果不提供結(jié)束索引,則一直填充到數(shù)組末尾。負(fù)值索引從數(shù)組末尾開始計算。也可以將負(fù)索引想象成數(shù)組長度加上它得到的一個正索引:
const zeroes = [0, 0, 0, 0, 0];
// 用5填充整個數(shù)組
zeroes.fill(5);
console.log(zeroes); // [5, 5, 5, 5, 5]
zeroes.fill(0); // 重置
// 用6填充索引大于等于3的元素
zeroes.fill(6, 3);
console.log(zeroes); // [0, 0, 0, 6, 6]
zeroes.fill(0); // 重置
// 用7填充索引大于等于1且小于3的元素
zeroes.fill(7, 1, 3);
console.log(zeroes); // [0, 7, 7, 0, 0];
zeroes.fill(0); // 重置
// 用8填充索引大于等于1且小于4的元素
// (-4 + zeroes.length = 1)
// (-1 + zeroes.length = 4)
zeroes.fill(8, -4, -1);
console.log(zeroes); // [0, 8, 8, 8, 0];
????????fill()靜默忽略超出數(shù)組邊界、零長度及方向相反的索引范圍:
const zeroes = [0, 0, 0, 0, 0];
// 索引過低,忽略
zeroes.fill(1, -10, -6);
console.log(zeroes); // [0, 0, 0, 0, 0]
// 索引過高,忽略
zeroes.fill(1, 10, 15);
console.log(zeroes); // [0, 0, 0, 0, 0]
// 索引反向,忽略
zeroes.fill(2, 4, 2);
console.log(zeroes); // [0, 0, 0, 0, 0]
// 索引部分可用,填充可用部分
zeroes.fill(4, 3, 10)
console.log(zeroes); // [0, 0, 0, 4, 4]
????????與fill()不同,copyWithin()會按照指定范圍淺復(fù)制數(shù)組中的部分內(nèi)容,然后將它們插入到指定索引開始的位置。開始索引和結(jié)束索引則與fill()使用同樣的計算方法:
let ints,
reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset();
// 從ints中復(fù)制索引0開始的內(nèi)容,插入到索引5開始的位置
// 在源索引或目標(biāo)索引到達(dá)數(shù)組邊界時停止
ints.copyWithin(5);
console.log(ints); // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
reset();
// 從ints中復(fù)制索引5開始的內(nèi)容,插入到索引0開始的位置
ints.copyWithin(0, 5);
console.log(ints); // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9]
reset();
// 從ints中復(fù)制索引0開始到索引3結(jié)束的內(nèi)容
// 插入到索引4開始的位置
ints.copyWithin(4, 0, 3);
alert(ints); // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9]
reset();
// JavaScript引擎在插值前會完整復(fù)制范圍內(nèi)的值
// 因此復(fù)制期間不存在重寫的風(fēng)險
ints.copyWithin(2, 0, 6);
alert(ints); // [0, 1, 0, 1, 2, 3, 4, 5, 8, 9]
reset();
// 支持負(fù)索引值,與fill()相對于數(shù)組末尾計算正向索引的過程是一樣的
ints.copyWithin(-4, -7, -3);
alert(ints); // [0, 1, 2, 3, 4, 5, 3, 4, 5, 6]
·????????copyWithin()靜默忽略超出數(shù)組邊界、零長度及方向相反的索引范圍:
let ints,
reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset();
// 索引過低,忽略
ints.copyWithin(1, -15, -12);
alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset()
// 索引過高,忽略
ints.copyWithin(1, 12, 15);
alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset();
// 索引反向,忽略
ints.copyWithin(2, 4, 2);
alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset();
// 索引部分可用,復(fù)制、填充可用部分
ints.copyWithin(4, 7, 10)
alert(ints); // [0, 1, 2, 3, 7, 8, 9, 7, 8, 9];
6.2.7 轉(zhuǎn)換方法
????????前面提到過,所有對象都有toLocaleString()、toString()和valueOf()方法。其中,valueOf()返回的還是數(shù)組本身。而toString()返回由數(shù)組中每個值的等效字符串拼接而成的一個逗號分隔的字符串。也就是說,對數(shù)組的每個值都會調(diào)用其toString()方法,以得到最終的字符串。來看下面的例子:
let colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
alert(colors.toString()); // red,blue,green
alert(colors.valueOf()); // red,blue,green
alert(colors); // red,blue,green
????????首先是被顯式調(diào)用的toString()和valueOf()方法,它們分別返回了數(shù)組的字符串表示,即將所有字符串組合起來,以逗號分隔。最后一行代碼直接用alert()顯示數(shù)組,因為alert()期待字符串,所以會在后臺調(diào)用數(shù)組的toString()方法,從而得到跟前面一樣的結(jié)果。
????????toLocaleString()方法也可能返回跟toString()和valueOf()相同的結(jié)果,但也不一定。在調(diào)用數(shù)組的toLocaleString()方法時,會得到一個逗號分隔的數(shù)組值的字符串。它與另外兩個方法唯一的區(qū)別是,為了得到最終的字符串,會調(diào)用數(shù)組每個值的toLocaleString()方法,而不是toString()方法。看下面的例子:
let person1 = {
toLocaleString() {
return "Nikolaos";
},
toString() {
return "Nicholas";
}
};
let person2 = {
toLocaleString() {
return "Grigorios";
},
toString() {
return "Greg";
}
};
let people = [person1, person2];
alert(people); // Nicholas,Greg
alert(people.toString()); // Nicholas,Greg
alert(people.toLocaleString()); // Nikolaos,Grigorios
????????這里定義了兩個對象person1和person2,它們都定義了toString()和toLocaleString()方法,而且返回不同的值。然后又創(chuàng)建了一個包含這兩個對象的數(shù)組people。在將數(shù)組傳給alert()時,輸出的是"Nicholas,Greg",這是因為會在數(shù)組每一項上調(diào)用toString()方法(與下一行顯式調(diào)用toString()方法結(jié)果一樣)。而在調(diào)用數(shù)組的toLocaleString()方法時,結(jié)果變成了"Nikolaos, Grigorios",這是因為調(diào)用了數(shù)組每一項的toLocaleString()方法。
????????繼承的方法toLocaleString()以及toString()都返回數(shù)組值的逗號分隔的字符串。如果想使用不同的分隔符,則可以使用join()方法。join()方法接收一個參數(shù),即字符串分隔符,返回包含所有項的字符串。來看下面的例子:
let colors = ["red", "green", "blue"];
alert(colors.join(",")); // red,green,blue
alert(colors.join("||")); // red||green||blue
????????這里在colors數(shù)組上調(diào)用了join()方法,得到了與調(diào)用toString()方法相同的結(jié)果。傳入逗號,結(jié)果就是逗號分隔的字符串。最后一行給join()傳入了雙豎線,得到了字符串"red||green||blue"。如果不給join()傳入任何參數(shù),或者傳入undefined,則仍然使用逗號作為分隔符。
注意 ????????如果數(shù)組中某一項是null或undefined,則在join()、toLocaleString()、toString()和valueOf()返回的結(jié)果中會以空字符串表示。
6.2.8 棧方法
????????ECMAScript給數(shù)組提供幾個方法,讓它看起來像是另外一種數(shù)據(jù)結(jié)構(gòu)。數(shù)組對象可以像棧一樣,也就是一種限制插入和刪除項的數(shù)據(jù)結(jié)構(gòu)。棧是一種后進(jìn)先出(LIFO,Last-In-First-Out)的結(jié)構(gòu),也就是最近添加的項先被刪除。數(shù)據(jù)項的插入(稱為推入,push)和刪除(稱為彈出,pop)只在棧的一個地方發(fā)生,即棧頂。ECMAScript數(shù)組提供了push()和pop()方法,以實現(xiàn)類似棧的行為。
????????push()方法接收任意數(shù)量的參數(shù),并將它們添加到數(shù)組末尾,返回數(shù)組的最新長度。pop()方法則用于刪除數(shù)組的最后一項,同時減少數(shù)組的length值,返回被刪除的項。來看下面的例子:
let colors = new Array(); // 創(chuàng)建一個數(shù)組
let count = colors.push("red", "green"); // 推入兩項
alert(count); // 2
count = colors.push("black"); // 再推入一項
alert(count); // 3
let item = colors.pop(); // 取得最后一項
alert(item); // black
alert(colors.length); // 2
????????這里創(chuàng)建了一個當(dāng)作棧來使用的數(shù)組(注意不需要任何額外的代碼,push()和pop()都是數(shù)組的默認(rèn)方法)。首先,使用push()方法把兩個字符串推入數(shù)組末尾,將結(jié)果保存在變量count中(結(jié)果為2)。
????????然后,再推入另一個值,再把結(jié)果保存在count中。因為現(xiàn)在數(shù)組中有3個元素,所以push()返回3。在調(diào)用pop()時,會返回數(shù)組的最后一項,即字符串"black"。此時數(shù)組還有兩個元素。
????????棧方法可以與數(shù)組的其他任何方法一起使用,如下例所示:
let colors = ["red", "blue"];
colors.push("brown"); // 再添加一項
colors[3] = "black"; // 添加一項
alert(colors.length); // 4
let item = colors.pop(); // 取得最后一項
alert(item); // black
????????這里先初始化了包含兩個字符串的數(shù)組,然后通過push()添加了第三個值,第四個值是通過直接在位置3上賦值添加的。調(diào)用pop()時,返回了字符串"black",也就是最后添加到數(shù)組的字符串。
6.2.9 隊列方法
????????就像棧是以LIFO形式限制訪問的數(shù)據(jù)結(jié)構(gòu)一樣,隊列以先進(jìn)先出(FIFO,F(xiàn)irst-In-First-Out)形式限制訪問。隊列在列表末尾添加數(shù)據(jù),但從列表開頭獲取數(shù)據(jù)。因為有了在數(shù)據(jù)末尾添加數(shù)據(jù)的push()方法,所以要模擬隊列就差一個從數(shù)組開頭取得數(shù)據(jù)的方法了。這個數(shù)組方法叫shift(),它會刪除數(shù)組的第一項并返回它,然后數(shù)組長度減1。使用shift()和push(),可以把數(shù)組當(dāng)成隊列來使用:
let colors = new Array(); // 創(chuàng)建一個數(shù)組
let count = colors.push("red", "green"); // 推入兩項
alert(count); // 2
count = colors.push("black"); // 再推入一項
alert(count); // 3
let item = colors.shift(); // 取得第一項
alert(item); // red
alert(colors.length); // 2
????????這個例子創(chuàng)建了一個數(shù)組并用push()方法推入三個值。加粗的那行代碼使用shift()方法取得了數(shù)組的第一項,即"red"。刪除這一項之后,"green"成為第一個元素,"black"成為第二個元素,數(shù)組此時就包含兩項。
????????ECMAScript也為數(shù)組提供了unshift()方法。顧名思義,unshift()就是執(zhí)行跟shift()相反的操作:在數(shù)組開頭添加任意多個值,然后返回新的數(shù)組長度。通過使用unshift()和pop(),可以在相反方向上模擬隊列,即在數(shù)組開頭添加新數(shù)據(jù),在數(shù)組末尾取得數(shù)據(jù),如下例所示:
let colors = new Array(); // 創(chuàng)建一個數(shù)組
let count = colors.unshift("red", "green"); // 從數(shù)組開頭推入兩項
alert(count); // 2
count = colors.unshift("black"); // 再推入一項
alert(count); // 3
let item = colors.pop(); // 取得最后一項
alert(item); // green
alert(colors.length); // 2
????????這里,先創(chuàng)建一個數(shù)組,再通過unshift()填充數(shù)組。首先,給數(shù)組添加"red"和"green",再添加"black",得到["black","red","green"]。調(diào)用pop()時,刪除最后一項"green"并返回它。
6.2.10 排序方法
????????數(shù)組有兩個方法可以用來對元素重新排序:reverse()和sort()。顧名思義,reverse()方法就是將數(shù)組元素反向排列。比如:
let values = [1, 2, 3, 4, 5];
values.reverse();
alert(values); // 5,4,3,2,1
????????這里,數(shù)組values的初始狀態(tài)為[1,2,3,4,5]。通過調(diào)用reverse()反向排序,得到了[5,4,3,2,1]。這個方法很直觀,但不夠靈活,所以才有了sort()方法。
????????默認(rèn)情況下,sort()會按照升序重新排列數(shù)組元素,即最小的值在前面,最大的值在后面。為此,sort()會在每一項上調(diào)用String()轉(zhuǎn)型函數(shù),然后比較字符串來決定順序。即使數(shù)組的元素都是數(shù)值,也會先把數(shù)組轉(zhuǎn)換為字符串再比較、排序。比如:
let values = [0, 1, 5, 10, 15];
values.sort();
alert(values); // 0,1,10,15,5
????????一開始數(shù)組中數(shù)值的順序是正確的,但調(diào)用sort()會按照這些數(shù)值的字符串形式重新排序。因此,即使5小于10,但字符串"10"在字符串"5"的前頭,所以10還是會排到5前面。很明顯,這在多數(shù)情況下都不是最合適的。為此,sort()方法可以接收一個比較函數(shù),用于判斷哪個值應(yīng)該排在前面。
????????比較函數(shù)接收兩個參數(shù),如果第一個參數(shù)應(yīng)該排在第二個參數(shù)前面,就返回負(fù)值;如果兩個參數(shù)相等,就返回0;如果第一個參數(shù)應(yīng)該排在第二個參數(shù)后面,就返回正值。下面是使用簡單比較函數(shù)的一個例子:
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
????????這個比較函數(shù)可以適用于大多數(shù)數(shù)據(jù)類型,可以把它當(dāng)作參數(shù)傳給sort()方法,如下所示:
let values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 0,1,5,10,15
????????在給sort()方法傳入比較函數(shù)后,數(shù)組中的數(shù)值在排序后保持了正確的順序。當(dāng)然,比較函數(shù)也可以產(chǎn)生降序效果,只要把返回值交換一下即可:
function compare(value1, value2) {
if (value1 < value2) {
return 1;
} else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
let values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 15,10,5,1,0
????????此外,這個比較函數(shù)還可簡寫為一個箭頭函數(shù):
let values = [0, 1, 5, 10, 15];
values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
alert(values); // 15,10,5,1,0
????????在這個修改版函數(shù)中,如果第一個值應(yīng)該排在第二個值后面則返回1,如果第一個值應(yīng)該排在第二個值前面則返回-1。交換這兩個返回值之后,較大的值就會排在前頭,數(shù)組就會按照降序排序。當(dāng)然,如果只是想反轉(zhuǎn)數(shù)組的順序,reverse()更簡單也更快。
????????如果數(shù)組的元素是數(shù)值,或者是其valueOf()方法返回數(shù)值的對象(如Date對象),這個比較函數(shù)還可以寫得更簡單,因為這時可以直接用第二個值減去第一個值:
function compare(value1, value2){
return value2 - value1;
}
????????比較函數(shù)就是要返回小于0、0和大于0的數(shù)值,因此減法操作完全可以滿足要求。
6.2.11 操作方法
????????對于數(shù)組中的元素,我們有很多操作方法。比如,concat()方法可以在現(xiàn)有數(shù)組全部元素基礎(chǔ)上創(chuàng)建一個新數(shù)組。它首先會創(chuàng)建一個當(dāng)前數(shù)組的副本,然后再把它的參數(shù)添加到副本末尾,最后返回這個新構(gòu)建的數(shù)組。如果傳入一個或多個數(shù)組,則concat()會把這些數(shù)組的每一項都添加到結(jié)果數(shù)組。如果參數(shù)不是數(shù)組,則直接把它們添加到結(jié)果數(shù)組末尾。來看下面的例子:
let colors = ["red", "green", "blue"];
let colors2 = colors.concat("yellow", ["black", "brown"]);
console.log(colors); // ["red", "green","blue"]
console.log(colors2); // ["red", "green", "blue", "yellow", "black", "brown"]
????????這里先創(chuàng)建一個包含3個值的數(shù)組colors。然后colors調(diào)用concat()方法,傳入字符串"yellow"和一個包含"black"和"brown"的數(shù)組。保存在colors2中的結(jié)果就是["red", "green", "blue", "yellow", "black","brown"]。原始數(shù)組colors保持不變。
????????打平數(shù)組參數(shù)的行為可以重寫,方法是在參數(shù)數(shù)組上指定一個特殊的符號:Symbol.isConcatSpreadable。這個符號能夠阻止concat()打平參數(shù)數(shù)組。相反,把這個值設(shè)置為true可以強制打平類數(shù)組對象:
let colors = ["red", "green", "blue"];
let newColors = ["black", "brown"];
let moreNewColors = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: "pink",
1: "cyan"
};
newColors[Symbol.isConcatSpreadable] = false;
// 強制不打平數(shù)組
let colors2 = colors.concat("yellow", newColors);
// 強制打平類數(shù)組對象
let colors3 = colors.concat(moreNewColors);
console.log(colors); // ["red", "green", "blue"]
console.log(colors2); // ["red", "green", "blue", "yellow", ["black", "brown"]]
console.log(colors3); // ["red", "green", "blue", "pink", "cyan"]
????????接下來,方法slice()用于創(chuàng)建一個包含原有數(shù)組中一個或多個元素的新數(shù)組。slice()方法可以接收一個或兩個參數(shù):返回元素的開始索引和結(jié)束索引。如果只有一個參數(shù),則slice()會返回該索引到數(shù)組末尾的所有元素。如果有兩個參數(shù),則slice()返回從開始索引到結(jié)束索引對應(yīng)的所有元素,其中不包含結(jié)束索引對應(yīng)的元素。記住,這個操作不影響原始數(shù)組。來看下面的例子:
let colors = ["red", "green", "blue", "yellow", "purple"];
let colors2 = colors.slice(1);
let colors3 = colors.slice(1, 4);
alert(colors2); // green,blue,yellow,purple
alert(colors3); // green,blue,yellow
????????這里,colors數(shù)組一開始有5個元素。調(diào)用slice()傳入1會得到包含4個元素的新數(shù)組。其中不包括"red",這是因為拆分操作要從位置1開始,即從"green"開始。得到的colors2數(shù)組包
含"green"、"blue"、"yellow"和"purple"。colors3數(shù)組是通過調(diào)用slice()并傳入1和4得到的,即從位置1開始復(fù)制到位置3。因此colors3包含"green"、"blue"和"yellow"。
注意 ????????如果slice()的參數(shù)有負(fù)值,那么就以數(shù)值長度加上這個負(fù)值的結(jié)果確定位置。比如,在包含5個元素的數(shù)組上調(diào)用slice(-2,-1),就相當(dāng)于調(diào)用slice(3,4)。如果結(jié)束位置小于開
始位置,則返回空數(shù)組。
????????或許最強大的數(shù)組方法就屬splice()了,使用它的方式可以有很多種。splice()的主要目的是在數(shù)組中間插入元素,但有3種不同的方式使用這個方法。
- 刪除。需要給splice()傳2個參數(shù):要刪除的第一個元素的位置和要刪除的元素數(shù)量??梢詮臄?shù)組中刪除任意多個元素,比如splice(0, 2)會刪除前兩個元素。
- 插入。需要給splice()傳3個參數(shù):開始位置、0(要刪除的元素數(shù)量)和要插入的元素,可以在數(shù)組中指定的位置插入元素。第三個參數(shù)之后還可以傳第四個、第五個參數(shù),乃至任意多個要插入的元素。比如,splice(2, 0, "red", "green")會從數(shù)組位置2開始插入字符串"red"和"green"。
- 替換。splice()在刪除元素的同時可以在指定位置插入新元素,同樣要傳入3個參數(shù):開始位置、要刪除元素的數(shù)量和要插入的任意多個元素。要插入的元素數(shù)量不一定跟刪除的元素數(shù)量一致。比
- 如,splice(2, 1, "red", "green")會在位置2刪除一個元素,然后從該位置開始向數(shù)組中插入"red"和"green"。
????????splice()方法始終返回這樣一個數(shù)組,它包含從數(shù)組中被刪除的元素(如果沒有刪除元素,則返回空數(shù)組)。以下示例展示了上述3種使用方式。
let colors = ["red", "green", "blue"];
let removed = colors.splice(0,1); // 刪除第一項
alert(colors); // green,blue
alert(removed); // red,只有一個元素的數(shù)組
removed = colors.splice(1, 0, "yellow", "orange"); // 在位置1插入兩個元素
alert(colors); // green,yellow,orange,blue
alert(removed); // 空數(shù)組
removed = colors.splice(1, 1, "red", "purple"); // 插入兩個值,刪除一個元素
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,只有一個元素的數(shù)組
????????這個例子中,colors數(shù)組一開始包含3個元素。第一次調(diào)用splice()時,只刪除了第一項,colors中還有"green"和"blue"。第二次調(diào)用slice()時,在位置1插入兩項,然后colors包含"green"、"yellow"、"orange"和"blue"。這次沒刪除任何項,因此返回空數(shù)組。最后一次調(diào)用splice()時刪除了位置1上的一項,同時又插入了"red"和"purple"。最后,colors數(shù)組包含"green"、"red"、"purple"、"orange"和"blue"。
6.2.12 搜索和位置方法
????????ECMAScript提供兩類搜索數(shù)組的方法:按嚴(yán)格相等搜索和按斷言函數(shù)搜索。
1、嚴(yán)格相等
????????ECMAScript提供了3個嚴(yán)格相等的搜索方法:indexOf()、lastIndexOf()和includes()。其中,前兩個方法在所有版本中都可用,而第三個方法是ECMAScript 7新增的。這些方法都接收兩個參數(shù):要查找的元素和一個可選的起始搜索位置。indexOf()和includes()方法從數(shù)組前頭(第一項)開始向后搜索,而lastIndexOf()從數(shù)組末尾(最后一項)開始向前搜索。
????????indexOf()和lastIndexOf()都返回要查找的元素在數(shù)組中的位置,如果沒找到則返回-1。includes()返回布爾值,表示是否至少找到一個與指定元素匹配的項。在比較第一個參數(shù)跟數(shù)組每一項時,會使用全等(===)比較,也就是說兩項必須嚴(yán)格相等。下面來看一些例子:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
alert(numbers.indexOf(4)); // 3
alert(numbers.lastIndexOf(4)); // 5
alert(numbers.includes(4)); // true
alert(numbers.indexOf(4, 4)); // 5
alert(numbers.lastIndexOf(4, 4)); // 3
alert(numbers.includes(4, 7)); // false
let person = { name: "Nicholas" };
let people = [{ name: "Nicholas" }];
let morePeople = [person];
alert(people.indexOf(person)); // -1
alert(morePeople.indexOf(person)); // 0
alert(people.includes(person)); // false
alert(morePeople.includes(person)); // true
2、斷言函數(shù)
????????ECMAScript也允許按照定義的斷言函數(shù)搜索數(shù)組,每個索引都會調(diào)用這個函數(shù)。斷言函數(shù)的返回值決定了相應(yīng)索引的元素是否被認(rèn)為匹配。
????????斷言函數(shù)接收3個參數(shù):元素、索引和數(shù)組本身。其中元素是數(shù)組中當(dāng)前搜索的元素,索引是當(dāng)前元素的索引,而數(shù)組就是正在搜索的數(shù)組。斷言函數(shù)返回真值,表示是否匹配。
????????find()和findIndex()方法使用了斷言函數(shù)。這兩個方法都從數(shù)組的最小索引開始。find()返回第一個匹配的元素,findIndex()返回第一個匹配元素的索引。這兩個方法也都接收第二個可選的參數(shù),用于指定斷言函數(shù)內(nèi)部this的值。
const people = [
{
name: "Matt",
age: 27
},
{
name: "Nicholas",
age: 29
}
];
alert(people.find((element, index, array) => element.age < 28));
// {name: "Matt", age: 27}
alert(people.findIndex((element, index, array) => element.age < 28));
// 0
????????找到匹配項后,這兩個方法都不再繼續(xù)搜索。
const evens = [2, 4, 6];
// 找到匹配后,永遠(yuǎn)不會檢查數(shù)組的最后一個元素
evens.find((element, index, array) => {
console.log(element);
console.log(index);
console.log(array);
return element === 4;
});
// 2
// 0
// [2, 4, 6]
// 4
// 1
// [2, 4, 6]
6.2.13 迭代方法
????????ECMAScript為數(shù)組定義了5個迭代方法。每個方法接收兩個參數(shù):以每一項為參數(shù)運行的函數(shù),以及可選的作為函數(shù)運行上下文的作用域?qū)ο螅ㄓ绊懞瘮?shù)中this的值)。傳給每個方法的函數(shù)接收3個參數(shù):數(shù)組元素、元素索引和數(shù)組本身。因具體方法而異,這個函數(shù)的執(zhí)行結(jié)果可能會也可能不會影響方法的返回值。數(shù)組的5個迭代方法如下。
- every():對數(shù)組每一項都運行傳入的函數(shù),如果對每一項函數(shù)都返回true,則這個方法返回true。
- filter():對數(shù)組每一項都運行傳入的函數(shù),函數(shù)返回true的項會組成數(shù)組之后返回。
- forEach():對數(shù)組每一項都運行傳入的函數(shù),沒有返回值。
- map():對數(shù)組每一項都運行傳入的函數(shù),返回由每次函數(shù)調(diào)用的結(jié)果構(gòu)成的數(shù)組。
- some():對數(shù)組每一項都運行傳入的函數(shù),如果有一項函數(shù)返回true,則這個方法返回true。
?????????這些方法都不改變調(diào)用它們的數(shù)組。
????????在這些方法中,every()和some()是最相似的,都是從數(shù)組中搜索符合某個條件的元素。對every()來說,傳入的函數(shù)必須對每一項都返回true,它才會返回true;否則,它就返回false。而對some()來說,只要有一項讓傳入的函數(shù)返回true,它就會返回true。下面是一個例子:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let everyResult = numbers.every((item, index, array) => item > 2);
alert(everyResult); // false
let someResult = numbers.some((item, index, array) => item > 2);
alert(someResult); // true
????????以上代碼調(diào)用了every()和some(),傳入的函數(shù)都是在給定項大于2時返回true。every()返回false是因為并不是每一項都能達(dá)到要求。而some()返回true是因為至少有一項滿足條件。
????????下面再看一看filter()方法。這個方法基于給定的函數(shù)來決定某一項是否應(yīng)該包含在它返回的數(shù)組中。比如,要返回一個所有數(shù)值都大于2的數(shù)組,可以使用如下代碼:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let filterResult = numbers.filter((item, index, array) => item > 2);
alert(filterResult); // 3,4,5,4,3
????????這里,調(diào)用filter()返回的數(shù)組包含3、4、5、4、3,因為只有對這些項傳入的函數(shù)才返回true。這個方法非常適合從數(shù)組中篩選滿足給定條件的元素。
????????接下來map()方法也會返回一個數(shù)組。這個數(shù)組的每一項都是對原始數(shù)組中同樣位置的元素運行傳入函數(shù)而返回的結(jié)果。例如,可以將一個數(shù)組中的每一項都乘以2,并返回包含所有結(jié)果的數(shù)組,如下所示:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let mapResult = numbers.map((item, index, array) => item * 2);
alert(mapResult); // 2,4,6,8,10,8,6,4,2
????????以上代碼返回了一個數(shù)組,包含原始數(shù)組中每個值乘以2的結(jié)果。這個方法非常適合創(chuàng)建一個與原始數(shù)組元素一一對應(yīng)的新數(shù)組。
????????最后,再來看一看forEach()方法。這個方法只會對每一項運行傳入的函數(shù),沒有返回值。本質(zhì)上,forEach()方法相當(dāng)于使用for循環(huán)遍歷數(shù)組。比如:
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
numbers.forEach((item, index, array) => {
// 執(zhí)行某些操作
});
????????數(shù)組的這些迭代方法通過執(zhí)行不同操作方便了對數(shù)組的處理。
6.2.14 歸并方法
????????ECMAScript為數(shù)組提供了兩個歸并方法:reduce()和reduceRight()。這兩個方法都會迭代數(shù)組的所有項,并在此基礎(chǔ)上構(gòu)建一個最終返回值。reduce()方法從數(shù)組第一項開始遍歷到最后一項。而reduceRight()從最后一項開始遍歷至第一項。
????????這兩個方法都接收兩個參數(shù):對每一項都會運行的歸并函數(shù),以及可選的以之為歸并起點的初始值。傳給reduce()和reduceRight()的函數(shù)接收4個參數(shù):上一個歸并值、當(dāng)前項、當(dāng)前項的索引和數(shù)組本身。這個函數(shù)返回的任何值都會作為下一次調(diào)用同一個函數(shù)的第一個參數(shù)。如果沒有給這兩個方法傳入可選的第二個參數(shù)(作為歸并起點值),則第一次迭代將從數(shù)組的第二項開始,因此傳給歸并函數(shù)的第一個參數(shù)是數(shù)組的第一項,第二個參數(shù)是數(shù)組的第二項。
????????可以使用reduce()函數(shù)執(zhí)行累加數(shù)組中所有數(shù)值的操作,比如:
let values = [1, 2, 3, 4, 5];
let sum = values.reduce((prev, cur, index, array) => prev + cur);
alert(sum); // 15
????????第一次執(zhí)行歸并函數(shù)時,prev是1,cur是2。第二次執(zhí)行時,prev是3(1 + 2),cur是3(數(shù)組第三項)。如此遞進(jìn),直到把所有項都遍歷一次,最后返回歸并結(jié)果。
????????reduceRight()方法與之類似,只是方向相反。來看下面的例子:
let values = [1, 2, 3, 4, 5];
let sum = values.reduceRight(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); // 15
????????在這里,第一次調(diào)用歸并函數(shù)時prev是5,而cur是4。當(dāng)然,最終結(jié)果相同,因為歸并操作都是簡單的加法。文章來源:http://www.zghlxwxcb.cn/news/detail-801618.html
????????究竟是使用reduce()還是reduceRight(),只取決于遍歷數(shù)組元素的方向。除此之外,這兩個方法沒什么區(qū)別。文章來源地址http://www.zghlxwxcb.cn/news/detail-801618.html
到了這里,關(guān)于第六章 集合引用類型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!