数组方法及自己实现

170 阅读10分钟

栈、队列方法

  • 栈:一个门

    • push 从尾加,pop从尾删。

    • push(): 可以接受任意类型的参数,将他们逐个添加到数组的末尾,并返回数组的长度

    • pop():从数组的末尾移除最后一项,减少数组的length值,返回移除的项

    var arr = [1,2]
    var len = arr.push(3,4,5)
    console.log(arr)  //[ 1, 2, 3, 4, 5 ]
    console.log(len)  //5
    var delItem = arr.pop()  
    console.log(arr)	//[ 1, 2, 3, 4 ]
    console.log(delItem)	//5  
    
  • 队列:两个门

    • shift 从头删,unshift从头加
    • shift() :移除数组中的第一个项,返回移除的项,同时将数组的长度减1
    • unshift():在数组的前端添加任意个项,并返回数组的长度

排序方法

  • reverse(): 反转数组项的顺序,返回反转后的数组

  • sort(): 返回排序后的数组 默认不带参数时:该方法会先调用每个数组项的toString()方法,然后按照字符序列排列ASIIC码。

以下代码是在node交互命令行中运行的

 > arr3 = [ 4,1,5,,undefined,null,undefined,{}]
 > [ 4, 1, 5, <1 empty item>, undefined, null, undefined, {} ]
 >arr3.sort()
 >[ 1, 4, 5, {}, null, undefined, undefined, <1 empty item> ]

ps、null,undefined没有toString方法

自定义排序:该方法可以接受一个比较函数作为参数,该比较函数返回结果,根据自己需要,修改if中的条件 ;返回负数,两个位置不交换;返回正数,两个数位置交换

var arr2 = [3,7,2,9,8]
arr2.sort(compare)
function compare(a,b){
    if(a>b){ //a<b升序 a>b降序
        return -1; //负数不交换
    }
    return 1;//正数交换
}
console.log(arr2)

//compare可精简为以下
function compare(a,b){
  return a-b; //降序 
  //return b-a;  //升序
}
  • 可以自己封装万能比较函数

截取方法

  • concat():数组拼接,先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,返回副本,不改变原数组。【如果是参数是数组展开放进去,如果是单个其他的直接放到末尾】

    > var arr = [1,2,3]
    undefined
    > arr.concat(7,8,9)
    [ 1, 2, 3, 7, 8, 9 ]
    >
    
  • slice():数组切割,

    slice(参数1 [,参数2])参数1:返回项的起始位置,参数2:结束位置,不包含结束位置。若只有一个参数,表示从该参数指定位置开始,截取到当前数组末尾的所有项。返回切割的元素组成的数组,不改变原数组。

    不传参数,还可用于数组的深复制

    var arr0 = [1,2,3]
    var arr1 = arr0;
    arr1[0] = 0
    arr //[0,2,3]  arr1会更改arr
    //---
    var arr2 = arr0.slice()
    arr2[0] = 2
    arr  //不会因为arr2改变而变化
    
  • splice():返回的是删除的元素组成的数组,改变原数组

    splice(参数1[,参数2,参数3,参数4])

    参数1:开始位置;参数2:删除的项数;参数3,添加元素一个或多个

    删除

    > var arr = [1,2,3]
    undefined
    > arr.splice(1)
    [ 2, 3 ]
    > arr
    [ 1 ]
    >只传一个参数,默认删到最后和slice传一个参数的效果一样
    
    > var arr = [32,7,3,26,0,1]
    > undefined
    > arr.splice(1,3)
    > [ 7, 3, 26 ]
    > arr
    > [ 32, 0, 1 ]
    >
    
    

    添加/插入:第二个参数为0

    >var arr = [32,7,3,26,0,1]
    >undefined
    >arr.splice(1,0,1,2)
    >[]
    >arr
    [ 32, 1, 2, 7, 3, 26, 0, 1 ]
    >  
    

    替换:将1,2替换成了3,4;【接着上面的code继续】

    [ 32, 1, 2, 7, 3, 26, 0, 1 ]
    > arr.splice(1,2,3,4)
    [ 1, 2 ]
    > arr
    [ 32, 3, 4, 7, 3, 26, 0, 1 ]
    >
    

索引方法

  • indexOf(参数1[,参数2]):从数组开头向后查找,使用全等操作符找不到该元素返回-1,找到了返回所在的下标

    参数1:要查找的项,参数2:索引开始的位置,默认为0

  • lastIndexOf:除了是从数组末尾向前查找,其余和indexOf一样

迭代方法

参数:第一个参数是每一项上运行的函数,第二个参数是用于更改函数内部的this指向,一般不写

  • every 所有元素符合条件,返回true 相当于&&
  • some 有元素符合条件 返回true 相当于||
  • filter 过滤满足条件的元素,返回被过滤的元素组成的数组
  • map 返回每次函数调用的结果组成的数组
  • forEach 对数组中的每一元素运行给定的函数 没有返回值 常用来遍历元素

是否更改原数组

改:push pop shift unshift reverse sort splice

不改:concat slice every some forEach map filter-->迭代方法

数组的方法可以链式调用

遍历数组的常用方法

