打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Javascript數組操作 | 晚晴幽草軒

使用JS也算有段時日,然對於數組的使用,總侷限於很初級水平,且每每使用總要查下API,或者寫個小Demo測試下才算放心,一來二去,浪費不少時間;思慮下,堪能如此繼續之?當狠心深學下方是正道。

一, 數組常用方法

1. 數組的創建

1
2
3
var arrayObj = new Array(); //創建一個數組
var arrayObj = new Array([size]); //創建一個數組並指定長度,注意不是上限,是長度
var arrayObj = new Array([element0[, element1[, ...[, elementN]]]]); //創建一個數組並賦值

要說明的是,雖然第二種方法創建數組指定了長度,但實際上所有情況下數組都是變長的,也就是說即使指定了長度為5,仍然可以將元素存儲在規定長度以外的,注意:這時長度會隨之改變

2. 數組的元素的訪問

1
2
var testGetArrValue=arrayObj[1]; //獲取數組的元素值
arrayObj[1]= "這是新值"; //給數組元素賦予新的值

3. 數組元素的添加

1
2
3
4
5
6
7
8
arrayObj.push([item1 [item2 [. . . [itemN ]]]]);
// 將一個或多個新元素添加到數組結尾,並返回數組新長度
arrayObj.unshift([item1 [item2 [. . . [itemN ]]]]);
// 將一個或多個新元素添加到數組開始,數組中的元素自動後移,返回數組新長度
arrayObj.splice(insertPos,0,[item1[, item2[, . . . [,itemN]]]]);
//將一個或多個新元素插入到數組的指定位置,插入位置的元素自動後移,返回""。

4. 數組元素的刪除

1
2
3
4
5
6
arrayObj.pop(); //移除最後一個元素並返回該元素值
arrayObj.shift(); //移除最前一個元素並返回該元素值,數組中元素自動前移
arrayObj.splice(deletePos,deleteCount);
//刪除從指定位置deletePos開始的指定數量deleteCount的元素,數組形式返回所移除的元素

5. 數組的截取和合併

1
2
3
4
5
arrayObj.slice(start, [end]);
//以數組的形式返回數組的一部分,注意不包括 end 對應的元素,如果省略 end 將複製 start 之後的所有元素
arrayObj.concat([item1[, item2[, . . . [,itemN]]]]);
//將多個數組(也可以是字符串,或者是數組和字符串的混合)連接為一個數組,返回連接好的新的數組

6. 數組的拷貝

1
2
arrayObj.slice(0); //返回數組的拷貝數組,注意是一個新的數組,不是指向
arrayObj.concat(); //返回數組的拷貝數組,注意是一個新的數組,不是指向

7. 數組元素的排序

1
2
arrayObj.reverse(); //反轉元素(最前的排到最後、最後的排到最前),返回數組地址
arrayObj.sort(); //對數組元素排序,返回數組地址

8. 數組元素的字符串化

join()方法是一個非常實用的方法,它把當前Array的每個元素都用指定的字符串連接起來,然後返回連接後的字符串:

1
2
3
4
arrayObj.join(separator); //返回字符串,這個字符串將數組的每一個元素值連接在一起,中間用 separator 隔開。
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'

如果Array的元素不是字符串,將自動轉換為字符串後再連接。

valueOf: 與String類似,Array也可以通過indexOf()來搜索一個指定的元素的位置:

1
2
3
4
var arr = [10, 20, '30', 'xyz'];
arr.indexOf(10); // 元素10的索引為0
arr.indexOf(30); // 元素30沒有找到,返回-1
arr.indexOf('30'); // 元素'30'的索引為2

toLocaleString 、toString:可以看作是join的特殊用法,不常用;
toLocaleString(): 方法可根據本地時間把 Date 對象轉換為字符串,並返回結果。

toString:把數組轉換為字符串,並返回結果。

lastIndexOf:返回在數組中搜索到的與給定參數相等的元素的最後(最大)索引。

toSource(): 返回一個字符串,代表該數組的源代碼.該特性是非標準的,請儘量不要在生產環境中使用它! Array.prototype.toSource()

二、數組對象的3個屬性

1. length屬性

