ES6擴充套件運算子
- 擴充套件運算(spread)
(1). 定義
擴充套件運算子(spread)是三個點(…)。它好比 rest 引數的逆運算,將一個數組轉為用逗號分隔的引數序列。主要用於函式的呼叫中。
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
和正常函式的藏書結合使用,也可以放置表示式。如果擴充套件運算子後面是一個空陣列,則不產生任何效果。
function f(v, w, x, y, z) { } const args = [0, 1]; f(-1, ...args, 2, ...[3]); //array.push(...items)和add(...numbers)這兩行,都是函式的呼叫,它們的都使用了擴充套件運算子。該運算子將一個數組,變為引數序列。 //放置表示式 const arr = [ ...(x > 0 ? ['a'] : []), 'b', ]; //空陣列 [...[], 1] // [1] // 擴充套件運算子所在的括號不是函式呼叫,而console.log(...[1, 2])就不會報錯,因為這時是函式調 (...[1,2]) // Uncaught SyntaxError: Unexpected number console.log((...[1,2])) // Uncaught SyntaxError: Unexpected number
(2).替換函式 的apply用法
擴充套件運算子可以展開陣列,將陣列轉換為函式的引數。所以不需要apply()方法。
// ES5 的寫法
console.log('ES5方式:' + Math.max.apply(null, [14, 3, 77]))
// ES6 的寫法
console.log('ES6方式:' + Math.max(...[14, 3, 77]))
// 等同於
console.log('等價ES6:' + Math.max(14, 3, 77))
(3). 擴充套件運算子的應用
a,複製陣列
b,合併陣列
c,與解構的賦值組合,用於生成陣列,語法:[a, …rest] = list,注意:如果將擴充套件運算子用於陣列賦值,只能放在引數的最後一位,否則會報錯
d,把字串轉為真正的陣列
e,實現了 Iterator 介面的物件
任何 Iterator 介面的物件,都可以用擴充套件運算子轉為真正的陣列。
f,Map 和 Set 結構,Generator 函式
擴充套件運算子內部呼叫的是資料結構的 Iterator 介面,因此只要具有 Iterator 介面的物件,都可以使用擴充套件運算子,比如 Map 結構。
// 陣列是複合的資料型別,直接複製的話,只是複製了指向底層資料結構的指標,而不是克隆一個全新的陣列
const a1 = [1,1];
const a2 = a1;
console.log(a2[0] = 2 // 2
console.log(a1) //2,1
console.log(a2) //2,1
//合併陣列
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合併陣列
console.log(arr1.concat(arr2, arr3)) ;
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合併陣列
console.log([...arr1, ...arr2, ...arr3])
//-------------------------與解構的賦值組合生成陣列
// 與解構賦值合併生成陣列
const [f1, ...f2] = [1, 2, 3, 4, 5, 6];
console.log(f1);
console.log(...f2);
//將擴充套件運算子用於陣列賦值,只能放在引數的最後一位
// 報錯
const [...butLast, last] = [1, 2, 3, 4, 5];
// 報錯
const [first, ...middle, last] = [1, 2, 3, 4, 5];
//------------------------------把字串裝為陣列
console.log([...'hello_world'])
//凡是涉及到操作四個位元組的 Unicode 字元的函式,都有這個問題。因此,最好都用擴充套件運算子改寫。
function length(str) {
return [...str].length;
}
length('x\uD83D\uDE80y') // 3
//凡是涉及到操作四個位元組的 Unicode 字元的函式,都有這個問題。因此,最好都用擴充套件運算子改寫。
let str = 'x\uD83D\uDE80y';
str.split('').reverse().join('')
// 'y\uDE80\uD83Dx'
[...str].reverse().join('')
// 'y\uD83D\uDE80x'
//------------------------------------------任何 Iterator 介面的物件,都可以用擴充套件運算子轉為真正的陣列
//querySelectorAll方法返回的是一個NodeList物件。它不是陣列,而是一個類似陣列的物件。這時,擴充套件運算子可以將其轉為真正的陣列,原因就在於NodeList物件實現了 Iterator 。
let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
//對於那些沒有部署 Iterator 介面的類似陣列的物件,擴充套件運算子就無法將其轉為真正的陣列。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
//arrayLike是一個類似陣列的物件,但是沒有部署 Iterator 介面,擴充套件運算子就會報錯。這時,可以改為使用Array.from方法將arrayLike轉為真正的陣列。
let arr = Array.from(arrLike)
console.log(arr);
//Array.from方法用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括 ES6 新增的資料結構 Set 和 Map)
//只要具有iterate介面的物件,都可使用擴充套件運算子
let map = new Map([
[1,'no.1'],
[2,'no.2'],
]);
let arrMap = [...map];
console.log(arrMap)
console.log(...map.keys())
console.log(...map.values())
//generate函式執行後,返回一個遍歷器的物件,也可使用擴充套件運算子
const ge = function*() {
yield 1;
yield 2;
yield 3;
};
console.log([...ge()]);
//變數ge是一個 Generator 函式,執行後返回的是一個遍歷器物件,對這個遍歷器物件執行擴充套件運算子,就會將內部遍歷得到的值,轉為一個數組。
-
Array.from()
用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括 ES6 新增的資料結構 Set 和 Map)
用於將兩類物件轉為真正的陣列:類似陣列的物件和可遍歷的物件(包含ES6新增的Set和Map)
常見的類似陣列的物件是DOM操作後返回的NodeList集合,以及函式內部的argunments物件物件
只要部署了Iterator介面的資料結構,Array.from()都能將其轉化為真正的陣列
Arrry.from主要解決沒有部署iterate介面,擴充套件運算子 … 就不能轉化為陣列的問題擴充套件運算子背後呼叫的是遍歷器介面(Symbol.iterator),如果一個物件沒有部署這個介面,就無法轉換。
Array.from方法還支援類似陣列的物件。所謂類似陣列的物件,本質特徵只有一點,即必須有length屬性。
因此,任何有length屬性的物件,都可以通過Array.from方法轉為陣列,而此時擴充套件運算子就無法轉換。
可以接收第二個引數,作用類似於陣列的map方法,用於對每個元素進行處理,將處理後的值放入返回的陣列。
返回各種資料型別
總結:
1,只要有一個原始的資料結構,你就可以先對它的值進行處理,然後轉成規範的陣列結構,進而就可以使用數量眾多的陣列方法。
2,rray.from()的另一個應用是,將字串轉為陣列,然後返回字串的長度。
因為它能正確處理各種 Unicode 字元,可以避免 JavaScript 將大於\uFFFF的 Unicode 字元,算作兩個字元的 bug。
let arrLike = {
'0' : 'a1',
'1' : 'a2',
'2' : 'a3',
'length' : 3,
};
var arrEs5 = [].slice.call(arrLike);
let arrEs6 = Array.from(arrLike);
console.log("es5:"+arrEs5)
console.log("es6:"+arrEs6)
//NodeList物件
let nlObject = document.querySelectorAll('hello');//返回類似陣列的物件(array-like object)
let nls = Array.from(nlObject).filter( p => {
return p.textContent.length > 100;
});
console.log(nls)
//函式的arguments物件
function argsObject() {
var args = Array.from(arguments);
console.log(args)
}
//部署了Iterator介面
console.log(Array.from('hello,小明!'))
let namesSet = new Set(['set1','set2','set3'])
console.log(Array.from(namesSet))
//只要有length屬性的物件,都可以通過array.from方法轉化為陣列
//Array.from返回了一個具有2個成員的陣列,每個位置的值都是undefined。擴充套件運算子轉換不了這個物件
console.log(Array.from({length:2}))
//對於還沒有部署該方法的瀏覽器,可以用Array.prototype.slice方法替代。
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
console.log(toArray)
//====================== 接收第二個引數,類似map方法
//Array.from(arrayLike, x => x * x);
// 等同於
//Array.from(arrayLike).map(x => x * x);
//Array.from可以接受第二個引數,作用類似於陣列的map方法,用來對每個元素進行處理,將處理後的值放入返回的陣列。
let arrMap = Array.from([1, 2, 3], (x) => x * x)
console.log(arrMap)// [1, 4, 9]
//取出一組DOM節點的文字內容
let spans = document.querySelectorAll('span.name');
// map()
let names1 = Array.prototype.map.call(spans, s => s.textContent);
console.log(names1)
// Array.from()
let names2 = Array.from(spans, s => s.textContent)
console.log(names2)
//返回資料型別
function typesOf () {
return Array.from(arguments, value => typeof value)
}
console.log(typesOf(null, [], NaN)) //["object", "object", "number"]
//第一個引數指定第二個引數的執行次數
console.log(Array.from({length:3},() => 'hello'))//["hello", "hello", "hello"]
//將字串轉為陣列,然後返回字串的長度
//理由:能夠正確的處理各種Unicode字元,可以避免JS將大於\uFFFF的Unicode字元算作兩個字元的bug。
function countSymbols(string) {
return Array.from(string).length;
}
console.log(countSymbols(''))//0
- Array.of
Array.of 將一組值轉換為陣列
該方法主要用於彌補陣列建構函式Array()的不足,因為引數的不同,會導致Array()的行為有差異
只有引數不少於2個是,才會返回一個新的陣列,如果只有一個時,其實是指定了陣列的長度
可以用來替換Array()或new Array(),並且不會因引數不同而導致過載。
總是返回引數值組成的陣列,如果沒有引數,就返回一個空陣列。
//把一組值轉化為陣列
console.log(Array.of(1,2,3,4))//(4) [1, 2, 3, 4]
console.log(Array.of(2).length)//1
console.log(Array.of().length)//0
//只有當引數個數不少於 2 個時,Array()才會返回由引數組成的新陣列。引數個數只有一個時,實際上是指定陣列的長度。
console.log(Array.of()) // []
console.log( Array.of(undefined)) // [undefined]
console.log(Array.of(1)) // [1]
console.log(Array.of(1, 2)) // [1, 2]
- 陣列例項的 copyWithin()
陣列例項的copyWithin方法,在當前陣列內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前陣列。也就是說,使用這個方法,會修改當前陣列。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個引數。
target(必需):從該位置開始替換資料。如果為負值,表示倒數。
start(可選):從該位置開始讀取資料,預設為 0。如果為負值,表示倒數。
end(可選):到該位置前停止讀取資料,預設等於陣列長度。如果為負值,表示倒數。
這三個引數都應該是數值,如果不是,會自動轉為數值。
console.log([1, 2, 3, 4, 5].copyWithin(0, 3))//將從 3 號位直到陣列結束的成員(4 和 5),複製到從 0 號位開始的位置,結果覆蓋了原來的 1 和 2。
// [4, 5, 3, 4, 5]
// 將3號位複製到0號位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相當於3號位,-1相當於4號位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 將3號位複製到0號位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 將2號位到陣列結束,複製到0號位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 對於沒有部署 TypedArray 的 copyWithin 方法的平臺
// 需要採用下面的寫法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
- 陣列例項的 find() 和 findIndex()
find:
陣列例項的find方法,用於找出第一個符合條件的陣列成員。它的引數是一個回撥函式,所有陣列成員依次執行該回調函式,直到找出第一個返回值為true的成員,然後返回該成員。如果沒有符合條件的成員,則返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
find方法的回撥函式可以接受三個引數,依次為當前的值、當前的位置和原陣列。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
陣列例項的findIndex:
返回第一個符合條件的陣列成員的位置,如果所有成員都不符合條件,則返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
console.log([10, 12, 26, 15].find(f, person)); // 26 find函式接收了第二個引數person物件,回撥函式中的this物件指向person物件。
總結:
兩個方法都可以接受第二個引數,用來繫結回撥函式的this物件。
都可以發現NaN,彌補了陣列的indexOf方法的不足。
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
- 陣列例項的 fill()
fill方法使用給定值,填充一個數組。
fill方法用於空陣列的初始化非常方便。陣列中已有的元素,會被全部抹去。
fill方法還可以接受第二個和第三個引數,用於指定填充的起始位置和結束位置
注意:如果填充物件的型別為物件,那麼被賦值的是同一個記憶體地址的物件,而不是深拷貝物件
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
//指定起始位置
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
//填充物件
let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]
let arr2 = new Array(3).fill([]);
arr2[0].push(5);
arr2
// [[5], [5], [5]]
- 陣列例項的 entries(),keys() 和 values()
ES6 提供三個新的方法——entries(),keys()和values()——用於遍歷陣列。它們都返回一個遍歷器物件(詳見《Iterator》一章),可以用for…of迴圈進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
如果不使用for…of迴圈,可以手動呼叫遍歷器物件的next方法,進行遍歷.
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
//用next進行遍歷
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']
-
陣列例項的 includes()
Array.prototype.includes方法返回一個布林值,表示某個陣列是否包含給定的值,與字串的includes方法類似。ES2016 引入了該方法。
該方法的第二個引數表示搜尋的起始位置,預設為0。如果第二個引數為負數,則表示倒數的位置,如果這時它大於陣列長度(比如第二個引數為-4,但陣列長度為3),則會重置為從0開始。 -
Map 和 Set 資料結構有一個has方法,與includes區別。
Map 結構的has方法,是用來查詢鍵名的,比如Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)。
Set 結構的has方法,是用來查詢值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)。
以前通常使用陣列的indexOf方法,檢查是否包含某個值的缺點:
一是不夠語義化,它的含義是找到引數值的第一個出現位置,所以要去比較是否不等於-1,表達起來不夠直觀。
二是,它內部使用嚴格相等運算子(===)進行判斷,這會導致對NaN的誤判。
[NaN].indexOf(NaN)
// -1
//includes使用的是不一樣的判斷演算法,就沒有這個問題。
[NaN].includes(NaN)
// true