有:for 、forEach、for of、for in(不推荐)

var array = ["xjm","sjak"]

for(var i =0;i<array.length;i++){
  console.log(array[i])//xjm sjak
}

for(value of array){
  console.log(value) //xjm sjak
}

for(key in array){
  console.log(array[key]) //xjm sjak
}

array.forEach(function(item,index,arr){
  console.log(item) //xjm sjak
})

虽然for… in 、 for… of 都能够变历数组,但是两者还是有很大区别的

主要区别在于他们的迭代方式:

  • for-in循环出来的式key,for-of循环出来的是value
  • for-in是ES5标准,for-of是ES6标准,兼容性可能存在问题
  • 推荐for-in遍历对象,for-of遍历数组

for...in 循环除了遍历数组元素外,还会遍历自定义属性。所以万一给一个数组加了个属性也会被遍历出来, 故不推荐for-in遍历数组。(数组本质是对象,是可以自定义添加属性的)。 for-of则不会,for...of只可以循环可迭代的可迭代属性,不可迭代属性在循环中被忽略了

未赋值的值是不会在foreach循环迭代的,但是手动赋值为undefined的元素是会被列出的

自己实现数组的一系列方法

在学习了数组的一系列方法后,脑海中始终有很多的好奇和疑惑,想知道他们是如何实现的,如果换做是我,我又该如何实现这些功能呢 ?

目前实现:reverse,pop,push,shift,unshift,concat,every,some,slice,map,filter, forEach。

【尽管代码不够最优,但我先记录一下自己最初的想法,往后会多多学习并修正】

总结:重点关注,参数,返回值,是否更改原来的数组;基于关注的点,把要实现的功能理一下,然后用代码表达出来。

1,参数不确定,借助arguments对象

2,是否更改原数组,涉及到this的更改问题--如何实现数组的深复制or浅复制

​ 改原数组:push pop shift unshift reverse sort splice

​ 不改原数组:concat slice every some forEach map filter

3,return 返回值;

​ 返回更改后数组的长度:push pop shift unshift 【注意push,unshift接收一个或多个参数的情况】

​ 返回更改后的数组: reverse sort

​ 返回操作后的数组:concat slice splice(返回删除元素组成的数组)map filter【注意concat可接收数组作为参数,和多个罗列的数值作为参数时的处理情况】

​ 返回布尔值:some,every

​ 没有返回值:forEach

参考code:

var array = [1, 2, 3,{}];
/*思路:
var arr0 = []
arr0.length = arr.length;
 arr0[0]=arr[arr.length-1]
 arr0[1]=arr[arr.length-2]
arr0[2]=arr[arr.length-3]
 arr0[i]=arr[arr.length-i-1]
for(var i=0;i<arr.length;i++){
   arr0[i]=arr[arr.length-i-1]
 }
 console.log(arr0)
*/
/**------myReverse */
//this 谁调用就是谁
Array.prototype.myReverse = function() {
  var arr = [];
  for (var i = 0; i < this.length; i++) {
    arr[i] = this[this.length - i - 1];
  }
  //加了,就是更改原数组 
  for(var j=0;j<this.length;j++){
    this[j] = arr[j]
  }
  return this;
};
array.myReverse();

/*----myPop*/
Array.prototype.myPop = function(){
    this.length -= 1;
    return this.length;
}
array.myPop()

/*----myPush*/
//array[len+0]=arguments[0]
// array[len+1]=arguments[1]
// array[len+2]=arguments[2]
Array.prototype.myPush = function() {
  // var nums = arguments.length;//实参个数,arguments是类数组对象
  for (key in arguments) {
    this[this.length] = arguments[key]; //this.length随着我们给this[this.length]赋值,长度会增加
  }
  return this.length;
};
array.myPush(7, 8, 9);

/**------myShift */
/**shift从头部删除
 * arr[0] = arr[1]
 * arr[1] = arr[2]
*/
Array.prototype.myShift = function(){
  for(var i=0;i<this.length;i++){
    this[i] = this[i+1]
  }
  this.length--;//删掉最后一个undefined
  return this.length;
}
array.myShift()
/**---myUnshift---在头部加*/
/**嫁入3个参数的话 arguments.length=3
 * arr[arguments.length]=arr[0]
 * arr[arguments.length+1]=arr[1]
 * arr[arguments.length+2]=arr[2]
 * arr[0]=arguments[0]
 * arr[1]=arguments[1]
 * arr[2]=arguments[2]
 */
var arr2=[1,2,3]
Array.prototype.myUnshift=function(){
// console.log(arguments) for in 、for都可遍历它
for(let i=0;i<arguments.length;i++){
   this[arguments.length+i]=this[i]
    this[i]=arguments[i]
}
  return this.length;
}
arr2.myUnshift(4,5,6)

/**----myConcat */
var array = [1,1,1,1]
/**
 * 不改变原数组
 * 如果参数是数组,需要拆开放到末尾
 * 如果参数是其他,直接放进末尾
 * 返回副本
 */
