全面認識JavaScript的Array對象
首先一個是對JavaScript中Array的理解:JavaScript中函數是一等公民,寫在代碼中的 Array/Object/String/Number/Function
其實都是一個構造函數,是用來生成相應的數據類型的變量的。
數組描述:數組是一種類列表對象,JS數組的長度和元素類型都是非固定的。因為數組的長度可隨時改變,并且其數據在內存中也可以不連續,所以JavaScript數組不一定是密集型的,這取決于它的使用方式。
只能用整數作為數組元素的索引,而不能使用字符串。使用非整數并通過方口號或點號來訪問或設置數組元素時,所操作的并不是數組列表中的元素,而是數組對象的屬性集合上的變量。數組對象的屬性和數組元素列表是分開存儲的,并且數組的遍歷和修改操作也不能作用于這些命名屬性。
幾點注意事項
1、雖然數組可以看做是數組對象的屬性,但是不能用點號引用數組元素,因為在JavaScript中,以數字開頭的屬性不能用點號引用,必須用方括號。
console.log(years.0);//錯誤
console.log(years[0]);//正確
//假如render對象中有一個名為3d的屬性
render.3d//錯誤
render['3d']//正確,引號是必須的
示例:arr[2]
這樣的可以寫成arr['2']。arr[2]中的2會被JavaScript解釋器通過調用toString隱式地轉換成字符串。所以arr['2']
和arr['02']
在arr中引用的可能不是同一個位置上的元素,而arr[2]
和arr[02]
訪問的是同一個位置上的元素。下面是一個示例圖:
2、使用一個合法的下標為數組元素賦值,如果該下標超出了當前數組的大小,解釋器會同時修改length的值。可以手動為length賦值,賦值小于當前數組元素個數的時候會刪除一部分元素。
========== 屬性 ==========
1、length:length是Array的實例屬性。返回或設置一個數組中的元素個數。該值是一個無符號32-bit整數(0到2^32-1),并且總是大于數組最高項的下標。
2、prototype:前面說到Array在代碼中是一個函數,也就是數組對象的構造函數,所以它有自己的原型,也就是prototype。Array實例繼承自Array.prototype。與所有構造函數一樣,我們可以更改構造函數的原型對象,以對所有Array實例進行更改。Array.prototype本身也是一個Array
========= 方法 ==========
1、Array.from(),從一個類似數組或可迭代對象中創建一個新的數組實例。
語法:
Array.from(arrayLike[,mapFn,[,thisArg]]);
//參數
arrayLike //想要轉換成數組的偽數組對象或可迭代對象。
//偽數組對象:擁有一個length屬性和若干索引屬性的任意對象
//可迭代對象:可以獲取對象中的元素,如Map和Set等
mapFn //如果指定了該參數,新數組中的每個元素會執行一遍該回調函數
thisArg //執行回調函數mapFn時this對象
//返回值:一個新的數組實例
示例:
Array.from('foo');//['f','o','o']
Array.from(new Set(['foo',window]));//['foo',window]
Array.from(new Map([[1,2],[2,4],[4,8]]));//[[1,2],[2,4],[4,8]]
function f(){
console.log(Array.isArray(arguments)); //false
return Array.from(arguments,function(val,index){
console.log(val);//1,2,3
console.log(index);//0,1,2
return val++
});
}
f(1,2,3); // 2,3,4
注:Array.from()本身是淺拷貝,如果數組的某個元素是對象或數組等,拷貝過去的是一個指向。
2、Array.isArray(),用于確定傳遞的值是否是一個Array。
語法:
Array.isArray(obj);
// 參數
obj //需要檢測的值
// 返回值:如果對象是Array,則為true;否則為false
// 當檢測Array實例時, Array.isArray 優于 instanceof,因為Array.isArray能檢測iframes.
let iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
Array.isArray(arr);//true
arr instanceof Array;//false
3、Array.of(),創建一個具有可變數量參數的新數組實例,而不考慮參數的數量和類型。
語法:
Array.of(element0[, element1[, ...[, elementN]]])
// 參數
elementN //任意個參數,將按順序成為返回數組中的元素
// 返回值:新的Array實例
Array.of(7);//[7]
Array.of(1,2,3);//[1,2,3]
Array(7); //[,,,,,,]
Array(1,2,3);//[1,2,3]
========= 實例方法 =========
這些方法可以直接在數組實例上使用,故整理為幾個大類(自建,無任何官方說明)。
第一類,增刪改查:push/pop/shift/unshift/splice/slice/concat/copyWithin/fill。
1.push(),將一個或多個元素添加到數組的末尾,并返回該數組的新長度。
語法
arr.push(element1,...,elementN);
// 參數
elementN //被添加到數組末尾的元素
// 返回值:當調用該方法時,新的length屬性值將被返回
push 方法有意具有通用性。該方法和 call() 或 apply() 一起使用時,可應用在類似數組的對象上。push 方法根據 length 屬性來決定從哪里開始插入給定的值。如果 length 不能被轉成一個數值,則插入的元素索引為 0,包括 length 不存在時。當 length 不存在時,將會創建它。
示例一:合并兩個數組
let arr = [1,2];
let arr1 = [3,4];
Array.prototype.push.apply(arr,arr1);//相當于:arr.push(3,4);
console.log(arr);//[1,2,3,4]
//注意第二個數組太大時不要使用該方法,應為一個函數能夠接受的參數個數是有限制的。
示例二:像數組一樣使用對象
let obj = {
length : 0,
addElem: function addElem(elem){
[].push.call(this,elem);
}
};
obj.addElem({});
obj.addElem({});
console.log(obj);
// 盡管 obj 不是數組,但是 push 方法成功地使 obj 的 length 屬性增長了,就像我們處理一個實際的數組一樣
2.unshift(),將一個或多個元素添加到數組的開頭,并返回該數組的新長度。
語法:
arr.unshift(element1,...,elementN);
// 參數
elementN //要添加到數組開頭的元素
// 返回值:當一個對象調用該方法時,返回其length屬性值。
unshift 特意被設計成具有通用性;這個方法能夠通過 call 或 apply 方法作用于類數組對象上。不過對于沒有 length 屬性(代表從0開始的一系列連續的數字屬性的最后一個)的對象,調用該方法可能沒有任何意義。
上面push的示例一和示例二都適用于unshift,只是參數放在數組前插入。
3.shift(),從數組中刪除第一個元素,并返回該元素的值。
語法:
arr.shift();
// 返回值:從數組中刪除的元素;如果數組為空則返回undefined。
shift方法移除索引為0的元素(即第一個元素),并返回被移除的元素,其他元素的索引值隨之減一。如果length屬性的值為0(長度為0),則返回undefined。
shift方法并不局限于數組:這個方法能夠通過 call 或 apply 方法作用于類似數組的對象上。但是對于沒有 length 屬性(從0開始的一系列連續的數字屬性的最后一個)的對象,調用該方法可能沒有任何意義。
4.pop(),刪除數組中的最后一個元素,并返回該元素的值。
語法:
arr.pop();
// 返回值:從數組中刪除的元素(當數組為空時返回undefined)。
pop 方法從一個數組中刪除并返回最后一個元素。
pop 方法有意具有通用性。該方法和 call() 或 apply() 一起使用時,可應用在類似數組的對象上。pop方法根據 length屬性來確定最后一個元素的位置。如果不包含length屬性或length屬性不能被轉成一個數值,會將length置為0,并返回undefined。
5.slice(),返回一個新的數組對象,這個對象是一個由begin和end(不包含end)決定的的原數組的淺拷貝。原始數組不會被改變。
語法:
arr.slice(begin,end);
// 參數
begin //可選,如果省略begin,則從0開始提取。如果該參數為負數,則表示從原數組中的倒數第幾個元素開始提取。
end // 可選,如果省略end,則slice會一直提取到原數組末尾。如果該參數為負數,則它表示在原數組中的倒數第幾個元素結束抽取。
// 返回值:一個含有提取元素的新數組
描述:slice不修改原數組,只會返回一個淺復制了原數組中的元素的一個新數組。原數組的元素會按照下述規則拷貝(即淺拷貝):
- 如果該元素是個對象引用(不是實際的對象),slice會拷貝這個對象引用到新的數組里。兩個對象引用都引用了同一個對象。如果被引用的對象發生改變,則新的和原來的數組中的這個元素也會發生改變。
- 對于字符串、數字及布爾值來說(不是 String、Number 或者 Boolean 對象),slice 會拷貝這些值到新的數組里。在別的數組里修改這些字符串或數字或是布爾值,將不會影響另一個數組。
示例一:返回現有數組的一部分
let fruits = ['Banana','Orange','Lemon','Apple','Mango'];
let citrus = fruits.slice(1,3);
console.log(citrus);//['Orange','Lemon']
console.log(fruits);//['Banana','Orange','Lemon','Apple','Mango']
示例二:類數組對象
function list(){
return Array.prototype.slice.call(arguments,0,2);
}
let list1 = list(1,2,3);
console.log(list1);//[1,2]
6.splice(),方法通過刪除現有元素和/或添加新元素來修改數組,并以數組返回原數組中被修改的內容。
語法:
array.splice(start,[,deleteCount[,item1[,item2[,...]]]]);
// 參數
start //指定修改的開始位置(從0計數),刪除的時候包含該下標元素。
// ① 如果超出了數組的長度,則從數組末尾開始添加內容
// ② 如果是負值,則表示從數組末位開始的第幾位(從-1計數)
// ③ 如果負數的絕對值大于數組的長度,則表示開始位置為第0位
deleteCount //可選,整數,表示要移除的數組元素的個數。
// ① 如果deleteCount是0或者負數,則不移除元素。這中情況下,至少應該添加一個元素
// ② 如果deleteCount大于start之后的元素的總數,則從start后面的元素都將被刪除(含第start位)
// ③ 如果deleteCount被省略,則其相當于(arr.length-start)
item1,item2,... //可選,要添加進數組的元素,從start位置開始。如果不指定,則splice()將只刪除數組元素。
// splice方法使用deleteCount參數來控制是刪除還是添加:
// start參數是必須的,表示開始的位置(從0計數),如:start=0從第一個開始;start>=array.length-1表示從最后一個開始。
// ① 從start位置開始刪除[start,end]的元素
arr.splice(start);
// ② 從start位置開始刪除[start,count]的元素
arr.splice(start,deleteCount);
// ③ 從start位置開始添加item1,item2,...元素
arr.splice(start,0,item1,item2,...);
// 返回值:由被刪除的元素組成的一個數組。如果只刪除了一個元素,則返回只包含一個元素的數組。如果沒有刪除元素,則返回空數組。
// 如果添加進數組的元素個數不等于被刪除的元素個數,數組的長度會發生相應的改變。
示例
let myFish = ['angel','clown','mandarin','surgeon'];
let removed = myFish.splice(2,0,'drum');
console.log(myFish);//["angel", "clown", "drum", "mandarin", "surgeon"]
console.log(removed);//[]
let replaced = myFish.splice(2,1,'hahha');
console.log(myFish);// ["angel", "clown", "hahha", "mandarin", "surgeon"]
console.log(replaced);//["drum"]
let deleted = myFish.splice(-1,1);//相當于 myFish.splice(myFish.length-1,1);
console.log(myFish);//["angel", "clown", "hahha", "mandarin"]
console.log(deleted);//["surgeon"]
let one = myFish.splice(1);
console.log(myFish);//["angel"],可以看出下標為1的元素也被刪除
console.log(one);//["clown", "hahha", "mandarin"]
7.concat(),方法用于合并兩個或多個數組。此方法不會改變現有數組,而是返回一個新數組。
語法:
let new_array = old_array.concat(value1[,value2[,value3[,...[,valueN]]]]);
// 參數
valueN //將數組和/或值連接成新數組。
// 返回值:新的Array實例
concat方法創建一個新的數組,它由被調用的對象中的元素組成,每個參數的順序依次是該參數的元素或元素本身。它不會遞歸到嵌套數組參數中。
concat方法不會改變this或任何作為參數提供的數組,而是返回一個淺拷貝(參考slice中的描述)。
示例:
let num1= [1,2,3],
num2= ['a','b','c'];
let nums = num1.concat('hah',num2);
console.log(num1);//[1, 2, 3]
console.log(num2);//["a", "b", "c"]
console.log(nums);//[1, 2, 3, "hah", "a", "b", "c"]
let num3 = ['yao',['peng','kun']];
let res = num1.concat(num2,num3);
console.log(num3);//["yao", ["peng","kun"]],標記一
console.log(res);//[1, 2, 3, "a", "b", "c", "yao", ["peng","kun"]]
res[7][0]="666";
console.log(num3);//["yao", ["666","kun"]]
console.log(res);//[1, 2, 3, "a", "b", "c", "yao", ["666","kun"]]
// 這里有個奇怪的現象,當我們沒有寫res[7][0]="666";的時候,“標記一”處顯示的是["yao",Array(2)];點開array(2)里面是["peng","kun"],而如果我們在后面修改了,再次點開“標記一”處console出的Array(2)顯示的是["666","kun"]。
// 個人猜測:控制臺中打印的東西,如果需要通過點擊再顯示出來的,會在點擊的時刻去內存中獲取,所以指向型的數據,后面修改會影響上面的打印結果。(可以通過轉為string避免這樣的地址指向即時獲取,如:console.log(JSON.stringify(num3)))
8.copyWithin(),方法淺復制數組的一部分到同一數組中的另一個位置,并返回它,而不修改其大小。
語法:
arr.copyWithin(target[,start[,end]]);
// 參數
target //0為基底的索引,復制序列到該位置。如果是負數,target將從末尾開始計算。
// 如果target大于等于arr.length,將會不發生拷貝。如果target在start之后,復制的序列將被修改以符合arr.length。
start //0為基底的索引,開始復制元素的起始位置。如果是負數,start將從末尾開始計算。
// 如果start被忽略,copyWithin將會從0開始復制
end // 0為基底的索引,開始復制元素的結束位置。copyWithin將會拷貝到該位置,但不包括end這個位置的元素。如果是負數,end將從末尾開始計算。
// 如果end被忽略,copyWithin將會復制到arr.length。
//返回值:改變了的數組
參數target,start,end必須為正數。
copyWithin方法不要求其this值必須是一個數組對象;除此之外,copyWithin是一個可變方法,它可以改變this對象本身,并且返回它,而不僅僅是它的拷貝。
copyWithin函數的設計為通用的,其不要求其this值必須是一個數組對象。
示例:
[1,2,3,4,5].copyWithin(-2);//[1,2,3,1,2]
[1,2,3,4,5].copyWithin(0,3);//[4,5,3,4,5]
[1,2,3,4,5].copyWithin(0,3,4);//[4,2,3,4,5]
[1,2,3,4,5].copyWithin(-2,-3,-1);//[1,2,3,3,4]
[].copyWithin.call({length:5,3:1},0,3);//{0:1,3:1,length:5}
9.fill(),方法用一個固定值填充一個數組中從起始索引到終止索引內的全部元素。不包括終止索引。
語法:
arr.fill(value[,start[,end]]);
// 參數
value // 用來填充數組元素的值,為對象時填充的是對象的引用
start // 可選,起始索引,默認值為0
end //可選,終止索引,默認值為this.length
//start和end都可以是負數,計算方法均為:length+start/end
// 返回值:修改后的數組
fill方法故意設計為通用方法,該方法不要求this是數組對象
示例:
[1,2,3].fill(4);//[4,4,4]
[1,2,3].fill(4,1);//[1,4,4]
[1,2,3].fill(4,1,2);//[1,4,3]
[1,2,3].fill(4,1,1);//[1,2,3]
[1, 2, 3].fill(4, 3, 3);// [1, 2, 3]
[1, 2, 3].fill(4, -3, -2);// [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5);// [1, 2, 3]
Array(3).fill(4);// [4, 4, 4]
Array(3).fill(4,1);// [empty, 4, 4]
[].fill.call({ length: 3 }, 4); // {0: 4, 1: 4, 2: 4, length: 3}
[].fill.call({ length: 3 }, 4,1); // {1: 4, 2: 4, length: 3}
let obj = {};
let arr = Array(3).fill(obj);
arr[0].hi='hi';
console.log(arr);//[{hi:'hi'},{hi:'hi'},{hi:'hi'}]
第二類,遍歷數組
1. forEach/map/filter/every/some/find/findIndex 這七個方法有相似的語法:
//fn表示“forEach/map/filter/every/some/find/findIndex”中的任意一個
arr.fn(function callback(currentValue[,index[,array]]){
//執行一些操作,并返回一個值
}[,thisArg]);
// 參數
callback // 生成新數組元素的函數,使用三個參數:
currentValue // 數組中正在處理的當前元素的值
index // 可選,數組中正在處理的當前元素的索引
array // 可選,fn方法被調用的數組
thisArg //可選,執行callback函數時使用的this值
// 如果 thisArg 參數有值,則每次 callback 函數被調用的時候,this 都會指向 thisArg 參數上的這個對象。
// 如果省略了 thisArg 參數,或者賦值為 null 或 undefined,則 this 指向全局對象 。
// callback 函數最終可觀察到 this 值,這取決于函數觀察到 this 的常用規則。
// 如果callback是箭頭函數,則thisArg參數無效
下面是幾點需要格外注意的點:
- ① 這七個方法不修改調用它的原數組本身(當然可以在callback執行時改變原數組)。
- ② 使用這七個方法處理數組時,數組元素的范圍是在callback方法第一次調用之前就已經確定了。在方法執行的過程中:原數組中新增加的元素將不會被 callback 訪問到;若已經存在的元素被改變或刪除了,則它們的傳遞到 callback 的值是方法遍歷到它們的那一時刻的值;如果已訪問的元素在迭代時被刪除了(例如使用 shift()),某個元素將被跳過。即這些方法不會在調用之前創建數組的副本。
- ③ forEach/map/filter/every/some在執行過程中,那些已刪除或者未初始化的項將被跳過(例如在稀疏數組上),而find/findIndex在執行過程中會調用每個索引,而不僅僅是那些被賦值的索引,這意味著對于稀疏數組來說,這兩個方法的效率要低。
- ④ forEach/map/filter/every/some對于被刪除的元素將不會被訪問到,而find/findIndex仍舊會被訪問到被刪除的元素
forEach(),按升序為數組中含有有效值的每一項執行一次callback函數。
forEach(),沒有辦法終止或者跳出循環,除非拋出一個異常(這種使用方法明顯是錯誤的)。如果需要提前終止循環可以使用:簡單循環、for...of
、every
、some
、find
、findIndex
。
示例:
function logArrayElements(element,index,array){
console.log('a['+index+']='+element);
}
let arr=[2,3,,null,undefined,9];
arr.length=10;
arr.forEach(logArrayElements);
console.log(arr);
結果:可以看到索引2被跳過了,null/undefined
還是會被遍歷,多余的empty
也不會被遍歷
示例:使用thisArg
function Counter(){
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array){
array.forEach(function(entry){
this.sum +=entry;
++this.count;
},this);
}
let obj = new Counter();
obj.add([1,3,5,6]);
console.log(obj.count);//4
console.log(obj.sum);//15
示例:對象復制函數
//創建一個給定對象的副本
function copy(obj){
let copy = Object.create(Object.getPrototypeOf(obj));
let propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name){
let desc = Object.getOwnPropertyDescriptor(obj,name);
Object.defineProperty(copy,name,desc);
});
return copy;
}
let obj1 = { a:1,b:2};
let obj2 = copy(obj1);
console.log(obj1);//{a: 1, b: 2}
console.log(obj2);//{a: 1, b: 2}
示例:如果數組在迭代時被修改了,則其他元素會被跳過
let words = ['one','two','three','four'];
words.forEach(function(word){
console.log(word);
if(word === 'two'){
words.shift();
}
});
// one 、 tow 、 three
// 上面的代碼輸出one/two/four。當到達包含two的項時,數組的第一個項被移除了,這導致所有剩下的項上移一個位置。因為元素four現在在數組更前的為孩子,three會被跳過。
map(),創建一個新數組,其結果是該數組中的每個元素都調用一個提供的函數后返回的結果。
返回一個新數組,每個元素都是回調函數的結果。
示例:
let numbers = [1,4,9];
let roots = numbers.map(Math.sqrt);//Math.sqrt只接受一個參數,會自動忽略map傳遞的后兩個參數
console.log(numbers);//[1, 4, 9]
console.log(roots);//[1, 2, 3]
// String上使用map放會獲取每一個字符
let res = Array.prototype.map.call('Hello World',function(x){
console.log(x);
return x.charCodeAt(0);//返回字符對應的ASCII碼
});
console.log(res);//[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
// 注意:map的回調函數會接受三個參數,在調用中需注意接受多個參數的函數,如parseInt
// map中經常使用箭頭函數
[].map((value,index,arr)=>{});//適用多種形式,此時thisArg參數無效,箭頭函數沒有this
filter(),創建一個新數組,其包含通過所提供函數實現的測試的所有元素。
filter為數組中的每個元素調用一次callback函數,并利用所有使得callback返回true或等價于true的值的元素創建一個新數組。
返回一個新的通過測試的元素的集合的數組,如果沒有通過測試則返回空數組
示例:
// 實現一個簡單的搜索
let fruits = ['apple','banana','grapes','mango','orange'];
function filterItems(query){
return fruits.filter(function(el){
return el.toLowerCase().indexOf(query.toLowerCase())>-1;
})
}
console.log(filterItems('ap'));// ["apple", "grapes"]
console.log(filterItems('an'));// ["banana", "mango", "orange"]
console.log(fruits);// ["apple", "banana", "grapes", "mango", "orange"]
every(),測試數組的所有元素是否都通過了指定函數的測試。
every方法為數組中的每個元素執行一次callback函數,直到它找到一個使callback返回false(表示可轉換為布爾值false的值)的元素。如果發現了一個這樣的元素,every方法將會立即返回false。否則,callback為每一個元素返回true,every就會返回true。
every和數學中的“所有”類似,當所有的元素都符合條件才返回true。另外,空數組也是返回true。(空數組中所有元素都符合給定的條件,注:因為空數組沒有元素)。
示例:
function isBigEnough(element,index,array){
console.log(element);// 12 、 130 、5
return (element >= 10);
}
let passed = [12,130,5,8,44].every(isBigEnough);
console.log(passed);// false
some(),測試是否至少有一個元素通過提供的函數實現的測試。
some()為數組中的每一個元素執行一次callback函數,直到找到一個使得callback返回一個真值。如果找到了這樣一個值,some()將會立即返回true。否則some()返回false。
示例:
function isBigEnough(element,index,array){
console.log(element);// 5 、 8 、12
return (element >= 10);
}
let passed = [5,8,12,3,130,44].some(isBigEnough);
console.log(passed);// true
find(),對數組中的每一項元素執行一次callback函數,直至有一個callback返回true。當找到了這樣一個元素后,該方法會立即返回這個元素的值,否則返回undefined。
示例:查找數組中的質數
function isPrime(element,index,array){
console.log(index);
let start = 2;
while(start <= Math.sqrt(element)){
if(element % start++ < 1){
return false
}
}
return element > 1;
}
console.log('res::'+[4,6,8,12].find(isPrime));
console.log('-----------')
console.log('res::'+[4,5,8,12].find(isPrime));
結果:可以看到當找到一個質數(5)后,便結束執行。
示例:當在回調中刪除數組中的一個值時,當訪問到這個位置時,其傳入的值為 undefined
let a=[0,1,,,,5,6];
a.find(function(value,index){
console.log('index::'+index+'& value::'+value);
});
console.log('----刪除后面的某個元素-----')
a.find(function(value,index){
if(index == 0){
console.log('del a[5]=='+a[5]);
delete a[5];
}
console.log('index=='+index+'& value == ' + value);
});
console.log('----刪除前面的某個元素----')
let arr=[0,1,2,3,4,5]
arr.find(function(value,index){
console.log('index=='+index+'& value == ' + value)
if(index == 2){
arr.shift();
}
});
結果:可以看到,無論未初始化的還是之后被刪除的都遍歷,值都為undefined。而在執行過程中刪除元素,find還是會按照原來的索引去找當前的索引對應的值(索引3對應成了4),即使數組長度已經不夠,也會訪問到初始化時的最后一個索引(索引5對應成了undefined)。
findIndex(),對數組中的每個數組索引0~length-1執行一次callback函數,知道找到一個callback函數返回真值。如果找到這樣的元素findIndex會立即返回該元素的索引。如果回調不返回真值,或者數組的length為0,則findIndex返回-1。
執行規則基本與find相同,只是find返回的是值,而findIndex返回的是索引。
2. reduce/reduceRight方法與上面七個方法的語法類似,但有所不同,語法如下:
//fn代替reduce/reduceRight
arr.fn(callback[,initialValue]);
//參數
callback //執行數組中每個值的函數,包含四個參數:
accumulator // 累計器,累計回調的返回值;它是上一次調用回調時返回的累積值,或initialValue(第一次)
currentValue // 數組中正在處理的元素
currentIndex // 可選,數組中正在處理的當前元素的索引。如果提供了initialValue,則起始索引號為0,否則為1。
array //可選,調用reduce()的數組
initialValue //可選,作為第一次調用callback函數時的第一個參數的值。如果沒有提供初始值,則將使用數組中的第一個元素。在沒有初始值的空數組上調用reduce將報錯。
// 返回值:函數累計處理的結果
幾點注意事項:
① 回調函數第一次執行時,accumulator和currentValue的取值有兩種情況:如果調用時提供了initialValue,accumulator取值為initialValue,currentValue取數組中的第一個值;如果沒有提供initialValue,那么accumulator取數組中的第一個值,currentValue取數組中的第二個值。
② 如果數組為空且沒有提供initialValue,會拋出TypeError。如果數組僅有一個元素(無論位置如何)并且沒有提供initialValue,或者有提供initialValue但是數組為空,那么此唯一值將被返回并且callback不會被執行。
③ 提供初始值通常更安全。
④ 數組中被刪除或從未被賦值的元素不會被執行。
reduce(),對數組中的每個元素升序執行提供的reducer函數(callback),將結果匯總為單個返回值。
示例:執行過程中刪除第一個元素
let arr = [0,1,2,3,4];
let sum = arr.reduce(function(accumulator,currentValue,currentIndex){
console.log('currentIndex::' + currentIndex + '¤tValue::'+ currentValue);
if(currentIndex == 2){
arr.shift();
}
return accumulator + currentValue;
},0);
console.log('sum::' + sum);
結果:可以看到當刪除第一個,值3被跳過,執行索引3時對應的值為4
示例:未初始化和執行中被刪除的值
let arr = [0,1,,3,4,5];
let sum = arr.reduce(function(accumulator,currentValue,currentIndex){
console.log('currentIndex::' + currentIndex + '¤tValue::'+ currentValue);
if(currentIndex == 1){
delete arr[4];
}
return accumulator + currentValue;
},0);
console.log('sum::' + sum);
結果:可以看到索引2和索引4都被跳過
結合上面兩個示例,可以看到在遍歷執行上reduce與forEach/map/filter/some/every規則相同。
示例:將二維數組轉化為一維
let flattened = [[0,1],[2,3],[4,5]].reduce(function(a,b){
return a.concat(b);
},[])
console.log(flattened);//[0, 1, 2, 3, 4, 5]
示例:計算數組中每個元素出現的次數
let names = ['Alice','Bob','Tiff','Bruce','Alice'];
let countedNames = names.reduce(function(allNames,name){
if(name in allNames){
allNames[name]++;
}else{
allNames[name] = 1;
}
return allNames;
},{});
console.log(countedNames);//{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
示例:數組去重
let arr=[1,2,1,2,1,2,1,1,1,2,2,2,1];
let result = arr.sort().reduce((init,current)=>{
if(init.length === 0 || init[init.length-1]!==current){
init.push(current);
}
return init;
},[])
console.log(result);//[1,2]
示例:按順序執行Promise
function runPromiseInSequence(arr,input){
return arr.reduce(
(promiseChain,currentFunction)=>promiseChain.then(currentFunction),
Promise.resolve(input)
);
}
function p1(a) {
return new Promise((resolve, reject) => {
resolve(a * 5);
});
}
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2);
});
}
//這里f3也能正確觸發后面的p4,因為f3可以被.then()封裝,所以只要reduce的第二個參數inititalValue是一個promise對象,則后面的函數都可以按順序執行
function f3(a) {
return a * 3;
}
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4);
});
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10).then(console.log); // 1200
reduceRight(),接受一個函數作為累加器(accumulator)和數組的每個值(從右到左)將其減少為單個值。
reduceRight運行規則與reduce基本相同,只是遍歷的方向不同。
示例:將二維數組轉為一維數組
let flattened = [[0, 1], [2, 3], [4, 5]].reduceRight(function(a, b) {
return a.concat(b);
}, []);
console.log(flattened); //[4, 5, 2, 3, 0, 1]
示例:reduce與reduceRight之間的區別
let a=['1','2','3','4','5'];
let left = a.reduce(function(prev,cur){ return prev + cur;});
let right = a.reduceRight(function(prev,cur){return prev+cur;});
console.log(left);//'12345'
console.log(right);//'54321'
第三類,獲取鍵值:entries/keys/values
entries(),返回一個新的Array Iterator對象,該對象包含數組中每個索引的鍵/值對。
語法: arr.entries()
返回值:一個新的Array迭代器對象。
示例:
let arr = ['a','b','c'];
let iterator = arr.entries();
console.log(iterator);
console.log('----------------')
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log('----------------')
for( let e of iterator){
console.log(e);//什么也沒打印
}
console.log('----------------')
let iterator1=arr.entries();
for(let e of iterator1){
console.log(e);//有打印輸出
}
console.log(iterator1.next());
結果:此處注意迭代器的next()方法
keys(),返回一個包含數組中每個索引鍵的Array Iterator對象。
語法:arr.keys()
。
返回值:一個新的Array迭代器對象。
示例:迭代器對象會包含那些沒有對應元素的索引
let arr = ['a',,'c'];
let sparseKeys = Object.keys(arr);
let denseKeys = [ ...arr.keys()];
console.log(sparseKeys);//["0", "2"]
console.log(denseKeys);//[0, 1, 2]
values(),返回一個新的Array Iterator對象,該對象包含數組每個索引的值。
語法:arr.values()
。基本與keys()相同,參照使用。
第四類,查找元素:indexOf/lastIndexOf/includes
這三個方法的語法類似:
// fn代替indexOf/lastIndexOf/includes
arr.fn(searchElement,fromIndex);
// 參數
searchElement // 需要查找的元素值。
fromIndex // 可選,開始查找的位置。由于indexOf和includes是從左到右查找,而lastIndexOf是從右到左查找,所以這個參數的規則有所不同
// indexOf/includes: 默認值為0,從左到右正向查找。
// 如果該索引值大于或等于數組長度,意味著不會在數組里查找,返回-1,includes返回false;
// 如果參數中提供的索引值是一個負值,則將其作為數組末尾的一個抵消,即表示從length-|fromIndex|開始查找;
// 如果參數中提供的索引值是一個負值,并不改變其查找順序,查找順序仍然是從前向后查詢數組。
// 如果抵消后的索引值仍小于0,則整個數組都將會被查詢。
// lastIndexOf: 默認值為數組的長度減1,從右到做逆向查找。
// 如果該值為負時,其絕對值大于數組長度,則方法返回-1,即數組不會被查找。
// 如果為負值,將其視為從數組末尾向前的偏移(length-|fromIndex|)。
// 即使該值為負,數組仍然會被從后向前查找。
// 如果該值大于或等于數組的長度,則整個數組會被查找。
它們三個判斷均使用嚴格相等(===)進行比較,區分大小寫。
indexOf(),返回在數組中可以找到一個給定元素的第一個索引,如果不存在,則返回-1。
返回首個被找到的元素在數組中的索引位置;若沒有找到則返回-1。
示例:找出指定元素出現的所有位置
let includes=[];
let arr=['a','b','a','c','a','d'];
let element = 'a';
let idx = arr.indexOf(element);
while(idx!=-1){
includes.push(idx);
idx = arr.indexOf(element,idx+1);
}
console.log(includes);//[0, 2, 4]
lastIndexOf(),返回指定元素(也即有效的JavaScript值或變量)在數組中的最后一個的索引,如果不存在則返回-1。
返回數組中最后一個元素的索引,如果未找到返回-1。
用法規則參照indexOf。
includes(),用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回true,否則返回false。
示例:includes()方法有意設計為通用方法。它不要求this值是數組對象,所以它可以被用于其它類型的對象(比如類數組對象)。
(function() {
console.log([].includes.call(arguments, 'a')); // true
console.log([].includes.call(arguments, 'd')); // false
})('a','b','c');
第五類,數組轉變為String:join/toString/toLocaleString
join(),將一個數組(或一個類數組對象)的所有元素連接成一個字符串并返回這個字符串。
join()方法不會改變數組本身。
如果元素是undefined或者null,則會轉化成空字符串。
語法:
let str = arr.join(separator);
// 參數
separator // 指定一個字符串來分隔數組的每個元素。
// 如果需要(separator),將分隔符轉換為字符串。
// 如果省略(),數組元素用逗號分隔。默認為“,”。
// 如果separator是空字符串(''),則所有元素之間都沒有任何字符。
// 返回值:一個所有數組元素連接的字符串。如果arr.length為0,則返回空字符串。
示例:
let arr = [1,2,'',undefined,null,true,false];
let str = arr.join();
console.log(str);//1,2,,,,true,false
console.log(arr);//[1, 2, "", undefined, null, true, false]
arr.length = 0;
console.log(arr);//[];
示例:應用于類數組對象
function fn(a,b,c){
let s = Array.prototype.join.call(arguments);
console.log('fn::',s);
}
fn(1,'a','undefined');
function ff(a,b,c){
arguments.length = 0;
console.log('ff>arguments::',arguments);
let s = Array.prototype.join.call(arguments);
console.log('ff::',s);
}
ff(1,'a','undefined');
結果:可以看到,數組設置length為0后數組相當于被刪除,而類數組對象的length設置為0后屬性還在,但是輸出的同樣是一個空字符串,所以length屬性對join影響很大。(可以測試,將arugments的length設置為1,則只有索引為0的元素被轉化返回了)
toString(),返回一個字符串,表示指定的數組及其元素。
語法:arr.toString()
;
返回值:一個表示指定的數組及其元素的字符串。
Array對象覆蓋了Object的toString方法。對于數組對象,toString方法連接數組并返回一個字符串,其中包含用逗號分隔的每個數組元素。
示例:當一個數組被作為文本值或者進行字符串連接操作時,將會自動調用其toString方法
console.log('a'+[1,2,3,4]); //a1,2,3,4
console.log('a'+[1,2,3,4].toString()); //a1,2,3,4
toLocaleString(),返回一個字符串表示數組中的元素。數組中的元素將使用各自的toLocaleString方法轉成字符串,這些字符串將使用一個特定的語言環境的字符串(例如一個逗號)隔開。
語法:
arr.toLocaleString([locales[,opitons]]);
// 參數
locales //可選,帶有BCP 47語言標記的字符串或字符串數組,關于locales參數的形式與解釋:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl
optionds //可選,一個可配置屬性的對象,對于數字Number.prototype.toLocaleString(),對于日期Date.prototype.toLocaleString()。
// 返回值:表示數組元素的字符串
注:這塊參數方面還很懵逼,有時間回頭要好好看看!切記回看
第六類,數組排序:sort/reverse
sort(),方法用原地算法對數組的元素進行排序,并返回數組。
由于它取決于具體實現,因此無法保證排序的時間和空間復雜性。
語法:
arr.sort([compareFunction]);
// 參數
compareFunction //可選,用來指定按某種順序進行排列的函數。如果省略,元素按照轉換為的字符串的各個字符的Unicode位點進行排序。
firstEL // 第一個用于比較的元素
secondEL // 第二個用于比較的元素
// 返回值:排序后的數組。注意:數組已原地排序,并且不進行復制。
如果沒有指明compareFunction,那么元素會按照轉換為的字符串的諸個字符的Unicode位點進行排序。
如果指明了compareFunction,那么數組會按照調用該函數的返回值排序。
- 如果 compareFunction(a, b) 小于 0 ,那么 a 會被排列到 b 之前
- 如果 compareFunction(a, b) 等于 0 , a 和 b 的相對位置不變。備注: ECMAScript 標準并不保證這一行為,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003 年之前的版本);
- 如果 compareFunction(a, b) 大于 0 , b 會被排列到 a 之前。
- compareFunction(a, b) 必須總是對相同的輸入返回相同的比較結果,否則排序的結果將是不確定的。
示例:可以看到數組本身會被修改
let arr1=['cherry','Banana'];
console.log(arr1.sort());//["Banana", "cherry"]
console.log(arr1);//["Banana", "cherry"]
//比較的數字會先被轉換為字符串,所以在Unicode順序上 "80" 要比 "9" 要靠前
let arr2 = [9,80,23,53,7,3];
console.log(arr2.sort());//[23, 3, 53, 7, 80, 9]
console.log(arr2);//[23, 3, 53, 7, 80, 9]
reverse(),將數組中的元素位置顛倒。第一個數組元素成為最后一個數組元素,最后一個數組元素成為第一個。
語法:arr.reverse();
。reverse 方法顛倒數組中元素的位置,并返回該數組的引用。
示例:
let myarr = ['one','two','three'];
let copyarr = myarr.reverse();
console.log(myarr);//["three", "two", "one"]
console.log(copyarr);//["three", "two", "one"]
console.log(copyarr === myarr);//true