ES6給數組增加的常用新特性
目錄(紅色爲相對來說比較重要的):
-
spread運算符
-
Array.from
-
Array.of
-
實例方法: copyWithin()
-
實例方法: findIndex() , find()
-
實例方法: fill()
-
實例方法: entries(), keys(), values()
-
實例方法: includes
spread運算符
在之前的專欄中我應該有跟大家提到過rest運算符...
, 在解構賦值中我們常用他來收集剩餘參數, 當時應該順帶提過一嘴, 這哥們還有個展開的作用, 而當他進行展開作用的使用時, 官方將它稱之爲擴展運算符(spread), 他的使用方法相對來說比較簡單所以我直接用實例, 相信你看到實例會恍然大悟(如果你之前有看過我關於解構的專欄的話)
spread運算符應用場景實例
數組的非地址拷貝
在ES5中, 如果我們要將一個數組拷貝給另一個數組且不僅僅是複製地址的話, 我們通常會使用數組的原型方法巧取新數組
var arr = ['fst', 'sec', 'trd'];
var fstArr = arr; // 這樣僅僅是在拷貝地址
console.log(fstArr === arr); // true
var secArr = arr.concat([]);
console.log(secArr === arr); //false
var trdArr = arr.slice();
console.log(trdArr === arr); //false
// 還有很多的獲取新數組的方法, 就不一一列舉了
而在ES6中, 我們可以通過擴展運算符來獲取一個新數組, 這樣的寫法會讓人覺得更加的舒服, 代碼的可讀性也大很多
const arr = ['fst', 'sec', 'trd'];
const newArr = [...arr]; // 將arr數組展開放進一個新數組中
console.log(arr === newArr); //false
轉化元素類數組
而這個擴展運算符的作用還不止於此, 我們可以通過擴展運算符將一個元素類數組(nodeList)轉化爲真正的數組
const divs = document.querySelectorAll('div');
console.log(divs.map); //由於是類數組, 所以有些數組上的方法我們並不能使用
const domArr = [...divs];
console.log(domArr.map); // 已經是真正的數組了所以可以在原型中找到map方法
而將元素類數組轉化爲數組, 在ES5中我們不得不這麼做
var divs = document.querySelectorAll('div');
const domArr = [].slice.call(divs);
一般的類數組和對象我們是沒辦法進行展開的, 因爲他們沒有部署迭代接口, 至於迭代接口是什麼我們在後面會有介紹, 如果你必須要將一個類數組轉化爲數組, 可以使用Array.from, 也是我們下一個要講的內容
數組的合併
在ES5中, 我們如果要合併兩個數組, 我們一般可能會這樣做
var arr = [1, 2, 3];
var arr2 = [4, 5, 6];
var newArr = arr.concat(arr2);
console.log(newArr); // [1, 2, 3, 4, 5, 6]
在ES6中, 擴展運算符讓一切變得特別的自然
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
const newArr = [...arr, ...arr2];
console.log(newArr); // [1, 2, 3, 4, 5, 6]
與解構的不解之緣(收集作用)
解構賦值跟...
運算符的巧妙使用在解構那一篇中我已經寫得比較詳細了, 這裏再提一嘴
在ES5中, 如果我們需要將一個數組拆分開, 把數組的第一位給一個變量, 而數組的剩餘所有數據都放進一個數組的話
var arr = ['loki', 'thor', 'monkey'];
var userName = arr[0];
var restArr = arr.slice(1);
console.log(userName, restArr); // loki ["thor", "monkey"]
而在ES6中, 我們可以將rest運算符跟解構聯合
const arr = ['loki', 'thor', 'monkey'];
const [ userName, ...restArr ] = arr;
console.log(userName, restArr); // loki ["thor", "monkey"]
關於解構跟...
運算符還有很多細節, 這裏不多說了, 可以去看我專欄中專門描述解構賦值的一章
將字符串轉化爲數組
在ES5中, 官方給我們提供了split方法來實現字符串轉換爲數組
var str = 'loki';
var arr = str.split('');
console.log(arr); // ['l', 'o', 'k', 'i']
ES6追求的就是一種簡潔自然, 乾淨清爽的狀態
const str = 'loki';
const arr = [...str];
console.log(arr); // ['l', 'o', 'k', 'i']
任何部署了迭代接口的數據結構都可以通過擴展運算符將其轉化爲真正的數組
在這塊的話, 可能你們還不是很瞭解迭代接口是什麼概念, 實際上元素類數組(nodeList)就是部署有迭代接口的, 所以我們可以將它直接轉化爲新的數組, 迭代接口在後面我會詳細介紹, 到時候也會提一嘴這裏的事兒
而如果沒有部署迭代接口的會直接報錯
比如一個對象
let obj = {
name: 'loki',
age: 18
}
const arr = [...obj]; // 報錯: TypeError: can't spread non-iterable object
Array.from(data, handle)
Array.from 致力於將兩種數據結構轉化爲真正的數組,Array.from 的第一個參數就是你想要轉化爲真數組的數據結構
- 類數組
在ES5中將一個類數組轉化爲數組的方法我在上面已經寫過了, [].slice.call, 同時spread運算符也只能展開帶有迭代接口的數據結構
那麼在ES6中, Array.from 也幫你自然的將一個類數組轉化爲數組
const likeArr = {
'0': 'loki',
'1': 'thor',
'length': 2
}
const newArr = Array.from(likeArr);
console.log(newArr); // ["loki", "thor"]
- 可遍歷對象, 包括ES6的Set和Map
ES6規定, 任何具有迭代接口的數據結構都可以被Array.from轉化爲真正的數組, 比如字符串
const str = 'hello';
const strArr = Array.from(str);
而至於ES6新的部署迭代接口的數據結構, 暫時你可以不必深究, 當你學習到那塊的時候自然會明白什麼是Set什麼是Map
Array.from 的第二個參數
Array.from的第二個參數類似於數組的map方法, 它允許你對你所傳入的數據結構中的每一項進行一定的處理以後再將處理後的值放入新的數組
const str = 'abc';
const strArr = Array.from(str, ele => ele + 1);
console.log(strArr); // ["a1", "b1", "c1"]
// 你可以把上面的寫法理解爲
// const strArr = Array.from(str).map(ele => ele + 1);
如果我們填入的第一個參數本身就是一個真實數組, 那麼 Array.from會返回給我們一個一模一樣但是地址不同的新數組
const arr = ['loki', 'thor'];
const newArr = Array.from(arr);
console.log(arr, newArr, arr === newArr);
// ["loki", "thor"], ["loki", "thor"], false
Array.of
Array.of這個方法主要是用來彌補ES6之前數組構造函數的缺陷
我們來看看ES5中我們使用構造函數的場景
var arr = new Array();
consle.log(arr); // []相對來說比較合理
var arr = new Array(1, 2, 3); // [1, 2, 3], 也比較合理
var arr = new Array(3); // [, , ,] 這就不太對了, 我其實是想數組就只有一位, 值爲3
看了上面的例子, 你應該就知道爲什麼我們幾乎都是用字面量建立數組了, 因爲真的很卑微, 於是ES6推出了Array.of來彌補開發者
const arr = Array.of(); // []
const secArr = Array.of(1); // [1]
const trdArr = Array.of(1, 2, 3); // [1, 2, 3]
實際上 Array.of的原理也很簡單的
Array.of = function() {
return [].slice.call(arguments);
}
copyWithin(target, start, end)
數組實例上的copyWithin方法就是把一個數組中的某一個索引位的值複製一遍, 然後賦值給另一個索引位,並改變該數組的原數組
這是我總結出來的話, 如果覺得看不懂沒關係, 不看了, 直接看使用方法, 看完使用方法就懂了
這個方法接收三個參數
-
target: 從該位置開始替換數據必填
-
start: 默認值0, 從該位置開始讀取數據, 如果爲-1就是倒數
-
end: 默認length, 讀取到該位置結束, 同理爲-1就是倒數
直接上實例:
const arr = ['loki', 'thor', 'diana', 'ben']
arr.copyWithin(0, 2, 4);
// 就是我去找數組第二位到第四位的值,
// 然後將這兩個值從第0位開始替換, 於是arr變成了如下
console.log(arr); // ['diana', 'ben','diana', 'ben']
這個方法相對來說沒那麼使用的頻繁, 我提出來也是讓你在以後遇到這個需求的時候腦子有這麼個印象說ES6有這麼個方法可以進行數組的替換
find() 和findIndex()
在ES5中我們要進行對於數組中數組元素的查找一般會使用indexOf, 或者filter之類的, 如下
var arr = ['loki', 'thor'];
let result = arr.indexOf('loki');
console.log(result); // 返回索引 - 0
而在ES6中, Es6給我們提供了更加精準的尋找內容的方法, 說白話這兩哥們就是ES6拿出來幫你找人的, 咱來看看他們是咋找的人
find(func)
find用於尋找到數組中第一個符合條件的成員並返回成員值, 他接收一個參數, 該參數爲一個函數, 如果你用過字符串的filter, sort方法的話應該能夠聯想到, 參數函數會有一個返回值用來返回尋找的規則
const arr = ['loki', 'thor'];
const value = arr.find(ele => ele === 'loki');
console.log(value); // 返回loki
const numberArr = [1, 2, 3];
const number = numberArr.find(ele => ele > 1);
console.log(number); // 2
// 記住這哥們是找到符合條件的第一個, filter是找到符合條件的所有
findIndex(func)
findIndex無論是參數還是使用方式都與find一致, 兩人唯一的區別也就是find是將成員值返回, 而findIndex是返回成員索引
const arr = ['loki', 'thor'];
const value = arr.findIndex(ele => ele === 'loki');
console.log(value); // 返回索引值 - 0
const numberArr = [1, 2, 3];
const number = numberArr.findIndex(ele => ele > 1);
console.log(number); // 返回的是索引值 - 1
// 記住這哥們是找到符合條件的第一個, filter是找到符合條件的所有
同時這兩哥們都可以找到NaN, 而在過去的indexOf中並不能辦到
[NaN].indexOf(NaN); // -1
[NaN].find(NaN); // NaN
[NaN].findIndex(NaN); // 0
至於實現原理的話我想不用多說吧, 字符串轉化
再提一嘴的就是, 這個兩個方法都可以接收第二個參數用來綁定第一個回調函數的this指向, 但是這個用到的實在是少
fill()
數組實例上的fill方法用於填充數組元素, 用給定參數值給調用它的數組填充值
let arr = [];
arr.fill('thor');
console.log(arr); // ["thor"]
let secArr = new Array(3).fill('loki')
console.log(secArr); // ["loki", "loki", "loki"]
這個方法還可以接收第二個參數, 用於確認填充起始位置和結束位置, 比如我現在有個需求, 這裏有一個數組我需要將其中的第三項改爲’hellWorld’, 操作如下
let arr = ['loki', 'thor', 'monkey'];
arr.fill('helloWorld', 2, 3);
console.log(arr); //["loki", "thor", "helloWorld"]
這個方法也是瞭解一下就好, 至少以後遇到這種情況你知道ES6還有一個可以進行填充的方法
entries(), keys(), values()
es6提供了entries, keys, values這三個全新的方法給我們用來遍歷數組, 這三個方法都會返回一個遍歷器對象, 什麼是遍歷器對象以後我會介紹, 當下混個臉熟就好, 而這個遍歷器對象可以用for…of進行循環取值
直接上實例吧, 看得懂的就看着, 看不懂的先混個臉熟
const arr = ['loki', 'thor'];
const entriesResult = arr.entries();
const keyResult = arr.keys();
const valueResult = arr.values();
console.log(entriesResult, keyResult, valueResult);
遍歷器對象就是下面這樣, 看不懂也沒關係, 不看了, 你就知道這三個新方法會返回一個這樣的哥們, 這哥們必須用forof來循環, 不同方法的循環結果也會不一樣, 隨着知識的增多自然就懂了
得到這個遍歷器對象以後我們可以用for…of來循環他,
const arr = ['loki', 'thor'];
const keyResult = arr.keys();
for(let key of keyResult) {
// 這個循環會依次輸出0, 1 代表索引
consolo.log(key); // 依次輸出 0 , 1
}
const valueResult = arr.values();
for(let value of valueResult) {
// 這個循環會依次輸出loki, thor 代表數組每一項的值
console.log(value);
}
const entriesResult = arr.entries();
for(let entry of entriesResult) {
// 這個循環依次輸出{0, 'loki'}, {1, 'thor'} 代表數組每一項索引和值的對象
console.log(entry);
}
對於遍歷器對象, 如果我們不用for…of循環則要調用它的next方法進行遍歷
const arr = ['loki', 'thor'];
const keyResult = arr.keys();
console.log(keyResult.next().value); // 0
console.log(keyResult.next().value); // 1
也混個臉熟, 至於他爲什麼這樣搞, 降到迭代器那塊兒我會詳細介紹, 這裏你只需要知道數組上有這幾個方法是幹嘛的就ok
includes(value, startIndex)
關於includes這個方法呢, 如果你知道字符串上的includes方法, 那這個實例方法其實你很好理解
該方法返回一個布爾值, 表示某個數組中是否包含給定的值
該方法的第一個參數代表要檢索的值
const arr = ['loki', 'thor'];
arr.includes('loki'); //返回true
arr.includes('archer'); // 返回false
該方法的第二個值表示從哪個位置開始搜索,默認值爲0索引位, 而如果第二個參數小於0, 則是倒數, 而如果填的小於0的參數的絕對值大於數組長度則會按照0操作
const arr = ['loki', 'thor', 'thor'];
arr.includes('loki', 1); //false 因爲索引位1的值是thor, 而thor後面無值了
arr.includes('thor', -1); // true
arr.includes('loki', -10); //true, 絕對值大於了長度所以會歸0
同時inlcudes方法也支持對於NaN的檢索
[NaN].includes(NaN); //true