Length屬性表示數組的長度,即其中元素的個數。因為數組的索引總是由0開始,所以一個數組的上下限分別是:0和length-1。和其他大多數語言不同的是,JavaScript數組的length屬性是可變的,這一點需要特別注意。當length屬性被設置得更大時,整個數組的狀態事實上不會發生變化,僅僅是length屬性變大;當length屬性被設置得比原來小時,則原先數組中索引大於或等於length的元素的值全部被丟失。下面是演示改變length屬性的例子:

1
2
3
4
5
6
7
8
9
10
11
12
var arr=[12,23,5,3,25,98,76,54,56,76];//定義了一個包含10個數字的數組
alert(arr.length); //顯示數組的長度10
arr.length=12; //增大數組的長度
alert(arr.length); //顯示數組的長度已經變為12
alert(arr[8]); //顯示第9個元素的值,為56
arr.length=5; //將數組的長度減少到5,索引等於或超過5的元素被丟棄
alert(arr[8]); //顯示第9個元素已經變為"undefined"
arr.length=10; //將數組長度恢復為10
alert(arr[8]); //雖然長度被恢復為10,但第9個元素卻無法收回,顯示"undefined"

由上面的代碼我們可以清楚的看到length屬性的性質。但length對象不僅可以顯式的設置,它也有可能被隱式修改。JavaScript中可以使用一個未聲明過的變量,同樣,也可以使用一個未定義的數組元素(指索引超過或等於length的元素),這時,length屬性的值將被設置為所使用元素索引的值加1。例如下面的代碼:

1
2
3
4
5
6
7
8
9
var arr=[12,23,5,3,25,98,76,54,56,76];
console.log(arr.length); // 10
arr[15] = 34;
console.log(arr.length); //16
console.log(arr[10]); //undefine
console.log(arr.toString())
//12,23,5,3,25,98,76,54,56,76,,,,,,34

代碼中同樣是先定義了一個包含10個數字的數組,通過alert語句可以看出其長度為10。隨後使用了索引為15的元素,將其賦值為15,即 arr[15]=34,這時再用alert語句輸出數組的長度,得到的是16。無論如何,對於習慣於強類型編程的開發人員來說,這是一個很令人驚訝的特性。事實上,使用new Array()形式創建的數組,其初始長度就是為0,正是對其中未定義元素的操作,才使數組的長度發生變化。

大多數其他編程語言不允許直接改變數組的大小,越界訪問索引會報錯。然而,JavaScript的Array卻不會有任何錯誤。在編寫代碼時,不建議直接修改Array的大小,訪問索引時要確保索引不會越界。

由上面的介紹可以看到,length屬性是如此的神奇,利用它可以方便的增加或者減少數組的容量。因此對length屬性的深入瞭解,有助於在開發過程中靈活運用。

2. prototype 屬性

返回對象類型原型的引用。prototype 屬性是 object 共有的。

objectName.prototype:objectName 參數是object對象的名稱。
說明:用 prototype 屬性提供對象的類的一組基本功能。 對象的新實例「繼承」賦予該對象原型的操作。
對於數組對象,以以下例子說明prototype 屬性的用途。

給數組對象添加返回數組中最大元素值的方法。要完成這一點,聲明一個函數,將它加入 Array.prototype, 並使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function array_max()
{
var i,
max = this[0];
for (i = 1; i < this.length; i++)
{
if (max < this[i])
max = this[i];
}
return max;
}
Array.prototype.max = array_max;
var x = new Array(1, 2, 3, 4, 5, 6);
var y = x.max();

該代碼執行後,y 保存數組 x 中的最大值,即:6。

3. constructor 屬性

表示創建對象的函數。object.constructor //object是對象或函數的名稱。

說明:constructor 屬性是所有具有 prototype 的對象的成員。它們包括除 Global 和 Math 對象以外的所有 JScript 固有對象。constructor 屬性保存了對構造特定對象實例的函數的引用。

1
2
3
4
5
6
7
8
9
10
x = new String("Hi");
if (x.constructor == String) // 進行處理(條件為真)。
function MyFunc {
// 函數體。
}
y = new MyFunc;
if (y.constructor == MyFunc) // 進行處理(條件為真)。
y = new Array();

三、判斷是否為數組

js因為設計上的某些缺陷,導致在對於Array的判斷,也是頗費周折的。
typeof 操作符:對於Function, String, Number ,Undefined 等幾種類型的對象來說,他完全可以勝任,但是為Array時,難免會讓人失望:

