JS數組方法以及它們常用的地方


下面是我對 ES3/ES5/ES6 一些數組操作的總結,附帶了一些我曾經遇到和用上的一些實際需求。

map處有待更內容。

首先先來講講ES3的數組方法


(影響原數組)棧方法之 push:可向數組的末尾添加一個或多個元素,並返回新的長度。

好像沒有什么可說的?

var a = [1];
a.push(2);
console.log(a); // [1,2]
console.log(a.push(8));  //3

(影響原數組)棧方法之 pop:用於彈出數組最后一個元素,返回該元素的值

push 和 pop 組成了后進先出的棧數據結構。

var a = [1,2,3];
console.log(a.pop()); // 3
console.log(a); // [1,2]

通過這個函數,可以幫我們實現一道OJ題目,輸出文件的后綴名

const getExtName = (filename) => {
	let position = filename.indexOf('.');
	if (position !== 0 && position !== -1) {
		filename = filename.split('.');
		postfix = filename.pop();
		return '.' + postfix
	} else {
		return ''
	}
};
console.log(getExtName('xx.avi')); // .avi

(不影響原數組)join:接收一個參數作為分隔符,以字符串的形式返回。(這其實是兩個功能,1. 將每個元素都轉成字符串 2.用自定義的連接符分割)

它接受0參數或者1個參數,若是傳了多個參數只有第一個是有效的。

  • 參數若是為無,默認為','
[考古時刻]

古代的前端代碼或者古代的博客往往能看到這樣的東西(哦,讓我想起來了 Ext 的 tpl,也就是 JSX 語法的前前前代思想吧)

var array = ['1','2','3'];
var html="<h1>"+array.join("</h1><h1>")+"</h1>";

上述可以生成用array作為數據的指定結構的html.
通過 join,我們能夠將[數組]變為[字符串],而我們若要倒着來,可以用 split

'a,b,c'.split(','); // ["a", "b", "c"]
'a,b,c'.split(',').join(',') // 'a,b,c'

(不影響原數組)slice[start, ?end]:用於切割並且創造一個新的數組

  • 1個參數(普通形態): 從開始位置截取到結尾
  • 2個參數(完全體): 從開始位置截取到結束位置 [a, b) 不包括結束位置本身 -.-
  • 結束位置還可以是負數( 實際計算為:該負數 + 數組長度)

    結束位置小於起始位置,返回 空數組 []

var a = [1, 2, 3];
console.log(a.slice(1));  // [2, 3]
console.log(a.slice(1, 2));  // [2]
console.log(a);   // [1, 2, 3] - 原數組沒有被改變
console.log(a.slice(1, -1)); // (-1 + 3) = 2  => a.slice(1, 2) => [2]
console.log(a.slice(1, -100)); // []

slice 有一個挺經典的的代碼: [rgb顏色值轉換為十六進制]

rgb值 (0, 0, 0) ~ (255, 255, 255),十六進制 #000000 ~ #ffffff

const rgbToHex = (color) => {
  let rgb = color.split(','); // 將 rgb 字符串分解,例子:'rgb(1, 1, 1)',得到 ['rgb(1', '1,', '1)']
  let r = parseInt(rgb[0].split('(')[1]), // 去掉數組第一項的多余的東西
    g = parseInt(rgb[1]), // 第二項本身很干凈
    b = parseInt(rgb[2].split(')')[0]); // 同第一項注釋
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
  // 這里采用了位移操作 256 * 256 * 256 => 2^8 * 2^8 * 2^8
	// 這里的 (1<<24) 是為了防止我們的最高位的 r 顏色數字小於 16 的情況,出現了五位數的情況,如 #10000,顏色值嘛,得是六位的
}
let color = 'rgb(10,128,233)';
console.log(rgbToHex(color));

(1<<24)是為了規避 rgb 最高的 r 轉化的數字 < 16 的情況

通過slice(1),我們很容易地把多加上的那位1給去掉啦~是不是很取巧,我反正感覺這題、這需求挺經典的。


(影響原數組)splice(start, ?cutLen, ?data):用於截取數據,插入數據,刪除數據。!!操作的是數據本身

這里鄭重地提一句,字符串不能借用splice,即使使用了Array.porotpye.splice.call,因為在規范中,字符串是不可變的,使用splice會改變原數組。所以slice可以在字符串中用,而splice不能。所以,如果我們要在字符串中使用,需要注意。a.轉換成數組 b.注意splice返回的是被替換的東西,而不是本身。

以上同理。pop、push、reverse等改變原數組的,在 string 上都不能通過 call 來借用哦。

