本文将手写数组的常用方法
在我们常用到的forEach,map,filter,some,every,reduce....,你会发现,这些方法基本都是传入一个函数,然后对数据加工,处理,返回我们想要的结果。
map
map:
不会对空数组检测
返回一个新的数组
- 语法
array.map(function(currentValue,index,arr), thisValue)
Array.prototype._map = function (fn, thisArr) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let res = []
let mapArr = this // [1, 2, 3]
for (let i = 0; i < mapArr.length; i++) {
res[i] = fn.call(thisArr, mapArr[i], i, mapArr)
}
return res
}
let arr = [1, 2, 3]
let mapRes = arr._map((val, index, item) => {
return val + 1
})
console.log(mapRes)// [2,3,4]
所谓的这些个方法, 就是内部封装好了,方便使用。比如手写map,
- 因为返回新数组,所以要声明一个空数组,然后return出去,里面做的事,就是把每一次调用fn,并且给fn赋值这件事。赋值给返回的新数组。等到你用map的时候,就是看到了具体的逻辑操作,eg:
val+1
。内部怎么得到的val,index,item, 每一项是怎么对应的,你不用关心
forEach
forEach:
空数组是不会执行回调函数的。
改变原数组
返回值是undefined
- 语法
array.forEach(function(currentValue, index, arr), thisValue)
- 具体实现可以参考map,map是返回一个新数组,forEach是直接改变原数组。
filter
filter:
不会对空数组进行检测
返回一个新的数组
- 语法
array.filter(function(currentValue, index, arr), thisValue)
Array.prototype._filter = function (fn, thisArr) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let filterArr = this
let filterRes = []
for (let i = 0; i < filterArr.length; i++) {
if (fn.call(thisArr, filterArr[i], i, filterArr)) {
filterRes.push(filterArr[i])
}
}
return filterRes
}
let arr = [1, 2, 3]
let filRes = arr._filter((val, index, item) => {
return val > 1
})
console.log(filRes)// [2,3]
-
filter方法,是筛选数据。当
满足条件
时,返回满足条件的值
-
满足条件 : if (fn.call(thisArr, filterArr[i], i, filterArr)) {}
-
满足条件的值: filterRes.push(filterArr[i])
some
some:
不会对空数组进行检测
返回一个新的数组
- 语法
array.some(function(currentValue, index, arr), thisValue)
Array.prototype._some = function (fn, thisArr) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let someArr = this
for (let i = 0; i < someArr.length; i++) {
if (fn.call(thisArr, someArr[i], i, someArr)) {
return true
}
}
return false
}
let arr = [1, 2, 3]
let someRes = arr._some((val, index, item) => {
return val > 1
})
console.log(someRes)// true
- some也是筛选数据,只是他返回的是true/false。如果有
一个
元素满足条件,则返回true
,剩余的元素不会再执行检测
。
every
- every也是筛选数据。只是他返回的是true/false。如果
所有元素都满足条件
,则返回true
, 只要有一个元素不满足条件,就返回false
Array.prototype._every = function (fn, thisArr) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let everyArr = this
for (let i = 0; i < everyArr.length; i++) {
if (!fn.call(thisArr, everyArr[i], i, everyArr)) {
return false
}
}
return true
}
let arr = [1, 2, 3]
let everyRes = arr._every((val, index, item) => {
return val > 0
})
console.log(everyRes)// true
-
every和some的实现,我觉得很有意思
-
every是所有的满足条件,才返回true。所以要先判断
-
if (!fn.call(thisArr, everyArr[i], i, everyArr)) { return false }
-
some是只要有一个满足条件,就返回true.所以要先判断
-
if (fn.call(thisArr, everyArr[i], i, everyArr)) { return true }
-
你品,你细品😁
reduce
- reduce 法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
- 语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
Array.prototype._reduce = function (fn, init) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let reduceArr = this
let index = arguments.length === 1 ? 1 : 0 // 求索引。如果没有传初始值,那么index就是1,因为没有传初始值,prev就是初始值,下标是0,那么自然curr下标就是1
let prev = arguments.length === 1 ? reduceArr[0] : init // 求初始值。如果没有传入初始值,那么初始值就是数组的第一项,否则就是传入的初始值init
for (let i = index; i < reduceArr.length; i++) {
prev = fn(prev, reduceArr[i], i, reduceArr) // 迭代累加
}
return prev
}
let arr = [1, 2, 3]
let reduceRes = arr._reduce((prev, curr) => {
return prev + curr
})
console.log('reduceRes', reduceRes)// 6
let reduceResParams = arr._reduce((prev, curr) => {
return prev + curr
}, 2)
console.log('reduceResParams', reduceResParams)// 8
- argument是类数组,用于接收传入的不定参数,如果argument.length === 1 代表没有传入参数,可自行输出观察
- reduce的index是一个需要注意的点,因为如果
传入初始值
,那么当前值curr
是从第一个
算起,所以index是0
,如果没有传入初始值
,那么curr
是从第二个
开始算起,所以index是1
- reduce的prev初始值。如果
传入初始值
,那么初始值是init
, 没有传入初始值,初始值则是数组的第一个元素reduceArr[0]
- reduce作为一个累加器,可以想成是
a = a+ 1
的简单逻辑.换成代码就是
prev = fn(prev, reduceArr[i], i, reduceArr) // 迭代累加
includes
-
方法用于判断字符串是否包含指定的子字符串。
-
如果找到匹配的字符串则返回 true,否则返回 false。
-
语法
String.includes(searchvalue, start)
Array.includes(searchvalue, start)
- fromIndex 可选
开始搜索的索引(从零开始),会转换为整数。
负索引从数组末尾开始计数——如果 fromIndex < 0,那么实际使用的是 fromIndex + array.length。然而在这种情况下,数组仍然从前往后进行搜索。 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,这将导致整个数组被搜索。 如果 fromIndex >= array.length,则不会搜索数组并返回 false。
Array.prototype._includes = function (searchElement, formIndex = 0) {
let arr = this;
let len = arr.length;
if (formIndex >= len || !searchElement) return false; // 如果传入起始位大于数据的长度,或者没有数据的
if (formIndex < 0) {
formIndex = formIndex + arr.length;
} else if (formIndex < -len) {
formIndex = 0;
}
for (let i = formIndex; i < len; i++) {
if (arr[i] === searchElement)
// 当传入的数据和查找的数据一致,返回true
return true;
}
return false;
};
let arr = [1, 2, 3];
let includes = arr._includes(1);
console.log('includes', includes); // true
- 这里判断了
len
的存在,和 if (this == undefined) { throw new TypeError('this is null or not undefined'); }是一个道理
indexOf
-
indexOf(item, start) start为开始检索位置,找到返回下标值,没找到返回-1
-
他的实现和includes可以说一模一样。includes 是返回true/ false ,那么indexOf中,true就是返回数组下标,false就是返回-1
Array.prototype._indexOf = function (searchElement, formIndex = 0) {
let arr = this;
let len = arr.length;
if (formIndex >= len || !searchElement) return false; // 如果传入起始位大于数据的长度,或者没有数据的
if (formIndex < 0) {
formIndex = formIndex + arr.length;
} else if (formIndex < -len) {
formIndex = 0;
}
for (let i = formIndex; i < len; i++) {
if (arr[i] === searchElement)
// 当传入的数据和查找的数据一致,返回true
return i;
}
return -1;
};
let arr = [1, 2, 3];
let _indexOf = arr._indexOf(3);
let _indexOf2 = arr._indexOf(33);
console.log('_indexOf', _indexOf); // 2
console.log('_indexOf2', _indexOf2); // -1
find
- find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
Array.prototype._find = function (fn) {
let arr = this;
let len = arr.length;
for (let i = 0; i < len; i++) {
if (fn(arr[i])) {
return arr[i];
}
}
return undefined;
};
let arr = [1, 2, 3];
let result = arr._find((el) => el > 0);
console.log('result', result); // 1
findIndex
- findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回 -1。
Array.prototype._findIndex = function (fn) {
let arr = this;
let len = arr.length;
for (let i = 0; i < len; i++) {
if (fn(arr[i])) {
return i;
}
}
return -1;
};
let arr = [1, 5, 3];
let result = arr._findIndex((el) => el > 2);
console.log('result', result); // 1
findLast
- findLast() 方法反向迭代数组,并返回满足提供的测试函数的第一个元素的值。如果没有找到对应元素,则返回 undefined。
Array.prototype._findLast = function (fn) {
let arr = this;
let len = arr.length;
for (let i = len; i >= 0; i--) {
if (fn(arr[i])) {
return arr[i];
}
}
return -1;
};
let arr = [1, 5, 3];
let result = arr._findLast((el) => el > 2);
console.log('result', result); // 1
总结
-
在所以的方法里,都是传入一个函数,那么首先就是要判断是否是函数
Object.prototype.toString.call(fn) !== '[object Function]' typeof fn !== 'function'
-
我们在用的过程中,其实thisValue这个参数,一般都不写,所以在文中遇到fn.call(thisArr, filterArr[i], i, filterArr) 可直接写成fn(filterArr[i], i, filterArr) 。我这里是为了迎合他的用法😂 thisValue:可选。传递给函数的值一般用 "this" 值。 如果这个参数为空, "undefined" 会传递给 "this" 值
-
关于this,我们记的是,谁调用他,他就指向谁,他在指向的同时,其实,
this也代表了指向的那个数据
。比如let filterArr = this 这是一种很好的获取调用者数据
的方式 -
返回的是新数据,所以要声明一个空数组 然后将其return出去
-
以上手写,发现有很多相似之处
Array.prototype._every = function (fn, thisArr) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw new TypeError(fn + 'is not a function');
}
let everyArr = this
for (let i = 0; i < everyArr.length; i++) {
}
}