js手写数组常用的方法(面试常问)push、pop、unshift、shift、splice、indexOf、concat、reverse、forEach、map、filter、every等方法


前言

面试中经常会问到,比如让手写一个push方法等等,这期就写一些数组常用的方法

一、手写push方法

Array.prototype.push = Array.prototype.push || function() {
	for(var i = 0; i < arguments.length; i++) {
	  // 将传递的参数每次都追加到数组的最后面
	  this[this.length] = arguments[i]
	}
	// 返回数组长度
	return this.length;
}

二、手写pop方法

Array.prototype.pop = Array.prototype.pop || function() {
	// 如果数组为空,直接return;
	if(this.length === 0) return;
	// 先保存最后一个元素
	var lastItem = this[this.length - 1];
	// 通过设置数组的length属性来实现删除元素的效果
	this.length = this.length - 1;
	return lastItem;
}

三、手写unshift方法

Array.prototype.unshift = Array.prototype.unshift || function() {
	var argLen = arguments.length; // 获取实参列表的长度并保存
	var len = this.length + argLen; // unift完以后数组的长度 = 原数组的长度+实参列表的长度
	// 在这里采用倒着循环,目的是空出原数组的位置,然后将原数组中的元素后移
	for(var i = len - 1; i >= 0; i--) {
	  this[i] = this[i - argLen] // 将原数组元素后移
	  // 举例说明:假设原数组为[1, 2], 要插入的元素为a, b。经此操作原数组变成:[undefined, undefined, 1, 2]
	}
	// 循环实参列表,将实参列表中的元素依次填入原数组的空白位置
	for(var i = 0; i < argLen; i++) {
	  this[i] = arguments[i]
	}
	// 返回数组长度
	return len;
}

四、手写shift方法

Array.prototype.shift = Array.prototype.shift || function() {
	var res = this[0]; // 保存数组的第0项的值
	var len = this.length; // 获取并保存数组的长度
	for(var i = 0; i < len; i++) {
	  if(i >= len - 1) { // 当下标是数组的长度-1时,直接退出循环。因为最后一项没必要赋值
	    break;
	  } else {
	    // 将数组的后一项赋值给前一项
	    this[i] = this[i + 1];
	  }
	}
	// 删除数组的最后一项
	this.length = len - 1;
	
	return res; // 返回数组的第0项
}

五、手写splice方法

Array.prototype.splice = Array.prototype.splice || function() {
	// 没有传递参数的时候,直接返回一个空数组
	if(arguments.length === 0) return [];
	
	var startIndex = arguments[0]; // 起始下标
	var delArr = []; // 删除的元素
	// 传递一个参数的时候
	if(arguments.length === 1) {
	 if(startIndex >= 0) { // 起始下标是正数的情况
	   // 保存删除的元素
	   for(var i = startIndex; i < this.length; i++) {
	     delArr[i - startIndex] = this[i];
	   }
	   this.length = startIndex;
	 } else { // 如果起始下标是负数的话,应该是从后面开始
	   for(var i = 0; i < Math.abs(startIndex); i++) {
	     delArr[i] = this[this.length + startIndex + i]
	   }
	   // 删除对应的元素
	   this.length = this.length + startIndex;
	 }
	}
	// 当传递两个以上参数的时候
	if(arguments.length >= 2) {
	 var num = arguments[1]; // 替换的元素个数
	 var argLen = arguments.length - 2; // 待替换的元素的个数
	 // 如果splice的第二个参数是负数,则返回空数组,不做任何处理
	 if(num < 0) return [];
	
	 for(var i = startIndex; i < startIndex + num; i++) {
	   // 先保存代替换的元素
	   delArr[i - startIndex] = this[i];
	   // 替换对应的元素
	   this[i] = arguments[i - startIndex + 2];
	 }
	 // 处理待替换元素个数大于替换元素个数的情况
	 if(argLen > num) {
	   // 提前保存可能会被替换的元素但是不应该被替换的元素。
	   // 例如:['a', 'b', 'c', 'd'],假设被替换的元素应该是a,b,但是实际传递了1, 2, 3 这三个元素。那么c, d就不应该被替换
	   var saveArr = [];
	   // 计算可能会被替换的元素的个数
	   var count = this.length - num - startIndex;
	   // 保存会被替换的元素的个数
	   for(var i = 0; i < count; i++) {
	     saveArr[i] = this[this.length - count + i];
	   }
	   // console.log(saveArr)
	   // 将数组中对应的元素个数进行替换
	   for(var i = startIndex; i < startIndex + argLen; i++) {
	     // 替换对应的元素
	     this[i] = arguments[i - startIndex + 2];
	   }
	   for(var i = 0; i < saveArr.length; i++) {
	     this[this.length] = saveArr[i];
	   }
	 }
	 // 处理待替换的元素个数小于替换的元素个数的情况
	 if(argLen < num) {
	   for(var i = 0; i < num - argLen; i++) {
	     // 循环数组中的元素
	     for(var j = 0; j < this.length; j++) {
	       // 将不是undefined的元素前置,是undefined的元素后置
	       this[j] == undefined && (this[j] = this[j + 1]);
	     }
	   }
	   // 删除是undefined的元素
	   this.length = this.length - (num - argLen);
	 }
	}
	return delArr;
}

