JS——手写数组Api
# 1、前言
在我们 conding 中,我们都用过数组这个数据结构,我们除了使用了数组自带的增删改查方法,还会使用带数组身上各类的 API 方法,下面我就来介绍数组身上的各类 api 方法。
# 2、迭代属性的 API 方法
# 2.1 Array.every()
# 1. evrry()原理:
every()
方法接受一个回调函数作为参数,它是检查数组中的所有元素是否都通过了我们的限制条件,every()
方法对数组中存在的每个元素都会去执行一次:
- 如果找到函数返回 false 值的数组元素,
every()
返回 false,并且不检查剩余的元素 - 如果没有出现 false,
every()
返回 true every()
不改变原始数组
# 2. evrry()方法的手写实现:
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_every = function (callback) {
for (let i = 0; i < this.length; i++) {
if (!callback(this[i], i, this)) {
return false;
}
}
return true;
};
const newSinger = singer.my_every((item, index, arr) => {
return item.num > 10; // singer[4].num < 10
});
console.log(newSinger); //false
# 2.2 Array.some()
# 1.some()原理
some()
方法也接受一个回调函数作为参数,与every()
相反,它是检查数组中的任何元素是否通过我们的限制条件,some()
方法对数组中存在的每个元素执行一次函数:
- 如果找到函数返回真值的数组元素,some() 返回 ture,并且不检查剩余的元素
- 否则返回 false
some()
不改变原始数组。
# 2.some()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_some = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return true;
}
}
return false;
};
const newSinger = singer.my_some((item, index, arr) => {
return item.num > 20; // singer[1].num > 20
});
console.log(newSinger); // true
# 2.3 Array.map()
# 1.map()原理
map()
方法接收三个参数,分别为 item
(值)、index
(下标)、arr
(原数组),它使用为每个数组元素调用函数的结果创建新数组, 且函数一定要有return
,并不会改变原始数组。
# 2.map()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_map = function (callback) {
const res = [];
for (let i = 0; i < this.length; i++) {
res.push(callback(this[i], i, this));
}
return res;
};
const newSinger = singer.my_map((item, index, arr) => {
return item.num > 20;
});
console.log(newSinger); // [ false, true, false, false, false ]
# 2.4 Array.reduce()
# 1.reduce()原理
reduce()
方法接受两个参数,第一个参数是一个回调函数,这个回调函数接受四个参数,分别为pre
(上次函数的执行结果)、next
(值)、index
(下标)、arr
(原数组),第二个参数为第一次执行回调函数时pre
的初始值。
如果我们没有给第二个参数,则pre
为数组的第一个值,next
为数组的第二个值。reduce()
方法也不会改变原始数组。一般我们会使用reduce
方法实现数组的累加。
# 2.reduce()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 1 },
];
Array.prototype.my_reduce = function (callback, ...args) {
let pre,
start = 0;
if (args.length) {
pre = args[0];
} else {
pre = this[0];
start = 1;
}
for (let i = start; i < this.length; i++) {
pre = callback(pre, this[i], i, this);
}
return pre;
};
const newSinger = singer.my_reduce((pre, next, index, arr) => {
console.log(pre); //0 20 45 64 74
return pre + next.num;
}, 0);
console.log(newSinger); // 75
# 2.5 Array.filter()
# 1.filter()原理
filter()
方法接收三个参数,分别为item
、index
、arr
,他创建了一个新的数组,其中填充了所有满足我们的限制条件的元素。filter()
也不会改变原始数组。
# 2.filter()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_filter = function (callback) {
const res = [];
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this) && res.push(this[i]);
}
return res;
};
const newSinger = singer.filter((item, index, arr) => {
return item.num > 15;
});
console.log(newSinger);
// [ // { name: '111', num: 20 }, // { name: '222', num: 25 }, // { name: '333', num: 19 } // ]
# 2.6 Array.forEach()
# 1.forEach()原理
forEach()
方法一个回调函数作为参数,forEach()
方法按顺序为数组中的每个元素调用一次函数。
# 2.forEach()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_forEach = function (callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};
singer.my_forEach((item, index, arr) => {
console.log(item, index);
});
// { name: '111', num: 20 } 0
// { name: '222', num: 25 } 1
// { name: '333', num: 19 } 2
// { name: '444', num: 10 } 3
// { name: '555', num: 0 } 4
# 3、查找属性的 API 方法
# 3.1 Array.find()
# 1.find()原理
find()
方法接收三个参数,分别为 val
(你要查找元素)、start
(开始查找的位置)、end
(结束的查找的位置),它返回数组中第一个通过限制的的元素的值。find()
方法对数组中存在的每个元素执行一次函数:
- 如果找到函数返回 true 值的数组元素,则 find() 返回该数组元素的值(并且不检查剩余值)
- 否则返回 undefined
find()
不会改变原始数组。
# 2.find()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_find = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return this[i];
}
}
return undefined;
};
const res = singer.my_find((item, index, arr) => {
return item.num > 20;
});
console.log(res); // { name: '222', num: 25 }
# 3.2 Array.findIndex()
# 1.findIndex()原理
findIndex()
方法与find()
方法原理相同,如果找到了返回对应下标值,否则返回-1
# 2.findIndex()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_findIndex = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return i;
}
}
return -1;
};
const res = singer.findIndex((item, index, arr) => {
return item.num > 30;
});
console.log(res); // -1
# 4、其他的好用 API 方法
# 4.1 Array.include()
# 1.includes()原理
includes()
方法接收两个参数,第一个参数为数组是否包含的元素,第二个参数为从数组某个位置开始向后查找,如果第二个个参数为负数,则从负数加上数组长度的位置开始。如果数组包含元素,则此方法返回 true
,否则返回 false
。且该方法区分大小写。
使用includes()
方法时,我们要考虑当我们第一个参数为NaN
时,由于NaN != NaN
,所以在我们手写实现时,我们将这种情况考虑进去
# 2.includes()方法的手写实现
let obj = { name: "111", num: 25 };
const singer = [
{ name: "222", num: 20 },
obj,
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
const nums = [1, 2, 3, 4, 5, NaN];
Array.prototype.my_includes = function (val, start = 0) {
if (start < 0) start = this.length + start;
const isNaN = Number.isNaN(val);
for (let i = start; i < this.length; i++) {
if (this[i] === val || (isNaN && Number.isNaN(this[i]))) {
// NaN === NaN
return true;
}
}
return false;
};
const res = nums.my_includes(NaN, 0);
console.log(res); // true
# 4.2 Array.join()
# 1.join()原理
join()
方法将数组作为字符串返回。元素将由指定的分隔符分隔。默认分隔符是逗号 ,
。join()
方法不会改变原始数组。
# 2.join()方法的手写实现
const arr = [1, 2, 3, 4, "hello"];
Array.prototype.my_join = function (s = ",") {
let str = "";
for (let i = 0; i < this.length; i++) {
if (i === this.length - 1) {
str += this[i];
} else {
str += this[i] + s;
} // str += i === this.length - 1 ? this[i] : this[i] + s // 简洁版
}
return str;
};
const str = arr.my_join();
console.log(str);
# 4.3 Array.flat()
# 1.flat()原理
flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。其接收一个参数默认值为 1,表示需要深度遍历几次,超出数组维度则指挥遍历数组维度-1次,使其成为以为数组
# 2.flat()方法的手写实现
const arr = [1, 2, [3, 4, [5]], 6, { a: 7 }];
Array.prototype.myflat = function (num = 1) {
let newArr = this;
let i = 0;
while (newArr.some((item) => Array.isArray(item)) && i < num) {
newArr = [].concat(...newArr);
i++;
}
return newArr;
};
const newArr = arr.myflat(4);
console.log(newArr); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]
# 4.4 Array.splice()
# 1.splice()原理
splice()
方法可以接收任意个参数,它的第一个参数为切割数组的起始位置(start),第二个参数为我们需要切割的长度(length),而后面的参数则为我们需要添加到原数组上的值。splice 会改变原来的数组。在我们手写splice()
方法时,我们要注意以下几点:
- splice 方法是先切割数组,再向其添加我们的想要向原数组的值
- splice 方法会改变原数组,同时也会返回出一个新数组
- 当我们传进来的第一个参数为复数时,我们需要从(start+数组长度)的位置开始切割
- 当我们切割的长度大于原数组长度的最大值时,我们要改变新数组的长度,去除新数组后面的
undefined
值
# 2.splice()方法的手写实现
const singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_splice = function (start, length, ...args) {
const result = [],
targetArr = []; // 新数组 原数组
// 判断第一个参数是否小于零
if (start < 0) {
start = this.length + start;
}
// 生成新数组同时将原数组剩余的值和要添加的值给到 targetArr
for (let i = 0; i < this.length; i++) {
if (i === start) {
for (let j = start; j < start + length; j++) {
result.push(this[j]);
i = j;
}
targetArr.push(...args);
if (!length) {
targetArr.push(this[i]);
}
} else {
targetArr.push(this[i]);
}
}
// 给原数组赋值
for (let i = 0; i < targetArr.length; i++) {
this[i] = targetArr[i];
}
// 去除新数组上的undefined
if (start + length > this.length) {
if (start === 0) {
result.length = this.length;
} else {
result.length = this.length - 1 - start;
}
}
this.length = targetArr.length;
return result;
};
const arr = singer.my_splice(-2, 10, "hello", "world", "123");
console.log(singer);
// [
// { name: '111', num: 20 },
// { name: '222', num: 25 },
// { name: '333', num: 19 },
// 'hello',
// 'world',
// 'laoli'
// ]
console.log(arr);
// [ { name: '444', num: 10 }, { name: '555', num: 0 } ]
# 4.5 Array.fill()
# 1.fill()原理
fill()
方法接受三个参数,分别是分别为 val
(你要填充元素)、start
(开始查找的位置)、end
(结束的查找的位置),它是向数组中填充指定的元素。可以指定开始和结束填充的位置。如果未指定,则将填充所有元素。fill()
会覆盖原数组。要注意的一点是:当我们填充一个引用类型的数据时,当我们改变了该引用类型的值时,数组的值也会改变
# 2.fill()方法的手写实现
let obj = { name: "666", num: 12 };
let singer = [
{ name: "111", num: 20 },
{ name: "222", num: 25 },
{ name: "333", num: 19 },
{ name: "444", num: 10 },
{ name: "555", num: 0 },
];
Array.prototype.my_fill = function (val, start = 0, end) {
end = end || this.length;
for (let i = start; i < end; i++) {
this[i] = val;
}
return this;
};
singer.my_fill(obj, 0, 1);
console.log(singer[0]); //{ name: '666', num: 12 }
obj.num = 13;
console.log(singer[0]); // { name: '666', num: 12 }