ECMAScript 6之数组的扩展
目录
- ECMAScript 6之数组的扩展
- 1. 扩展运算符
- 2. Array.from()
- 3. Array.of
- 4. Array.prototype.find()和Array.prototype.findIndex()
- 5. Array.prototype.fill()
- 6. Array.prototype.entries(),Array.prototype.keys() 和 Array.prototype.values()
- 7. Array.prototype.includes
- 8. Array.prototype.flat(),Array.prototype.flatMap()
- 9. Array.prototype.sort() 的排序稳定性
- 10. 参考链接
1. 扩展运算符
扩展运算符(...
),将一个数组转为用逗号分隔的参数序列。扩展运算符背后是调用的遍历器接口,只要部署了遍历器接口的对象,都能使用扩展运算符。
let x = [1, 2, 3, 4, 5, 6];
console.info(...x); // 1 2 3 4 5 6
扩展运算符主要用于函数的调用。
function add(x, y) {
return x + y;
}
let a = [1, 2];
add(...a); // 3
如果扩展运算符后面跟一个空数组,不会有任何效果。
console.info(...[], 1); // 1
扩展运算符后面可以是表达式。
function f(x) {
if (x > 0) {
return [1, 2, 3, 4];
}else {
return [-1, -2, -3, -4];
}
}
console.info(...f(1)); // 1 2 3 4
console.info(...f(-1)); // -1 -2 -3 -4
扩展运算符不能放在括号里面,除非是函数调用。
// 扩展运算符只能放在函数调用的括号里面
console.info(...[1, 2, 3]); // 1 2 3
console.info((...[1, 2, 3])); // Uncaught SyntaxError: Unexpected token '...'
(...[1, 2, 3]); // Uncaught SyntaxError: Unexpected token '...'
1.1 扩展运算符的应用
(1).替代函数的apply
方法。apply
方法经常被用来将数组转为函数的参数,而有了扩展运算符,可以完全替代apply
方法。
let x = [23, 34, 56];
// ES5写法
Math.max.apply(null, x); // 56x
// ES6写法
Math.max(...x); // 56
(2).复制数组。直接将数组赋值给另一个数组,其中一个数组的数据发生变化,另一个会跟着变化。
const a1 = [1, 2];
const a2 = a1;
a2; // [1, 2]
// 改变a1数据,a2数据跟着改变
a1[1] = 4;
a2; // [1, 4]
// 改变a2数据,a1数据跟着改变
a2[0] = 5;
a1; // [5, 4]
通过扩展运算符可以获得数组的一个克隆,从而避免上述情况。
const a1 = [1, 2];
// ES5写法
const a2 = a1.concat();
// ES6 写法
const a2 = [...a1];
(3).合并数组。扩展运算符提供了新的写法。
const a1 = [1, 2, 3];
const a2 = [4, 5, 6];
const a3 = [7, 8, 9];
// ES5写法
let a4 = a1.concat(a2, a3);
a4; // [1, 2, 3, 4, 5, 6, 7, 8, 9]
//ES6写法
let a5 = [...a1, ...a2, ...a3];
a5; // [1, 2, 3, 4, 5, 6, 7, 8, 9]
上述代码中,不管是ES5写法还是ES6写法,获得的新数组都是浅拷贝,即新数组的成员是对原数组成员的引用,修改了引用指向的值,会同步到新数组。
(4).与解构赋值结合。扩展运算符可以与解构赋值结合,用于生成数组。
const [first, ...rest] = [1, 2, 3, 4, 5];
first; // 1
rest; // [2, 3, 4, 5]
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
扩展运算符用于数组赋值,只能放在数组的最后一位,否则会报错。
const [ ...rest, first] = [1, 2, 3, 4, 5]; // Uncaught SyntaxError: Rest element must be last element
(5).字符串。扩展运算符可以将字符串转换为真正的数组,而且还能正确识别四个字节的Unicode字符。
[..."jidi"]; // ["j", "i", "d", "i"]
// 能正确识别四个字节的字符
[...String.fromCodePoint(134072)].length; // 1
可以使用扩展运算符正确获得字符串的长度,不用关注字符串是几个字节。
function getStringLegth(s) {
return [...s].length;
}
(6).实现了Iterator
接口的对象。任何定义了遍历器的对象,都可以使用扩展运算符转为真正的数组。
// 返回所有script节点
let scripts = document.scripts;
[...scripts]; // [script, script, script, script, script, script, script, script]
2. Array.from()
Array.from
方法用于将类似数组的对象和可遍历的对象转换为真正的数组。
// 类似数组的对象
let b = {
0: "jidi",
1: "xuxiake",
length: 2
}
Array.form(b); // ["jidi", "xuxiake"]
// 可遍历的对象
let a = new String ("jidi");
Array.from(a); // ["j", "i", "d", "i"]
如果参数是一个真正的数组,Array.from
会返回一个一模一样的新数组。
Array.from([1, 2, 3]); // [1, 2, 3]
Array.from
还可以接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组,可以参考数组的map
方法。
// 数组中的每个值+1
Array.from([1, 2, 3, 4], x => x + 1); // [2, 3, 4, 5]
3. Array.of
Array.of
方法用于将一组值,转换为数组。
Array.of("jidi", 2, 3); // ["jidi", 2, 3]
Array.of(12); // [12]
Array.of(); // []
Array()
构造函数有一个很大的缺陷,就是不同的参数,会导致它的行为不一致。Array.of
方法主要是为了弥补数组构造函数Array()
的不足。
// 无参数时,返回一个空数组
new Array(); // []
// 单个正整数参数,表示返回的新数组的长度
new Array(1); // [ empty ]
new Array(2); // [ empty x 2
// 多参数时,所有参数都是返回的新数组的成员
new Array(1, 2) // [1, 2]
4. Array.prototype.find()和Array.prototype.findIndex()
Array.prototype.find()
用于找出符合条件的第一个数组成员。参数是一个回调函数,数组成员依次执行回调函数,直到找到第一个返回true
的成员,然后会返回改成员。如果没找到,返回undefined
。
// 找到数组中的第一个偶数
[1, 2, 3, 4, 5].find( x => x % 2 === 0); // 2
// 没找到,返回undefined
[1, 3, 7].find( x => x % 2 === 0); // undefined
Array.prototype.find()
方法的回调函数可以接受三个参数:依次为当前的值、当前的位置和原数组。
[1, 2, 3].find((value, index, array) => value === index + 1); // 1
Array.prototype.find()
方法可以接受第二个参数,用来绑定回调函数的this
对象。
// 定义回调函数,找出年龄比this所指向的对象年龄大的数组成员
function f(x) {
if (this.age < x.age) {
return true;
}
}
let rycony = {name: "rycony", age: 22};
[{name: "jidi", age: 22}, {name: "xuxiake", age: 23}].find(f, rycony ); // {name: "xuxiake", age: 23}
Array.prototype.findIndex()
用法参照Array.prototype.find()
,只是它返回的是第一个符合条件的数组成员的下标位置,如果没找到返回-1
。
5. Array.prototype.fill()
Array.prototype.fill()
使用给定值,填充一个数组。
["jidi", "xuxiake", "rycony"].fill("*"); // ["*", "*", "*"]
Array.prototype.fill()
可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
// 使用指定值填充指定起始位置的数组元素
["jidi", "xuxiake", "rycony"].fill("*",1, 2); // ["jidi", "*", "rycony"]
6. Array.prototype.entries(),Array.prototype.keys() 和 Array.prototype.values()
Array.prototype.entries()
,Array.prototype.keys()
和 Array.prototype.values()
用于遍历数组。它们都返回一个遍历器对象,可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
// 遍历键名
for (let key of ['a', 'b'].keys()) {
console.log(key);
}
// 0 1
// 遍历键值
for (let value of ['a', 'b'].values()) {
console.log(value);
}
// “a” “b”
// 遍历键值对
for (let [key, value] of ['a', 'b'].entries()) {
console.log(key, value);
}
// 0 "a" 1 "b"
7. Array.prototype.includes
Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值。
["jidi", "xuxiake", "rycony"].includes("jidi"); // true
Array.prototype.includes
方法可以接受第二个参数,表示搜索的起始位置,默认为0
。如果为负数,表示倒数的位置,如果参数值大于数组长度,会重置为0
。
["jidi", "xuxiake", "rycony"].includes("jidi", 1); // false
//为负数,表示倒数的位置,从-1开始计数
["jidi", "xuxiake", "rycony"].includes("jidi", -2); // false
8. Array.prototype.flat(),Array.prototype.flatMap()
Array.prototype.flat()
用于将嵌套的数组变成一维的数组。该方法返回一个新数组。
[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
Array.prototype.flat()
默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()
方法的参数写成一个整数,表示想要拉平的层数,默认为1
。
// 默认拉平一层
[1, 2, [3, [4, 5]]].flat(); // [1, 2, 3, Array(2)]
// 拉平两层
[1, 2, [3, [4, 5]]].flat(2); // [1, 2, 3, 4, 5]
如果不管有多少层嵌套,都要转成一维数组,可以用Infinity
关键字作为参数。
[1, 2, [[3, [4, [5]]]]].flat(Infinity); // [1, 2, 3, 4, 5]
如果原数组有空位,flat()
方法会跳过空位。
// 会忽略数组中空位元素
[, , 1, 2, , 4, 5].flat(); // [1, 2, 4, 5]
Array.prototype.flatMap()
方法会对每个数组成员执行一个函数,然后对返回值组成的数组执行flat()
方法。该方法返回一个新数组,不会改变原数组。
// 相当于先调用数组map方法,然后对执行了map方法的结果组成的数组执行flat方法
[2, 3, 4, 5, 6].flatMap(x => [x, x + 1]); // [2, 3, 3, 4, 4, 5, 5, 6, 6, 7]
// flatMap方法只能展开一层数组
[2, 3, 4, [5, 6]].flatMap(x => [x, x + 1]); // [2, 3, 3, 4, 4, 5, Array(2), "5,61"]
9. Array.prototype.sort() 的排序稳定性
常见的排序算法之中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等是不稳定的。。ES2019 明确规定,Array.prototype.sort()
的默认排序算法必须稳定。
10. 参考链接
本篇博文是我自己学习笔记,原文请参考:ECMAScript 6 入门
如有问题,请及时指出!
欢迎沟通交流,邮箱:jidi_jidi@163.com。