參數有三:

  • 1.[開始位置]: 當只有1個參數的時候:從數組的開始位置刪掉剩下的所有數據,返回刪掉的數據。

    slice就像只是我們想象中的裁剪,而splice則是現實中的,從splice中醒來,已是物是人非。splice是直接對數組進行操作的。

    var array = [15, 5, 10, 2, 0];
    console.log(array.splice(1)); // [5, 10, 2, 0]
    console.log(array);           // [15]
    
  • 2.[開始位置,截斷的個數](注意,這和 slice 的第二個參數意義可是不一樣的,這是 cutLen):以 start 為起點,cutLen 為長度,刪掉 [start, start + cutLen) 的元素
    var array = [15, 5, 10, 2, 0];
    array.splice(1, 2);
    console.log(array); // [15, 2, 0]
    
  • 3.[開始位置,截斷個數,插入的數據]: (start, cutLen, data): 以 start 為起點, cutLen 為長度,刪掉 [start, start + cutLen] 的元素,並填充 data 到刪除的位置
    var array = [15, 5, 10, 2, 0];
    array.splice(2, 2, 8);
    console.log(array); // [15, 5, 8, 0]
    

(影響原數組)隊列方法shift:將數組第一個元素從其中刪除,並且返回第一個元素的值

和pop差不多

var a = [1,2,3];
console.log(a.shift()); // 1
console.log(a); // [2,3]

(影響原數組)unshift: 在數組開頭添加元素,返回該元素值

pop,unshift,shift,push 這幾個都是直接對原數組進行操作的。
unshift和shift組成的數據結構也是棧,只是棧頂和棧尾位置交換了罷了。

var a = [1,2,3];
console.log(a.unshift(4)); // 4
console.log(a); // [4,1,2,3]

(不影響原數組)concat:用於連接兩個或者多個數組,返回一個新的數組

concat是不會改變原來的數組的

var a = [1,4];
var b = [2,4];
var c = [];
console.log(c.concat(a,b));  // [1,4,2,4]
console.log(a,b); 			// [1,4] [2,4]

(影響原數組)數組顛倒之 reverse:用於將數組元素顛倒順序

無參數,無視參數, !!!!!操作的是數組本身,會改變原數組

var a = [1,2,3,5];
console.log(a.reverse());  // [5,3,2,1]

(影響原數組)排序sort(fn):排序方法

參數:fn(a,b):比較函數,無參數的時候按照字母表 ASCII 升順排序

var a = [1, 2, 3, 5, 10, 25];
console.log(a.sort()); // [1, 10, 2, 25, 3, 5]

看,我們發現了什么?這個排序結果明顯不是我們所想要的結果,對吧哈哈?是不是很有趣!

這是因為 sort()默認對每一個 子項 調用轉型方法 toString(),之后才進行判斷。而這個時候其實是根據 ASCII 碼的大小來判斷的。因此 "15" < "5"。先比較第一位,再比較第二位。

即使每一項都是 Number ,如果我們不后期加工,sort()比較的也是每一項轉化為的String的大小。

  • 那么就讓我們來實現一下其內部的方法使得 sort 能夠按照我們的預期工作吧!

    var a = [1, 2, 3, 5, 10, 25];
    a.sort((a, b) => a - b);
    console.log(a); // [1, 2, 3, 5, 10, 25]
    

    我已經總結出來了一個規律。正所謂后來居上

    • return a-b; 即是從小到大(a-b)排序(b是在后面的,所謂后來)
    • return b-a; 即是從大到小(b-a)排序
  • 百尺竿頭,更進一步!讓我們再來增加一下難度吧,實現對簡單對象根據某個 key 值排序

    let obj_list = [{
    	name: 'li',
    	value: 99
    }, {
    	name: 'chen',
    	value: 100
    }, {
    	name: 'huang',
    	value: 1
    }]
    console.log(obj_list.sort((a, b) => { return a.value - b.value }))
    // 排序結果:'huang','li','chen'
    
  • 在一次面試(深圳-XMind)中,我還得知了這個函數還可以用於隨機排序,其實就是利用了sort(compare)的特性。附:官方特性

    • 1.可選參數compare需為函數,sort函數使用compare函數比較數組中的兩個元素,因此,compare函數需要兩個參數compare(a,b)。
    • 2.當compare函數返回任何大於0的值時,它將使第1個參數的索引值小於第2個參數的索引值
    • 3.當compare函數返回任何小於0的值時,它將使第2個參數的索引值小於第1個參數的索引值

    所以!!!如果我們要亂序,我們只要讓返回的隨機數 大於0和小於0 的概率一樣即可!上代碼:

    var a = [1, 2, 3, 5, 10, 25];
    a.sort((a, b) => 0.5 - Math.random());
    console.log(a);
    


就這樣,ES3常用的數組方法告一段落;下面就是隨着時代的進步所衍生出的ES5,6的方法了

我們曾經所有的復雜的,麻煩的操作,在新技術中,都被簡化,就像拖把和拖把一樣,這就是進步,擁有進步能力的語言往往不容易被這個時代所淘汰。

  • ES5 更新了 indexOf 方法
  • ES5 增加了 every/filter/some/forEach/map 這些方法都不會修改數組的值

(不影響原數組)Array.prototype.indexOf(此方法在 ES3 也有,不過那是針對 String的):返回在該數組中第一個找到的元素位置,若不存在則返回 -1

我們可以首先來用ES3來模擬一下先,有助於我們理解。