Array.prototype.myConcat=function() {
    var temp = [];
    for(let i =0;i<this.length;i++){
        temp[i] = this[i]
    }
    // console.log(arguments.length)
    // console.log(Array.isArray(arguments[0]))
    if(arguments.length==1&&(Array.isArray(arguments[0]))){
        //如果只有一个是数组的参数
        var paramsArr = arguments[0];
        for(var i =0;i<paramsArr.length;i++){
            temp.push(paramsArr[i])
        }
    }else{
        //是其他散列的参数,类似1,2,3,就从arguments中遍历出来
        for(key in arguments){
            temp.push(arguments[key])
        }
    }
    return temp;
  }
//   var result = array.myConcat('hello','h')
  var result = array.myConcat([6,7])
  console.log(result,array)

/**--------myEvery---- */
Array.prototype.myEvery =function(func,funcThis){
    //this==>arr
    for(var i=0;i<this.length;i++){
        var result = func.call(funcThis,this[i],i,this)
        if(!result){
            break;           
        }
    }
    return result;
}

/**------mySome---- */

Array.prototype.mySome = function(func,funcThis) {
//第二个参数是用于更改函数内部的this指向,一般不写
    for(var i=0;i<this.length;i++){
        var result = func.call(funcThis,this[i],i,this);
        if(result){
            break;
        }    
    }
    return result;
}
var result = array.mySome(function(item,index,arr){
    // console.log(item,index,arr) 对应上面在call中传递的参数
    return item>5;
});
console.log(result) //false

/**------myMap---*/
Array.prototype.myMap = function(func, funcThis) {
  var temp = [];
  // console.log(this)
 for (var i = 0; i < this.length; i++) {
  funcThis ? temp.push(func.call(funcThis, this[i], i, this)):
  temp.push(func(this[i], i, this));
  }
  return temp;
};

console.log(
  array.myMap(function(item) {
    return item * 2; //注意要return
  })
);
var array = [1, 2, 3, 4, 5, 6, 7];

/**------myFilter---*/
Array.prototype.myFilter = function(func, funcThis) {
  var temp = [];
  // console.log(this)
  for (var i = 0; i < this.length; i++) {  
    var result = func.call(funcThis, this[i], i, this);
    if(result){
         temp.push(this[i]);
    }
   
  }
  return temp;
};

console.log(
  array.myFilter(function(item) {
    return item<4; //注意要写return
  })
);
/**------myForEach---*/
Array.prototype.myForEach = function(func, funcThis) {
  for (var i = 0; i < this.length; i++) {  
    func.call(funcThis, this[i], i, this);  
  }
};

array.myForEach(function(item) {
	console.log(item); //forEach没有返回值,用来遍历数组,不用写return
})

封装mySlice时this问题

var array = [1, 2, 3,4,5,6,7];

/**------mySlice 不更改原数组*/
//接收0个,一个或两个参数
/**
 * 0个 可实现数组的深复制【即单纯复制一份数组里的值出去,不与原数组有其他瓜葛】
 * 1个 表示从该参数的指定位置开始,截取到当前数组的末尾所有项
 * 2个 表示从指定位置开始到结束位置,不包含结束的
 * 还需要注意有参时参数为负数的情况
 * */

Array.prototype.mySlice = function() {
  switch (arguments.length) {
    case 0:
      var temp = [];
      for (let i = 0; i < this.length; i++) {
        temp.push(this[i]);
      }
      return temp;

    case 1:
      // console.log(arguments[0])
      arguments[0]=arguments[0] > 0 ? arguments[0]: this.length+arguments[0];
      var temp = [];
      for (let i = arguments[0]; i < this.length; i++) {
        temp.push(this[i]);
      }
      return temp;
    case 2:
      arguments[0]=arguments[0] > 0 ? arguments[0]: this.length+arguments[0];
      arguments[1]=arguments[1] > 0 ? arguments[1]: this.length+arguments[1];
      var temp = [];
      for (let i = arguments[0]; i < arguments[1]; i++) {
        temp.push(this[i]);
      }
      return temp;
  }
};
console.log(array.mySlice(-6,2));
console.log(array.mySlice(-1));
console.log(array.mySlice(-1,-2));
console.log(array.mySlice(-1,2));

  • 代码优化,提取一些重复的code,进行封装!

  • 还可以为了保证代码的健壮性,对传入的参数做一些错误处理

  • 后续有想法的话根据情况写测试函数

Array.prototype.mySlice = function() {
  switch (arguments.length) {
    case 0:
      // return te(); 不能这样用,因为这样this指向的时全局对象
      return te.call(this);
    case 1:
      return te.call(this, arguments[0]);
    case 2:
      return te.call(this, arguments[0], arguments[1]);
  }
};
/*te是我瞎取的,习惯不好,不要学*/
function te(params1, params2) {
  // console.log(this) //this-->global 搞了半天undefined,原来是this指向问题
  if (params1 || params2) {
    params1 = params1 > 0 ? params1 : this.length + params1;
    params2 = params2 > 0 ? params2 : this.length + params2;
  }

  var temp = [];
  for (let i = params1 || 0; i < (params2 || this.length); i++) {
    temp.push(this[i]);
  }
  return temp;
}

总之往代码更优雅和高效的方向进击!

送一张图:数组我们能赢!