JavaScript 中的數組類型提供了不少原生方法供咱們使用,本文會 模擬實現 一些經常使用的數組 API。javascript
另外我本身也是在不斷的學習中,若是有不對的地方麻煩你們斧正,我會及時更新,感謝。前端
博客地址 🍹🍰 fe-codevue
數組的 API 有不少,我這裏放一些經常使用的。若是你們有其餘的實現方法,能夠放在評論區,我看到了會更新到文章中 ^_^。java
本文不是具體講某個 API 的基本用法,因此對這些 API 用法不太熟悉的同窗須要先自行學習。另外大部分實現,在 MDN 上都有。 node
前往 —> MDN 學習基礎用法。git
在正式開始實現以前,先看一個例子。github
let arr = [];
arr[3] = 3;
// arr.length ? arr[0] ? 0 in arr ?
// 4 undefined fasle
複製代碼
這個東西在後面的實現中會出現,因此你們先了解一下。數組的下標不必定是連續的,直接賦值還會影響它的長度。面試
// forEach 支持傳入兩個參數,callback、thisArg
// callback 返回3個參數,當前元素、當前元素索引、原數組
// thisArg 傳入後,改變 callback 的 this 指針
Array.prototype.myforeach = function (fn, context = null) {
let index = 0;
let arr = this;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < arr.length) {
if (index in arr) { // 數組的下標並不必定是連續的
fn.call(context, arr[index], index, arr);
}
index ++;
}
};
複製代碼
以前見大佬們討論過這個問題,因此提一下。forEach 在正常狀況像下面這麼寫確定是作不到同步的,程序不會等一個循環中的異步完成再進行下一個循環。緣由很明顯,在上面的模擬中,while 循環只是簡單執行了 callback,因此儘管 callback 內使用了 await ,也只是影響到 callback 內部。mongodb
arr.myforeach(async v => {
await fetch(v);
});
複製代碼
要支持上面這種寫法,只要稍微改一下就好。數組
Array.prototype.myforeach = async function (fn, context = null) {
let index = 0;
let arr = this;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < arr.length) {
if (index in arr) {
try {
await fn.call(context, arr[index], index, arr);
} catch (e) {
console.log(e);
}
}
index ++;
}
};
複製代碼
map 的實現大致和 forEach 相似,只是返回了一個新數組。
// 參數和forEach同樣
// callback 須要有一個返回值
Array.prototype.mymap = function (fn, context = null) {
let arr = this;
let len = arr.length;
let index = 0;
let newArr = [];
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
let result = fn.call(context, arr[index], index, arr);
newArr[index] = result; // 返回值做爲一個新數組
}
index ++;
}
return newArr;
};
複製代碼
reduce 稍微麻煩一些,須要根據第二個參數是否存在,使用不一樣的處理方式。
Array.prototype.myreduce = function (...arg) {
let arr = this;
let len = arr.length;
let index = 0;
let fn = arg[0], result;
if (arg.length >= 2) { // 判斷是否有第二個參數,有的話做爲回調函數運行的初始值
result = arg[1];
} else {
// reduce 在沒有第二個參數的時候,會把數組的第一項做爲回調的初始值
// 第一項並不必定是 a[0]
while (index < len && !(index in arr)) {
// 下標小於數組長度且下標不屬於該數組就一直循環,用來找到數組的第一項
index++;
}
if (index >= len) { // 若是第一項大於等於數組長度,則說明是空數組
throw new TypeError( '空數組且沒有初始值' );
}
result = arr[index++]; // 賦值以後下標+1
}
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
result = fn(result, arr[index], index, arr); // 每次回調的返回值,都會傳入下次回調
}
index ++;
}
return result;
};
複製代碼
常常會有面試問到這道題,順便寫一下。
Array.prototype.mapByreduce = function (fn, context = null) {
let arr = this;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
return arr.reduce((pre, cur, index, array) => {
let res = fn.call(context, cur, index, array);
return [...pre, res]; // 返回一個新數組
}, []);
};
複製代碼
filter 通常用來篩選。
Array.prototype.myfilter = function (fn, context = null) {
let arr = this;
let len = arr.length;
let index = 0, k = 0;
let newArr = [];
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
let result = fn.call(context, arr[index], index, arr);
if (result) newArr[k++] = arr[index]; // 若是返回值爲真,就添加進新數組
}
index ++;
}
return newArr;
};
複製代碼
find 和 filter 很相似,找到一個就返回當前元素,找不到返回 undefined。
findIndex 找到返回下標,找不到返回 -1。和 indexOf 相似,區別是支持回調。
Array.prototype.myfind = function (fn, context = null) {
let arr = this;
let len = arr.length;
let index = 0;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
let result = fn.call(context, arr[index], index, arr);
if (result) return arr[index]; // 知足條件就返回
}
index ++;
}
return undefined;
};
複製代碼
some 和 find,除了返回值有區別,其餘的能夠說都同樣。
Array.prototype.mysome = function (fn, context = null) {
let arr = this;
let len = arr.length;
let index = 0;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
let result = fn.call(context, arr[index], index, arr);
if (result) return true; // 找到一個知足的,當即返回true
}
index ++;
}
return false; // 找不到返回 false
};
複製代碼
跟 some 相比,每一個成員都知足條件才返回 true,有一個不知足就返回 false。
Array.prototype.myevery = function (fn, context = null) {
let arr = this;
let len = arr.length;
let index = 0;
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
while (index < len) {
if (index in arr) {
let result = fn.call(context, arr[index], index, arr);
if (!result) return false; // 有一個不知足,就返回false
}
index ++;
}
return true;
};
複製代碼
剛剛接連幾個 filter、find、some、every 在實現和功能上都很類似,只是返回值上有一些差異,因此更要在合適的場景使用合適的方法。
這兩個均可以用來查找數組中是否有某個元素,只是返回值有區別。
Array.prototype.myincludes = function (val, fromIndex = 0) {
let arr = this;
let len = arr.length;
let k = Math.max(fromIndex >= 0 ? fromIndex : len - Math.abs(fromIndex), 0);
// 容許傳入負數,意爲從倒數第幾位開始查找
// 負數依然是按升序查找
// 避免傳入負數絕對值大於len而使k出現負數,k設置最小值 0
function check(x, y) {
return x === y ||
(typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
// 判斷 NaN
}
while (k < len) {
if (k in arr) {
if (check(val, arr[k])) return true; // 找到一個符合條件的,返回 true
}
k ++;
}
return false; // 沒找到 返回false
};
複製代碼
// indexOf 不支持查找 NaN
Array.prototype.myindexOf = function (val, fromIndex = 0) {
let arr = this;
let len = arr.length;
let k = Math.max(fromIndex >= 0 ? fromIndex : len - Math.abs(fromIndex), 0);
// 處理負數
while (k < len) {
if (k in arr) {
if (val === arr[k]) return k; // 找到返回下標
}
k ++;
}
return -1; // 找不到返回 -1
};
複製代碼
使用鏈接符,將數組轉成字符串
Array.prototype.myjoin = function (connector = ',') {
let arr = this;
let len = arr.length;
let str = '';
let k = 0;
while (k < len) {
if (k in arr) {
if (k === len -1) { // 最後一位不用鏈接
str += arr[k];
} else {
str += arr[k] + connector.toString();
}
}
k ++;
}
return str;
};
複製代碼
好了,大體就寫這些,若是你們以爲有必要補充的能夠跟我說。
qq前端交流羣:960807765,歡迎各類技術交流,期待你的加入;
微信羣:有須要的同窗能夠加我好友,我拉你入羣,文末有二維碼 ^_^。
若是你看到了這裏,且本文對你有一點幫助的話,但願你能夠動動小手支持一下做者,感謝🍻。文中若有不對之處,也歡迎你們指出,共勉。好了,又耽誤你們的時間了,感謝閱讀,下次再見!
近期文章:
感興趣的同窗能夠關注下個人公衆號 前端發動機,好玩又有料。也可加我好友,你們一塊兒學習交流 ^_^。