1
2
var arr=new Array("1","2","3","4","5");
alert(typeof(arr)); // Object

instanceof 操作符: 運算符會返回一個 Boolean 值,指出對象是否是特定類的一個實例。

1
2
var arrayStr=new Array("1","2","3","4","5");
alert(arrayStr instanceof Array); //true

雖然此時能夠完好的工作,但,事實上在多個frame中穿梭就會產生大問題了。所以~~~

1
2
3
4
5
6
7
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray("1","2","3","4","5");//這個寫法IE大哥下是不支持的,FF下才有
alert(arr instanceof Array); // false
alert(arr.constructor === Array); // false

ECMA-262中規範定義了Object.prototype.toString的行為:首先,取得對象的一個內部屬性[[Class]],然後依據這個屬性,返回一個類似於」[object Array]」的字符串作為結果(看過ECMA標準的應該都知道,[[]]用來表示語言內部用到的、外部不可直接訪問的屬性,稱為「內部屬性」)。利用這個方法,再配合call,我們可以取得任何對象的內部屬性[[Class]],然後把類型檢測轉化為字符串比較,以達到我們的目的。於是利用這點,就有了下面這種方法:

1
2
3
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}

call改變toString的this引用為待檢測的對象,返回此對象的字符串表示,然後對比此字符串是否是』[object Array]』,以判斷其是否是Array的實例。也許你要問了,為什麼不直接o.toString()?嗯,雖然Array繼承自Object,也會有toString方法,但是這個方法有可能會被改寫而達不到我們的要求,而Object.prototype則是老虎的屁股,很少有人敢去碰它的,所以能一定程度保證其「純潔性」:)。這也是Array.isArray()方法的兼容舊環境(Polyfill)。

如此很好的解決了跨frame對象構建的問題,經過測試,各大瀏覽器兼容性也很好,可以放心使用。很多框架,比如jQuery、Base2等等,都計畫借鑑此方法以實現某些特殊的,比如數組、正則表達式等對象的類型判定!當然也可以寫成如下這樣:

1
2
3
function isArray2 (v){
return toString.apply(v) === '[object Array]';
}

要注意的是:toString方法極有可能被重寫,所以需要使用的時候,可以直接使用Object.prototype.toString()方法。

Array.isArray()^See方法用來判斷某個值是否為數組。如果是,則返回 true,否則返回 false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 下面的函數調用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// 鮮為人知的事實:其實 Array.prototype 也是一個數組。
Array.isArray(Array.prototype);
// 下面的函數調用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ __proto__: Array.prototype });

四, 數組迭代過程

filter() ^See

方法使用指定的函數測試所有元素,並創建一個包含所有通過測試的元素的新數組。
語法:arr.filter(callback[, thisArg])
如果為 filter 提供一個 thisArg 參數,則它會被作為 callback 被調用時的 this 值。否則,callback 的 this 值在非嚴格模式下將是全局對象,嚴格模式下為 undefined。

filter 為數組中的每個元素調用一次 callback 函數,並利用所有使得 callback 返回 true 或 等價於 true 的值 的元素創建一個新數組。callback 只會在已經賦值的索引上被調用,對於那些已經被刪除或者從未被賦值的索引不會被調用。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新數組中。

示例:篩選排除掉所有的小值

