數組的slice
方法能夠將相似數組的對象
(好比document.getElementsByName('li')
)變成真正的數組。
通常是須要使用數組的方法時作下轉換。html
var arr = Array.prototype.slice.call(arrayLike); var arr = [].slice.call( arrayLike);
也能夠使用es6
的Array.from
。git
var arr = Array.from(arrayLike);
因此,Array.from
其實能夠這樣簡單實現:es6
Array.from = function(arr){ return Array.prototype.slice.call(arr); }
但事實上沒有這麼簡單,好比Set
類型的數據就沒法這樣轉換。它須要更復雜的處理。github
大多數人的作法:面試
var arr = [123, 456, 789]; var len = arr.length; var end = arr[len-1] console.log('end:', end) // 'end:' 789
優化方法:數組
var array = [1, 2, 3, 4, 5, 6]; console.log( array.slice(-1) ); // [6] console.log( array.slice(-1)[0] ); // 6 console.log( array.slice(-2) ); // [5,6] console.log( array.slice(-3) ); // [4,5,6]
能夠封裝一個函數:app
Array.prototype.last = function(){ return this.slice(-1)[0]; };
當數組數量越大,性能差別越大。如下是一個測試方法dom
let arr1 = Array.from(new Array(100000000), (x, index)=>{ return Math.random(); }); //耗時7.923ms console.time('a'); console.log(arr1[arr1.length-1]); console.timeEnd('a'); //耗時0.075ms console.time('b'); console.log(arr1.slice(-1)[0]); console.timeEnd('b');
好比,當數組中有 10
個元素,而你只想獲取其中前 5
個的話,你能夠截斷數組,經過設置 array.length = 5
使其更小。函數
var array = [1, 2, 3, 4, 5, 6]; console.log( array.length ); // 6 array.length = 3; console.log( array.length ); // 3 console.log( array ); // [1,2,3]
通常合併兩個數組的話,一般會使用 Array.concat()
。性能
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.concat(array2)); // [1,2,3,4,5,6];
然而,這個函數並不適用於合併大的數組,由於它須要建立一個新的數組,而這會消耗不少內存。
這時,你能夠使用 Array.prototype.push.apply( arr1, arr2 )
來代替建立新的數組,它能夠把第二個數組合併到第一個中,從而較少內存消耗:
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; Array.prototype.push.apply(array1, array2); console.log(array1); // [1,2,3,4,5,6];
對於es6
,能夠使用擴展符:
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; array1.push(...array2); console.log(array1); // [1,2,3,4,5,6];
方法一:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; function randSort1(arr){ for(var i = 0,len = arr.length;i < len; i++ ){ var rand = parseInt(Math.random()*len); var temp = arr[rand]; arr[rand] = arr[i]; arr[i] = temp; } return arr; } console.log(randSort1(arr));
方法二:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; function randSort2(arr){ var mixedArray = []; while(arr.length > 0){ var randomIndex = parseInt(Math.random()*arr.length); mixedArray.push(arr[randomIndex]); arr.splice(randomIndex, 1); } return mixedArray; } console.log(randSort2(arr));
方法三:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; arr.sort(function () { return Math.random() - 0.5; }); console.log(arr);
完成一個flatten
的函數,實現拍平
一個多維數組
爲一維
。示例以下:
var testArr1 = [[0, 1], [2, 3], [4, 5]]; var testArr2 = [0, [1, [2, [3, [4, [5]]]]]]; flatten(testArr1) // [0, 1, 2, 3, 4, 5] flatten(testArr2) // [0, 1, 2, 3, 4, 5]
先來一個最簡單的解法:
flatten = (arr) => arr.toString().split(',').map((val) => parseInt(val));
它是利用數組toString
的特殊性,很巧妙地處理。
再來一種:
flatten2 = function (arr) { return arr.reduce(function (pre, val) { if (!Array.isArray(val)) { pre.push(val); } else { pre.push(...flatten2(val)); } return pre; }, []); };
也能簡寫爲:
flatten2 = (arr) => arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatten2(val) : val), []);
如今es6
已有內置的flat
方法:
var testArr1 = [[0, 1], [2, 3], [4, 5]]; testArr1.flat() // [0, 1, 2, 3, 4, 5] var testArr2 = [0, [1, [2, [3, [4, [5]]]]]]; testArr2.flat() // [0, 1, [2, [3, [4, [5]]]]]
咱們看到,testArr2
只解放了一層。爲何呢?
原來這個函數有個默認參數depth
,咱們能夠稱之爲深度,默認是1
,當不傳遞參數時,只處理一層。
要想所有鋪展的話,須要傳遞參數Infinity
,如testArr1.flat(Infinity)
。
下面是個人一個簡單實現。
Array.prototype.flat = function (depth = 1) { var arr = []; for (var i = 0; i < this.length; i++) { if (Array.isArray(this[i]) && depth > 0) { arr.push(...this[i].flat(depth-1)); } else { arr.push(this[i]); } } return arr; };
數組有2個判斷是否包含某個元素的方法,includes
與indexOf
,後者很早就有了,前者是es6
才加的。
不少人覺得2個的區別只是前者返回bool
,後者返回索引數字,其實不止如此,關鍵一點在於對NaN
的判斷。
indexOf
是不會判斷NaN
的。好比:
var arr = [1, 2, 3, NaN]; console.log(arr.includes(NaN)); // true console.log(arr.indexOf(NaN)); // -1
因此,咱們要實現includes
的時候,須要這樣:
Array.prototype.includes = function (item) { for (var i = 0; i < this.length; i++) { // if (this[i] === item || (this[i] !== this[i] && item !== item)) { if (this[i] === item || (isNaN(this[i]) && isNaN(item))) { return true; } } return false; };
其中,我註釋掉的那行,就是isNaN
的一個實現,它是js
中惟一一個不等於自身的。
數組的多數方法都能用原生的for
循環實現,而數組的循環遍歷的方法,幾乎都同樣,都有2個參數,第一個是callback
函數,第二個是改變前面callback
做用域的。
經過對這些函數的實現,能夠更好地理解和記憶怎麼使用。
以最簡單的forEach
爲例,能夠這麼實現:
Array.prototype.forEach = function (callback, scope) { for (var i = 0; i < this.length; i++) { callback.call(scope, this[i], i, this); } }
map
與forEach
的區別只是有返回值罷了:
Array.prototype.map = function (callback, scope) { var arr = []; for (var i = 0; i < this.length; i++) { arr.push(callback.call(scope, this[i], i, this)); } return arr; };
some
是知足條件時,就返回true
,沒有一個知足時返回false
Array.prototype.some = function (callback, scope) { for (var i = 0; i < this.length; i++) { if(callback.call(scope, this[i], i, this)){ return true; } } return false; };
every
與some
恰好相反,有一個不知足條件時,就返回false
,所有知足才返回true
Array.prototype.every = function (callback, scope) { for (var i = 0; i < this.length; i++) { if(!callback.call(scope, this[i], i, this)){ return false; } } return true; };
find
是找到符合條件的元素:
Array.prototype.find = function (callback, scope) { for (var i = 0; i < this.length; i++) { if(callback.call(scope, this[i], i, this)){ return this[i]; } } };
findIndex
是找到符合條件的元素的索引:
Array.prototype.findIndex = function (callback, scope) { for (var i = 0; i < this.length; i++) { if(callback.call(scope, this[i], i, this)){ return i; } } return -1; };
數組的reduce
方法比較特殊,它的參數不像其它循環遍歷的方法,有點另類。
用法是這樣的:
var arr = [1, 2, 3, 4]; var callbackfn = function (pre, cur, index, arr) { console.log(pre, cur, index); return pre + cur; }; var res = arr.reduce(callbackfn); console.log(res); // 10 var res2 = arr.reduce(callbackfn, 5); console.log(res2); // 15
它的回調函數裏,第1個參數是上一次callback
的執行結果。
第一次時,若是沒有傳遞第2個參數,這時它使用數組的第一個元素,也將從第二元素開始遍歷。
它會將最後一個執行結果返回。
經常使用使用場景是作累加。
還有這樣一種狀況,先過濾一次條件,再用map
返回一個新的數組。
var res3 = arr.filter(item => item > 2).map(i => i * 2); console.log(res3);
這樣有個弊端,會進行2次循環。最優是使用原生for
進行處理,固然也能夠使用reduce
。
var res4 = arr.reduce(function (pre, cur) { if (cur > 2) { pre.push(cur * 2); } return pre; }, []); console.log(res4);
咱們能夠這樣實現:
Array.prototype.reduce = function (callbackfn, preItem) { var i = 0; if (preItem === undefined) { //若是沒有預傳值,則表明默認爲第一個元素,從第2個元素開始遍歷 preItem = this[0]; i++; } for (var len = this.length; i < len; i++) { preItem = callbackfn(preItem, this[i], i, this); } return preItem; };
join
是合併數組爲字符串。
Array.prototype.join = function (sep = ',') { var str = ''; for (var i = 0; i < this.length; i++) { str += this[i] + (i === this.length - 1 ? '' : sep); } return str; };
做爲面試題問面試者,竟然鮮有人能回答。可能如今這種使用場景漸少的緣故。
在一個函數裏,return
關鍵字天然能夠跳出循環,但要繼續下面的邏輯的話,就只有使用break
。
對於多層循環嵌套,只須要在須要中斷的循環外面加個標籤(隨便一個字符串都行),就能夠繼續用break
關鍵字來中斷循環。
a: for(var i = 0; i < 10; i++){ for(var j = 10; j < 100; j++){ if(j == 10){ break a; } } }
更多更新內容會在 這裏