前言
很多新的特性在ES6中增加,让我们在使用js编写应用程序上,更为方便和便利。所以我们学习es6的一些语法特性还是很有必要的
常量和变量的定义
let命令
ES6中增加了let
命令,来取代以前的var
定义变量的使用。更符合我们在应用代码上直观的理解语义和作用范围。不会存在一些var的麻烦的问题
let i = 1;
const命令
声明一个只读的常量,一旦声明了,就不能改变它的值
const i = 0;
i = 3 // 这是有问题的
解构
ES6 中允许按照一定的格式,从数组,对象,字符串中提取值给变量赋值
数组解构
使用注意 :
- 等号右边必须是数组
- 数组必须按次序排列
let [a, b, c] = [1, 2, 3]; // 对数组中每个参数进行赋值 --> a=1 ,b=2, c=3
let [ , , third] = ["foo", "bar", "baz"]; // third = "baz"
let [head, ...tail] = [1, 2, 3, 4]; // head= 1 , tail = [2, 3, 4]
赋值的时候允许给变量设置默认值
// 给参数加默认值
let [x, y = 'b'] = ['a']; // x='a', y='b'
对象解构
使用注意 :
1.对象可以不按数据顺序,但名称必须一致
2.对象解构,就是会先找到同名属性
3.赋值给后者,前者用来是匹配的模式,后者才是变量 let { foo: baz }
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; // foo = 'aaa' ,bar = 'bbb'
let result = {a:1,b:2,c:3} ; let name = {d:1,...result} // name: {d: 1, a: 1, b: 2, c: 3}
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; // baz : "aaa" ,foo // error: foo is not defined
对象中的属性赋初始值
var {x, y = 5} = {x: 1}; // x=1 ,y =5
多个对象合并成一个对象
const obj1 = {a:1}
const obj2 = {b:1}
const obj = {...obj1,...obj2};//{a:1,b:1}
字符串解构
const [a, b, c] = 'hel'; // a= h ,b =e ,c =l
let {length : len} = 'hello'; // len = 5
字符串扩展
模板字符串
通过反引号(`)来标记,定义模板字符串,对其中嵌入的变量使用 ${}
来标记
let a = world
let result = `hello ${a}`
新增字符串方法
includes()
: 是否包含字符串startsWith()
: 是否以某字符串开头endsWith()
: 是否以某字符串结尾replaceAll()
: 替换所有匹配trimStart()/trimEnd()
: 去除头部/尾部的空格padStart()/padEnd()
: 头部/尾部补全repeat()
: 重复次数
let a = "123"
let b = " 123 "
a.include("2") // true
a.startWith("1") // true
a.endWith("3") // true
a.replaceAll("2",'1') // 113
b.trimStart() // 123
b.trimEnd() //123
a.padStart(6,"as") // asa123
a.padEnd(6,"as")// 123asa
Number的扩展
方法的扩展
- isInteger() : 是否是整数
- parseInt()/parseFloat() : 搬移到Number类型下的方法,转为整数/小数
Number.isInteger(12) // true
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
BigInt
使用BigInt
数据类型的目的是比Number
数据类型支持的范围更大的整数值。
创建BigInt
通过使用构造函数,整数末尾增加n, 可以通过转换函数
BigInt(9007199254740995n) // 9007199254740995n
BigInt(123) // 123n
BigInt('123') // 123n
对于运算,和普通Number
类型操作一致 , 除了 不带符号的右移位运算符>>>
, 和一元的求正运算符+
这两个运算还有问题。 同时不能和普通数值混合运算,普通数值的相等匹配也无意义
1n * 13 // 这样会有问题
1n * 13n // 这样可以
函数扩展
函数入参默认值
函数中的入参可以设置默认值,调用函数的时候可以不传递
function f1(x,y=1){
console.log(x,y)
}
f1(2) // 2,1
f1(2,3) //2,3
函数配合解构
function f2({x,y=5}){
conole.log(x,y)
}
f2({x:1,y=2}) // 1,2
f2({x:1}) //1,5
f2() // 会报错
针对入参是对象的时候,调用函数时入参必须要传递,我们可以将对象入参设置赋值默认空对象传递
function f2({x,y=5}={}){
conole.log(x,y)
}
f2() // undefined 5
箭头函数
es6 中允许使用箭头函数 (=>
)来定义函数
function f1(x){return x} 等价于 var f1 = x=>x
function f2(x,y){return x+y} 等价于 var f2=(x,y)=>x+y
function f2(x,y){return x+y} 等价于 var f2=(x,y)=> {return x+y}
注意箭头函数如果有返回,在使用大括号包裹起来的时候,就得加return
才行
数组扩展
扩展运算符
使用...
扩展符,可以将数组按照顺序的规整到参数上,可以将参数平铺到具体列表上
let [a,...b] = [1,2,3,4] // a=1,b=[2,3,4]
let arr = [1,2,3,4,5]
console.log(...arr) //1 2 3 4 5
配合函数的使用解构传参,将数组上的值对应函数入参顺序
let arr = [1,2]
function f(x,y){
console.log(x,y)
}
f(...arr)
数组的复制,数组的合并 也都能依靠扩展运算符直接实现
let a = [1,2,3]
let copy_a = [...a] // 数组拷贝
let arr1 = [1,2]
let arr2 = [3,4]
let arr = [...arr1,...arr2] // 数组合并
Array.of
将一组数字,构造成数组
Array.of(1,2,3) // [1,2,3]
实例方法
find()
: 找到第一个返回匹配到的值findIndex()
: 找到第一个返回匹配到的值的索引indexfindLast()
: 找到最后一个返回匹配到的值findLastIndex()
: 找到最后一个返回匹配到的值的索引indexfill()
: 填充数组,参数有填充值/开始位置/结束位置entries()
: 获取数组的键值对keys()
: 获取数组的键数组values()
:获取数组的值数组includes()
: 表示某个数组是否包含给定的值flat()
: 拉平指定层数数组Infinity
无论多少层flatMap()
: 执行map函数,在flat拉平toReversed()
: 不改变原数组的reversetoSorted()
:不改变原数组的sorttoSpliced()
:不该原数组的splicewith()
:不改变原数组的splice(index, 1, value)
[1,2,3].find((val,index,arr)=>val==2) // 2
[1,2,3].findIndex((val,index,arr)=>val==2) //1
[1,2,3].findLast((val,index,arr)=>val==2) //2
[1,2,3].findLastIndex((val,index,arr)=>val==2) //1
[1,2,3].fill(8) // [8,8,8]
[1,2,3].fill(8,1,2) // [1,8,3]
for(let [index,value] of [1,2,3].entries()){console.log(index,value)}
for(let index of [1,2,3].keys()){console.log(index)} // 0 1 2
for(let value of [1,2,3].keys()){console.log(value)} // 1 2 3
[1,2,[3,4]].flat(1) // [1,2,3,4]
[1,2,[3,4,[5,6]]].flat(2) // [1,2,3,4,5,6]
[1,2,3,4].flatMap(x=>[x*2]) // [2,,4,6,8]
[1,2,3].toReversed() // [3,2,1]
[1,2,3].toSorted() // [1,2,3]
对象扩展
简洁标识
对象的key有时候直接等同于变量的名称,对象的value就是变量的值 。
const key = 'value'
const result = {key} // {key:'value'}
function f(x,y){return {x,y}}
f(1,2) // {x:1,y:2}
const obj = {
name:'gjc',
key, // 直接省略的再定义键值名 ,等同于key:'value'
method(){return 'hello'} // 直接省略的再定义键值名 ,等同于method:function(){return 'hello'}
}
对象方法
Object.is()
: 比较是否相等,更为严谨的比较相等- 对比
==
: 会转换数据类型 - 对比
===
: NaN 不等于自身和 +0/-0不相等
- 对比
Object.assign()
: 对象合并 ,入参目标对象和多个源对象- 注意点一:浅拷贝,属性中对应的值是对象,还是得到的是引用
- 注意点二:同名属性的会被替换
Object.keys()
: 返回对象的key的名称列表Object.values()
: 返回对象的value的名称列表Object.entries()
:返回对象的键值对列表
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2); // target的结果{a:1, b:2, c:3}
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
运算符的扩展
指数运算符 **
指数运算符(**
),可以实现将数字进行指数运算
2**2 // 4 ,2的2次方
2**3 // 8 ,2的3次方
链判断运算符 ?.
在读取对象的很内部的属性值时候,需要判断每一层对象是否存在 , 也可以通过?.
判断是否对象存在,不存在返回undefined
const name = (data && data.user && data.user.name)
const name = data?.name
Null判断运算符 ??
因为单纯判断某个值是否null/undefined
,可以直接判断它的布尔值,但是会出现 空字符串/false/0
也会false ,这时候 通过 ??
严格判断只有null/undefined
才会返回右边的值
const name = data.name?? 'default name'
逻辑赋值运算符 ??=
逻辑运算符和赋值运算符的结合,判断逻辑后,满足条件再执行赋值
let data = {name:'gjc'}
let name = data.name || 'default name' //gjc
let name ||= data.name // gjc
Set 数据结构
类似于数组,内部成员不重复的数据结构
add()
: 加入成员size
: 返回数组长度个数delete()
: 删除元素has()
:是否有成员clear()
: 清除成员keys()
: 返回键名的遍历器values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
: 遍历每个成员intersection()
: 交集union()
: 并集difference()
: 差集symmetricDifference()
: 对称差集isSubsetOf()
: 判断是否为子集isSupersetOf()
: 判断是否为超集isDisjointFrom()
:判断是否为不相交
const set = new Set([1,2,3,4]) //构建set数组
set.size //4
set.add(1)
[...new Set([1,2,2,3,4])] // 去除重复数组 1,2,3,4
[...new Set('ababac')] //去除字符串重复字符 abc
set.delete(1) // true
set.has(2) // true
set.clear()
set.keys() 和 set.values() 结果一致
Map 数据结构
键值对集合 Hash结构,可以不用常规的字符串做健,和Objec对象不同
set()
: 设置键值get()
:获取值has()
: 判断是否存在值size
: 个数
const m = new Map() // 构建Map
const m = new Map([['key1",'cgj'],["key2","value2"]]) // 构建Map 通过数组入参
m.set("name":"gjc")
m.has('name') // true
m.get('name') // gjc
m.size // 3
Promise 对象
之前章节讲js的时候,有讲到过,再重新补充一下吧。
创建一个Promise 实例和基本使用
then()
方法处理promise
中resolve
的结果catch()
方法处理reject
的结果finally()
执行完then/catch
后都会执行的方法
const promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then((value)=>{}).catch((error)=>{}).finally()
使用注意:
- 调用
resolve
或reject
并不会终结Promise
的参数函数的执行,但不建议逻辑跟在resolve
或reject
后面 - 存在多个
Promise
时候,catch()
方法是直接捕获上述所有的Promise
的reject
的结果 - 虽然
then()
方法第二个参数也能接受到Promise
的reject
的结果,但是不建议,还是通过catch()
方法来实现 Promise
方法内部出现的异常,会被"吃掉",不会影响Promise
外部执行逻辑,建议调用Promise
对象的时候后面跟随catch()
方法
let p = new Promise((resolve, reject) => {
resolve(1);
console.log(2); // 会执行 ,但是不建议这么操作
})
p.then(res=>p())
function test(url){
return new Promise((resolve,reject)=>{
reject("success")
});
}
test("sdsf").then((res)=>return test(res))
.then(res=>{})
.catch(err=>{})
Promise.all
包装多个Promise实例为一块的Promise,接受类似数组格式的参数
作用: 执行所有获取成功结果,但途中遇到一个失败的就停止
const p1 = new Promise((resolve,error)=>{})
const p2 = new Promise((resolve,error)=>{})
const p3 = new Promise((resolve,error)=>{})
Promise.all([p1,p2,p3]).then(value=>{}).catch(error=>{})
注意:如果包装的多个Promise实例中,他们自己内部有catch
方法,则不会走到总体的Promise.all
的catch
方法
Promise.race
入参和Promise.all类似,但为了对接口返回的第一个结果的方法
作用: 获取第一个成功或者失败的结果
const p1 = new Promise((resolve,error)=>{})
const p2 = new Promise((resolve,error)=>{})
const p3 = new Promise((resolve,error)=>{})
Promise.race([p1,p2,p3]).then(value=>{}).catch(error=>{})
Promise.allSettled
为了针对 Promise.all
执行的时候有一个报错就不管其他请求的结果
作用: 获取所有成功和失败的结果,聚合到then
函数下综合返回
const resolved = Promise.resolve(42); // 成功的
const rejected = Promise.reject(-1); // 异常的
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// 最后返回的
// [
// { status: 'fulfilled', value: 42 }, // 返回的是成功的
// { status: 'rejected', reason: -1 } // 返回的是失败的
// ]
Promise.any
针对Promise.race的补充,因为一遇到一个reject失败的就不管其他实例。
作用: 获取第一个成功的结果,中间有失败的没关系还是继续等成功的
const p1 = new Promise((resolve,error)=>{})
const p2 = new Promise((resolve,error)=>{})
const p3 = new Promise((resolve,error)=>{})
Promise.race([p1,p2,p3])
.then(value=>{}) // 有一个成功的走
.catch(error=>{}) // 其他有实例都失败了走
快速构建Promise
将对象转成Promise 可以通过Promise.resolve/Promise.reject
方法来实现
Promise.resolve("hello") // 等价于new Promise((resolve,reject)=>resolve("hello"))
Promise.reject("error") // 等价于 new Promise((resolve,reject)=>reject("error"))
async和await
async
表示函数里有异步操作,且函数有返回的是Promise
对象,await
表示紧跟在后面的表达式需要等待结果。
基本使用
传统的定义function
返回值为promise
可以使用 async
替代
// f1和f2等价,返回成功的案例
function f1(){
return new Promise((resolve,reject)=>resolve('success'))
}
async function f2(){
return 'success'
}
// f3和f4等价,返回成功的案例
function f3(){
return new Promise((resolve,reject)=>reject('err'))
}
async function f4(){
throw new Error("err")
}
// 最终调用情况
f2().then(res=>{})
f4().then(res=>{}).catch(err=>{})
async不同表达方式
async function f() {} // 函数声明
const f = async function () {}; // 函数表达式
let obj = { async f() {} }; //对象的方法
const f = async () => {}; //箭头函数
await的异常
使用await
,对于里面出现的异常错误需要用try..catch
来捕获执行
async function f(){
await new Promise((resolve,reject)=>resolve(x+3))
}
f().then(res=>{}).catch(err=>{}) // 会执行到catch上
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 注意: 不会执行
}
async function f() {
try {
await Promise.reject('出错了');
}catch(e){}
await Promise.resolve('hello world'); // 注意: 会执行
}
使用注意点
- 对于
await
命令后面跟的Promise的对象,会出现reject场景,所以把await
命令放在try...catch
代码块中 - 对于多个await 同时触发,一个就是使用Promise.all 方法,一个就是先执行promise实例方法,后面在跟随await获取结果
await
只能使用在async
函数中
const getUrlResult = function (){
return new Promise((resolve,reject)=>{})
}
// 使用try..catch..
async function test(){
try{
let result = await getUrlResult()
}catch(e){
}
}
// 第一种
let [result1,result2] = await Promise.all([getUrlResult(),getUrlResult()])
// 第二种
let a = getUrlResult()
let b = getUrlResult()
let result1 = await a
let result2 = await b
实际应用场景
对于方法要实现依次执行获取结果
// 依次执行do异步函数获取它的结果
async function read(params) {
for (const param of params) {
const result = await do(param);
console.log(await result.text());
}
}
对于方法要并发执行,然后依次获取结果
// 定义异步任务
const getUrlResult = function (timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("c2 success"), timeout);
});
};
async function test() {
// 多个异步任务执行的promise实例封装
let promiseContentList = [2000, 1000, 3000].map(async (res) => {
let result = await getUrlResult(res);
return result;
});
// 对异步任务进行遍历获取每个的结果
for (const promiseContent of promiseContentList) {
console.log(typeof promiseContent);
console.log(await promiseContent);
}
}
Module 模块
export 输出
输出(变量/函数/类) 包含单个变量输出和整体一起输出, 优先使用末尾整体一起输出。 输出的时候可以使用as
使用别名
export var name = 'gjc'
export var age = 1 //单个输出
var i = 1
var j = 2
export {i,j} //整体输出
export {i as i1, j as i2} // 整体输出且赋值别名
export const name = 1 //输出常量
export function f(){} // 单个输出函数
function f1(){}
export {f1} //整体输出函数
export const f = ()=>{} // 箭头函数输出
注意: export
命令可以出现在模块的任何位置,只要处于模块顶层就可以
针对不希望使用者一定非得得知原来模块导出的名称才能使用,提供了默认输出 export default
export default 12
let i = 10
export default i
export default function f1(){}
function f2(){}
export default f2
export default ()=>{}
注意:一个模块只能有一个默认输出, export default
命令只能使用一次
import 输入
加载变量,加载文件导入指定的和输出一致的名称内容
import {name ,age } from './xxx.js'
console.log(name,age)
// 指定别名
import {name ,age as a } from './xxx.js'
console.log(name,a)
注意: 建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性
如果不想一一指定导入,可以使用整体加载,用*
代替整个对象
// 文件1,xx.js
export function f1(){}
export function f2(){}
// 文件2,yy.js
import * as ob from './xx.js'
ob.f1()
ob.f2()
针对export
是默认导出,这个时候使用import
也不同,此时不用需要在大括号了,且允许你取任何名称
// 文件1,xx.js
export default f1(){}
import f from './xx.js'
总结
在js的基础上,增加了很多语法糖,优化了js写代码的便捷性。