1
2
3
4
5
function isBigEnough(element) {
return element >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered is [12, 130, 44]

forEach() ^See

方法讓數組的每一項都執行一次給定的函數。
語法: array.forEach(callback[, thisArg])

forEach 方法按升序為數組中含有效值的每一項執行一次callback 函數,那些已刪除(使用delete方法等情況)或者從未賦值的項將被跳過(但不包括哪些值為 undefined 的項)。

forEach 遍歷的範圍在第一次調用 callback 前就會確定。調用forEach 後添加到數組中的項不會被 callback 訪問到。如果已經存在的值被改變,則傳遞給 callback 的值是 forEach 遍歷到他們那一刻的值。已刪除的項不會被遍歷到。 注意: 沒有辦法中止 forEach 循環。如果要中止,可使用 Array.every 或 Array.some。見下面的例子。

示例1:打印出數組的內容

1
2
3
4
5
6
7
8
9
function logArrayElements(element, index, array) {
console.log("a[" + index + "] = " + element);
}
[2, 5, 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[2] = 9

示例2:一個可以克隆對象的函數
使用下面的代碼可以複製一個給定的對象,雖然有很多不同的複製對象的方法.不過下面介紹的這種方法使用了Array.prototype.forEach和其他一些ECMAScript 5中的Object.*函數.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function copy(o){
var copy = Object.create( Object.getPrototypeOf(o) );
var propNames = Object.getOwnPropertyNames(o);
propNames.forEach(function(name){
var desc = Object.getOwnPropertyDescriptor(o, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var o1 = {a:1, b:2};
var o2 = copy(o1); // o2 looks like o1 now

every() ^See

方法測試數組的所有元素是否都通過了指定函數的測試。
語法:arr.every(callback[, thisArg])

every 方法為數組中的每個元素執行一次 callback 函數,直到它找到一個使 callback 返回 falsy(表示可轉換為布爾值 false 的值)的元素。如果發現了一個這樣的元素,every 方法將會立即返回 false。否則,callback 為每一個元素返回 true,every 就會返回 true。callback 只會為那些已經被賦值的索引調用。不會為那些被刪除或從來沒被賦值的索引調用。callback 被調用時傳入三個參數:元素值,元素的索引,原數組。 every 不會改變原數組。

實例:檢測所有數組元素的大小

1
2
3
4
5
6
7
8
//檢測數組中的所有元素是否都大於 10
function isBigEnough(element, index, array) {
return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// passed is true

map() ^See

方法返回一個由原數組中的每個元素調用一個指定方法後的返回值組成的新數組。

語法: array.map(callback[, thisArg])

map 方法會給原數組中的每個元素都按順序調用一次 callback 函數。callback 每次執行後的返回值組合起來形成一個新數組。 callback 函數只會在有值的索引上被調用;那些從來沒被賦過值或者使用 delete 刪除的索引則不會被調用。map 不修改調用它的原數組本身(當然可以在 callback 執行時改變原數組)。

實例一:求數組中每個元素的平方根

1
2
3
4
//在一個 String 上使用 map 方法獲取字符串中每個字符所對應的 ASCII 碼組成的數組:
var map = Array.prototype.map
var a = map.call("Hello World", function(x) { return x.charCodeAt(0); })
// a的值為[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

通常情況下,map 方法中的 callback 函數只需要接受一個參數,就是正在被遍歷的數組元素本身。但這並不意味著 map 只給 callback 傳了一個參數。這個思維慣性可能會讓我們犯一個很容易犯的錯誤。比如下面的語句返回什麼呢:

1
2
3
["1", "2", "3"].map(parseInt);
// 你可能覺的會是[1, 2, 3]
// 但實際的結果是 [1, NaN, NaN]

通常使用parseInt時,只需要傳遞一個參數.但實際上,parseInt可以有兩個參數.第二個參數是進制數.可以通過語句」alert(parseInt.length)===2」來驗證.

map方法在調用callback函數時,會給它傳遞三個參數:當前正在遍歷的元素, 元素索引, 原數組本身.
第三個參數parseInt會忽視, 但第二個參數不會,也就是說,parseInt把傳過來的索引值當成進制數來使用.從而返回了NaN。 因此此時應該使用如下的用戶函數returnInt:

1
2
3
4
5
6
function returnInt(element){
return parseInt(element,10);
}
["1", "2", "3"].map(returnInt);
// 返回[1,2,3]

some() ^See

方法測試數組中的某些元素是否通過了指定函數的測試。

語法: arr.some(callback[, thisArg])

描述:some 為數組中的每一個元素執行一次 callback 函數,直到找到一個使得 callback 返回一個「真值」(即可轉換為布爾值 true 的值)。如果找到了這樣一個值,some 將會立即返回 true。否則,some 返回 false。callback 只會在那些」有值「的索引上被調用,不會在那些被刪除或從來未被賦值的索引上調用。

示例:測試數組元素的值

1
2
3
4
5
6
7
8
//檢測在數組中是否有元素大於 10。
function isBigEnough(element, index, array) {
return (element >= 10);
}
var passed = [2, 5, 8, 1, 4].some(isBigEnough);
// passed is false
passed = [12, 5, 8, 1, 4].some(isBigEnough);
// passed is true

reduce() ^See

接收一個函數作為累加器(accumulator),數組中的每個值(從左到右)開始縮減,最終為一個值。
語法: arr.reduce(callback,[initialValue])
callback:執行數組中每個值的函數,包含四個參數

  • previousValue:上一次調用回調返回的值,或者是提供的初始值(initialValue)
  • currentValue 數組中當前被處理的元素
  • index 當前元素在數組中的索引
  • array 調用 reduce 的數組
    initialValue: 作為第一次調用 callback 的第一個參數。

描述:reduce 為數組中的每一個元素依次執行回調函數,不包括數組中被刪除或從未被賦值的元素,接受四個參數:初始值(或者上一次回調函數的返回值),當前元素值,當前索引,調用 reduce 的數組。

回調函數第一次執行時,previousValue 和 currentValue 可以是一個值,如果 initialValue 在調用 reduce 時被提供,那麼第一個 previousValue 等於 initialValue ,並且currentValue 等於數組中的第一個值;如果initialValue 未被提供,那麼previousValue 等於數組中的第一個值,currentValue等於數組中的第二個值。

如果數組為空並且沒有提供initialValue, 會拋出TypeError 。如果數組僅有一個元素(無論位置如何)並且沒有提供initialValue, 或者有提供initialValue但是數組為空,那麼此唯一值將被返回並且callback不會被執行。

示例1:將數組所有項相加

1
2
3
4
var total = [0, 1, 2, 3].reduce(function(a, b) {
return a + b;
});
// total == 6

示例2: 數組扁平化

1
2
3
4
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
return a.concat(b);
});
// flattened is [0, 1, 2, 3, 4, 5]

示例2: 統計一個數組中有多少個不重複的單詞

不使用reduce時的寫法

1
2
3
4
5
6
7
8
9
10
11
12
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
var obj = {};
for(var i= 0, l = arr.length; i< l; i++){
var item = arr[i];
obj[item] = (obj[item] +1 ) || 1;
}
return obj;
}
console.log(getWordCnt());

使用reduce()後的寫法:

1
2
3
4
5
6
7
8
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
return arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}
console.log(getWordCnt());

這其中一個需要注意的點在於,initialValue提供與否對prev和next的影響;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 二者的區別,在console中運行一下即可知曉*/
var arr = ["apple","orange",'pear','jade'];
function noPassValue(){
return arr.reduce(function(prev,next){
console.log("prev:",prev);
console.log("next:",next);
//console.info('prev type:'+ typeof(prev)); //prev type:string
return prev + " " +next;
});
}
function passValue(){
return arr.reduce(function(prev,next){
console.log("prev:",prev);
console.log("next:",next);
prev[next] = 1;
//console.info('prev type:'+ typeof(prev)); // object
return prev;
},{});
}
console.log("No Additional parameter:",noPassValue());
console.log("----------------");
console.log("With {} as an additional parameter:",passValue());

一點總結:

pop,push,reverse,shift,sort,splice,unshift 會改變原數組
join,concat,indexOf,lastIndexOf,slice,toString 不會改變原數組
map,filter,some,every,reduce,forEach這些迭代方法不會改變原數組

幾個注意點:

  1. shift,pop會返回那個被刪除的元素
  2. splice 會返回被刪除元素組成的數組,或者為空數組
  3. push 會返回新數組長度
  4. some 在有true的時候停止
  5. every 在有false的時候停止
  6. 上述的迭代方法可以在最後追加一個參數thisArg,它是執行 callback 時的 this 值。

JavaScript的數據類型分為:值類型和引用類型(地址值);而常見的引用類型有Object和Array/數組的存儲模型中,如果是諸如Number,String之類的類型數據會被直接壓入棧中,而引用類型只會壓入對該值的一個索引(即C中所說的保存了數據的指針)。這些數據時儲存在堆中的某塊區間中,堆棧並不是獨立的,棧中也可以在堆中存放。在使用Array的進行賦值操作的時候,也當注意是否要進行深度拷貝複製(可借助arr.slice(0)),以免造成對自身污染。對於Js數據,其實內容還是還有很多需要學習的,比如ArrayBuffer等。根據學以致用的原則,這些等到需要的時候再去學吧。

參考文章如下:
js數組操作
js如何判斷一個對象是不是Array?
MDN-Array
盤點JavaScript裡好用的原生API
5個現在就該使用的數組Array方法

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
jQuery数组处理汇总
数组
js数组的操作
js 解决字符串等问题
JS ARRAY 数组操作
13个JavaScript数组reduce的实例方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服