开始
前两节对数组的十多个API进行了总结整理,下面继续按照MDN 上的顺序和介绍依次来整理实现相关API。
数组方法
Array.prototype.includes()
作用
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
应用
console.log([NaN,1,2,2,NaN,0].includes(122));//false
console.log([NaN,1,2,2,NaN,0].includes(2,-2));//false
console.log([NaN,1,2,2,NaN].includes(2));//true
console.log([NaN,1,2,2,NaN,0].includes(NaN));//true
console.log([NaN,1,2,2,NaN,0].includes(-0));//true
实现
思路
- 在数组的原型上定义方法。
- fromIndex 入参是数字,默认是0(如果不能转为合法数字,默认为0)
- 比较时注意NaN。该API 任务+0.-0 和0相等,也得注意
- 如果 fromIndex 为负值,与数组长度相加计算出的索引将作为开始搜索searchElement的位置。如果计算出的索引小于 0,则整个数组都会被搜索。
代码实现
Array.prototype._includes=function(target,fromIndex){
let data=this
//转为数字
fromIndex=fromIndex-0;
//看是否是合法数字
if(!fromIndex ||typeof fromIndex!='number')
{
fromIndex=0;
}
if(fromIndex<0)
{
//索引为-1 与数组长度相加,如果还是负数,则从0开始遍历
fromIndex=(this.length+fromIndex)>0 ?(this.length+fromIndex):0
}
for(let i=fromIndex;i<data.length;i++)
{
// [NaN].indexOf(NaN) -1 indexof 对NaN 不准确
//但是API没有处理 +0 和-0,认为 0,+0,-0相等,也不能用Object.is
// if(Object.is(data[i],target))
// {
// return true;
// }
if(data[i]===target)
{
return true;
}
// 自己都不等于自己,代表是NaN
if(data[i]!=data[i] && target!=target)
{
return true;
}
}
return false;
}
测试
使用时需要注意fromIndex 为负数的情况
console.log([NaN,1,2,2,NaN,0].includes(122));//false
console.log([NaN,1,2,2,NaN,0].includes(2,-2));//false
console.log([NaN,1,2,2,NaN].includes(2));//true
console.log([NaN,1,2,2,NaN,0].includes(NaN));//true
console.log([NaN,1,2,2,NaN,0].includes(-0));//true
Array.prototype.indexOf()
作用
indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。既能得到索引,也可以根据返回的是-1判断数组中是否存在目标对象。但是需要注意的是NaN。[NaN].indexOf(NaN) 返回的是-1。
应用
以下是几种常见情况。
console.log([NaN,1,2,2,NaN,0].indexOf(2));//2
console.log([NaN,1,2,2,NaN,0].indexOf(2,4));//-1
console.log([NaN,1,2,2,NaN,0].indexOf(1,-5));//1
console.log([NaN,1,2,2,NaN,0].indexOf(NaN));//-1
实现
思路
- 在数组的原型上定义方法。
- 索引值可正也可以为负值,为负值,还是通过与数组长度相加计算索引起始值,不会影响比较顺序。默认为0
- 不用考虑NaN 的特殊性。
代码实现
其实和includes 方法基本一样。
Array.prototype._indexOf=function(target,fromIndex=0){
let data=this;
fromIndex=fromIndex-0 ||0;
if(!fromIndex ||typeof fromIndex!='number')
{
fromIndex=0;
}
if(fromIndex<0)
{
//索引为-1 与数组长度相加,如果还是负数,则从0开始遍历
fromIndex=(this.length+fromIndex)>0 ?(this.length+fromIndex):0
}
for(let i=fromIndex;i<data.length;i++)
{
if(data[i]===target)
{
return i;
}
}
//没有匹配返回-1
return -1;
}
测试
经过测试,和原生方法能得到同样的结果。
console.log([NaN,1,2,2,NaN,0]._indexOf(2));//2
console.log([NaN,1,2,2,NaN,0]._indexOf(2,4));//-1
console.log([NaN,1,2,2,NaN,0]._indexOf(1,-5));//1
console.log([NaN,1,2,2,NaN,0]._indexOf(NaN));//-1
Array.prototype.lastIndexOf()
作用
lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。 从名称就看出和indexOf的区别就是它返回的是最后一个索引。 基本实现方法和基本一致。
实现
思路
- 与indexOf 区别就是逆向查找
代码实现
Array.prototype._lastIndexOf=function(target,fromIndex=0){
let data=this;
fromIndex=fromIndex-0 ||0;
if(!fromIndex ||typeof fromIndex!='number')
{
fromIndex=this.length-1;
}
if(fromIndex<0)
{
//索引为-1 与数组长度相加,如果还是负数,则从0开始遍历
fromIndex=(this.length+fromIndex)<this.length-1 ?(this.length+fromIndex):this.length-1
}
for(let i=fromIndex;i>=0;i--)
{
if(data[i]===target)
{
return i;
}
}
//没有匹配返回-1
return -1;
}
测试
得到如下效果。
var array = [2, 5, 9, 2];
var index = array._lastIndexOf(2);
console.log(index) //3
var index2= array._lastIndexOf(2,-1);
console.log(index2)//3
var index2= array._lastIndexOf(2,2);
console.log(index2)//0
var index3= array._lastIndexOf(2,-2);
console.log(index3)//0
var index4= array._lastIndexOf(6);
console.log(index4)//-1
Array.prototype.map()
作用
map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
这也是一个很常用的方法,特别在React中,到处都是,其实很简单,它与forEach 方法唯一的区别就是将回调返回值存贮到数组中并返回。
实现
思路
*利用for 循环实现即可。
代码实现
Array.prototype._map=function(callback,thisArg){
if(typeof callback!='function')
{
throw new TypeError('callback is not a function')
}
let context=thisArg || this;
let res=[];
for(let i=0;i<this.length;i++)
{
let value=this[i];
res.push(callback.call(context,value));
}
return res;
}
测试
很简单,就不再多言。
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1._map(x => x * 2);
console.log(map1); //[ 2, 8, 18, 32 ]
const map = array1._map(function(x){
return x*(this.n)
},{n:3});
console.log(map); //[ 3, 12, 27, 48 ]
Array.prototype.reduce()
作用
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
这是一个很重要很常用的API,实现累加的效果,比如数组求和,react中常用到的复合函数
应用
- 数组求和
//无初始值
console.log([1,2,3,4].reduce((cur,next)=>cur+next)) //10
//有初始值
console.log([1,2,3,4].reduce((cur,next)=>cur+next,6)) //16
- 复合函数(React中常用)
function compose(...fns){
if(fns.length===0)
{
return false;
}
if(fns.length==1)
{
return fns[0];
}
return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
function A(name){
return name+'A执行';
}
function B(name){
return name+'B执行';
}
function C(name){
return name+'C执行';
}
let fn=compose(A,B,C) //A(B(C(...args)) //从里往外执行
console.log(fn('begin:'))//begin:C执行B执行A执行
实现
思路
- 在数组的原型对象定义方法
- 无初始值,取数组第一项。
- 不能改变原数组。
- 空数组执行抛出异常
代码实现
Array.prototype._reduce=function(accumulator,currentValue){
if(typeof accumulator!='function')
{
throw new TypeError('accumulator is not a function')
}
if(this.length===0)
{
throw new Error('Error: Reduce of empty array with no initial value')
}
//数组复制一份(为了简单,本应该写原理应该实现数组的复制,或者不应该采用shift方法)
let arr=[...this];
if(!currentValue && currentValue!=0)
{
currentValue=arr.shift();
}
let cur=currentValue;
arr.forEach((item,index)=>{
cur=accumulator(cur,item,index,this);
});
return cur;
}
测试
同样也能实现如下效果。
console.log([1,2,3,4]._reduce((cur,next)=>cur+next)) //10
console.log([1,2,3,4]._reduce((cur,next)=>cur+next,6)) //16
let fn=compose(A,B,C)
console.log(fn('begin:'))//begin:C执行B执行A执行
还有一个Array.prototype.reduceRight() 方法,从字面量就能看出只是遍历顺序从右往左了而已。实现时只需要把数组reverse就行了,在此就不做多的介绍了。