「前端練習場」原生 JavaScript 手寫各種 數(shù)組 API
前言
JavaScript 中的數(shù)組類型提供了很多原生方法供我們使用,本文會?模擬實現(xiàn)?一些常用的數(shù)組 API。
「前端練習場」?將會持續(xù)更新,不同于之前的?【前端進階之路】?和?【從頭到腳】?這兩個系列,練習場?主要側重于基礎知識的練習鞏固,大家對這個系列有好的建議也可以在評論區(qū)和我交流 ? 。
另外我自己也是在不斷的學習中,如果有不對的地方麻煩大家斧正,我會及時更新,感謝。
博客地址??? fe-code[1]
API
數(shù)組的 API 有很多,我這里放一些常用的。如果大家有其他的實現(xiàn)方法,可以放在評論區(qū),我看到了會更新到文章中 ^_^。`
本文不是具體講某個 API 的基本用法,所以對這些 API 用法不太熟悉的同學需要先自行學習。另外大部分實現(xiàn),在 MDN 上都有。
前往 —>?MDN[2]?學習基礎用法。
在正式開始實現(xiàn)之前,先看一個例子。
let arr = [];arr[3] = 3;// arr.length ? arr[0] ? 0 in arr ?// 4 undefined fasle
這個東西在后面的實現(xiàn)中會出現(xiàn),所以大家先了解一下。數(shù)組的下標不一定是連續(xù)的,直接賦值還會影響它的長度。
forEach
?簡單實現(xiàn)
// forEach 支持傳入兩個參數(shù),callback、thisArg// callback 返回3個參數(shù),當前元素、當前元素索引、原數(shù)組// 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) { // 數(shù)組的下標并不一定是連續(xù)的fn.call(context, arr[index], index, arr);}index ++;}};
?支持 async/await
之前見大佬們討論過這個問題,所以提一下。forEach 在正常情況像下面這么寫肯定是做不到同步的,程序不會等一個循環(huán)中的異步完成再進行下一個循環(huán)。原因很明顯,在上面的模擬中,while 循環(huán)只是簡單執(zhí)行了 callback,所以盡管 callback 內使用了 await ,也只是影響到 callback 內部。
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
map 的實現(xiàn)大體和 forEach 類似,只是返回了一個新數(shù)組。
// 參數(shù)和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; // 返回值作為一個新數(shù)組}index ++;}return newArr;};
reduce
reduce 稍微麻煩一些,需要根據(jù)第二個參數(shù)是否存在,使用不同的處理方式。
Array.prototype.myreduce = function (...arg) {let arr = this;let len = arr.length;let index = 0;let fn = arg[0], result;if (arg.length >= 2) { // 判斷是否有第二個參數(shù),有的話作為回調函數(shù)運行的初始值result = arg[1];} else {// reduce 在沒有第二個參數(shù)的時候,會把數(shù)組的第一項作為回調的初始值// 第一項并不一定是 a[0]while (index < len && !(index in arr)) {// 下標小于數(shù)組長度且下標不屬于該數(shù)組就一直循環(huán),用來找到數(shù)組的第一項index++;}if (index >= len) { // 如果第一項大于等于數(shù)組長度,則說明是空數(shù)組throw new TypeError( '空數(shù)組且沒有初始值' );}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;};
reduce 實現(xiàn)一個 map
經(jīng)常會有面試問到這道題,順便寫一下。
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]; // 返回一個新數(shù)組}, []);};
filter
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]; // 如果返回值為真,就添加進新數(shù)組}index ++;}return newArr;};
find 和 findIndex
find 和 filter 很類似,找到一個就返回當前元素,找不到返回 undefined。
findIndex 找到返回下標,找不到返回 -1。和 indexOf 類似,區(qū)別是支持回調。
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
some 和 find,除了返回值有區(qū)別,其他的可以說都一樣。
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};
every
跟 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 在實現(xiàn)和功能上都很相似,只是返回值上有一些差別,所以更要在合適的場景使用合適的方法。
includes 和 indexOf
這兩個都可以用來查找數(shù)組中是否有某個元素,只是返回值有區(qū)別。
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);// 允許傳入負數(shù),意為從倒數(shù)第幾位開始查找// 負數(shù)依然是按升序查找// 避免傳入負數(shù)絕對值大于len而使k出現(xiàn)負數(shù),k設置最小值 0function 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 不支持查找 NaNArray.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);// 處理負數(shù)while (k < len) {if (k in arr) {if (val === arr[k]) return k; // 找到返回下標}k ++;}return -1; // 找不到返回 -1};
join
使用連接符,將數(shù)組轉成字符串
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;};
好了,大致就寫這些,如果大家覺得有必要補充的可以跟我說。