六、手写indexOf方法

Array.prototype.indexOf = Array.prototype.indexOf || function(el, startIndex) {
	// 设置参数的默认值
	startIndex = startIndex || 0;
	var index = -1;
	if(startIndex >= 0) {
	  for(var i = startIndex; i < this.length; i++) {
	    this[i] === el && (index = i);
	  }
	}
	return index;
}

七、手写concat方法

Array.prototype.concat = Array.prototype.concat || function() {
	// 声明一个新数组
	var arr = [];
	// 将原数组的值拷贝到新数组
	for(var i = 0; i < this.length; i++) {
	  arr[i] = this[i];
	}
	if(arguments.length > 0) {
	  // 将传递的新数组依次拷贝到 arr 中
	  for(var i = 0; i < arguments.length; i++) {
	    // 判断传入的参数是否是一个数组
	    if(Object.prototype.toString.call(arguments[i]) === '[object Array]') {
	      for(var j = 0; j < arguments[i].length; j++) {
	        arr[arr.length] = arguments[i][j]
	      }
	    } else {
	      arr[arr.length] = arguments[i];
	    }
	  }
	}
	// 返回 arr 这个新数组
	return arr;
}

八、手写reverse方法

Array.prototype.reverse = function() {
	var result = []; // 声明一个新数组
	// 将原数组倒序放入新数组
	for(var i = this.length - 1; i >= 0; i--) {
	  result[result.length] = this[i];
	}
	// 修改原数组中的值
	for(var i = 0; i < result.length; i++) {
	  this[i] = result[i];
	}
	return result;
}

九、手写forEach方法

Array.prototype.forEach = Array.prototype.forEach || function(fn) {
	// this指向调用forEach方法的数组实例	
	for(var i = 0; i < this.length; i++) {
	  fn(this[i], i, this)
	}
}

十、手写map方法

Array.prototype.map = Array.prototype.map || function(fn) {
	// 声明一个新数组
	var arr = []
	for(var i = 0; i < this.length; i++) {
	  // fn 函数的执行结果即为调用map方法时传递的函数的返回值(即判断条件)
	  arr.push(fn(this[i], i, this))
	}
	return arr;
}

十一、手写filter方法

Array.prototype.filter = Array.prototype.filter || function(fn) {
	var arr = [];
	for(var i = 0; i < this.length; i++) {
	  // fn 的执行结果即为调用filter方法时传递的函数的返回值(即为过滤条件)
	  fn(this[i], i, this) && arr.push(this[i])
	}
	return arr;
}

十二、手写every方法

Array.prototype.every = Array.prototype.every || function(fn) {
	var flag = true;
	for(var i = 0; i < this.length; i++) {
	  // 思路:检查数组中的元素是否都满足条件,只要有一个不满足条件就将 flag 设置为false同时退出循环
	  if(!fn(this[i], i, this)) {
	    flag = false;
	    break;
	  }
	}
	// 返回flag,flag的最终结果即为every方法的最终执行结果
	return flag;
}

十三、手写some方法

Array.prototype.some = Array.prototype.some || function(fn) {
	var flag = false;
	for(var i = 0; i < this.length; i++) {
	  if(fn(this[i], i, this)) {
	    flag = true;
	    break;
	  }
	}
	return flag;
}

十四、手写reduce方法

Array.prototype.reduce = Array.prototype.reduce || function(fn, initialValue) {
	// 判断初始化的值是否存在,如果存在下标从0开始,不存在下标从1开始
	var startIndex = initialValue ? 0 : 1;
	// 如果初始化的值存在,回调函数的第一个参数为初始化的值,不存在初始化的值为数组的第一项
	var type = initialValue ? initialValue : this[0];
	for(var i = startIndex; i < this.length; i++) {
	  // 将返回值赋给回调函数的第一个参数
	  type = fn(type, this[i], i, this)
	}
	return type;
}

十五、手写findIndex方法

Array.prototype.findIndex = function(fn) {
	var index = -1;
	for(var i = 0; i < this.length; i++) {
	  // 如果满足条件
	  if(fn(this[i], i, this)) {
	    // 将 i 赋值给 index
	    index = i;
	    // 退出循环
	    break;
	  };
	}
	return index;
}

十六、手写flat方法

// 参数deep: 代表递归的层级
Array.prototype.flat = Array.prototype.flat || function(deep) {
	// 给deep设置默认值,如果传递deep择取传递的deep,如果没有传递则默认是1
	deep = deep == undefined ? 1 : deep;
	var arr = [];
	for(var i = 0; i < this.length; i++) {
	  // 判断数组中的元素是否是一个数组并且 deep 是否大于0
	  if(Object.prototype.toString.call(this[i]) === '[object Array]' && deep > 0) {
	    arr = arr.concat(this[i].flat(deep - 1))
	  } else {
	    // 如果不是数组则将其添加到 arr 中
	    arr.push(this[i])
	  }
	}
	return arr;
}


总结

喜欢的话可以关注一下博主

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值