后端上手学习es6基础知识

42 阅读14分钟

前言

很多新的特性在ES6中增加,让我们在使用js编写应用程序上,更为方便和便利。所以我们学习es6的一些语法特性还是很有必要的

常量和变量的定义

let命令

ES6中增加了let命令,来取代以前的var定义变量的使用。更符合我们在应用代码上直观的理解语义和作用范围。不会存在一些var的麻烦的问题

let i = 1;

const命令

声明一个只读的常量,一旦声明了,就不能改变它的值

const i = 0;
i = 3 // 这是有问题的

解构

ES6 中允许按照一定的格式,从数组,对象,字符串中提取值给变量赋值

数组解构

使用注意 :

  1. 等号右边必须是数组
  2. 数组必须按次序排列
let [a, b, c] = [1, 2, 3];  //  对数组中每个参数进行赋值 --> a=1b=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() : 找到第一个返回匹配到的值的索引index
  • findLast() : 找到最后一个返回匹配到的值
  • findLastIndex() : 找到最后一个返回匹配到的值的索引index
  • fill() : 填充数组,参数有填充值/开始位置/结束位置
  • entries() : 获取数组的键值对
  • keys() : 获取数组的键数组
  • values() :获取数组的值数组
  • includes() : 表示某个数组是否包含给定的值
  • flat() : 拉平指定层数数组 Infinity无论多少层
  • flatMap() : 执行map函数,在flat拉平
  • toReversed() : 不改变原数组的reverse
  • toSorted() :不改变原数组的sort
  • toSpliced() :不该原数组的splice
  • with() :不改变原数组的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 实例和基本使用

  1. then()方法处理 promiseresolve的结果
  2. catch()方法处理reject的结果
  3. finally()执行完then/catch后都会执行的方法
const promise = new Promise(function(resolve, reject) {
    if (/* 异步操作成功 */){
        resolve(value);
    } else {
        reject(error);
    }
});
promise.then((value)=>{}).catch((error)=>{}).finally()

使用注意:

  1. 调用resolvereject并不会终结 Promise 的参数函数的执行,但不建议逻辑跟在resolvereject后面
  2. 存在多个Promise时候,catch()方法是直接捕获上述所有的Promisereject的结果
  3. 虽然then()方法第二个参数也能接受到Promisereject的结果,但是不建议,还是通过catch()方法来实现
  4. 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.allcatch方法

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'); // 注意: 会执行
}

使用注意点

  1. 对于await命令后面跟的Promise的对象,会出现reject场景,所以把await命令放在try...catch代码块中
  2. 对于多个await 同时触发,一个就是使用Promise.all 方法,一个就是先执行promise实例方法,后面在跟随await获取结果
  3. 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写代码的便捷性。