ES6总结
视频链接:https://www.bilibili.com/video/BV1uK411H7on?p=1
一、概述
1、什么是 ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个 组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为 Ecma 国际;
2、什么是 ECMAScript
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言
3、什么是 ECMA-262
Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个,
4、ECMA-262 历史
ECMA-262(ECMAScript)
5、谁在维护 ECMA-262
TC39(Technical Committee 39)是推进 ECMAScript 发展的委员会。其会员都是公司(其中主要是浏 览器厂商,有苹果、谷歌、微软、因特尔等)。TC39 定期召开会议,会议由会员公司的代表与特邀专家 出席;
6、为什么要学习 ES6
ES6 的版本变动内容最多,具有里程碑意义; ES6 加入许多新的语法特性,编程实现更简单、高效; ES6 是前端发展趋势,就业必备技能;
7、ES6 兼容性
查看:http://kangax.github.io/compat-table/es6
参考地址: https://es6.ruanyifeng.com/
二、ES6 新特性
1. let 和 const 关键字
1.1 let
let 关键字用来声明变量,使用 let 声明的变量有以下几个特点:
let a; // 单个声明
let b,c,d; // 批量声明
let e = 100; // 单个声明并赋值
let f = 521, g = 'iloveyou', h = [] // 批量声明并赋值
- 不允许重复声明;
let a
let a = 12 // a已经声明,此处为错误用法!
- 块儿级作用域(局部变量);
{
let fruit='apple'
}
console.log(fruit) //error
// 在if()中同理
- 不存在变量提升;
// 什么是变量提升:就是在变量创建之前使用
//(比如输出:输出的是默认值),let不存在,var存在
console.log(people1) // 可输出默认值
console.log(people2) // 报错:Uncaught ReferenceError: people2 is not defined
var people1 = "Maria" // 存在变量提升
let people2 = "Jim" // 不存在变量提升
- 不影响作用域链;
// 什么是作用域链:
// 很简单,就是代码块内有代码块,跟常规编程语言一样,
// 上级代码块中的局部变量下级可用
{
let p = "Maria"
function fn(){
console.log(p) // 这里是可以使用的
}
fn()
}
1.2 let 案例
需求:点击div更换背景颜色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="width: 50px;height: 50px; margin: 10px; border: 1px solid black; "></div>
<div style="width: 50px;height: 50px; margin: 10px; border: 1px solid green; "></div>
<div style="width: 50px;height: 50px; margin: 10px; border: 1px solid yellow;"></div>
<script>
let items=document.getElementsByTagName("div");
for (let i=0;i<items.length;i++){
items[i].onclick=function (){
items[i].style.backgroundColor='pink';
}
}
console.log(windows.i) //3
// 当var=3的时候,点击事件开始向外层作用域找,找不到,就是windows.i,此时是3,如果是let i,具有块级作用域,所以每一次触碰事件的i都是不同的。
</script>
</body>
</html>

