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,欢迎各类技术交流,期待你的加入;
微信群:有须要的同窗能够加我好友,我拉你入群,文末有二维码 ^_^。
若是你看到了这里,且本文对你有一点帮助的话,但愿你能够动动小手支持一下做者,感谢🍻。文中若有不对之处,也欢迎你们指出,共勉。好了,又耽误你们的时间了,感谢阅读,下次再见!
近期文章:
感兴趣的同窗能够关注下个人公众号 前端发动机,好玩又有料。也可加我好友,你们一块儿学习交流 ^_^。