以下是ES6常用的一些语法和特性:
声明变量的关键字变化:使用let和const、var来声明变量。
箭头函数:使用箭头(=>)定义函数,简化函数的写法。
模板字符串:使用反引号(`)来创建字符串模板,可以方便地插入变量和换行符。
解构赋值:可以从数组或对象中提取值,并赋给对应的变量。
默认参数:在函数定义时,可以为参数设置默认值。
扩展运算符:使用三个点(...)可以将一个数组展开,或将多个参数合并成一个数组。
类和对象:引入了类的概念,可以使用class关键字来定义类,同时也支持面向对象的继承和封装。
模块化:通过export和import关键字,可以将代码分割成多个模块,方便管理和重用。
简化的对象字面量:可以直接在对象中使用变量名作为属性名,简化对象的定义。
Promise:引入了Promise对象,用于处理异步操作,解决了回调地狱的问题。
1、let、const、var
1)let:用let声明的变量,其作用域为该语句所在代码块内;不存在变量提升(考虑暂时性死区);同一个作用域不允许重复声明(包括普通变量和函数参数)。
2)const:使用const声明的变量,其作用域为该语句所在代码块内;只声明不赋值会报错;在后面出现的代码中不能在修改该常量的值。
3)var:其作用域为该语句所在的函数内且存在变量提升现象(变量提升是指在Javascript代码执行前,变量和函数的声明会被移动到它们所在作用域的顶部)。
2、function(){} 等价于 ()=>{}
3、Promise
Promise自身有我们常用的all、race、resoleve、reject等方法,原型中还有catch、then等方法,所以我们创建Promise实例时,将then会作为callback使用,避免回调地狱的出现。
Promise.all的提供了并行的操作能力,并且是在所有的一步操作执行完成后才执行回调。
all接收一个数组参数,它会把所有异步操作的结果放进一个数组中传给then。
// all的使用
function async1 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('async1');
resolve('async1完成')
}, 1000);
})
}
function async2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('async2');
resolve('async2完成')
}, 2000);
})
}
function async3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('async3');
resolve('async3完成')
}, 3000);
})
}
Promise.all([async1(), async2(), async3()]).then((result) => {
console.log(result);
// do something...
}).catch((err) => {
console.log(err);
});
// result:
async1
async2
async3
[ 'async1完成', 'async2完成', 'async3完成' ]
4、新增的实例方法:includes() ,startsWith() ,endsWith()
includes():includes()方法用来判断一个数组是否包含一个指定的值,如果是则返回true,否则false
startsWith():用于确定一个字符串是否以另一个字符串开始,并返回一个布尔值(
true
或false
)。endsWith():用于确定一个字符串是否以另一个字符串结束,并返回一个布尔值(
true
或false
)。
5、repeat()
repeat() 是一个函数,用于将一个元素重复n次后返回一个迭代器对象。
6、padStart() padEnd()
7、rest参数(形式为“…变量名”)
用于获取函数的多余参数
注意,rest 参数之后不能再有其他参数(即只能有最后一个参数),否则会报错
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
8、扩展运算符(…)
- 它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
- 扩展运算符可以用于函数的调用
function push(array, ...items) {
array.push(...items);
}
function add(x,y){
return x + y;
}
var numbers = [3,7];
console.log(add(...numbers)); //10
- 扩展运算符还可以与正常函数参数结合使用
function f(a,b,c,d,e){
console.log(a,b,c,d,e)
}
var number = [3,5];
f(1,...number,...[7,9]) //1,3,5,7,9
注意:
将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错!
加了扩展符就注定是数组!
9、函数前面加上async,他就成异步函数
async作为一个关键字放在函数前面,用于表示函数是一个异步函数。(函数前面加上async,就是异步函数。)
因为async就是异步的异步,异步函数也就是意味着这个函数的执行不会阻塞后面代码的执行。简单的写一个async函数
async函数,从而可以使用await关键字,await后面放置的是返回promise对象的一个表达式,所以它后面可以写上awaitMethod函数的调用。
async function test (){
let result = await awaitMethod(30);
console.log(result);
}
//调用
test();
调用test函数时,它遇到了里面的await关键字,await代表等待,代码到这里会暂停,等待后面的promise对象执行完成,然后拿到promise的返回值,拿到返回值之后它继续往下执行。
10、解构赋值
const colors = ['red', 'green', 'blue'];
const [firstColor, secondColor, thirdColor] = colors;
console.log(firstColor); // 输出 'red'
console.log(secondColor); // 输出 'green'
console.log(thirdColor); // 输出 'blue'
const numbers = [1, 2, 3, 4, 5];
const [firstNum, , thirdNum, ...rest] = numbers;
console.log(firstNum); // 输出 1
console.log(thirdNum); // 输出 3
console.log(rest); // 输出 [4, 5]
注意:逗号占位
const { name, age, city, job = 'Engineer' } = person;
console.log(job); // 输出 'Engineer'
const { name: fullName, age: yearsOld, city: location } = person;
console.log(fullName); // 输出 'Alice'
console.log(yearsOld); // 输出 25
console.log(location); // 输出 'Shanghai'
嵌套解构
const user = {
name: 'Bob',
age: 25,
address: {
city: 'San Francisco',
zip: 12345
}
};
const { name, address: { city } } = user;
11、es6模块化
(1)什么是模块
模块:一个一个的局部作用域的代码块。
(2)什么是模块系统
模块系统:系统的解决了模块化一系列问题。
模块化的写法(之前我们用立即执行函数模拟模块化,ES6 则实现了针对模块化的语法)
消除全局变量(模块中的变量都是局部的,不同模块之间不会相互干扰,可以通过特定语法暴露指定内容)
管理加载顺序(之前我们将一个总的 JavaScript 程序分几个文件写,但在最终合并调用时,js 的引入需要满足前后依赖关系。比如:被引用的 js 文件就一定要在引用它的 js 文件之前加载)
(3)Module的基本用法
- 一个 JS 文件就是一个模块
- 用 import 关键字导入模块
- 用 export 关键字导出模块需要暴露的部分
- 在使用 script 标签加载的时候,需要加上 type=“module”,否则就以普通 JS 文件的形式引入了,就不是模块了
(4)Module的导入导出
导出的东西可以被导入(import),并访问到!
对于导入和导出有两种方法:
- export default 导出,import 导入
- export 导出,import 导入
(5)导出导入时起别名
export {fn as func, className as cN, age};
import {func, cN, age as nl} from "./module.js";
(6)整体导入
import * as imObj from "./module.js";
(7)同时导入
当我们需要分别导入 export default 和 export 时,可以使用同时导入的方式。
// 我们可以分开实现
import {fn, className, age} from "./module.js";
import sex from "./module.js";
// 更推荐使用同时导入的方式
import sex, {fn, className, age} from "./module.js";
// 注意:export default 必须在 export 之前
12、es6新增一些常用的方法
1,Array.from()
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6 的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.from()
还可以接受一个函数作为第二个参数,作用类似于数组的map()
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
let arrayLike = [1, 2, 3, 4, 5];
let newArray1 = Array.from(arrayLike, (item) => item * 2);
console.log("newArray1:", newArray1); // [2, 4, 6, 8, 10]
// 等同于
let newArray2 = Array.from(arrayLike).map((item) => item * 2);
console.log("newArray2:", newArray2); // [2, 4, 6, 8, 10]
2,Array.of()
Array.of()方法
将一组值转换为数组。
Array.of(1, 2, 3, 4); // [1, 2, 3, 4]
Array.of(3); // [3]
Array.of(3).length; // 1
// 字符也可以
Array.of("1",2,"3",4) // ['1', 2, '3', 4]
Array.of("1",2,"3",4).length // 4
这个方法的主要目的,是弥补数组构造函数Array()
的不足。因为参数个数的不同,会导致Array()
的行为有差异。如下:
console.log(new Array()); // []
console.log(new Array(3)); // [empty × 3]
console.log(new Array(3,11,8)); // [3, 11, 8]
上面代码中,new Array()方法没有参数、一个参数、三个参数时,返回的结果都不一样。只有当参数个数不少于 2 个时,new Array()才会返回由参数组成的新数组。
参数只有一个正整数时,实际上是指定数组的长度。
Array.of()基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
Array.of()
总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
Array.of()
方法可以用下面的代码模拟实现
function ArrayOf(){
return [].slice.call(arguments);
}
3,find(),findIndex(),findLast()和findLastIndex()
find():
数组实例的find()
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。
[1, 4, -5, 10].find((n) => n < 0)
// -5
上面代码找出数组中第一个小于 0 的成员。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
上面代码中,find()
方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
findIndex:
数组实例的findIndex()
方法的用法与find()
方法非常类似,返回第一个符合条件的数组成员的位置(索引),如果所有成员都不符合条件,则返回-1
。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
这两个方法都可以接受第二个参数,用来绑定回调函数的this
对象。
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
上面的代码中,find()
函数接收了第二个参数person
对象,回调函数中的this
对象指向person
对象。
find()
和findIndex()
都是从数组的0号位,依次向后检查。
4、findLast()和findLastIndex():
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
上面示例中,findLast()
和findLastIndex()
从数组结尾开始,寻找第一个value
属性为奇数的成员。结果,该成员是{ value: 3 }
,位置是2号位。
5,Array.fill()
fill
方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]new Array(3).fill(7)
// [7, 7, 7]
上面代码表明,fill
方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
上面代码表示,fill
方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。
注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
let arr = new Array(3).fill({name: "Amber"});
arr[0].name = "Eula"; // 只改了第一个, 但是后面的所有全都改变了
console.log(arr);
// [{name: "Eula"}, {name: "Eula"}, {name: "Eula"}]
6、keys(),values() 和 entries()
ES6 提供三个新的方法:entries(),keys()和values()—用于遍历数组。
它们都返回一个遍历器对象(Iterator),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
1,keys()是对键名的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
// for of不支持遍历普通对象,可通过与Object.keys()搭配使用遍历
for (let key of Object.keys(obj)) {
console.log(key); // Amber,Eula,KamisatoAyaka 拿到的都是对象的键名
}
console.log(Object.keys(obj)); //(3) ['Amber', 'Eula', 'KamisatoAyaka']
2,values()是对键值的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
for (let key of Object.values(obj)) {
console.log(key); // 安柏,优菈,神里绫华 拿到的都是对象的值
}
console.log(Object.values(obj)); //(3) ['安柏', '优菈', '神里绫华']
3,entries()是对键值对的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
for (let key of Object.entries(obj)) {
console.log(key);
// ['Amber', '安柏']
// ['Eula', '优菈']
// ['KamisatoAyaka', '神里绫华']
}
console.log(Object.entries(obj));
// 会以一个数组重新包装起来
// [
// ["Amber", "安柏"],
// ["Eula", "优菈"],
// ["KamisatoAyaka", "神里绫华"]
// ];
entries方法还有个用法就是:将Object
转换为Map
,new Map()
构造函数接受一个可迭代的entries
。借助Object.entries
方法你可以很容易的将Object
转换为Map
:
let obj = {
name: "Eula",
age: 18
};
let map = new Map(Object.entries(obj));
console.log(map); // Map(2) {'name' => 'Eula', 'age' => 18}
7、Array.includes()
includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false
;
const inc = ["a", "b", "c"].includes("a");
console.log("inc:", inc); // true
该方法的第二个参数表示搜索的起始位置,默认为0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。
if (arr.indexOf(el) !== -1) {
// ...
}
indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1
,表达起来不够直观。二是,它内部使用严格相等运算符(===
)进行判断,这会导致对NaN
的误判。
[NaN].indexOf(NaN)
// -1
includes
使用的是不一样的判断算法,就没有这个问题。
[NaN].includes(NaN)
// true
8、flat()和flatMap()
数组扁平化方法 Array.prototype.flat() 也叫数组拉平、数组降维。
不传参数时,默认“拉平”一层,可以传入一个整数,表示想要“拉平”的层数。
传入 <=0 的整数将返回原数组,不“拉平”。
Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组。
如果原数组有空位,Array.prototype.flat() 会跳过空位。
案例:
const arr = [1, ["a", "b"], [2, ["c"], 3]];
// 1,不传参数时,默认“拉平”一层
console.log(arr.flat());
// [1, 'a', 'b', 2, ['c'], 3]
// 2,传入一个整数参数,整数即“拉平”的层数
console.log(arr.flat(2));
// [1, 'a', 'b', 2, 'c', 3]
// 3,Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组
console.log(arr.flat(Infinity));
// [1, 'a', 'b', 2, 'c', 3]
// 4,传入 <=0 的整数将返回原数组,不“拉平”
console.log(arr.flat(0));
console.log(arr.flat(-6));
// [1, ['a', 'b'], [2, ['c'], 3]]
// 5,如果原数组有空位,flat()方法会跳过空位
console.log([1, 2, 3, 4, 5, 6, ,].flat());
// [1, 2, 3, 4, 5, 6]
flatMap:
flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
flatMap()方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。
let newFlatList = [[2, 4], [3, 6], [4, 8]].flatMap((item,index,arr) => {
console.log("item:",item);// [2, 4] [3, 6] [4, 8]
return [item[0]*2,item[1]*3] // 第一项*2,第二项*3,每个item都执行这个操作,最后flat扁平化数组并返回
});
console.log("newFlatList:",newFlatList);
//最终返回一维数组: [4, 12, 6, 18, 8, 24]
上面的案例是传入一个二维数组,把二维数组的第一项乘以2,第二项乘以3;最终返回处理好的一维数组;
注意:
- flatMap 方法不会改变原数组
- flatMap 方法等效于 array.map().flat()
flatMap()
只能展开一层数组
9,Array.reduce()
reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。(就是会将上一次的运行结果当做下一轮计算的初始值
;)
语法如下:
箭头函数写法
reduce((previousValue, currentValue, currentIndex, array) => { /* … */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* … */ }, initialValue)
回调函数触发
reduce(callbackFn)
reduce(callbackFn, initialValue)
回调函数中有四个参数,回调的外面还有一个参数(initialValue给定就算的初始值
);参数截图如下:
Array.reduce()的使用:
const arr = [1, 2, 3]
const initValue = 10;
function reducer(previousValue, currentValue, currentIndex, arrryTarget) {
return preValue + currentValue
}
arr.reduce(reducer, initValue) // res === 16
reduce 会遍历 arr 数组,数组有多少个,reducer 就会执行多少次。下面是执行流程:
reducer 执行 | previousValue | currentValue | currentIndex | return |
第一次执行 | 10 | 1 | 0 | 11 |
第二次执行 | 11 | 2 | 1 | 13 |
第三次执行 | 13 | 3 | 2 | 16 |
第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被作为初始值 initialValue,迭代器将从第二个元素开始执行(索引为 1 而不是 0)
也就是第一次执行 reducer 函数时,不存在“上一次的计算结果”。这里传递了初始值,因此 reducer 函数才从数组索引为 0 的元素开始执行,也就是 arr.length
等于 reducer
执行次数。
但如果不传递初始值呢?我们改动一下代码:
const arr = [1, 2, 3]
const initValue = 10;
function reducer(previousValue, currentValue, currentIndex, arrryTarget) {
return preValue + currentValue
}
arr.reduce(reducer, initValue) // res === 16
arr.reduce(reducer) // res === 6
reducer 执行 | previousValue | currentValue | currentIndex | return |
第一次执行 | 1 | 2 | 1 | 3 |
第二次执行 | 3 | 3 | 2 | 6 |
因为没有传递初始值,因此 reducer 函数从数组索引为 1 的元素开始执行,首次执行的时候 arr[0] 被作为了 previousValue
,currentValue
就是 arr[1]。
reduce()的使用场景
1,累加数组所有的值,累加可以,那么 加减乘除 中其他三个的原理是一样的;
let sum = [0, 1, 2, 3].reduce(function (previousValue, currentValue) {
return previousValue + currentValue
}, 0)
// sum is 6
2,累加对象数组里的值
要累加对象数组中包含的值,必须要提供 initialValue
,以便各个 item
正确通过你的函数;
let initialValue = 0
let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(function (previousValue, currentValue) {
return previousValue + currentValue.x
}, initialValue)
console.log(sum) // logs 6
3,将二维数组转化为一维
第一种:
let flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(previousValue, currentValue) {
return previousValue.concat(currentValue)
},
[]
)
// flattened is [0, 1, 2, 3, 4, 5]
第二种:
let flattened = [[1,2], [3, 4]].reduce((arrA, arrB) => [...arrA, ...arrB])
多维数组组转为一维数组
function flaten(arr) {
if (!Array.isArray(arr)) {
return arr;
}
return arr.reduce((arrA, arrB) => {
if (!Array.isArray(arrB)) {
return [...arrA, arrB];
}
return flaten([...arrA, ...arrB]);
}, []);
}
let flaten2 = flaten([1, 2, [3, 4], [6, [7, 8]]]);
console.log(flaten2);
[
1, 2, 3, 4,
6, 7, 8
]
数组去重:
1,第一种使用reduce方法,利用数组的indexOf方法来筛选;
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myArrayWithNoDuplicates = myArray.reduce(function (previousValue, currentValue) {
if (previousValue.indexOf(currentValue) === -1) {
previousValue.push(currentValue)
}
return previousValue
}, [])
console.log(myArrayWithNoDuplicates)
2.第二种方式使用 new Set
集合(无序不重复)
// 另一种方式
let myArray2 = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
let arrayWithNoDuplicates = Array.from(new Set(myArray2));
console.log(arrayWithNoDuplicates);