数组API的调用使得我们在js的使用中更加方便和快捷,而今天我们要探索数组API方法的底层原理,接下来和大家一起学习数组中的一些方法重构。
我们主要学习一下,栈与队列方法、迭代方法两种数组API重构。
重写push方法()
可接受任意类型的参数,将它们逐个添加到数组的末尾,并返回数组的长度.
//1.在原型中添加自写的push
Array.prototype.myPush = function () {
//2.使用arguments接收传进来的参数,遍历传进来了多少个
for (var i = 0; i <= arguments.length-1; i++) {
//3.将遍历的arguments中的值添加进数组里
this[this.length] = arguments[i];
}
// 4.返回数组的长度
return this.length;
}
var arr = new Array();
arr = [1, 2, 3, 4, 5];
//例子1:
console.log(arr.myPush(6, 7, 8, 9));//9
console.log(arr);
//[
// 1, 2, 3, 4, 5,
// 6, 7, 8, 9
// ]
//例子2:
//console.log(arr.myPush({}));//6
// console.log(arr);//[ 1, 2, 3, 4, 5, {} ]
重写pop()方法
从数组的末尾移除最后一项,减少数组的length值,返回移除的项
Array.prototype.myPop = function () {
if (this.length == 0) {
// 判断数组是否为空
return undefined;
}
//先返回最后一个值
var last = this[this.length - 1];
//移除最后一个值
this.length = this.length - 1;
return last;
}
var arr = new Array();
//例子1:
arr = [1, 2, 3, 4, 5];
console.log(arr.myPop()); //5
console.log(arr); //[ 1, 2, 3, 4 ]
//例子2:
// arr = [];
// console.log(arr.myPop());//undefined
3.myUnshift()
myUnshift()
方法可向数组的开头添加一个或更多元素,并返回新的长度。
Array.prototype.myUnshift = function () {
for (var i = this.length + arguments.length - 1; i >= 0; i--) {
//当i递减循环到小于arguments的长度的时候,
// 说明数组已经向后移动完成,已经移动到arguments需要的长度的后面了。
if (this[i] < arguments[i]) {
//把arguments的值传进去给数组移动玩后空出来的位置
this[i] = arguments[i];
} else {
// 数组一个个向后挪位置,需要移动arguments.length长度,(i减去多少就是要移动多少位置)
this[i] = this[i - arguments.length];
}
}
return this.length;
}
var arr = new Array();
arr = [1, 2, 3, 4, 5];
console.log(arr.myUnshift(7, 8)); //7=5+2
console.log(arr);
// [
// 7, 8, 1, 2,
// 3, 4, 5
// ]
4.myShift()
shift()方法
移除数组中的第一个项并且返回该项,同时将数组的长度减一。
Array.prototype.myShift = function () {
var firstElemen = this[0];
for (var i = 1; i <= this.length - 1; i++) {
this[i - 1] = this[i];// 将数组一个一个往前挪
}
// 长度减一
this.length = this.length - 1;
return firstElemen;
}
var arr = new Array();
arr = [7,1,2,3,4,5,6];
console.log(arr.myShift());//7
console.log(arr);//[ 1, 2, 3, 4, 5, 6 ]
迭代方法采用的都是回调函数的形式来实现功能。
方法用于调用数组的每个元素,并将元素传递给回调函数,通常用于遍历。
Array.prototype.myForEach = function (fun) {
//遍历数组中的每个元素
for (var i = 0; i < this.length; i++) {
//将回调函数中的三个参数传进来,遍历出来
fun(i, this[i], this);
}
}
var arr = [1, 2, 3];
console.log(arr);//[ 1, 2, 3 ]
var result = arr.myForEach(function (index, item, arr) {
console.log(index, item, arr)
//0 1 [ 1, 2, 3 ]
//1 2 [ 1, 2, 3 ]
//2 3 [ 1, 2, 3 ]
});
只要有一个不符合就返回false,全部符合才返回true
myEvery()方法比较复杂,我们先看看他的雏形,先不看this指针。
// 雏形:
Array.prototype.myEvery = function () {
for (var i = 0; i < this.length; i++) {
if (!fun(this[i])) {
// '数组中的元素只要有一个不满足函数的条件'
// if(!fun(this[i])){return false};
//判断条件为当有一个元素不符合回调函数的条件则返回false
return false;
}
}
// 否则,也就是都满足函数的条件
return true;
}
var arr = [1, 2, 3, 4, 5];
var result = arr.myEvery(function (item, index, arr) {
console.log(this);
// 回调函数有三个参数:this[i](值) i(索引下标) this(指向)
// 而这里我们只需要一个this指向,输出我们需要的内容,在这里没修改指向前是global,
// 添加了第二个参数obj对象{name:xiaoxain}后,this指向修改为我们定义的arr
}, {
name: xiaoxian
})
console.log(result);
在我们自写的myEvery()方法里,在判断条件中,我们需要判断,是否有传入第二个参数obj(第一个参数是函数,回调函数),如果有,那么我们就通过bind修改this的指向,如果没有那么我们就原样输出。
也就是讲if语句修改为:
if (!(obj?fun.bind(obj)(this[i]):fun(this[i])))
第一开始this指向为global,判断是否有传入第二个参数obj,
如果有则通过bind修改this的指向,这时候this的指向是arr
完善后的代码:
Array.prototype.myEvery = function (fun, obj) {
for (var i = 0; i < this.length; i++) {
if (!(obj?fun.bind(obj)(this[i]):fun(this[i]))) {
return false;
}
}
return true;
}
var arr = [1, 2, 3, 4, 5];
var result = arr.myEvery(function (item, index, arr) {
console.log(this);
return item > 0;
// return item > 2;
}, {
name: 'xiaoxian'
})
console.log(result);
//{ name: 'xiaoxian' }
//{ name: 'xiaoxian' }
//{ name: 'xiaoxian' }
//{ name: 'xiaoxian' }
//{ name: 'xiaoxian' }
//true
//当return item>2时,结果为:
//{ name: 'xiaoxian' }
//false
有一个满足条件则返回true,只有全部不满足才返回false
与我们上面写的myEvery()恰恰相反,这时候我们只需要修改if语句的返回值即可,只要有一个满足则返回true。
Array.prototype.mySome = function (fun, obj) {
for (var i = 0; i < this.length; i++) {
if ((obj ? fun.bind(obj)(this[i]) : fun(this[i]))) {
return true;
}
}
return false;
}
var arr = [1, 2, 3, 4, 5];
var result = arr.mySome(function (item, index, arr) {
console.log(this);
return item > 2;
}, {
name: 'xiaoxian'
})
console.log(result);
//{ name: 'xiaoxian' }
//{ name: 'xiaoxian' }
//true
myEvery()与mySome()方法输出的结果来看,
**myEvery()**遇到不满足的条件时,就会立刻停止打印,例如myEvery()给出的第二个例子,return item>2时,遍历到2时,不满足条件,立刻停止,不继续打印,所以只打印了第一次;
**mySome()**方法,如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。遍历到第二个数字2>1,返回true,满足条件之后就不继续打印了,所以打印了两次,一次是1的打印,一次是2的打印。
方法返回一个新的数组,数组中的元素为原始数组元素调用函数处理后的值.
Array.prototype.myMap = function (fun, obj) {
//创建一个空的数组来接受函数处理后的元素的值
var newArr= [];
for (var i = 0; i < this.length; i++) {
//遍历后判断数组中的元素是否满足函数的需求,如果是则返回true,否则false
newArr.push(obj ? fun.bind(obj)(this[i]) : (fun(this[i])))
}
return newArr;
}
var arr = [1, 2, 3, 4, 5];
var result = arr.myMap(function (item, index, arr) {
console.log(this);
return item > 2;
}, {
name: 'xiaoxian'
})
console.log(result);
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// [ false, false, true, true, true ]
方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
Array.prototype.myFilter = function (fun, obj) {
//创建一个空的数组来接受函数处理后的元素
var newArr= [];
for (var i = 0; i < this.length; i++) {
if (obj ? fun.bind(obj)(this[i]) : fun(this[i])) {
//如果符合条件那么就将元素放入新建的数组内
newArr.push(this[i]);
}
}
//返回数组
return newArr;
}
var arr = [1, 2, 3, 4, 5];
var result = arr.myFilter(function (item, index, arr) {
console.log(this); //执行五次而不是三次[ false, false, true, true, true ]
return item > 2;
}, {
name: 'xiaoxian'
})
console.log(result);
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// { name: 'xiaoxian' }
// [ 3, 4, 5 ]
说实话第一次看到的时候比较难懂,但是多敲几遍,多看几遍,多理解几遍,你就会开窍了,别说话,多练!