ES6查漏补缺【数组的扩展】
·
Yin灏
1. 先简单复习一下 ES5 中的数组方法
改变原数组的:
添加: push / unshift
删除: pop / shift
任意位置的添加 或 删除: splice(startIndex,delCount, ...addElement)
翻转: reverse
排序: sort
不改变原数组的:
indexOf / lastIndexOf: 只能检索基本类型,不能检索复杂类型(引用类型)
cancat(arr1, arr2, ...)
join(str)
答案:除了 A 都可以
2.静态方法
ES5:Array.isArray()
- typeof:只能用于检测基本类型,不能用来检测引用类型
- 作用: 检测一个 变量/数据 是不是 数组(类型)
- 返回: 如果是数组(类型),返回 true;否则返回 false
const array = ["Webpack", "ES6", "TypeScript", "React"];
const obj = {};
console.log(typeof array); //object
console.log(typeof obj); //object
console.log(Array.isArray(array)); // true
console.log(Array.isArray(obj)); // false
ES6:array.from()
- 作用:
- 把一个类(假/伪)数组 转成 真数组,
- 如果传入了回掉函数,可以对遍历的结果做进一步处理,
- 并会用回掉函数 返回的结果 作为 新数组的成员
- 例如:
- 1.document.querySelectorAll()/document.getElementsByTagName()
- 2.arguments
- 返回:
- 转好之后的 真数组
function fn() {
console.log(arguments); // 伪数组
console.log(Array.from(arguments)); // 真数组
let arr = Array.from(arguments, (item, index) => item * 2);
console.log(arr); // 返回一个新数组
}
fn(1, 2, 3, 4, 5, 6);
ES6:array.of()
- 作用:把一堆零散的数据,转成一个数组返回
- 返回:转换好的数组
console.log(Array.of('零散', '数组', '返回')) //(3) ['零散', '数组', '返回']
3.数组实例方法
includes(el)
- 作用:判断 el 这个元素在不在数组中,用来取代 indexOf/lastindexOf 两个方法, 相比之下 includes 更具语义化,通常陪着 if 语句使用
- 注意:因为 includes 内部是通过三个等号判断的,因此它只能判断基本类型, 不能判断引用类型
- 返回:如果在,返回 true;否则返回 false
基本类型
const beauty = ["剑圣", "魔神", "元素", "缔造"];
// indexOf
console.log(beauty.indexOf("魔神")); // 1
console.log(beauty.indexOf("柔道")); // -1
// includes 内部通过 === 来判断,所以只能是基本类型
console.log(beauty.includes("魔神")); // true
console.log(beauty.includes("柔道")); // false
引用类型
const boys = [
{
name: "地下城",
from: "腾讯",
},
];
console.log(
{
name: "地下城",
from: "腾讯",
} ===
{
name: "地下城",
from: "腾讯",
}
); // false 因为内存指向不同
console.log(
boys.includes({
name: "地下城",
from: "腾讯",
})
); //false
fill()
- 作用:用一个固定值,替换/填充数组的元素,会改变原数组
- 语法:array.fill(value, startIndex = 0, endIndex = arr.length])
基本类型
const beauty = ["剑圣", "魔神", "元素", "缔造"];
// 全部替换
console.log(beauty.fill("地下城")); // (4) ['地下城', '地下城', '地下城', '地下城']
console.log(beauty.includes("地下城")); // 所以为true
// 替换前两个
console.log(beauty.fill("柔道", 0, 2)); // (4) ['柔道', '柔道', '地下城', '地下城']
引用类型
const boys = [
{
name: "地下城",
from: "腾讯",
},
{
name: "穿越火线",
from: "腾讯",
},
];
boys.fill({
name: "学习",
from: "天天向上",
});
console.log(boys);
// 创建一个数组长度为5,元素都是地下城的数组
console.log(Array(5).fill("地下城")); // 新增
console.log(new Array("q", "c", "m").fill("勇士")); // 替换数组中的元素
// 对象形式
console.log(
Array(5).fill({
name: "学习",
from: "天天向上",
})
); // 新增
console.log(
new Array("q", "c", "m").fill({
name: "学习",
from: "天天向上",
})
); // 替换数组中的元素
filter()
- 作用:用指定条件,对数组进行过滤/筛选,留下一部分,去掉一部分
- 返回:满足条件的新数组
- 语法: array.filter((value, index, arr) => { 回调的参数和 forEach 的回调参数是一样的 return true,回调返回 true,则当前 value 会放入到返回的新数组中 return false, 回调返回 false, 则当前 item 不会放到返回的新数组中 })
基本类型
// 中文
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.找出姓为库的人
const result = names.filter((value) => value.startsWith("库")); // index 和 arr不用的话去掉
console.log(result); // ['库里', '库春野']
console.log(names); // ['杜兰特', '库里', '巴特勒', '科比', '乔丹', '库春野'] 数组本身不变
// 数字
const waislines = [20, 40, 19, 70, 105, 60];
// 找出腿长小于60的人并升序排序
const result = waislines.filter((value) => value < 60).sort((a, b) => a - b);
console.log(result); // [19, 20, 40]
console.log(waislines); // 原数组 [20, 40, 19, 70, 105, 60]
引用类型
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的
const result = boys.filter((value) => value.height > 200);
console.log(result);
console.log(boys); // 原数组不变
手写一个 _filter()
// 手写一个 _filter()
Array.prototype._filter = function (callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的
const result = boys._filter((value) => value.height > 200);
console.log(result);
console.log(boys); // 原数组不变
find()
- 作用:根据指定条件,对数组进行查找
- 返回:如果找到了,返回第一个满足条件的元素;否则返回 undefined
- 语法:array.find((value, index, arr) => { return true, 表示 value 符合条件, 返回 value,就此结束查找 return false, 则当前 value 不符合条件, 继续查找 })
- 结论:跟 filter 一样,但只返回符合条件的第一个值,不返回数组
基本类型
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.找出姓为库的第一个人
const result = names.find((value) => value.startsWith("库")); // index 和 arr不用的话去掉
const result2 = names.find((value) => value.startsWith("李"));
console.log(result); // 库里
console.log(result2); // undefined
console.log(names); // ['杜兰特', '库里', '巴特勒', '科比', '乔丹', '库春野'] 数组本身不变
// 数字
const waislines = [20, 40, 19, 70, 105, 60];
// 找出腿长小于60的第一个人
const result = waislines.find((value) => value < 60);
console.log(result); // 20
console.log(waislines); // 原数组不变 [20, 40, 19, 70, 105, 60]
引用类型
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的第一个人
const result = boys.find((value) => value.height > 200);
console.log(result);
console.log(boys); // 原数组不变
手写一个 _find()
// 手写一个 _find()
Array.prototype._find = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return this[i];
}
}
return undefined;
};
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的
const result = boys._find((value) => value.height > 200);
console.log(result);
console.log(boys); // 原数组不变
findIndex()
- 作用:根据指定条件,对数组进行查找
- 返回:如果找到了,返回第一个满足条件的元素下标;否则返回 -1
- 语法:array.findIndex((value, index, arr) => { return true, 表示 value 符合条件, 返回 value 在数组中的下标,结束查找 return false, 则当前 value 不符合条件, 继续查找 })
基本类型
// 文字
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.找出姓为库的第一个人的下标
const result = names.findIndex((value) => value == "巴特勒"); // index 和 arr不用的话去掉
const result2 = names.findIndex((value) => value.startsWith("李"));
console.log(result); // 2
console.log(result2); // -1
console.log(names); // ['杜兰特', '库里', '巴特勒', '科比', '乔丹', '库春野']
// 数字
const waislines = [20, 40, 19, 70, 105, 60];
// 找出腿长小于60的第一个人的数组下标
const result = waislines.findIndex((value) => value < 60);
console.log(result); // 20
console.log(waislines); // 原数组 [20, 40, 19, 70, 105, 60]
引用类型
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的第一个人的下标
const result = boys.findIndex((value) => value.height > 200);
const result2 = boys.findIndex((value) => value.height > 300);
console.log(result);
console.log(result2); // -1
手写一个 _findIndex()
// 手写一个 _find()
Array.prototype._findIndex = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return i;
}
}
return -1;
};
// 筛选对象
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 找到身高两米以上的第一个人的下标
const result = boys._findIndex((value) => value.height > 200);
const result2 = boys._findIndex((value) => value.height > 300);
console.log(result);
console.log(result2); // -1
console.log(boys); // 原数组不变
map()
- 作用:映射,根据已有的数组得到一个新数组,两者数组元素是一一对应的
- 返回:映射后的新数组
- 语法:array.map((item,index,arr) => { return 新的值;// 把每次回调函数中返回的新值,加到新数组中 })
基本类型
// 文字
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.把每个名字后面都拼上编号
const result = names.map((value, index) => value + index); // 返回一个新的数组
console.log(result, "新数组");
console.log(names, "原数组");
// 数字都加10
const ages = [15, 18, 17, 20, 50, 45];
const result2 = ages.map((value) => value + 10); // 返回一个新的数组
console.log(result2, "新数组");
console.log(ages, "原数组");
引用类型
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 给每个对象添加id (下标 + 1)和 是性别属性
const result = boys.map((value, index) => {
return {
...value,
id: index + 1,
gender: "female",
};
});
console.log(result, "新数组"); // 得到的新数组
console.log(boys, "原数组"); // 原数组不变
手写一个 _map()
Array.prototype._map = function (callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 给每个对象添加id (下标 + 1)和 性别属性
const result = boys._map((value, index) => {
return {
...value,
id: index + 1,
gender: "female",
};
});
console.log(result, "新数组"); // 得到的新数组
console.log(boys, "原数组"); // 原数组不变
every()
- 作用:检测/判断 数组中的所有元素是否都满足指定条件
- 返回:如果都满足,返回 true,否则(只要有一个不满足)返回 false
- 执行流程:检测到了一个不满足条件的就停止了,否则继续检测
- 语法:array.every((item, index, arr) => { return 布尔值; return false: 表示 value 不符合条件,停止执行 return true:表示 value 符合条件,继续检查下一个; })
- 注意:空数组调用 every 会得到一个 true 的结果[].every(()=>{}) //true
基本类型
// 文字
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.检测数组中是否都是姓杜的
const result = names.every((value) => value.startsWith("杜"));
console.log(result, "检测数组中是否都是姓杜的"); // false,检测数组中是否都是姓杜的
// 2.检测数组中是否都不是姓李的
const result2 = names.every((value) => !value.startsWith("李"));
console.log(result2, "检测数组中是否都不是姓李的"); // true,检测数组中是否都不是姓李的
const ages = [15, 18, 17, 20, 50, 45];
// 检测数组中年龄是否都是20以上的
const result = ages.every((value) => value > 20);
console.log(result, "检测数组中年龄是否都是20以上的"); //false 检测数组中年龄是否都是20以上的
// 检测数组中年龄是否都是10以上的
const result2 = ages.every((value) => value > 10);
console.log(result2, "检测数组中年龄是否都是10以上的"); //true 检测数组中年龄是否都是10以上的
引用类型
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 判断数组中的身高是否都大于200cm
const result = boys.every((value) => value.height > 200);
console.log(result); // false
// 判断数组中的身高是否都大于190cm
const result2 = boys.every((value) => value.height > 190);
console.log(result2); // true
手写一个 _every()
// every方法的原理
Array.prototype._every = function (callback) {
for (let i = 0; i < this.length; i++) {
// 如果我们实例不符合条件
if (!callback(this[i], i, this)) {
return false;
}
}
// 否则都符合条件
return true;
};
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 判断数组中的身高是否都大于200cm
const result = boys._every((value) => value.height > 200);
console.log(result); // false
// 判断数组中的身高是否都大于190cm
const result2 = boys._every((value) => value.height > 190);
console.log(result2); // true
some()
- 作用:检测判断数组中是否存在满足指定条件的值
- 返回:如果存在,则返回 true,否则(都不满足条件)返回 false
- 执行流程:检测到一个满足条件的就停止了,否则继续检测
- 语法: array.some((value, index, arr) => { return 布尔值 return true:表示 value 符合条件,返回 true return false:表示 value 不符合条件,继续检测 })
基本类型
// 文字
const names = ["杜兰特", "库里", "巴特勒", "科比", "乔丹", "库春野"];
// 1.检测数组中是否有姓杜的,他首次出现的位置在第几个
const result = names.some((value, index) => {
console.log(value.startsWith("杜"), index + 1); // true 1
// 如果返回true,就不会再往下找了
return value.startsWith("杜");
});
console.log(result); // true
// 数字
const ages = [15, 18, 17, 20, 50, 45];
// 检测数组中年龄20以上的是否存在
const result = ages.some((value) => {
console.log(value);
return value > 20;
});
console.log(result); // true
引用类型
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// // 判断数组中的身高是否都大于200厘米
const result = boys.some((value) => {
console.log(value.height); // 200 192 205
return value.height > 200;
});
console.log(result); // true
手写一个 _some()
// some方法的原理
Array.prototype._some = function (callback) {
for (let i = 0; i < this.length; i++) {
// 如果我们实例不符合条件
if (callback(this[i], i, this)) {
return true;
}
}
// 否则都符合条件
return false;
};
// 操作的数组
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// // 判断数组中的身高是否都大于200cm
const result = boys._some((value) => {
console.log(value.height); // 200 192 205
return value.height > 200;
});
console.log(result); // true
reduce()
reduce(callback, [initiaIValue])
-
作用:汇总,根据已有的数组得到一个最终(整体)的结果,比如:对数组进行求和
-
返回:汇总的(整体)结果
-
两个参数 array.reduce((accumulator, currentValue, currentIndex, array)={ accumulator: 累加器 currentValue: 当前数组中的元素 currentIndex: 当前元素在数组中的下标 array: 数组对象本身 },[initialValue])
initiaIValue: 可选参数,会影响accumulator的初始值 如果没给initiaIValue 那么accumulator取自数组中第一个元素 此时cuurentValue会取自数组中的第二个元素,开始迭代 如果给定了initiaIValue 那么initiaIValue回作为accumulator的初始值 此时currentValue会取自数组中的第一个元素,开始迭代
基本类型
- 没有初始值
// 对数组中的数字进行求和
const waistlines = [55, 43, 70, 58];
const result = waistlines.reduce((accu, cur, index, arr) => {
console.log(accu, cur, index, arr); // 因为我们没给定initiaIValue所以accu作为初始值开始
// 55 43 1 (4) [55, 43, 70, 58]
// 98 70 2 (4) [55, 43, 70, 58]
// 168 58 3 (4) [55, 43, 70, 58]
// 如果return的话,accu的值源于上一次返回的值,否则第二次accu为undefined
return accu + cur;
});
console.log(result); // 226
- 有初始值
// 对数组中的数字进行求和
cconst waistlines = [55, 43, 70, 58]
const result = waistlines.reduce((accu, cur, index, arr) => {
console.log(accu, cur, index, arr) // 我们给了初始值的情况
// 0 55 0 (4) [55, 43, 70, 58]
// 55 43 1 (4) [55, 43, 70, 58]
// 98 70 2 (4) [55, 43, 70, 58]
// 168 58 3 (4) [55, 43, 70, 58]
// 如果return的话,accu的值源于上一次返回的值
return accu + cur
}, 0)
console.log(result) // 226
小结:什么时候可以不用给定初始值? 纯数字求和、求积
引用类型
- 没有初始值
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 不加初始值的情况
const result = boys.reduce((accu, cur) => {
// 第一次:200 + 192
// 第二次:392.height + 205
// 第三次:NaN + 233
return accu.height + cur.height;
});
console.log(result); // NaN
- 有初始值
// 引用类型,求平均身高
const boys = [
{
name: "杜兰特",
height: 200,
},
{
name: "库里",
height: 192,
},
{
name: "巴特勒",
height: 205,
},
{
name: "科比",
height: 233,
},
];
// 加上初始值的情况
const result = boys.reduce((accu, cur, index) => {
// 第一次:0 + 200
// 第二次:200 + 192
// 第三次:392 + 205
// 第四次:597 + 233
return accu + cur.height;
}, 0);
console.log(result / boys.length); // 207.5
小结:什么时候必须给初始值? 数组中放的是对象求和(汇总)
解决实际需求
- 根据数组[‘73’, ‘32’, ‘108’, ‘111’, ‘118’, ‘101’, ‘32’, ‘106’, ‘105’, ‘110’] 求出 ASCII 码字符串
const ASCII = [
"73",
"32",
"108",
"111",
"118",
"101",
"32",
"106",
"105",
"110",
];
const result = ASCII.reduce((accu, cur) => accu + String.fromCharCode(cur), "");
console.log(result); // I love jin
- 求总价格
// 求总价格
const shopping = [
{
name: "球鞋",
price: 200,
num: 2,
},
{
name: "球衣",
price: 400,
num: 1,
},
{
name: "手套",
price: 3,
num: 6,
},
{
name: "轮滑",
price: 300,
num: 10,
},
];
const result = shopping.reduce((accu, cur) => accu + cur.price * cur.num, 0);
console.log(result); // 3818
- 统计每个名字出现的次数: [‘Alice’,‘Bob’,‘Tiff’,‘Bruce’,‘Alice’]
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
const result = names.reduce((accu, cur) => {
accu[cur] = accu[cur] ? accu[cur] + 1 : 1
return accu
}, {})
console.log(result)
- 根据数据:[‘北京’,‘上海’,‘北京’,‘深圳’,‘杭州’,‘上海’,‘北京’] 得到数组:[[‘北京’,3],[‘上海’,2],[‘深圳’,],[‘杭州’,1]]
const citys = ["北京", "上海", "北京", "深圳", "杭州", "上海", "北京"];
const result = citys.reduce((accu, cur, index, arr) => {
accu[cur] = ++accu[cur] || 1;
// if(index === arr.length - 1){ // 迭代到了最后一次,返回整体结果
// // Object.entries() 将对象转换为二维数组
// return Object.entries(accu)
// }else{ // 不是最后一次,返回accu累计的结果
// return accu
// }
// 简化版
return index === arr.length - 1 ? Object.entries(accu) : accu;
}, {});
console.log(result);
手写一个 _reduce()
Array.prototype._reduce = function (callback, initiaValue) {
// 1.判断数组有没有传入initiaValue
const hasinitiaValue = typeof initiaValue !== "undefined";
// 2.如果有从0开始,没有从1开始
let i = hasinitiaValue ? 0 : 1;
// 3.决定accu的初始值
let accu = hasinitiaValue ? initiaValue : this[0];
// 循环数组,起始下标根据有无 initiaValue 来决定
for (; i < this.length; i++) {
// 不断的迭代accu
accu = callback(accu, this[i], i, this);
}
return accu;
};
const citys = ["北京", "上海", "北京", "深圳", "杭州", "上海", "北京"];
const result = citys._reduce((accu, cur, index, arr) => {
accu[cur] = ++accu[cur] || 1;
// if(index === arr.length - 1){ // 迭代到了最后一次,返回整体结果
// // Object.entries() 将对象转换为二维数组
// return Object.entries(accu)
// }else{ // 不是最后一次,返回accu累计的结果
// return accu
// }
// 简化版
return index === arr.length - 1 ? Object.entries(accu) : accu;
}, {});
console.log(result);
flat()
-
作用:扁平化数组,把数组打平 —> 消除数组嵌套数组 —> 多维数组转一维数组
-
返回:打平后的数组
-
语法:array.flat(depth = 1)
- 参数:扁平的深度,默认值是 1,只扁平一层
- every:不管数组嵌套数组多少层,全部变成一维,depth 可以传 infinity(无穷)
-
嵌套三层
const nums = [[123],[111,[456,[999]]],111,222,333]
const result = nums.flat(3)
console.log(result) // [123, 111, 456, 999, 111, 222, 333]
- 嵌套多层
const nums = [[123],[[[[[[111,[456,[999]]]]]]]],111,222,333]
const result = nums.flat(Infinity)
console.log(result) // [123, 111, 456, 999, 111, 222, 333]
- 手写一个 _flat()
// 实现flat方法的原理
Array.prototype._flat = function(depth = 1){
return flatten(this, depth >= 1 ? depth : 1)
function flatten(arr, depth){
// 递归的结束标识,返回一个拷贝新的数组实例
if(depth <= 0) return [...arr]
return arr.reduce((accu, cur)=>{
if(Array.isArray(cur)){ // cur是数组,递归处理
accu.push(...flatten(cur,depth-1))
}else{ // item不是数组,直接添加到accu中即可
accu.push(cur)
}
return accu
},[])
}
}
const nums = [[123],[[[[[[111,[456,[999]]]]]]]],111,222,333]
const result = nums._flat(Infinity)
console.log(result) // [123, 111, 456, 999, 111, 222, 333]
const nums1 = [[111],[2222],333,444]
const result1 = nums1._flat(1)
console.log(result1) // [111, 2222, 333, 444]