1.3 const
const 关键字用来声明常量,const 声明有以下特点: (常量的含义是指向的对象不能修改,但是可以改变对象内部的属性)
- 声明必须赋初始值
// const声明常量,一经声明,则不允许修改
// const fruit // 未赋值,报错
const fruit = "apple"
console.log(fruit) // "apple"
- 标识符一般为大写(习惯)
const FRUIT = "apple"
console.log(FRUIT) // "apple"
- 不允许重复声明
const FRUIT = "apple"
const FRUIT = "apple" // 报错,不可重复声明
- 值不允许修改
当我们修饰的标识符不会被再次赋值时,就可以使用const来保证数据的安全性
const FRUIT = "apple"
FRUIT = "banana" // 错误
注意:对数组元素的修改和对对象内部的修改是可以的(数组和对象存的是引用地址)
const FRUIT = ['apple', 'banana', 'peach', 'orange'];
FRUIT.push('watermalen'); //不报错,常量地址没有发生变化
- 块儿级作用域(局部)
{
const FRUIT = "apple"
console.log(FRUIT) // "apple"
}
console.log(FRUIT) // 错误,FRUIT未定义
1.4 解构赋值
ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。 使用场景:频繁使用对象方法、数组元素,就可以使用解构赋值形式;
- 数组的解构
const BOOKS = ['三体','海子的诗','西游记']
let [san,hai,xi] = BOOKS
console.log(san) // '三体'
console.log(hai) // '孩子的诗'
console.log(xi) // '西游记'
- 对象的解构
const ZHANGSAN = {
name : '张三',
age : 23 ,
gender : '男',
speak : function() {
console.log("Hello,I'm ZhangSan!")
}
}
let {name,age,gender,speak} = ZHANGSAN
console.log(name) // '张三'
console.log(age) // 23
console.log(gender) // '男'
console.log(speak) // function(){...}
speak() // "Hello,I'm ZhangSan!"
注意:
- 在
let {name,age,gender,speak} = ZHANGSAN的 { } 中,里面的变量名需要和对象中的属性名相同- 可以通过let {speak} = ZHANGSAN只获取里面的speak方法,之后也是通过speak()调用
2. 模板字符串
使用一对反引号 声明的字符串,特性如下:
- 里面可以直接使用换行
let str = `<ul>
<li>第一行</li>
<li>第二行</li>
</ul>`
let hello = `噢早上好呀,
海绵宝宝~`
- 变量拼接(替换/插入)
使用${变量名}定位插入的元素位置
let name = '海绵宝宝'
let hello = `早上好呀${name},好久不见。`
console.log(hello) // 早上好呀海绵宝宝,好久不见
3. 对象的简化写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁
let name = '海绵宝宝'
let sayHello = function() {
console.log('早上好呀,珊迪')
}
原来:
const hello = {
name : name,
sayHello : sayHello,
sayBye : function() { console.log('再见,珊迪') }
}
ES6:
const hello = {
name,
sayHello,
sayBye() { console.log('再见,珊迪') }
}
4. 箭头函数
ES6允许使用箭头 =>定义函数
函数声明:
// let fn = function() {
// ...
// }
let fn = (a,b) => {
return a + b
}
// 调用函数
console.log(fn(2,3)) // 5
特性:
- this是静态的,this
始终指向函数声明时所在作用域下的this的值
function getName1(){
console.log(this.name)
}
let getName2 = () => {
console.log(this.name)
}
window.name = '蛙哈哈'
const school = {
name: 'wahaha'
}
//直接调用
getName1() //蛙哈哈
getName2() //蛙哈哈
//call
getName1.call(school) //wahaha
getName2.call(school) //蛙哈哈
- 不能作为构造函数实例化对象
let Person = (name,age) => {
this.name = name
this.age = age
}
let zhangsan = new Person('张三',20) // 错误
- 不能使用
arguments变量
let fn = () => {
console.log(arguments) // 错误
}
fn(1,2,3)
- 箭头函数的简写 (1) 省略小括号,当形参有且只有一个的时候可以省略小括号。
// let add = (n) => {
// console.log(n + n)
// }
// add(3) // 6
// 简写:
let add = n => {
console.log(n + n)
}
add(3) // 6
(2) 省略花括号 { },仅当函数语句只有一条语句时。此时,'return' 也需要省略,结果即是返回值
let pow = n => n * n
console.log(pow(8)) // 64
5. 函数参数默认值
ES6允许给函数参数赋值初始值
特性:
- 可以给形参赋初始值,一般位置要靠后(潜规则)
function add(a,b,c=12){
return a+b+c;
}
let result = add (1,2)
console.log(result) // 15
如果上面代码没有给 形参c赋初始值,则执行add (1,2)时,形参c没有对应的参数,默认为NaN,所以add (1,2)的执行结果为NaN
- 与解构赋值结合
function ap({host='127.0.0.1', username, password, port}){
console.log(host,username,password,port) //
}
ap({
host: 'localhost',
username:'admin',
password:'000000',
port:3000
})
// 执行结果:localhost admin 000000 3000
6. rest参数
- ES6引入
rest参数,用于获取函数的实参,用来代替arguments- rest参数:以...为前缀,例如下面的...args
function date(...args){
console.log(args)
}
date(1,2,3,4,5,6,7)
// 执行结果:[1, 2, 3, 4, 5, 6, 7]
function date(a,b,c,...args){
console.log(args)
}
date(1,2,3,4,5,6,7)
// 执行结果:[4, 5, 6, 7]
7. 扩展运算符
扩展运算符... ,能将数组转换为逗号分隔的参数序列
const tfboys=['易烊千玺','王源','王俊凯']
function show(){
console.log(arguments)
}
show(tfboys) // 一个参数,数组:['易烊千玺', '王源', '王俊凯']
show(...tfboys) //0: "易烊千玺" 1: "王源" 2: "王俊凯"
应用:
- 数组的合并
const arr1 = ['aa','bb']
const arr2 = ['cc','dd']
// const arr = arr1.concat(arr2) // ['aa', 'bb', 'cc', 'dd']
const arr = [...arr1, ...arr2]
console.log(arr) // ['aa', 'bb', 'cc', 'dd']
- 数组的克隆
const arr1 = ['a','b','c']
const arr2 = [...arr1]
console.log(arr2) // ['a', 'b', 'c']
如果数组里面有引用类型的数据,则整个为浅拷贝 ;否则,就是完全拷贝
- 将伪数组转换为真正的数组
const divs = documents.querySelectorAll('div')
const divArr = [...divs]
console.log(divArr) // [div,div,div]
8. Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
- Symbol的值是唯一的,用来解决命名冲突的问题 - Symbol值不能与其他数据进行运算 - Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
特性:
- 创建
let s1 = Symbol('aa')
let s2= Symbol('aa')
console.log(s1===s2) //false
let s3 = Symbol.for('bb')
let s4 = Symbol.for('bb')
comsole.log(s3===s4) //true
- 不能与其他数据进行运算(不可运算、比较)
let result = s + 100 //error
let result = s > 100 //error
let result = s + s //error
应用:
- 给对象添加方法方式一:
let game = {
name : '超级麻利'
}
let methods = {
up:Symbol()
down:Symbol()
}
game[methods.up]=function(){
console.log('跳起来了!')
}
game[methods.down]=function(){
console.log('蹲下去了!')
}
console.log(game) // name: '超级麻利',Symbol(),Symbol()
- 给对象添加方法方式二:
let youxi = {,
name: '狼人杀'
[Symbol('say')]:function(){
console.log('我可以发言')
},
[Symbol('close')]:function(){
console.log('我可以闭眼')
}
}
console.log(youxi) // name:'狼人杀',Symbol(say),Symbol(close)
9. 迭代器
迭代器( lterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署lterator接口,就可以完成遍历操作。
- ES6创造了一种新的遍历命令for…of循环,lterator接口主要供 for…of消费- 原生具备iterator接口的数据(可用for of遍历)
for ... of 和 for ... in
let arr = ['a','b','c','d']
for(let n of arr) {
console.log(n) // a b c d
}
for(let n in arr) {
console.log(n) // 0 1 2 3
}
迭代器原理
- 创建一个
指针对象,指向数据结构的起始位置- 第一次调用next()方法,指针自动指向数据结构第一个成员- 接下来不断调用next(),指针一直往后移动,直到指向最后一个成员- 每次调用next()返回一个包含value和done属性的对象
const array = ['AA','BB','CC','DD']
// for(let v of array){
// console.log(v) // 'AA','BB','CC','DD'
// for in保存的是键名,for of保存的是键值
// }
let iterator = array[Symbol.iterator]()
console.log(iterator.next()) // {{value:'AA',done:false}}
console.log(iterator.next()) // {{value:'BB',done:false}}
console.log(iterator.next()) // {{value:'CC',done:false}}
console.log(iterator.next()) // {{value:'DD',done:false}}
console.log(iterator.next()) // {{value:undefined,done:true}}
done的值为true的时候表示循环完成了- 需要自定义遍历数组的时候,要想到迭代器
const banji = {
name: "终极一班",
stus:[
'张三',
'李四',
'王五',
'赵六'
],
[Symbol.iterator](){
//索引变量
let index = 0;
let that = this;
return{
next:function(){
if(index<that.stus.length){
const result = {value:that.stus[index],done:false};
//下标自增
index++;
//返回结果
return result;
}else{
return {value:undefined,done:true};
}
}
}
}
}
for(let v of banji){
console.log(v);
}
10. 生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同,是一种特殊的函数
- 一个generator看上去像一个函数,但可以
返回多次。- generator和函数不同的是,generator由function *定义(注意多出的*号),并且,除了return语句,还可以用yield返回多次。
function * generator (){ //函数名和function中间有一个 *
yield '耳朵' //yield是函数代码的分隔符
yield '尾巴'
yield '真奇怪'
}
let iterator = generator()
console.log(iteretor.next())
//{value:'耳朵',done:false} next() // 执行第一段,并且返回yield后面的值
console.log(iteretor.next()) //{value:'尾巴',done:false}
console.log(iteretor.next()) //{value:'真奇怪',done:false}
- 生成器函数的参数传递
function * gen(args){
console.log(args)
let one = yield 111
console.log(one)
let two = yield 222
console.log(two)
let three = yield 333
console.log(three)
}
let iterator = gen('AAA')
console.log(iterator.next())
console.log(iterator.next('BBB')) //next中传入的BBB将作为yield 111的返回结果
console.log(iterator.next('CCC')) //next中传入的CCC将作为yield 222的返回结果
console.log(iterator.next('DDD')) //next中传入的DDD将作为yield 333的返回结果
- 实例1:用生成器函数的方式解决回调地狱问题
function one(){
setTimeout(()=>{
console.log('111')
iterator.next()
},1000)
}
function two(){
setTimeout(()=>{
console.log('222')
iterator.next()
},2000)
}
function three(){
setTimeout(()=>{
console.log('333')
iterator.next()
},3000)
}
function * gen(){
yield one()
yield two()
yield three()
}
let iterator = gen()
iterator.next()
- 实例2:模拟异步获取数据
function one(){
setTimeout((){
let data='用户数据'
iterator.next(data)
},1000)
}
function two(){
setTimeout((){
let data='订单数据'
iterator.next(data)
},2000)
}
function three(){
setTimeout((){
let data='商品数据'
iterator.next(data)
},3000)
}
function * gen(){
let users=yield one()
console.log(users)
let orders=yield two()
console.log(orders)
let goods=yield three()
console.log(goods)
}
let iterator = gen()
iterator.next()
11. Promise
Promise是ES6引入的异步编程的新解决方案。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
const p = new Promise((resolve, reject) => {
setTimeout(()=>{
let data='数据库数据'
// resolve(data)
reject(data)
})
})
p.then(function (value){ // 成功则执行第一个回调函数
console.log(value)
},function (reason){ // 失败则执行第二个
console.error(reason)
})
Promise.then()方法
const p =new Promise((resolve, reject) =>{
setTimeout(() => {
resolve('用户数据')
})
});
//then()函数返回的实际也是一个Promise对象
//1.当回调后,返回的是非Promise类型的属性时,状态为fulfilled,then()函数的返回值为对象的成功值,如reutnr 123,返回的Promise对象值为123,如果没有返回值,是undefined
//2.当回调后,返回的是Promise类型的对象时,then()函数的返回值为这个Promise对象的状态值
//3.当回调后,如果抛出的异常,则then()函数的返回值状态也是rejected
let result = p.then(value=>{
console.log(value)
// return 123
// return new Promise((resolve, reject) => {
// resolve('ok')
// })
throw 123
},reason => {
console.log(reason)
})
console.log(result)
Promise.catch()方法
const p = new Promise((resolve, reject) => {
setTimeout(()=>{
reject('出错啦')
},1000)
})
p.catch(reason => {
console.log(reason)
})
发送AJAX请求
//ajax请求返回一个promise
function sendAjax(url){
return new Promise((resolve, reject) => {
//创建对象
const x = new XMLHttpRequest()
//初始化
x.open('GET',url)
//发送
x.send()
//时间绑定
x.onreadystatechange = ()=> {
if(x.readyState === 4 ){
if(x.status >= 200 && x.status < 300){
//成功
resolve(x.response)
}else {
//失败
reject(x.status)
}
}
}
})
}
//测试
sendAjax("https://api.apiopen.top/getJoke").then(value => {
console.log(value)
},reason => {
console.log(reason)
})
12. 集合
12.1 Set
ES6提供了新的数据结构set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用「扩展运算符』和「 for…of…』进行遍历
集合的属性和方法:
.size返回集合的元素个数-.add()增加一个新元素,返回当前集合-.delete()删除元素,返回boolean值-.has()检测集合中是否包含某个元素,返回boolean值
let s = new Set();
let s2 = new Set(['A','B','C','D'])
//元素个数
console.log(s2.size)
//添加新的元素E
s2.add('E')
//删除元素A
s2.delete('A')
//检测是否有 C
console.log(s2.has('C'))
//清空
s2.clear()
console.log(s2)
应用:
let arr = [1,2,3,4,5,4,3,2,1]
let arr2=[4,5,6,5,6]
//1.数组去重
let result = [...new Set(arr)]
console.log(result) //[1,2,3,4,5]
//2.交集
let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item))
console.log(result2) //[4,5]
//3.并集
let result3=[...new Set([...arr,...arr2])]
console.log(result3) //[1,2,3,4,5,6]
//4.差集
let result4= [...new Set(arr)].filter(item => !(new Set(arr2).has(item)))
console.log(result4) //[1,2,3]
12.2 Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用『扩展运算符』和「for…of…』进行遍历。
Map的属性和方法:
.size获取Map的键值对数量(最外层)-.set(key,value)添加键值对-.delete(key)删除键为key的键值对-.get(key)获取键为key的值-for...of遍历里面的每一个键值对
let m = new Map()
m.set('name','ran')
m.set('change',()=>{
console.log('改变!')
})
let key={
school:'atguigu'
}
m.set(key,['广州','深圳'])
//size
console.log(m.size)
//删除键为name的键值对,会返回修改后的Map集合
m.delete('name')
//获取
console.log(m.get('change'))
//清空
// m.clear()
//遍历里面的每一个键值对
for(let v of m){
console.log(v)
}
13. Class
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class shouji {
constructor(brand,price) { // 构造器
this.brand=brand
this.price=price
}
call(){
console.log('我可以打电话')
}
}
let A = new shouji('1+',1999)
console.log(A)
结果:
13.1 静态成员
class Person{
static name='手机' // 声明静态成员变量
}
let nokia = new Person()
console.log(nokia.name) // undefined
javascript13.2 构造函数继承
function Phone(brand,price){
this.brand=brand
this.price=price
}
Phone.prototype.call=function (){
console.log("我可以打电话")
}
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price)
this.color=color
this.size=size
}
//设置子级构造函数原型
SmartPhone.prototype=new Phone
SmartPhone.prototype.constructor=SmartPhone
//声明子类方法
SmartPhone.prototype.photo = function (){
console.log('我可以玩游戏')
}
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch')
console.log(chuizi)
结果:
13.3 Class类继承
class Phone{
constructor(brand,price) {
this.brand=brand
this.price=price
}
//父类的成员属性
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size) {
super(brand,price)
this.color=color
this.size=size
}
photo(){
console.log('拍照')
}
playGame(){
console.log('打游戏')
}
}
const xiaomi=new SmartPhone('小米',1999,'黑色','4.7inch')
xiaomi.call()
xiaomi.photo()
xiaomi.playGame()
结果:
13.4 子类对父类方法的重写
class Phone{
constructor(brand,price) {
this.brand=brand
this.price=price
}
//父类的成员属性
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size) {
super(brand,price)
this.color=color
this.size=size
}
photo(){
console.log('拍照')
}
playGame(){
console.log('打游戏')
}
//重写!
call(){
console.log('我可以进行视频通话')
}
}
const xiaomi=new SmartPhone('小米',1999,'黑色','4.7inch')
xiaomi.call()
xiaomi.photo()
xiaomi.playGame()
结果:
13.5 get和set设置
class Phone{
get price(){
console.log("价格被读取了")
return 'I LOVE YOU'
}
set price(val){
console.log('价格被修改了')
return val
}
}
//实例化对象
let s = new Phone()
s.price=12
console.log(s.price) //其实是调用price方法
结果:
14 数值扩展
// Number.EPSILON是 JavaScript的最小精度,属性的值接近于 2.22044...E-16
function equal(a,b){
if(Math.abs(a-b) < Number.EPSILON){
return true
}else {
return false
}
}
console.log(equal(0.1 + 0.2 === 0.3)) //false
console.log(equal(0.1+0.2,0.3)) //true
//二进制和八进制
let b = 0b1010 //2进制
let o = 0o777 //8进制
let d = 100 //10进制
let x = 0xff //16进制
console.log(x) //255
//检测一个数是否为有限数
console.log(Number.isFinite(100)) //true
console.log(Number.isFinite(100/0)) //false
console.log(Number.isFinite(Infinity)) //false
//检测一个数值是否为NaN
console.log(Number.isNaN(123)) //false
//字符串转整数
console.log(Number.parseInt('5213123love')); //5213123
console.log(Number.parseFloat('5.123123神器')); //5.123123
//判断是否为整数
console.log(Number.isInteger(5)) //true
console.log(Number.isInteger(2.5)) //false
//将小数部分抹除
console.log(Math.trunc(3.45345345345)) //3
//检测一个数到底是正数、负数、还是0
console.log(Math.sign(100)) //1
console.log(Math.sign(0)) //0
console.log(Math.sign(-123)) //-1
结果:
15.对象方法扩展
//1.Object.is 判断两个值是否完全相等
console.log(Object.is(120,120)) //true
console.log(Object.is(NaN,NaN)) //true
console.log(NaN===NaN) //false
//2.Object.assign 对象的合并
const a = {
name:'ran',
age:12
}
const b = {
pass:'i love you'
}
console.log(Object.assign(a,b)) //{name:'ran',age:'12',pass:'i love you'}
//3.Object.setPrototypeOf 设置原型对象 Object.getPrototypeof
const school = {
name:'哆啦A梦'
}
const cities = {
xiaoqu:['北京','上海']
}
Object.setPrototypeOf(school,cities)
console.log(Object.getPrototypeOf(school)) //{xiaoqu: Array(2)}
console.log(school) //{name: "哆啦A梦"}
结果:
16. 模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
1. 模块化的好处:
- 防止命名冲突- 代码复用- 高维护性- 模块化规范产品
2. ES6之前的模块化规范有:
- CommonJS ====> NodeJS、Browserify- AMD ====> requireJS- CMD ====> seaJS
3. 语法:
- 模块功能主要有两个命令构成:
export和import-export命令用于规定模块的对外接口-import命令用于输入其他模块提供的功能
// 下面js代码放在./src/js/m1.js文件中
export let school = '哆啦A梦'
export function teach(){
console.log('教技能')
}
<!-- html代码 -->
<script type="module">
import * as m1 from "./src/js/m1.js"
console.log(m1)
</script>
打开方式和结果: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B4rsSrv6-1657010404248)(C:\Users\CYY\AppData\Roaming\Typora\typora-user-images\1653480865699.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tLjL4usu-1657010404250)(C:\Users\CYY\AppData\Roaming\Typora\typora-user-images\1653480876600.png)]
16.1 暴露语法
16.1.1. 统一暴露
let school = '清华大学';
function findjob(){
console.log('找工作吧');
}
//统一暴露
export {school, findjob}
<script type="module">
import * as m1 from "./src/js/m1.js"
console.log(m1)
console.log(m1.school)
console.log(m1.findJob())
</script>
结果:
16.1.2. 默认暴露(多变量暴露)
//默认暴露 export default
export default {
school:'清华大学',
change:function(){
console.log('可以改变人的一生!')
}
}
<script type="module">
import * as m1 from "./src/js/m1.js"
console.log(m1.default)
console.log(m1.default.school)
console.log(m1.default.change())
</script>
结果:
16.1.3 单变量暴露
// a.js
function add(a, b) {
return a + b;
}
module.exports = add;
// 引入a.js
const add = require('./a.js');
console.log(add(10, 20));
16.2 引入语法
16.2.1. 通用导入方式
import * as m1 from "./src/js/m1.js"
import * as m2 from "./src/js/m2.js"
import * as m3 from "./src/js/m3.js"
16.2.2. 解构赋值方式
import {school,teach} from "./src/js/m1.js"
import {school as s,findJob} from "./src/js/m2.js"
import {default as m3 } from "./src/js/m3.js"
16.2.3. 简便形式(只针对默认暴露)
import m3 from "./src/js/m3.js"
16.3 模块化方式2
<script src="./src//js/app.js" type=modeule></script>
16.4 babel对ES5模块化代码转换
//1、安装工具 babel-cli babel-preset-env browserify(webpack)
终端输入:
1、npm init --yes 初始化
2、npm i babel-cli babel-preset-env browserify -D
3、npx babel src/js -d dist/js --presets=babel-preset-env //转换
4、打包 npx browserify dist/js/app.js -o dist/bundle.js
//<script src="dist/bundle.js" type=modeule></script>
16.5 ES6模块化引入NPM包
终端:
1、安装jquery包 npm i jquery
2、app.js 文件中添加
import $ from 'jquery'; //ES6 引入jquery语句
$('body').css('background','pink');
重新执行打包命令!!!
3、npx babel src/js -d dist/js --presets=babel-preset-env //转换
4、打包 npx browserify dist/js/app.js -o dist/bundle.js
17. ECMASript 7
Array.prototype.includes
includes方法用来检测数组中是否包含某个元素,返回布尔类型值。指数运算符
在 ES7 中引入指数运算符
**,用来实现幂运算,功能与Math.pow(a, b)结果相同。2 ** 3 // 8 Math.pow(2, 3) // 8
18. ECMASript 8
1. async 和 await
async 和 await 两种语法结合可以让异步代码像同步代码一样。(即:看起来是同步的,实质上是异步的。)
先从字面意思理解,async 意为异步,可以用于声明一个函数前,该函数是异步的。await 意为等待,即等待一个异步方法完成。
1.1 async
async 声明(function)的函数成为 async 函数,语法:
async function funcName() {
//statements
}
async 内部可以使用 await,也可以不使用。 async 函数的返回值是一个 Promise 对象,因此执行这个函数时,可以使用 then 和 catch 方法。 根据 函数体内部 的返回值, async 函数返回值具体情况如下:
函数体内不返回任何值,则 async 函数返回值为一个成功(fulfilled)的 Promise 对象,状态值为 undefined。
let a = async function() {}
let res = a()
console.log(res)
// Promise{<fullfilled>: undefined}
返回结果不是一个 Promise ,则 async 函数返回值为一个成功(fulfilled)的 Promise 对象,状态值为这个内部返回值。
let a = async function () {
return 'hello'
}
let res = a()
console.log(res)
// Promise{<fullfilled>: 'hello'}
内部抛出错误,则 async 函数返回值为一个失败的 Promise 对象。
let a = async function foo() {
throw new Error('出错了')
}
a().catch(reason => {
console.log(reason)
})
若函数内部返回值是一个 Promise 对象,则 async 函数返回值的状态取决于这个 Promise 对象。
let a = async function () {
return new Promise((resolve, reject) => {
resolve("成功")
})
}
a().then(value => {
console.log(value)
})
1.2 await
await 相当于一个运算符,右边接一个值。一般为一个 Promise 对象,也可以是一个非 Promise 类型。当右接一个非 Promise 类型,await 表达式返回的值就是这个值;当右接一个 Promise 对象,则 await 表达式会阻塞后面的代码,等待当前 Promise 对象 resolve 的值。
综合 async 和 await 而言。await 必须结合 async 使用,而 async 则不一定需要 await。 async 会将其后的函数的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,然后返回 resolve 的结果。当这个 Promise 失败或者抛出异常时,需要时使用 try-catch 捕获处理。
Promise 使用链式调用解决了传统方式回调地狱的问题,而 async-await 又进一步优化了代码的可读性。
const p = new Promise((resolve, reject)=>{
resolve('成功')
})
async function main() {
let res = await p
console.log(res)
}
main()
// '成功'
const p = new Promise((resolve, reject)=>{
reject('失败')
})
async function main() {
try {
let res = await p
console.log(res)
} catch(e) {
console.log(e)
}
}
main()
// '失败'
1.3 综合应用-读取文件
需求:先读取用户数据 user,然后读取订单数据 order,最后读取商品数据 goods。
对于这种异步操作很容易想到使用 Promise,代码如下:
const fs = require('fs')
let p = new Promise((resolve, reject) => {
fs.readFile('./files/user.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
p.then(value => {
return new Promise((resolve, rejecet) => {
fs.readFile('./files/order.md', (err, data) => {
if (err) rejecet(err)
resolve([value, data])
})
})
}, reason => {
console.log(reason)
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./files/goods.md', (err, data) => {
if (err) reject(err)
value.push(data)
resolve(value)
})
})
}, reason => {
console.log(reason)
}).then(value => {
console.log(value.join('\n'))
}, reason => {
console.log(reason)
})
但是,使用 Promise 链式调用虽然避免了回调地狱,但这种链式调用过多难免引起代码复杂,看起来不直观。可以使用 async 和 await 方法优化,代码如下:
const fs = require('fs')
function readUser() {
return new Promise((resolve, reject) => {
fs.readFile('./files/user.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
function readOrder() {
return new Promise((resolve, reject) => {
fs.readFile('./files/order.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
function readGoods() {
return new Promise((resolve, reject) => {
fs.readFile('./files/goods.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
async function read() {
let user = await readUser()
let order = await readOrder()
let goods = await readGoods()
console.log([user, order, goods].join('\n'))
}
read()
这样,代码看起来很直观,就好像是同步代码一样,实际上是异步操作。
1.4 综合应用-封装ajax
function sendAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
}
reject(xhr.status)
}
}
})
}
async function main() {
let res = await sendAjax('http://poetry.apiopen.top/sentences')
let poem = res.result.name + '——' + res.result.from
document.body.innerText = poem
}
main()
2. object.values 和 Object.entries
Object.values() 方法返回一个给定对象的所有可枚举属性值的数组,类似于 Object.keys(),只是前者返回属性值,后者返回键值组合的数组。
let obj = {
a: 1,
b: {1:2},
c: [1,2,3]
}
console.log(Object.values(obj))
// [1, {1: 2}, [1,2,3]]
console.log(Object.keys(obj))
// ['a', 'b', 'c']
Object.entries() 方法返回一个给定对象自身可遍历属性 [key,value] 的数组(数组元素也是一个个的数组的数组)
const obj = {a: 1, b: 2, c: 3};
console.log(Object.entries(obj))
// [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]
返回的是一个数组,这样就可以使用 for...of 遍历了。
const obj = { a: 1, b: 2, c: 3 };
for (let [k, v] of Object.entries(obj)) {
console.log(k, v)
}
19. ECMASript 10
1. Object.fromEntries
Object.fromEntries() 方法把可迭代对象的键值对列表转换为一个对象。语法:
Object.fromEntries(iterable)
iterable:类似 Array 、 Map 或者其它实现了可迭代协议的可迭代对象。- 返回值:一个由该迭代对象条目提供对应属性的新对象。
- 相当于
Object.entries(ES8)的逆运算。
const mp = new Map([
[1, 2],
[3, 4]
])
const obj = Object.fromEntries(mp)
console.log(obj)
// { '1': 2, '3': 4 }
const arr = [[1, 2]]
console.log(Object.fromEntries(arr))
// {'1': 2}
2. trimStart() 和 trimEnd()–去除前后空格
trimStart()去除字符串开头连续的空格(trimLeft是此方法的别名)trimEnd()去除字符串末尾连续的空格(trimRight是此方法的别名)
let str = ' iloveyou ';
console.log(str);// iloveyou
console.log(str.trimStart());//iloveyou
console.log(str.trimEnd());// iloveyou
3. Array.prototype.flat 和 Array.prototype.flatMap
Array.prototype.flat(i):展平一个多维数,i 为要展开的层数,默认为1,即展开一层。
let arr1 = [1, [2, 3], [4, 5]]
console.log(arr1.flat(1))
// [1,2,3,4,5]
let arr2 = [1, [2, 3, [4, 5]]]
console.log(arr2.flat(2))
// [1,2,3,4,5]
使用 Infinity 作为深度,展开任意深度的嵌套数组
[1, [2, 3, [4, 5]]].flat(Infinity)
// [1, 2, 3, 4, 5, 6]
也可以使用 flat 来去除数组空项
let arr = [1,2,3,,4]
arr.flat() // [1,2,3,4]
Array.prototype.flatMap:相当于 map 和 flat 的结合,方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
let arr = [1,2,3,4]
let res1 = arr.map(x => [x ** 2])
console.log(res1)
// [[1],[4],[9],[16]]
let res2 = arr.flatMap(x => [x ** 2])
console.log(res2)
// [1,4,9,16]
4. Symbol.prototype.description
使用 Symbol() 创建的 Symbol 字面量,可以直接使用 description 获取该字面量的描述。
let sym = Symbol('hello')
console.log(sym.description)
// hello
20. ECMASript 11
1. 类的私有属性
ES11 提供了类的私有属性,在类的外部无法访问该属性。只有再类的内部能访问。
class Person{
//公有属性
name;
//私有属性
#age;
#weight;
//构造方法
constructor(name, age, weight){
this.name = name;
this.#age = age;
this.#weight = weight;
}
intro(){
console.log(this.name);
console.log(this.#age);
console.log(this.#weight);
}
}
//实例化
const girl = new Person('晓红', 18, '45kg');
// 外部无法直接访问
// console.log(girl.name);
// console.log(girl.#age);
// console.log(girl.#weight);
girl.intro();
2. allSettled
该 Promise.allSettled() 方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。allSettled 方法返回的 Promise 对象始终是成功(fulfilled)的。
使用场景:
- 有多个彼此不依赖的异步任务成功完成时使用。
- 想得到每个
promise的结果时使用。
对比于 Promise.all(),all() 也接受一个 Promise 对象数组参数,只要有一个失败(rejected),那么返回的 Promise 对象就是失败(rejected)的。
使用场景:
- 传进去的
Promise对象彼此依赖,且需要在其中任何一个失败的时候停止。
两个 Promise 都是成功的情况:
let p1 = new Promise((resolve, reject) => {
resolve('用户数据-1')
})
let p2 = new Promise((resolve, reject) => {
resolve('订单数据-2')
})
let res1 = Promise.allSettled([p1, p2])
let res2 = Promise.all([p1, p2])
console.log(res1)
console.log(res2)
输出结果:
一个成功,一个失败:
let p1 = new Promise((resolve, reject) => {
resolve('用户数据-1')
})
let p2 = new Promise((resolve, reject) => {
reject('失败了')
})
let res1 = Promise.allSettled([p1, p2])
let res2 = Promise.all([p1, p2])
console.log(res1)
console.log(res2)
输出结果:
3. matchAll
matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const array = [...str.matchAll(regexp)];
console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]
console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]
4. 可选链
4.1 定义
可选链 ?. 是一种访问嵌套对象属性的安全的方式。即使中间的属性不存在,也不会出现错误。
原则:如果可选链 ?. 前面的部分是 undefined 或者 null,它会停止运算并返回该部分。
let user = {
address: {
}
}
console.log( user?.address?.street ); // undefined(不报错)
4.2 短路效应
短路效应:正如前面所说的,如果 ?. 左边部分不存在,就会立即停止运算(“短路效应”)。所以,如果后面有任何函数调用或者副作用,它们均不会执行。
这有和 && 的作用类似,但上述改用 && 会显得代码冗余度高:
console.log(user && user.address && user.address.street)
4.3 其他变体: ?.(), ?.[]
可选链 ?. 不是一个运算符,而是一个特殊的语法结构。它还 可以与函数和方括号一起使用。
例如,将 ?.() 用于调用一个可能不存在的函数(即使不存在也不报错)。
function foo() {
console.log('hello')
}
foo?.()
// hello
?.[] 允许从一个可能不存在的对象上安全地读取属性。(即使不存在也不报错)。
let obj = {
key: 123
}
console.log(obj?.['key'])
// 123
5. 动态import导入
const btn = document.getElementById('btn');
btn.onclick = function(){
import('./hello.js').then(module => {
module.hello();
}
6. BigInt
BigInt 是一种特殊的数字类型,它提供了对任意长度整数的支持。
创建 bigint 的方式有两种:在一个整数字面量后面加 n 或者调用 BigInt 函数,该函数从字符串、数字等中生成 bigint。
let n1 = 123n
let n2 = 456n
let n3 = BigInt(789)
console.log(typeof n1) // bigint
console.log(n1+n2) // 579n
console.log(n2+n3) // 1245n
比较运算符:
- 例如
<和>,使用它们来对bigint和 nu`mber 类型的数字进行比较没有问题:
alert( 2n > 1n ); // true
alert( 2n > 1 ); // true
- 但是请注意,由于
number和bigint属于不同类型,它们可能在进行==比较时相等,但在进行===(严格相等)比较时不相等:
alert( 1 == 1n ); // true
alert( 1 === 1n ); // false
7. globalThis
全局对象提供可在任何地方使用的变量和函数。默认情况下,这些全局变量内置于语言或环境中。
在浏览器中,它的名字是 window,对 Node.js 而言,它的名字是 global,其它环境可能用的是别的名字。
ES11中 globalThis 被作为全局对象的标准名称加入到了 JavaScript 中,所有环境都应该支持该名称。所有主流浏览器都支持它。
使用场景: 假设我们的环境是浏览器,我们将使用 window。如果你的脚本可能会用来在其他环境中运行,则最好使用 globalThis。
expected output: Array [“test2”, “e”, “st2”, “2”]
### 4. 可选链
#### 4.1 定义
可选链 `?.` 是一种访问嵌套对象属性的安全的方式。即使中间的属性不存在,也不会出现错误。
原则:如果可选链 `?.` 前面的部分是 `undefined` 或者 `null`,它会停止运算并返回该部分。
```javascript
let user = {
address: {
}
}
console.log( user?.address?.street ); // undefined(不报错)
4.2 短路效应
短路效应:正如前面所说的,如果 ?. 左边部分不存在,就会立即停止运算(“短路效应”)。所以,如果后面有任何函数调用或者副作用,它们均不会执行。
这有和 && 的作用类似,但上述改用 && 会显得代码冗余度高:
console.log(user && user.address && user.address.street)
4.3 其他变体: ?.(), ?.[]
可选链 ?. 不是一个运算符,而是一个特殊的语法结构。它还 可以与函数和方括号一起使用。
例如,将 ?.() 用于调用一个可能不存在的函数(即使不存在也不报错)。
function foo() {
console.log('hello')
}
foo?.()
// hello
?.[] 允许从一个可能不存在的对象上安全地读取属性。(即使不存在也不报错)。
let obj = {
key: 123
}
console.log(obj?.['key'])
// 123
5. 动态import导入
const btn = document.getElementById('btn');
btn.onclick = function(){
import('./hello.js').then(module => {
module.hello();
}
6. BigInt
BigInt 是一种特殊的数字类型,它提供了对任意长度整数的支持。
创建 bigint 的方式有两种:在一个整数字面量后面加 n 或者调用 BigInt 函数,该函数从字符串、数字等中生成 bigint。
let n1 = 123n
let n2 = 456n
let n3 = BigInt(789)
console.log(typeof n1) // bigint
console.log(n1+n2) // 579n
console.log(n2+n3) // 1245n
比较运算符:
- 例如
<和>,使用它们来对bigint和 nu`mber 类型的数字进行比较没有问题:
alert( 2n > 1n ); // true
alert( 2n > 1 ); // true
- 但是请注意,由于
number和bigint属于不同类型,它们可能在进行==比较时相等,但在进行===(严格相等)比较时不相等:
alert( 1 == 1n ); // true
alert( 1 === 1n ); // false
7. globalThis
全局对象提供可在任何地方使用的变量和函数。默认情况下,这些全局变量内置于语言或环境中。
在浏览器中,它的名字是 window,对 Node.js 而言,它的名字是 global,其它环境可能用的是别的名字。
ES11中 globalThis 被作为全局对象的标准名称加入到了 JavaScript 中,所有环境都应该支持该名称。所有主流浏览器都支持它。
使用场景: 假设我们的环境是浏览器,我们将使用 window。如果你的脚本可能会用来在其他环境中运行,则最好使用 globalThis。