ES6
let和const
- 使用let,声明的变量仅在块级作用域内有效
- var命令声明的,在全局范围内都有效
- const声明一个只读的常量。一旦声明,常量的值就不能改变。
- const的作用域与let命令相同:只在声明所在的块级作用域内有效。
- let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
- let不允许在相同作用域内,重复声明同一个变量。
- 什么时候用 let,什么使用用 const——如果不知道用什么的时候,就用 const
解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
let [a, b, c] = [1, 2, 3];
数组解构赋值应用
arguments
function func() {
const [a, b] = arguments;
console.log(a, b); // 1 2
}
func(1, 2);
NodeList
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NodeList</title>
</head>
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
const [p1, p2, p3] = document.querySelectorAll('p');
console.log(p1, p2, p3);
/*
<p>1</p>
<p>2</p>
<p>3</p>
*/
</script>
</body>
</html>
函数参数的解构赋值
const array = [1, 1];
// const add = arr => arr[0] + arr[1];
const add = ([x = 0, y = 0]) => x + y;
console.log(add(array)); // 2
console.log(add([])); // 0
交换变量的值
let x = 2, y = 1;
// 原来
let tmp = x;
x = y;
y = tmp;
// 现在
[x, y] = [y, x];
// 理解:[x, y] = [2, 1]
console.log(x, y);
// 1 2
跳过某项值使用逗号隔开
var [a, , , b] = [10, 20, 30, 40];
console.log(a); // 10
console.log(b); // 40
剩余参数中的使用
var [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]
对象的解构赋值
对象的解构和数组基本类似,对象解构的变量是在 {} 中定义的。对象没有索引,但对象有更明确的键,通过键可以很方便地去对象中取值。在 ES6 之前直接使用键取值已经很方便了
var obj = {
name: 'imooc', age: 7 };
var name = obj.name; // imooc
var age = obj.age; // 7
对象作为函数参数
// 之前
const logPersonInfo = user => console.log(user.name, user.age);
logPersonInfo({
name: 'jerry', age: 18});
// 之后
const logPersonInfo = ({
age = 21, name = 'ZJR'}) => console.log(name, age);
logPersonInfo({
name: 'jerry', age: 18}); // jerry 18
logPersonInfo({
}); // ZJR 21
剩余参数中的使用
var {
a, c, ...rest} = {
a: 1, b: 2, c: 3, d: 4}
console.log(a); // 1
console.log(c); // 3
console.log(rest); // { b: 2, d: 4 }
字符串解构赋值
// 数组形式解构赋值
const [a, b, , , c] = 'hello';
console.log(a, b, c); // h e o
// 对象形式解构赋值
const {
0: a, 1: b, 4: o, length} = 'hello';
console.log(a, b, o, length); // h e o 5
数值和布尔值的解构赋值
// 先来复习一下将数值和布尔值转化为对象
console.log(new Number(123));
console.log(new Boolean(true));
// 转化后的对象里没有任何的属性(没有 123 这个属性,也没有 true 这个属性)和方法,
// 所有的属性和方法都在它的继承 __proto__ 中,比如 toString 方法就是继承来的。
// 里面的值只能是默认值,继承的方法倒是可以取到
const {
a = 1, toString} = 123;
console.log(a, toString); // 1 [Function: toString]
// 里面的值只能是默认值,继承的方法倒是可以取到
const {
b = 1, toString} = true;
console.log(b, toString); // 1 [Function: toString]
函数扩展
默认值
// ES6 默认值实现方式
const multiply = (x, y = 3) => {
return x * y;
};
console.log(multiply(2, 2)); // 4
console.log(multiply(2)); // 6
剩余参数
在 ES5 的开发模式下,想要使用传递的参数,则需要按位置把对应的参数取出来。尽管 arguments 是一个类数组且可遍历的变量,但它终究不是数组,它不支持数组方法,因此我们不能调用 arguments.forEeach (…) 等数组的方法。需要使用一些特殊的方法转换成数组使用,如:
function fn() {
var arr = [].slice.call(arguments);
console.log(arr)
}
fn('ES6');
// ["ES6"]
fn('imooc', 7, 'ES6');
// ["imooc", 7, "ES6"]
终于借助 call 方法把 arguments 转化成一个真正的数组了。但是这样无疑是一个繁琐的过程,而且不容易理解。这时 ES6 给出了它的完美解决方案 —— 剩余参数,那剩余参数是如何在函数传参中使用的呢?下面我们来看看实例:
function fn(...args) {
console.log(args)
}
fn('ES6');
// ["ES6"]
fn('imooc', 7, 'ES6');
// ["imooc", 7, "ES6"]
function fn(name, ...args) {
console.log(name); // 基础参数
console.log(args); // 剩下的参数组成的数组
}
fn('ES6');
// 'ES6'
// []
fn('imooc', 7, 'ES6');
// "imooc"
// [7, "ES6"]
应用
- 作为数组的应用:
const add = (...args) => {
let sum = 0;
for (let i = 0; i < args.length; i++) {
sum += args[i];
} // 当然此处,arguments 也可以
return sum;
};
console.log(add()); // 0
console.log(add(1, 1)); // 2
console.log(add(1, 2, 3)); // 6
- 与数组解构赋值结合
let array = [1, 2, 3, 4, 5];
let [a, b, ...others] = array;
console.log(a); // 1
console.log(b); // 2
console.log(others); // [3,4,5]
- 与对象解构赋值结合
const {
x, y, ...z} = {
a: 3, x: 1, y: 2, b: 4};
console.log(x, y, z);
// 1 2 { a: 3, b: 4 }
// 这里的剩余参数是个对象(准确的应该叫:剩余元素)
箭头函数
const add = (x) => {
return x + 1;
};
// 单个参数可以省略 ()
const add = x => {
return x + 1;
};
// 无参数
const test = () => {
return 1;
};
//或者
const test = _ => {
return 1;
};
不适用的场景
- 作为构造函数
因为箭头函数没有 this,而构造函数的核心就是 this。
- 需要 this 指向调用对象的时候
因为箭头函数没有 this,所以如果箭头函数中出现了 this,那么这个 this 就是外层的!
- 需要使用 arguments 的时候
箭头函数没有 arguments。
数组的扩展
扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
由于扩展运算符可以展开数组,所以不再需要apply()方法将数组转为函数的参数了。
// ES5 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
应用
复制数组
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;
a1 // [2, 2]
合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
字符串转为数组
console.log(...'alex'); // a l e x
console.log('a', 'l', 'e', 'x'); // a l e x
console.log([...'alex']); // [ 'a', 'l', 'e', 'x' ]
// ES6 之前字符串转数组是通过:'alex'.split('');
类数组转为数组
// arguments
function func() {
console.log(arguments); // [Arguments] { '0': 1, '1': 2 }
console.log([...arguments]); // [ 1, 2 ]
}
func(1, 2);
// NodeList
console.log([...document.querySelectorAll('p')].push);
Array.from()
Array.from() 方法会接收一个类数组对象然后返回一个真正的数组实例,返回的数组可以调用数组的所有方法。
类数组转化
var arrLike = {
'0': 'apple',
'1': 'banana',
'2': 'orange',
length: 3
};
var arr = Array.from(arrLike);
console.log(arr) // ['apple', 'banana', 'orange']
从字符串里生成数组
Array.from('imooc');
// [ "i", "m", "o", "o", "c" ]
从 Set 中生成数组
const set = new Set(['a', 'b', 'c', 'd']);
Array.from(set);
// [ "a", "b", "c", "d" ]
从 Map 中生成数组
const map = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(map); // [[1, 2], [2, 4], [4, 8]]
Array.of()
Array.of()方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
Array.of()基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
find(),findIndex(),findLast(),findLastIndex()
数组实例的find()方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
find()方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
indexOf()方法无法识别数组的NaN成员,但是findIndex()方法可以借助Object.is()方法做到。
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
find()和findIndex()都是从数组的0号位,依次向后检查。ES2022 新增了两个方法findLast()和findLastIndex(),从数组的最后一个成员开始,依次向前检查,其他都保持不变
const array = [
{
value: 1 },
{
value: 2 },
{
value: 3 },
{
value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2
filter()
filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。
它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
// [4, 5]
filter()方法还可以接受第二个参数,用来绑定参数函数内部的this变量。
var obj = {
MAX: 3 };
var myFilter = function (item) {
if (item > this.MAX) return true;
};
var arr = [2, 8, 3, 4, 1, 3, 2, 9];
arr.filter(myFilter, obj) // [8, 4, 9]
map()
map()方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
var numbers = [1, 2, 3];
numbers.map(function (n) {
return n + 1;
});
// [2, 3, 4]
numbers
// [1, 2, 3]
map()方法接受一个函数作为参数。该函数调用时,map()方法向它传入三个参数:当前成员、当前位置和数组本身。
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
如果数组有空位,map()方法的回调函数在这个位置不会执行,会跳过数组的空位。
var f = function (n) {
return 'a' };
[1, undefined, 2].map(f) // ["a", "a", "a"]
[1, null, 2].map(f) // ["a", "a", "a"]
[1, , 2].map(f) // ["a", , "a"]
reduce()
reduce()方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce()是从左到右处理(从第一个成员到最后一个成员)。
[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log(a, b);
return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15
如果要对累积变量指定初值,可以把它放在reduce()方法的第二个参数。
[1, 2, 3, 4, 5].reduce(function (a, b) {
return a + b;
}, 10);
// 25
some(),every()
这两个方法类似“断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件。
它们接受一个函数作为参数,所有数组成员依次执行该函数。该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值。
some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false。
var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >= 3;
});
// true
every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false。
var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >= 3;
});
// false
fill()
arr.fill(value[, start[, end]])方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。 起始索引,默认值为 0。 终止索引,默认值为 this.length。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
entries(),keys() 和 values()
ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
toReversed(),toSorted(),toSpliced(),with()
很多数组的传统方法会改变原数组,比如push()、pop()、shift()、unshift()等等。数组只要调用了这些方法,它的值就变了。现在有一个提案,允许对数组进行操作时,不改变原数组,而返回一个原数组的拷贝。
toReversed()对应reverse(),用来颠倒数组成员的位置。
toSorted()对应sort(),用来对数组成员排序。
toSpliced()对应splice(),用来在指定位置,删除指定数量的成员,并插入新成员。
with(index, value)对应splice(index, 1, value),用来将指定位置的成员替换为新的值。
const sequence = [1, 2, 3];
sequence.toReversed() // [3, 2, 1]
sequence // [1, 2, 3]
const outOfOrder = [3, 1, 2];
outOfOrder.toSorted() // [1, 2, 3]
outOfOrder // [3, 1, 2]
const array = [1, 2, 3, 4];
array.toSpliced(1, 2, 5, 6, 7) // [1, 5, 6, 7, 4]
array // [1, 2, 3, 4]
const correctionNeeded = [1, 1, 3];
correctionNeeded.with(1, 2) // [1, 2, 3]
correctionNeeded // [1, 1, 3]