Array.prototype.indexOf = function (index) {
  var res = -1,
    now = null,
    len = this.length;
  if (len == 0) return res;
  for (var i = 0; i < len; i++) {
    now = this[i];
    if (index == now) {
      res = i;
      break;
    }
  }
  return res;
}
var test = ['234', '23424', '30'];
console.log(test.indexOf('30'));

(不影響原數組)forEach:對數組中的每一項運行給定函數,這個方法沒有返回值.

參數為function類型,默認有傳參(遍歷數組內容,對應數組索引,數組本身)
在這里引用一個segmentfault的回答:https://segmentfault.com/q/1010000006127658?_ea=1022444,以下為引用的內容
題目之添加一個 fullName

所傳函數有三: forEach(function (子項,Index,所有項All) {})
  • 不用 forEach

    var users = [
    	{
    		lastName: 'Li',
    		firstName: 'Lei'
    	},
    	{
    		lastName: 'Han',
    		firstName: 'Meimei'
    	}
    ];
    // 在這里,我們用原始的語法處理
    for (var i = 0; i < users.length; i++) {
    	users[i].fullName = users[i].firstName + users[i].lastName;
    }
    console.log(users);
    
    • 分析缺點:我們多了個 i 變量,直觀上多了個 for 循環
    • 若是用forEach
  • 使用 forEach

    users.forEach(function(user,index,arr){
    	user.fullName = user.firstName + user.lastName;
    });
    console.log(users);
    

你可能認為這里的 forEach 其實改變了數組的值,其實不然。

只需知道,引用對象是不同的,他們指向的是一個內存位置,而非實際的對象。

var arr = [1, 2, 3];
arr.forEach(item => {
	item = 8;
});
console.log(arr); // [1, 2, 3]

(不影響原數組)map:對數組中的每一項運行給定函數,返回每次調用結果組成的數組。

我們能夠在 Redux 中的 reducer 看到大量的 map 函數,因為 redux 追求這種函數式極簡的酷炫的風格;
Vue 中的 v-for 本質上就是 map 實現的;
map是指映射,和英文中的地圖不同。

所傳函數有三: forEach(function (子項,Index,所有項All) {})
  • map(function (item, index, allItem) {})
var a = [1, 32, 442, 234];
b = a.map(item => item + 1);
console.log(a, b);	 // [1,32, 442, 234]   [2, 33, 443, 235]
  • 如果我們什么都返回,則每一項都會返回 undefined
var a = [1, 32, 442, 234];
c = a.map(() => {});
console.log(c); // [ undefined, undefined, undefined, undefined ]
  • 回調函數中第二個參數 index 的作用,在react中我們主要將它賦值給key
d = a.map((item, i) => {
	console.log(i);   // 0,1,2,3
});
  • 回調函數中第三個參數,所有元素,即數組本身
e = a.map((item, i, array) => {
	console.log(array);   // 最后會打印四遍  [1,34,442,234]
})

(不影響原數組)filter:對數組中的每一項運行給定函數,返回該函數會返回 true 的項組成的數組

以上不改變原數組的函數如果結果需要多次使用,最好都定義一個新的變量來存儲。
讓我看一個簡短的filter,是不是有一種四兩撥千斤的感覺。

var a = [{
  flag: false,
  name: 'lihua'
}, {
  flag: true,
  name: 'zengcuo'
}];
console.log(a.filter(item => item.flag));  // [{flag:true,name:'zengcuo'}]

除此之外,ES5 新增了兩個歸並的方法

  • (不影響數組)reduce(function(pre, next, index, array) {}) 從左到右
  • (不影響數組)reduceRight(function(pre, next, index, array) {}) 從右到左
let arr = [1, 2, 3];
console.log(arr.reduce((pre, next) => pre + next)); // 6

(不影響原數組)some和every

  • some: 對數組中的每一項運行給定函數,如果函數對任一項返回 true,就會返回 true,否則 false
  • every:對數組中的每一項運行給定函數,如果函數對所有的項都返回 true,才返回 true,否則 false
var people = ['crimeA', 'crimeB'];
console.log(people.some(item => item.indexOf('crimeA') > -1)); // true 只要有罪行A 就返回 true
console.log(people.every(item => item.indexOf('crimeA')> -1)); // false 每個數組都要 罪行A 才返回 true

以上就是 ES5 的方法啦,讓我們簡介下 ES6 的方法吧!

  • Array.from:從一個類似數組或可迭代的對象中創建一個新的數組

可以用來作數組去重

let arr = [1, '11', '11', 1, 8,];
let res = Array.from(new Set(arr));
console.log(res); // [1, '11', 8]
  • Array.isArray(arr):返回傳遞的值是否是數組
  • Array.fill(target, start, end):用一個固定的值填充一個數組

可以用來快速的繁殖數組

let arr = new Array(3);
let obj = {a: 1};
arr.fill(obj);
console.log(arr); // [ { a: 1 }, { a: 1 }, { a: 1 } ]
  • Array.includes():判斷是否包含某個值
  • Array.find(callback)
  • Array.findIndex
  • Array.keys()
  • Array.values()
  • Array.entries()

complete.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM