ES6基础知识详解

73 阅读39分钟

1.let,const,var之间的区别?

var,let声明变量,const声明常量

1.1重复声明

var允许重复声明,let,const不允许重复声明

1.2变量提升

var会提升变量的声明到当前作用域的顶部,let,const不允许变量提升

为了养成良好的编程习惯,对于所有的变量或常量,做到先声明,后使用

1.3暂时性死区

let,const有暂时性死区,var没有暂时性死区

只要作用域内存在let,const,它们所声明的变量或者常量就自动"绑定"这个区域不在受外部作用域的影响

比如:

let a=2
function func(){
    console.log(a)
    let a=1
}
func()//报错
//按道理来说作用域链的关系,找a的时候本身作用域没找到会向外一作用域找的,但是当前作用域中有声明同一个变量,let或者const就仅仅局限于当前作用域了,不会向外层作用域找了
​
let b=1
function func(){
    console.log(b)
}
func()//1
//内部作用域没用let或者const声明相同的变量,所以不会绑定当前作用域

函数只有调用的时候才会形成作用域,声明函数的时候是不会形成作用域的

1.4window对象属性和方法

全局作用中,var声明的变量,通过function声明的函数,会自动变成window对象的属性和方法,let,const不会

1.5块级作用域

1.什么是块级作用域

var是没有块级作用域的

for(var i=0;i<3;i++){
	console.log(i)//0 1 2
}
console.log(i)//3

let,const有块级作用域

for(let i=0;i<3;i++){
	i=i+1
	console.log(i)//1 3
}
//for循环执行结束后,这个作用域就销毁了,所以找不到

console.log(i)//报错

2.作用域链【只能由内层作用域向外层作用域找】

function func(){
	for(let i=0;i<3;i++){
		console.log(i)//0 1 2
	}
}
func()
console.log(i)//报错

//for内部使用的是let声明的变量,此时for是一个块级作用域,for的外一层作用域是func函数作用域,func函数的外一层作用域是全局作用域
//块级作用域----》函数作用域-----》全局作用域

作用域链:内层作用域--》外层作用域--》...全局作用域

3.有哪些块级作用域

{}花括号就是一个块级作用域

for(){}

while(){}

do{}while()

if(){}

switch(){}

const person={
	getAge:function(){}
}
对象本身花括号 是不构成块级作用域; 内部函数是函数作用域

1.6.let,const的应用

<body>
  <button class="btn">0</button>
  <button class="btn">1</button>
  <button class="btn">2</button><script>
    //1.var
    // var btns = document.querySelectorAll(".btn")
​
    // for (var i = 0; i < btns.length; i++) {
    //   btns[i].addEventListener('click', function () {
    //     console.log(i);
    //   }, false)
    //   //只有函数在调用的时候才有函数作用域
    // }
​
​
    
    //闭包
    // var btns = document.querySelectorAll(".btn")
​
    // for (var i = 0; i < btns.length; i++) {
    //   (function (index) {
    //     btns[index].addEventListener('click', function () {
    //       console.log(index);
    //     }, false)
    //     //只有函数在调用的时候才有函数作用域
    //   })(i)
    // }
​
​
    // let 块级作用域
    var btns = document.querySelectorAll(".btn")
​
    for (let i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', function () {
          console.log(i);
        }, false)
        //只有函数在调用的时候才有函数作用域
    }
  </script>
</body>

2.模板字符串

2.1 模板字符串是什么?

1. 认识模板字符串

""  ''一般字符串
``模板字符串

2. 模板字符串与一般字符串的区别

模板字符串可以通过 ${} 解析变量

const person={
    username:'Alen',
    age:18,
    sex:'male'
}
//字符串拼接
const info1="我的名字"+person.username+'年龄'+person.age+'性别'+perosn.sex//模板字符
const info2=`我的姓名:${person.username},年龄:${person.age},性别:${person.sex}`

和其他东西一起使用的时候,使用模板字符串,方便注入;其他情况下使用模板字符串或者一般字符串都行

2.2 模板字符串的注意事项

模板字符串中,所有的空格,换行或者缩进被保留在输出之中

1.输出多行字符串

//一般字符串  \n
const info='第一行\n第二行'
console.log(info)

//模板字符串 
const info=`第一行\n第二行`
const info1=`第一行
第二行`
console.log(info1,info2)

2.输出`和\等特殊字符【\进行转义】

//模板字符串 \转换特殊字符const info=``\`
console.log(info)

3.模板字符串的注入${}

只要最终可以得到一个值(表达式)的就可以通过 ${} 注入到模板字符串中

const username='alen'
const person={
    age:18,sex:'male'
}
const getSex=function(){
    return sex==='male'?'男':'女'
}
​
const info2=`${username},${person.age},${getSex(person.sex)}`

2.3 模板字符串的应用

<body>
<p>学生信息表</p>
  <ul id="list">
    <li style="list-style:none;">信息加载中...</li>
  </ul>
​
<script>
// 数据
const students = [    {        username:'小明',        age:18,        sex:'male'    },    {        username:'小强',        age:29,        sex:'male'    },    {        username:'小花',        age:17,        sex:'female'    }]
const list=document.getElementById("list")
let html=""
​
for(let i=0;i<students.length;i++){
    html+=`<li>我的名字:${students[i].username},年龄:${students[i].age},性别:${students[i].sex}</li>`
}
console.log(html);
list.innerHTML=html
​
</script>
</body>

3.箭头函数

3.1 箭头函数是什么?

1.认识箭头函数

省略了function关键字,包括参数部分和函数体部分,参数部分和函数体部分用 =>连接

()=>{}//匿名函数,没法再别处去调用const func=()=>{}//将箭头函数复制给一个常量或者变量 然后可以调用

2.箭头函数的结构

参数=>函数体

const/let 函数名 = 参数=>函数体

const add=(x,y)=>{
    return x+y
}
add(1,2)

3.如何将原来的函数改写成箭头函数

//声明形式
function func(){
    
}
//函数表达式
const fun=function(){
​
}
​
//函数表达式形式 转 箭头函数形式
const fun=()=>{}

3.2 箭头函数的注意事项

1.单个参数【 x = >{} 】

单个参数可以省略参数部的圆括号的,无参或者多个参数不能省略圆括号

//单个参数可以省略圆括号
const add = x=>{
    return x+1
}
add(1)
​
//无参和多个参数不能省略圆括号
const mul = ()=>{
    return 1+1
}
​
cnst sum = (x,y)=>{
    return x+y
}

2.单行函数体【 (x,y) =>x+y 】

单行函数体,可以同时省略花括号和return关键字,多行函数体不能省略花括号和return

//单行函数体
const add = (x,y)=>x+y

//多行函数体不能省略花括号和return关键字
const mul = ( x,y )=>{
	const m=x-y
	return m
}

3.单行对象【(x,y)=>({value:x+y})】

如果箭头函数返回单行对象,可以再 {}外面加上() ,让浏览器不在认为那个是函数体的花括号

单行数组不需要加(),可以直接省略{}和return

//单行对象
const add = (x,y)=>({value:x+y})

//没有简写的时候
const add = (x,y)=>{
	return{
		value:x+y
	}
}

3.3 箭头函数的this指向?

1.非箭头函数的this指向

1.1 全局作用域中的this指向:window
console.log(this)//window
1.2 一般函数(非箭头函数)中this指向:函数调用的时候才可以确定this指向,this指向和函数在哪里调用没关系,只和谁在调用有关
//一般函数的this指向是当函数调用的时候才可以确定this指向,this指向和函数在哪里调用没有关系,只和谁在调用有关
function add(){
	console.log(this)
}
add()//严格模式下是undefined,非严格模式下转为window

window.add()//此时add函数的this指向windwo,因为是window在调用


const calc={
	add:add
}
calc.add()//calc调用,this指向calc

const adder=calc.add
adder()//undefined非严格模式下转为window



//事件函数的this,就是指向给谁绑定的就是谁
document.onclick=function(){
	console.log(this)
}
document.onclick
1.3 构造函数中的this指向: this指向构造函数实例化后生成的对象
function Person(username){
	this.username=username
	console.log(this)
}
const p = new Person('小明')//this指向Person

2.箭头函数的this指向: 箭头函数没有自己的this 和 作用域链结合起来可以找到箭头函数的this

const calc={
	add:()=>{
		console.log(this)//箭头函数也是函数也有作用域但是箭头函数作用域中没有this,所以这里this延着作用域链向外层作用域找
	}
}
calc.add()//window


//练习
const person={
	add:function(){
		const mul=()=>{
			console.log(this)//箭头函数没有this会向外层作用域找,找到add这个函数,一般函数的this谁调用指向谁
		}
		mul()
	}
}
person.add()//this指向calc对象
const addFn=person.add
addFn()//此时this指向是window

3.4 不适用箭头函数的场景

1.作为构造函数

const Person=()=>{}
new Person()//不可以,因为箭头函数没有this,构造函数的this指向构造函数实例化的对象

2.需要this指向调用对象的时候

document.addEventListener('click',function(){
	console.log(this)//this指向document
},false)

3.需要使用arguments的时候【arguments主要记录你传入进来的参数】

function add(){
	console.log(arguments)
}
add(1,2)

3.5 箭头函数的应用

<button id="btn">开始</button>
<span id="result">0</span>

const btn=document.getElementById("btn")
const result=document.getElementById("result")

const timer={
      time:0,
      start:function(){
        btn.addEventListener('click',function{
          setInterval(function(){
  			console.log(this)//此时的this指向window
            this.time++
            result.innerHTML=this.time
          },1000)
        },false)//冒泡形式
      }
    }

timer.start()

const timer1={
      time:0,
      start:function(){
        btn.addEventListener('click',function{
          setInterval(()=>{
  			console.log(this)//此时的this指向btn这个按钮
            this.time++
            result.innerHTML=this.time
          },1000)
        },false)//冒泡形式
      }
    }

timer1.start()

//多次使用箭头函数,将this变为外层作用域timer2这个对象,这样就可以访问time了
const timer2={
      time:0,
      start:function(){
      
        btn.addEventListener('click',()=>{
        
          setInterval(()=>{
  			console.log(this)//
            this.time++
            result.innerHTML=this.time
          },1000)
        },false)//冒泡形式
      }
    }

timer2.start()//此时这个可以启动页面定时器走动

4.解构赋值

4.1 数组解构赋值

1. 原理

1.1 认识解构赋值
const arr=[1,2,3]
const a=arr[0]
const b=arr[1]
const c=arr[2]
console.log(a,b,c)

const [aa,bb,cc]=[1,2,3]
console.log(aa,bb,cc)//1 2 3
1.2 什么是解构赋值

解析某一数据解构,将我们想要的东西提取出来,赋值给变量或常量

1.3 原理
1.模式(解构)匹配
//右边是数组,左边也必须是数组;右边是对象,左边也要是对象
[a,b,c]=[1,2,3]
2.索引值相同的完全赋值
//a对应1
const [a]=[1,2,3]
console.log(a)

//不取得,可以直接用逗号跳过
const [aa,[,,bb],cc] = [1, [2,4,5], 3]
console.log(aa,bb,cc)

2. 默认值

2.1 默认值的基本用法
const [a,b]=[]
相当于下面这个
const [a,b]=[undefined,undefined]
console.log(a,b)

//设置默认值
const [aa=1,bb=2]=[]
console.log(aa,bb)
2.2 默认值的生效条件

只有当一个数组成员严格等于(===)undefined时,对应默认值才会生效

const [a=1,b=2]=[3,0]
console.log(a,b)//3 0

const [aa=1,bb=2]=[3,null]
console.log(aa,bb)//3 null

const [q,w]=[1]
console.log(q,w)//1 undefined

const [qq,ww=2]=[1]
console.log(qq,ww)//1 2
2.3 默认值表达式

如果默认值是表达式,默认值表达式是惰性求值的(用不到就不会执行,用到才会花费时间执行)

const func=()=>{
	console.log('我被执行了')
	return 2
}
const [x=func()]=[1]
console.log(x)

3. 应用

3.1 常见类数组的解构赋值【arguments NodeList】

arguments:类数组(函数实际传递的参数)

NodeList:类数组,节点列表

//arguments
function func(){
	console.log(arguments)//Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
	const [a,b]=arguments
	console.log(a,b)//1 2
}
func(1,2)


//NodeList节点列表
<p>123</p>
<p>211</p>
<p>312</p>
console.log(document.querySelectorAll("p"))//NodeList [p,p,p]
const [p1,p2,p3]=document.querySelectorAll("p")

console.log(p1,p2,p3)
3.2 函数参数的解构赋值
const array=[1,2]
cosnt add = arr=>arr[0]+arr[1]
console.log(add(array))//3

//函数参数解构赋值
const array1=[1,2]
cosnt add1 = ([x=0,y=0])=>x+y

console.log(add1(array1))//3

//交换变量的值
let x=1;
let y=2

let temp=x
x=y
x=temp
console.log(x,y)

//解构赋值直接交换变量
const [y,x]=[x,y]
console.log(x,y)

4.2 对象解构赋值

1. 原理

1.1 模式(解构)匹配

赋值符号右边是对象,左边也一定要是对象

{}={}
1.2 属性名相同的完全赋值【不用注意顺序】
const {age,username}={username:'alen',age:18}

const {'age':age,'username':username}={username:'alen',age:18}

const {age:age,username:username}={username:'alen',age:18}

console.log(age,username)

//取别名,属性名先对应上,值对应值上
const {age:uage,username:uname}={username:'alen',age:18}
console.log(uage,uname)

2. 注意事项

2.1 对象解构赋值的默认值

对象的属性值严格等于undefined的时候,对应的默认值才会生效

const {username1='小白',age1}={username:'alen'}
console.log(username1,age1)//alen undefined


const {username='小白',age=0}={username:'alen'}
console.log(username,age)//alen 0
2.1.1 默认值是表达式

如果默认值是表达式,默认值表达式是惰性求值的

2.2 将一个已经声明的变量用于解构赋值

如果将一个已经声明的变量用于对象的解构赋值,整个赋值需在()圆括号进行

let {x:1}={x:1}
console.log(x)

//将一个已经声明的变量用于解构赋值
let y=2
{y}={y:1}//此时左边{}浏览器会认为是代码块对象,解决办法加()
({y})={y:1}
console.log(y)

//数组不用加()
let z=3
{z}={z:3}
console.log(z)
2.3 可以取到继承的属性(包括方法)
const {a=1}={}
console.log(a)//1

const {toString}={}
console.log(toString)
console.log(Object.prototype)

3. 应用

3.1 函数参数的解构赋值
const logPersonInfo=user=>console.log(user.name,user.age)
logPersonInfo({name:'alen',age:18})//alen 18

//函数的对象参数的解构赋值
const logPersonInfo1=({age,name})=>console.log(name,age)
logPersonInfo1({name:'alen',age:18})//alen 18

//复杂嵌套
const obj={
	x:1,
	y:[2,3,4],
	z:{
		a:5,
		w:6
	}
}
const {x,y,z}=obj
//console.log(x,y,z)// 1 [2,3,4] {a:5,b:6}

const {
	y,
	y:[,yy],
	z:z,
	z:{w:w}
}=obj
console.log(y,yy,z,w)//[2, 3, 4]  3  {a: 5, w: 6}  6

4.3 其他类型的解构赋值

1. 字符串的解构赋值【可以按照数组或对象的方式解构赋值】

右边是字符串,左边只能是数组或者对象

// ''='hell0' X错误//数组形式的解构赋值
const [a,b,,,c]='hello'
console.log(a,b,c)//h e o//对象形似的解构赋值
const {0:aa,1:bb,length}='hello'
console.log(aa,bb,length)//h e 5

2. 数值和布尔值的解构赋值【不能进行数组形式解构赋值,只能进行对象形式解构赋值】

先将等号右边的值转为对象,在进行解构赋值

//数值和布尔值解构赋值会将本身转换为对象,在进行对象解构赋值//数值解构赋值
console.log(new Number(123))
​
const {ab=1,toString}=123
console.log(ab,toString)
​
//布尔值解构赋值
const {ba=2,toString}=true
console.log(ba,toString)

3. undefined和null的解构赋值【无法进行解构赋值】

undefined和null会尝试转换为对象,由于undefined和null无法转换为对象,所以对它们进行解构赋值,都会报错,注意:undefined和null没有对应的包装对象,所以无法通过它们转换为相应的对象

5.对象字面量的增强与函数参数的默认值

5.1 对象字面量的增强

1. 属性和方法的简洁表示法

1.1 对象字面量是什么

一种是实例化构造函数函数生成对象,一种对象字面量生成对象

//1.实例化构造函数生成对象
const person=new Object()
person.age=18
person.speak=function(){
	console.log("说话")
}

//2.对象字面量写法
const per={
	age:12,
	speak:function(){
		console.log("说话")
	}
}
1.2 属性的简洁表示法

键名和变量或常量名一样的时候,可以只写一个

const age=18

const person={
	'age':age
	age//用常量或者别的变量作为这个对象的属性的属性值,当属性名和变量或者常量名一样的时候,可以省略属性值
}
1.3 方法的简洁表示法

方法可以 省略冒号和function关键字

const person={
	//speak:function(){}
	speak(){
		console.log('说话')
	}
}

2. 方括号语法[] 相当于求值

2.1 方括号语法的用法

方括号语法可以放对象字面量中了

const prop='age'
const person={}

//person.prop=12//点语法不符合要求,这是prop添加到perosn这个对象中了,本意是想age添加进person的属性的

person[prop]=18
console.log(person)

//增强
const person1={
	[prop]:18
}
console.log(person1)
2.2 方括号中可以放什么?类似于模板字符串得${}

[值或通过计算可以得到值得 (表达式)]

const prop='age'
const func=()=>'sex'

const person={
	[prop]:18,
	[func()]:'male'
	['speak']:function(){console.log('说话')}
	['u'+'sername']:'lisi'
}
console.log(person)
2.3 方括号语法和点语法的区别

点语法是方括号语法的特殊形式,

什么时候使用点语法:属性名由数字,字母,下划线以及$构成,并且数字还不能打头的时候可以使用点语法(合法标识符)

合法标识符可以用来做变量或常量名

当你的属性或方法名是合法标识符的时候,可以使用点语法,其他情况下请使用方括号的语法

const person={}
person.age等价于person['age']

5.2 函数参数的默认值

1. 函数参数的默认值是什么?

1.1 认识函数参数的默认值

调用函数的时候传参了,就用传递参数,如果没有传参,就用默认值

mul(2,1)
mul(2)
1.2 函数参数默认值的基本用法【定义的时候默认值参数用 =】
//旧写法
const multiply=(x,y)=>{
	if(typeof y==='undefined'){
		y=1
	}
	return x*y
}
multiply(2)//2

//新的写法,简洁
const multiply=(x,y=1)=>x*y

multiply(2)//2

2. 函数参数默认值的注意事项

2.1 默认值的生效条件

不传参数,或者明确的传递undefined作为参数,只有这两种情况下,默认值才会生效

const multiply=(x,y=1)=>x*y
multiply(2,0)//0
multiply(2,null)//0
multiply(2,undefined)//2
multiply(2)//2
2.2 默认值表达式

如果默认值是表达式,默认值表达式是惰性求值的

2.3 设置默认值小技巧

函数参数的默认值,最好从参数列表的右边开始设置

3.函数参数默认值的应用

3.1 接受很多参数的时候
const logUser=(username,sex,age)=>console.log(username,sex,age)
logUser('alen','男',18)

const logUser=(username='lisi',sex='male',age=12)=>console.log(username,sex,age)
3.2 接收一个对象作为参数
const logUser=options=>console.log(options.username,options.sex,options.age)

logUser({
	username:'alen',
	sex:'male',
	age:18
})

//解构
const logUser1=({username,sex,age})=>console.log(username,sex,age)
logUser1({
	username:'alen',
	sex:'male',
	age:18
})

const logUser2=({username='lisi',sex='male',age=18})=>console.log(username,sex,age)

logUser2({username:'alen'})//对象传一个属性值,定义函数参数对象解构赋值,没传的使用默认值
logUser2()//什么都不传,相当于传一个undefined,如果解构相当于对undefined解构赋值,所以报错


const logUser3=(options={})=>console.log(username,sex,age)
const logUser3=({username='lisi',sex='male',age=18}={})=>console.log(username,sex,age)
logUser3()//什么都不传,空对象解构赋值

6.剩余参数与展开运算符

6.1 剩余参数【是数组】

1. 剩余参数是什么?【const add=function (x,y,...args){} ,(...args)=>{}】

1.1 剩余参数是什么?
//...args是剩余参数
const add=(x,y,...args)=>{}
1.2 剩余参数的本质

剩余参数永远是个数组,即使没有值,也是空数组

//...args是剩余参数
//使用的时候不需要...
const add=(x,y,...args)=>{
	console.log(x,y,args)
}
add()//undefined undefined Array(0)
add(1)//1 undefined Array(0)
add(1,2)//1 2 Array(0)
add(1,2,3)//1 2 Array(1)
add(1,2,3,4,5,6,7)//1 2 Array(5)

//3,4,5,6,7===>[3,4,5,6,7]

2. 剩余参数的注意事项

2.1 箭头函数的剩余参数

箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号

const add=(...args)=>console.log(args)
add(1,2)//[1,2]
2.2 使用剩余参数替代arguments获取实际参数
//arguments可以获取实际参数
const add=function(){
	console.log(arguments)
}
add(1,2)//类数组[1,2 f,f]

const mul=(...args)=>{
	console.log(args)
}
mul(1,2)//单纯的数组[1,2]
2.3 剩余参数的位置

剩余参数只能是最后一个参数,之后不能再有其他参数,否则会报错

3. 剩余参数的应用

3.1 箭头函数的剩余参数
const add=(...args)=>{
	let sum=0
	for(let i=0;i<args.length;i++){
		sum+=args[i]
	}
	return sum
}
add(1,2,3,4)//10
3.2 与结构赋值结合使用

剩余参数不一定非要作为函数参数使用,与解构赋值一起使用,同样要求是最后一个值

//剩余参数配合解构赋值数组
const [num,...args]=[1,2,3,4]
console.log(num,args)//1 [2,3,4]

const func=([num,...args])=>{console.log(num,args)}
func([1,2,3])//1 [2,3]

//剩余参数配合解构赋值对象
const {x,y,...z}={a:3,x:1,y:2,b:4}
console.log(x,y,z)//1 2 {a:3,b:4}

const func1=({x,y,...z})=>console.log(x,y,z)
func1a({a:3,x:1,y:2,b:4})//1 2 {a:3,b:4}

6.2 数组的展开运算符

1. 数组展开运算符的基本用法

1.1 认知展开运算符
[3,1,2]
console.log(Math.min([3,1,2]))//报错

//怎样将数组展开
[3,1,2]==>3,1,2
1.2 数组展开运算符的基本使用
console.log(Math.min(...[3,1,2]))//1
...[3,1,2]相当于3 1 2

2. 区分剩余参数和展开运算符

2.1 根本区别【剩余参数转换为对应是数组,展开运算符展开的是数组或对象】

展开运算符:[3,2,1]==>3,2,1

剩余参数:3,1,2==>[3,1,2]

2.2 区别剩余参数和扩展运算符
//剩余参数
const add=(...args)=>{
	console.log(args)
}
add(1,2,3)//[1,2,3]
//展开运算符
console.log(Math.min(...[3,1,2]))//1

3. 数组展开运算符的应用

3.1 复制数组
//以前
const a=[1,2]
const b=a
console.log(b)//[1,2]

//新的方式展开数组-复制数组
const aa=[1,2]
const c=[...aa]
console.log(c)//[1,2]
3.2 合并数组
const a=[1,2]
const b=[3,4]

const c=[...a,...b]
console.log(c)//[1,2,3,4]
3.3 字符串转为数组
//展开字符串(字符串就类似于数组)
console.log(...'hello')//'h' 'e' 'l' 'l' 'o'
​
//字符串转数组
console.log([...'hel'])//['h','e','l']
3.4 常见的类数组转化为数组【argments,NodeList】
//arguments
function func(){
    console.log([...arguments])
}
func(1,2)//[1,2]//NodeList
<p>12</p>
<p>23</p>
console.log([...document.querySelectorAll("p")])//[p,p]

6.3 对象的展开运算符

1.基本用法

1.1 展开对象

对象不能直接展开,必须在{}中展开,展开后得到的新的对象

对象的展开:把属性罗列出来,用逗号分隔,放到一个{}中,构成新的对象

const apple={
    color:'red',
    shape:'球形',
    taste:'甜'
}
console.log({...apple})//{color:'red',shape:'球形',taste:'甜'}
console.log({...apple}===apple)//false
1.2 合并对象

合并后的新对象拥有全部的属性,相同属性,后者覆盖前者

const apple={
	color:'red',
	shape:'球形',
	taste:'甜'
}
const pen={
	color:'black',
	shape:'圆柱形',
	use:'写字'
}
console.log({...apple,...pen})//{color: 'black', shape: '圆柱形', taste: '甜', use: '写字'}

console.log({apple,pen})//{apple: {…}, pen: {…}}
console.log({...apple,pen})//{color: 'red', shape: '球形', taste: '甜', pen: {…}}

2.注意事项

2.1 空对象的展开

如果展开一个空对象,则没有任何效果

console.log({...{}})
console.log({...{},a:1})//{a:1}
2.2 非对象的展开

非对象展开得到的是一个空对象

如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象

console.log({...1})//{}
console.log({...true})//{}
console.log({...undefined})//{}
console.log({...null})//{}

console.log({...'hello'})//{0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
console.log([...'hello'])//['h', 'e', 'l', 'l', 'o']
console.log({...[1,2,3]})//{0:11:22:3}
2.3 对象中对象属性的展开

不会展开对象中的对象属性

const apple1={
	feature:{
		taste:'甜'
	}
}
const pen1={
	feature:{
		color:'黑色',
		shape:'圆柱形'
	},
	use:'写字'
}
console.log({...apple1})//{feature: {…}}
console.log({...apple1,...pen1})//{feature: {…}, use: '写字'} feature是pen1对象的

3.应用

3.1 复制对象
const a={x:1,y:2}
const b={...a}
console.log(c,c===a)//{x:1,y:2} false
3.2 用户参数和默认参数
//userParam用户参数
const logUSer=(userParam)=>{
	//defaultParam为默认参数
	const defaultParam={
		username:'lisi',
		age:18,
		sex:'male'
	}
	//合并参数
	//const param={...defaultParam,...userParam}
	//console.log(params.username)
	
	//展开运算符合并对象,然后解构赋值
	const {username,age,sex}={...defaultParam,...userParam}
	console.log(username,age,sex)
}

7.Set和Map数据解构

1.Set

1.1 Set是什么?

1.什么是Set【集合】

数组是一系列有序的数据集合;Set是一系列无序,没有重复值的数据集合

2.理解Set
//实例化构造函数创建Set
const s=new Set()
s.add(1)
console.log(s)//{1}

1.2 Set实例的方法和属性

1.方法

add:添加成员

has:判断是否有某个成员

delete:删除指定成员

clear:清除所有成员

forEach:遍历Set成员【按照成员添加进集合的顺序遍历】

const s=new Set()

//增加一个成员
s.add(1).add(2)
console.log(s)//{1,2}

//判断是否有无
console.log(s.has(1))//true

//删除单个成员
s.delete(2)
console.log(s)//{1}

//删除全部成员
s.clear()
console.log(s)//Set(0) {}

//forEach遍历Set成员 ,第二个参数代表this的指向
const ss=new Set()
ss.add(1),add(2)
ss.forEach(function(value,key,set){
	//Set中的value和key是同一个
	console.log(value,key,set)
	console.log(this)
},document)
2.属性

size:表示集合成员个数

1.3 Set构造函数的参数

1.数组
const s=new Set([1,2,3])
console.log(s)//Set(3) {1,2,3}
2.字符串,arguments,NodeList,Set等
//string
console.log(new Set('hel'))//Set(2) {'h','e','l'}//arguments
function func(){
    console.log(new Set(arguments))
}
func(1,2)//Set(2) {1,2}//Nodelist
<p>11</p>
<p>22</p>
console.log(new Set(document.querySelectorAll("p")))//Set(2) {p,p}//Set
const s=new Set([1,2,3]) 
console.log(new Set(s))//Set(3) {1,2,3}  相当于复制了一份

1.4 Set的注意事项

1.判断重复的方式

Set对重复值的判断基本遵循严格相等(===),但是对于NaN的判断与===不同,Set中NaN等于NaN

const s1=new Set([1,2,1])
console.log(s1)//Set(2) {1,2}
​
​
console.log(NaN===NaN)//falseconst s2=new Set([NaN,2,NaN])
console.log(s2)//Set(2) {NaN,2}const s3=new Set()
s3.add({}).add({})
console.log(s3)//Set(2) {{},{}}
2.什么时候使用Set
1.数组或字符串去重的时候
2.不需要通过下标访问,只需要遍历时
3.为了使用Set提供的方法或属性的时候【add delete clear has forEach size】

1.5 Set的应用

1.数组去重
const arr=[1,2,1,2,3]
const s4=new Set(arr)
​
//怎么将Set去重后的数组转换为新的数组:forEach方法;展开运算符
//1,s4.forEach()
//2,展开运算符
console.log([...s4])//[1,2,3]//一步到位
console.log([...new Set([1,2,3,1])])//[1,2,3]
2.字符串去重【通过转为数组,利用数组方法join将字符串去重后转为字符串)】
const s5=new Set('ababac')
console.log([...s5].join(""))//abc

//一步到位
console.log([...new Set('asdasd')].join(""))//asd
3.存放DOM元素
<p>111</p>
<p>222</p>

const ps = new Set(document.querySelectorAll("p"))
ps.forEach(function(elem)=>{
	console.log(elem)
})

2.Map

2.1 Map是什么?

1.认识Map

映射;Map和对象都是键值对的集合

//对象 键:值	 key:value
const person={
	name:'elen',
	age:18
}

//Map和Set都没字面量写法,只能实例化
const m=new Map()

m.set('name':'alen')
console.log(m)//Map(0) {'name'=>'alen'}
2.Map和对象的区别

对象一般用字符串当作键;

基本数据类型:数字,字符串,布尔值,undefined,null;引用数据类型:对象,([],{},函数,Set,Map等)以上都可以作为Map的键

const m1=new Map()
m1.set("name",'hel')
m1.set(true,"true")
m1.set({},'对象')
m1.set(1,2)
m1.set(undefined,'undefined')
m1.set(null,'null')

console.log(m1)Map(6) {}

2.2 Map实例的方法和属性

1.方法

set:添加成员,使用set添加的新成员,键如果存在,后添加的键值对覆盖已有的

get:获取指定成员,不存在的时候获取的是undefined

has:判断是否存在改成员

delete:删除某个成员

clear:删除所有成员

forEach:遍历Map成员

//添加成员
const m2=new Map()
m2.set("age",18).set("name",'alen').set("age",19)
console.log(m2)

//获取指定成员
m2.get("age")


//判断成员是否存在
m2.has("name")

//删除
m2.delete("age")

//删除所有
m2.clear()


m2.forEach((value,key,map)=>{
	console.log(value,key,map)
},document)
2.属性

size:获取Map成员个数

2.3 Map构造函数的参数

1.数组【二维数组】
const m3=new Map([["name","alen"]])
console.log(m2)
2.Set,Map等
const m4=new Map(new Set([["name","bbb"]]))
console.log(m4)
​
const m5=new Map(new Map([["age",18]]))
console.log(m5)

2.4 Map的注意事项

1.判断键名是否相同的方式

如果键名相同,后面的会覆盖前面的;基本遵循严格相等(===) 例外:就是NaN,Map中的NaN也是等于NaN

console.log(NaN===NaN)//false
​
const m6=new Map()
m6.set(NaN,1).set(NaN,2)
console.log(m6)//Map(1) {NaN=>2}
2.什么时候使用Map

如果只是需要key->value 的结构,或者需要字符串以外的值作键,使用Map更合适

forEach for in

只有模拟显示世界的实体,才使用对象

2.5 Map的应用

<p>111</p>
<p>222</p>
<p>333</p>
const [p1,p2,p3]=document.querySelectorAll("p")
​
const m7=new Map()
m7.set(p1,"red")
m7.set(p2,"green")
m7.set(p3,"blue")
​
m7.forEach((color,elem)=>{
    elem.style.color=color
})
​
​
​
const m8=new Map([
    [p1,{
        color:'red',
        backgroundColor:'pink',
        fontSize:'40px'
    }],
    [p2,{
        color:'green',
        backgroundColor:'aqua',
        fontSize:'36px'
    }],
    [p3,{
        color:'red',
        backgroundColor:'orange',
        fontSize:'32px'
    }]
])
m8.forEach((propObj,elem)=>{
    for(const p in propObj){
        elem.style[p]=propObj[p]
    }
})

8.遍历器与for...of循环

8.1 遍历器

1. Iterator是什么

1.1 Iterator的作用

Iterator:遍历器(迭代器)

for()

[1,2].forEach()

new Set().forEach()

1.2 寻找Iterator
console.log([1,2][Symbol.iterator]())//Array Iterator {}

const it=[1,2][Symbol.iterator]()
console.log(it)
1.3 使用Iterator【Iterator遍历器的next()返回一个对象,value代表值,done代表是否结束遍历】

it:为可遍历对象 Symbol.iterator:可遍历对象的生成方法

//it为可遍历对象
//Symbol.iterator可遍历对象的生成方法
const it=[1,2][Symbol.iterator]()

console.log(it.next())//{value: 1, done: false}
console.log(it.next())//{value: 2, done: false}
console.log(it.next())//{value: undefined, done: true}
1.4 什么是Iterator

Symbol.iterator(可遍历对象的生成方法)==》it(可遍历对象)==》it.next()==》it.next()==>...(直到done为true)

2. Iterator解惑

2.1 为什么需要Iterator遍历器

遍历数组:for循环 和 forEach方法

遍历对象:for in循环

Iterator遍历是一个统一的遍历方式

console.log([][Symbol.iterator]())//Array Iterator {}
console.log({}[Symbol.iterator])//undefined
2.2 如何更方便的使用Iterator

我们一般不会直接使用Iterator去遍历;而是使用iterator分装好的方法:for...of循环

3. 使用了Iterator的场合【除了for...of循环】

3.1 数组的展开运算符
console.log(...[1,2,3])//1 2 3
console.log(...'hel')//h e l
console.log(...new Set([1,2,3]))//1 2 3
3.2 数组的解构赋值
const [a,b]=[1,2]
const {x,y}={x:1,y:2}
const [q,w]='hi'
const [e,r]=[...new Set([3,4])]
3.3 Set和Map的构造函数
new Set(iterator可遍历的)
new Map(iterator可遍历的二维数组)

8.2 for...of循环

1.for...of的用法

1. 认识for...of

for...of循环只会遍历出哪些done为false时,对应的value的值

//原生的iterator
const arr=[1,2]
const it=arr[Symbol.iterator]()
let next=it.next()
console.log("第一次:",next)

while(!next.done){
	console.log("遍历的值:",next.value)
	next=it.next()
	console.log("第二次以及往后的next:"next)
}

//封装后的为for...of
for(const item of arr){
	console.log(item)
}
2. 与break,continue一起使用
const arr1=[1,2,3]

for(const item of arr1){
	if(item==2){
		//break//结束整个循环
		continue//跳过本次循环执行下次循环
	}
	console.log(item)
}
3. 在for...of中取得数组的索引【keys(),values(),entries()】

数组实例的方法keys() ,得到的是索引的可遍历对象 ,可遍历出索引值

数组实例的方法values() ,得到的是值的可遍历对象,可以遍历出值

数组实例的方法entries() ,得到的是既有索引又有值得数组

const arr2=[1,2,3]
console.log(arr2.keys())

//得到索引
for(const key of arr2.keys()){
	console.log(key)//0 1 2
}
//得到值
for(const value of arr2.values()){
	console.log(value)//0 1 2
}

//同时遍历索引和值
for(const value of arr2.entries()){
	console.log(value)//[0,1] [1,2] [2,3]
}
//结构
for(const [index,value] of arr2.entries()){
	console.log(index,value)//
}

8.3 原生可遍历与非元素可遍历

1. 什么是可遍历

只要有Symbol.iterator方法,并且这个方法可以生成可遍历对象,就是可遍历的

只要是可遍历,就可以使用for...of循环来统一遍历

2. 原生可遍历的有哪些

数组,字符串,Set,Map,arguments,NodeList

//数组
for(const item of [1,2,3]){
	console.log(item)
}
//字符串
for(const item of 'hi'){
	console.log(item)
}
//Set
for(const item of new Set([1,2])){
	console.log(item)
}
//Map
for(const item of new Map([["name","alen"]])){
	console.log(item)
}
//类数组:函数实际arguments
function func(){
	for(const item of arguments){
		console.log(item)
	}
}
func(1,2)
//类数组:节点列表
for(const item of document.querySelectorAll("p")){
	console.log(item)
}

3. 非原生可遍历可遍历的有哪些

1.一般对象,【for..in】【自己添加】

2.有length和索引属性的对象

3.obj[Symbol.iterator]=Array.prototype[Symbol.iterator]

const person1={
    sex:'male',
    age:18
}
//对象的遍历 for...in
for(const key in person1){
    console.log(person1[key])
}
​
//自己添加
const person2={
    username:'alen',
    test:'测试'
}
person2[Symbol.iterator]=()=>{
    let index=0
    return {
        next(){
            index++;
            if(index==1){
                return {
                    value:person2.username,
                    done:false
                }
            }else if(index===2){
                return {
                    value:person2.test,
                    done:false
                }
            }else{
                return {
                    value:undefined,
                    done:true
                }
            }
            
        }
    }
}
for(const item of person2){
    console.log(item)
}
​
​
​
//有length和索引属性的对象
const obj={
    0:1,
    1:2,
    length:2
}
obj[Symbol.iterator]=()=>{
    let index=0;
    return{
        next(){
            let value,done;
            if(index<obj.length){
                value=obj[index]
                done:false
            }else{
                value=undefined
                done=true
            }
            index++
            
            return{
                value,
                done
            }
        }
    }
}
for(const item of obj){
    console.log(item)
}
​
//直接拿数组的
obj[Symbol.iterator]=Array.prototype[Symbol.iterator]

9. ES6新增方法

9.1 字符串的新增方法

1. includes()【判断是否包含】

判断字符串是否包含某些字符;字符串实例的方法

第一个参数是需要查找判断是否包含的字符串;第二个参数是开始搜索的位置,默认是0

//一个参数
console.log('abc'.includes('ab'))//true
console.log('abc'.includes('ac'))//false
//二个参数
console.log('abc'.includes('a',1))//false//应用--1.url地址拼接
let url="http://www.baidu.com"
const addURLParam=(url,key,value)=>{
    url+=url.includes('?')?'&':'?';
    url+=`${key}=${value}`
    return url
}
url=addURLParam(url,'c','fe')

2. padStart()和padEnd【填充字符串】

补全字符串长度;字符串实例的方法

第一个参数代表,补全之后的长度,第二个参数代表用什么补

注意事项:原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串

用来补全的字符串长度与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动

如果省略第二个参数,默认使用空格补全长度

console.log('x'.padStart(5'ab'))//ababx
console.log('x'.padEnd(5'ab'))//xababconsole.log('x'.padEnd(4'ab'))//xaba//注意事项
console.log('xxx'.padStart(2,'ab'))//xxx
console.log('abc',padEnd(10,"0123456789"))//abc0123456
console.log("a".padStart(4))//   a//应用--1.显示日期格式
console.log('10',padStart(2,0))//10
console.log('1',padStart(2,0))//01

3. trimStart()和trimEnd()【trim去除左右空格】

清除字符串的首或尾的空格,中间空格不会清除

trimLeft()和trimRight()

const s="  abc bc d "
console.log(s.trimStart())//abc bc d  
console.log(s.trimEnd())//  abc bc d
​
console.log(s.trim())////应用--1.表单提交
<input type="text" id='username'>
<input type="submit" value='提交' id='btn'>
​
const usernameInput=document.getElementById("username")
const btn=document.getElementById("btn")
​
btn.addEventListener('click',()=>{
    //验证表单
    if(usernameInput.value.trim()!==''){
        //可以提交
        
    }else{
        //不可以提交
    }
    
    
    //手动提交
},false)

9.2 数组的新增方法

1.includes()【判断是否包含】

判断数组中是否包含某个成员的;数组实例的方法

第一个参数代表:数组要判断的成员是否存在,第二个参数:代表从那个位置开始判断,默认是0

基本遵循严格相等(===),但是对于NaN的判断与===不同

console.log([1,2,3].includes('2'))//false
console.log([1,2,3].includes(2))//true

console.log([1,2,NaN].includes(NaN))//true

console.log([1,2,NaN].includes(NaN,1))//true

//应用--1.数组去重
const arr=[]
for(const item of [1,2,1]){
	if(!arr.includes(item)){
		arr.push(item)
	}
}
console.log(arr)

2.Array.from()【转换为数组】

1.将其他数据类型:所有可遍历的(数组本身,字符串,Set,Map,arguments,NodeList....)可以转化为数组

2.拥有length属性的任意对象,只会把字符串数字键的转化为数组的值

第一个参数代表:需要转化为数组的其他数据类型的值,第二个参数代表:回调函数,类似于数组map方法,用来对每个元素进行处理,将处理后的值放入返回的数组,第三个参数代表:修改this指向

//转字符串
console.log(Array.from('str'))//['s','t','r']
//转Set
console.log(Array.from(new Set([1,1,2])))//[1,2]
console.log([...new Set([1,2,1])])//[1,2]

//转换对象
const obj={
	"0":1
	a:1,
	length:1
}
console.log(Array.from(obj))//[1]

//map
const arr=[1,2].map((value)=>{
	return value*2
})
console.log(arr)//[2,4]

//第二个参数
console.log(Array.from("12",(value)=>value*2))//[2,4]
Array.from("12",function(){
	console.log(this)
},document)//#document	#document

3.find()和findIndex()【查找满足条件的】

find():找到满足条件的第一个立即返回

findIndex():找到满足条件的第一个,立即返回改索引

第一个参数代表:相当于forEach的内部的回调函数,第二个参数代表:this的指向

//find
const arr1=[1, 2, 3, 1, 2, 3].find((value, key, arr) => {
       return value > 2;
 });
 
console.log(arr1)//[3]//findIndex
const arr1=[1, 2, 3, 1, 2, 3].findIndex((value, key, arr) => {
       return value > 2;
 });
 
console.log(arr1)//2//应用---1.通过条件筛出一些东西,选出所需要的
const students=[
    {
        name:"小明",
        age:19,
        sex:'male'
    },
    {
        name:"小白",
        age:20,
        sex:'female'
    },
    {
        name:"小美",
        age:16,
        sex:'female'
    }
]
const s=students.find((value)=>{
    return value.sex=='female'
})
console.log(s)//{name: '小白', age: 20, sex: 'female'}

9.3 对象的新增方法

1.Object.assign()【合并对象】

注意事项:Object.assign(目标对象,源对象1,源对象2,...)直接合并到第一个参数中,返回的就是合并的目标对象

基本数据类型作为(对象):与对象的展开类似,先转换成对象,在合并

同名属性的替换:后面的直接覆盖前面的

//第一种合并方法
const obj1={
    number:'1111'
}
const obj2={
    num:'2222'
}
console.log({...obj1,...obj2})//得到新的新对象
​
​
//第二种合并对象的方法
const obj3={
    number:'1111'
}
const obj4={
    num:'2222'
}
console.log(Object.assign(obj3,obj4))//合并到第一个参数对象
console.log(Object.assign({},obj3,obj4))//此时为一个新的对象console.log(Object.assign({},undefined))//{}
console.log(Object.assign({},null))//{}
console.log(Object.assign({},1))//{}
console.log(Object.assign({},true))//{}
console.log(Object.assign({},'s'))//{0:'s'}//应用---1.合并默认参数和用户参数
const logUser=function(userParam){
    const defaultParam={
        name:'alen',
        age:18
    }
    const options=Object.assign({},defaultParam,userParam)
}
logUser()//{name: 'alen', age: 18}
logUser({})//{name: 'alen', age: 18}
logUser({name:'aaa'})//{name: 'aaa', age: 18}

2.Object.keys(),Object.values(),Object.entries()【得到对象的对应的键,值,键和值】

Object.keys():获取对象键值组成的集合

Object.values():获取对象值组成的集合

Object.entries():获取对象的键和值二维数组

注意:对象的调用方式是构造函数返回的是数组,数组的调用方式是数组的实例对象;返回的是Array Iterator的对象

Object.keys/values()/entries()并不能保证顺序一定是你看到的样子,这一点和for...in是一样的

const person={
	name:"alen",
	age:19
}

console.log(Object.keys(person))//['name', 'age']
console.log(Object.values(person))//['alen', 19]
console.log(Object.entries(person))// [Array(2), Array(2)]


//与数组类似方法的区别
console.log([1,2].keys())//Array Iterator {}
console.log([1,2].values())//Array Iterator {}
console.log([1,2].entries())//Array Iterator {}

//应用---1.使用for...of循环遍历对象
const person1={
	name:'alen',
	age:17
}
for(const item of Object.keys(person1)){
	console.log(item)
}
for(const item of Object.values(person1)){
	console.log(item)
}
for(const [key,value]of Object.entries(person1)){
	console.log(key,value)
}

10.ES6的Promise

10.1 初识Promise

1. Promise是什么

是异步操作的一种解决方案,一般用来解决层层嵌套的回调函数的问题,解决的不是回调函数,而是回调地狱

2. Promise的基本用法

2.1 实例化构造函数生成实例对象
const p=new Promise(()=>{})
2.2 Promise的状态【pending,fulfilled,rejected】

Promise有三种状态,一开始是pending等待状态|初始状态(未完成),执行resolve,变成fulfilled(resolved),(已成功),如果执行reject,变成rejected,(已失败

状态转换只能是:pending===>fulfilled

pending===>rejected

Promise的状态一旦变化,就不会在改变了

初始化Promise():pending 等待状态(未完成)

resolve():fulfilled 成功状态(已成功)

reject():rejected 失败状态(已失败)

const p1=new Promise((resolve,reject)=>{

	//resolve()   //pending--->fulfilled
	//reject()	//pending--->rejected
})
2.3 then方法

Promise的实例化对象的方法then,有二个回调函数:第一个回调什么时候执行【状态由pending-->fulfilled的时候】,第二回调什么时候执行【状态由pending--->rejected的时候】

const p2=new Promise((resolve,reject)=>{
	resolve()
	//reject()
})

//Promise的实例化对象的方法then,有二个回调函数:第一个回调什么时候执行【状态由pending-->fulfilled的时候】
p2.then(()=>{
	console.log('success')
},()=>{
	console.log('error')
})
2.4 resolve和reject函数的参数【分别对应Promise实例对象的then方法参数的二个回调函数】

resolve()携带成功的参数,到then方法的第一个回调函数,用于接收resolve()传递的参数;reject()携带的失败的参数,到then方法第二个回调函数用于接收reject()传递的参数

const p3=new Promise((resolve,reject)=>{
	resolve('成功数据')
	//reject('失败原因')
	//reject(new Error('reason'))
})

//Promise的实例化对象的方法then,有二个回调函数:第一个回调什么时候执行【状态由pending-->fulfilled的时候】
p3.then((res)=>{
	console.log(res)
},(err)=>{
	console.log(err)
})

10.2 Promise的实例方法

1.then()【处理Promise对象成功或者失败的】

1.1 什么时候执行

pending--->fulfilled时,执行then 的第一个回调函数

pending--->rejected时,执行then 的第二个回调函数

1.2 then执行后的返回值【新的Promise对象】

then方法执行后返回一个新的Promise对象

const p4=new Promise((resolve,reeject)=>{
	resolve()
	//reject()
})
const p5=p4.then(()=>{
	console.log("对应p4的resolve()函数")
},()=>{
	console.log("对应p4的reject()函数")
}).then()

console.log(p4,p5)
1.3 then方法返回的Promise对象的状态改变
const p6=new Promise((resolve,reeject)=>{
	resolve()
	//reject()
})
const p7=p6.then(()=>{
	console.log("对应p6的Promise对象resolve()函数")
	//默认是undefined但是会包装成Promise对象
	//return undefined
	//等价于
	return new Promise((resolve,reject)=>{
		//默认调用的是resolve()
		resolve(undefined)
	})
},()=>{
	console.log("对应p6的Promise对象reject()函数")
	
}).then((res)=>{
	console.log('对应p7的Promise对象',res)//undefined
	
}.()=>{
	console.log('对应p7的Promise对象')
})

console.log(p6,p7)
1.4 向后传值

在then的回调函数中,return 后面的东西,默认是return undefined,但是会包装成Promise对象,return后的值会传给then返回的新的Promise对象,默认传到resolve()里面,

then方法的回调函数的return,默认返回的都是then后返会新的Promise对象的,永远都是成功状态的Promise对象

const p6=new Promise((resolve,reeject)=>{
	resolve()
	//reject()
})
const p7=p6.then(()=>{
	console.log("对应p6的Promise对象resolve()函数")
	//默认是undefined但是会包装成Promise对象
	//return undefined
	//等价于
	return new Promise((resolve,reject)=>{
		//默认调用的是resolve()
		resolve(undefined)
	})
},()=>{
	console.log("对应p6的Promise对象reject()函数")
	
}).then((res)=>{
	console.log('对应p7的Promise对象',res)//undefined
	return new Promise((reslove,reject)=>{
		reject('错误')
	})
},()=>{
	console.log('对应p7的Promise对象')
	
}).then(()=>{
	console.log('上一个then返回的Promise对象')
},(error)=>{
	console.log('上一个then返回的Promise对象',error)
})

console.log(p6,p7)
1.5 使用Promise解决回调地狱
const move=(el,{x=0,y=0}={},end=()=>{})=>{
    el.style.left=x+"px"
    el.style.top=y+'px'
}
​
const div=document.getElementById("div")
​
const movePromise=(div,point=>{
    return new Promise((resolve,reject)=>{
      move(div,point,()=>{
         resolve()
          })
        })
      })
document.addEventListener("click",()=>{
    movePromise(div,{x:150}).
    then(()=>{
       return movePromise(div,{x:150,y:150})
    }).
    then(()=>{
          return movePromise(div,{y:150})
    }).
    then(()=>{
          return movePromise(div,{x:0,y:0})
    })
  })
​
// document.addEventListener("click",()=>{
//   move(div,{x:150},()=>{
//     move(div,{x:150,y:150},()=>{
//       move(div,{y:150},()=>{
//         move(div,{x:0,y:0})
//       })
//     })
//   })
// },false)

2.catch()【处理reject状态】

catch()可以捕获它前面的错误,一般总是建议,Promise对象后面要跟catch方法,这样可以处理Promise内部发生的错误

2.1 有什么用

catch专门用来处理reject状态,then此时专门处理成功状态;catch本质是then的特列

2.2 基本用法
const p8=new Promise((resolve.reject)=>{
    //resolve()
    reject("error message")
})
p8.then(null,err=>{
    console.log("失败",err)
})
​
//等价于下面这个代码
p8.then(()=>{
    console.log("成功")
})
p8.catch((err)=>{
    console.log("失败",err)
    return new Promise((resolve,reject)=>{
        resolve('success')
    })
}).then((res)=>{
    console.log("catch的返回值",res)
})

3.finally()【Promise状态发生变化的时候,无论如何变化都会执行】

3.1 什么时候执行

当Promise状态发生变化的时候,无论如何变化都会执行,不变化不执行

const p9=new Promise((resolve,reject)=>{
	resolve()
	//reject()
}).finally(()=>{
	console.log("无论状态如何变化都要执行的回调函数;实际应用场景:关闭数据库")
}).catch(()=>{
	console.log('错误')
})
3.2 本质

finally()本质上是then()的特列

const p10=new Promise((resolve,reject)=>{
    resolve()
    //reject()
}).finally(()=>{
    console.log("无论状态如何变化都要执行的回调函数;实际应用场景:关闭数据库")
}).catch(()=>{
    console.log('错误')
})
​
//等价于const p9=new Promise((resolve,reject)=>{
    resolve()
    //reject()
}).then(result=>{
    return result
},err=>{
    return new Promise((resolve,reject)=>{
        reject()
    })
}).then(data=>{
    console.log(data)
}).catch(err=>{
    console.log(err)
})

10.3 Promise的构造方法

1.Promise.resolve()【成功状态Promise的一种简写形式】

1.1 本质

是成功状态Promise的一种简写形式

new Promise(resolve=>resolve('foo'))
​
//简写
Promise.resolve('foo')
1.2 参数
1.一般参数
2.特殊参数【Promise对象】

当Promise.resolve()接收Promise对象时,直接返回这个Promise对象,什么都不做

当resolve函数接收的是Promise对象时,后面的then会根据传递的Promise对象的状态变化决定执行哪一回调

3.具有then方法的一般对象
//一般参数
Promise.resolve('foo').then(data=>{
    console.log(data)
})
​
//特殊参数
const pp=new Promise(resolve=>{
    setTimeOut(resolve,1000,'我执行了')
    //setTimeOut(()=>{
    //  resolve('我执行了')
    //},1000,)
})
Promise.resolve(pp).then(data=>{
    console.log(data)
})
​
//等价于
pp.then(data=>{
    console.log(data)
})
​
//resolve传Promise对象----
new Promise(resolve=>resolve(pp)).then(data=>{//这个then归pp这个Promise这个对象管
    console.log(data)//此时的data是pp这个Promise对象resolve()传递过来的
})
​
//具有then方法的对象const thenable={
    then(resolve,reject){
        console.log("then")
        resolve("thenable的then的resolve")
    }
}
Promise.resolve(thenable).then(data=>{
    console.log(data)
},err=>{
    console.log(err)
})//then
1.3 在then方法中的应用

2.Promise.reject()【失败状态Promise的一种形式】

2.1 本质

失败状态Promise的一种形式

new Promise((resolve,reject)=>{
    reject('reason')
})
//等价于
Promise.reject('reason')
2.2 参数

不管什么参数,都会原封不动地向后传递,作为后续方法的参数

const p1=new Promise(resolve=>{
    setTimeout(resolve,1000,'执行了')
})
Promise.reject(p1).catch(err=>console.log(err))//Promise {pending}new Promise(resolve,reject=>{
    resolve(123)
})
.then(data=>{
    console.log(data)
    //return data
    return Promise.reject(data)
})
.then(data=>{
    console.log(data)
})
.catch(err=>{
    console.log(err)
})

3.Promise.all()【关注多个Promise对象的状态变化;都成功则成功,一个失败则失败】

3.1 有什么用

Promise.all(),是关注多个Promise对象的状态变化;传入多个Promise实例,包装成一个新的Promise实例返回

3.2 基本用法

Promise.all():当传入数组中值为Promise实例对象,所有Promise实例对象都为(已成功)pending===>resolved【fulfilled】 时第一个回调才执行,如果传入的有一个Promise实例对象为(已失败)pending===>rejected【rejected】时第二个参数回调执行。

Promise.all()状态变化与传入的Promise实例对象状态有关

所有状态都变成resolved,最终的状态才会变成resolved

只要有一个变成rejected,最终的状态就变成rejected

const delay=ms=>{
	return new Promise((resolve)=>{
		//ms秒后Promise状态由pending变为fuldilled状态
		setTimeout(resolve,ms)
	})
}
const pr1=delay(1000).then(()=>{
	console.log('pr1完成了')
	return 'pr1'
})
const pr2=delay(2000).then(()=>{
	console.log("pr2完成了")
	// return "pr2";
    return Promise.reject("pr2错误")
})

//Promise.all()
const pro=Promise.all([pr1,pr2])
pro.then(data=>{
	console.log("所有Promise对象状态都为fulfilled的",data)
},err=>{
	console.log("传入的Promise对象有一个为rejected",err)
})

//运行结果
//pr1完成了
//pr2完成了
//传入的Promise对象有一个为rejected pr2错误

4.Promise.race()【关注多个Promise对象的状态变化;只关注第一个Promise的状态,第一个成功则成功,第一个失败则失败】

Promise.race()的状态取决于第一个完成的Promise实例对象,如果第一个完成的成功了,那最终的就是成功,如果第一个完成的是失败了,那最终的就是失败

const delay=ms=>{
	return new Promise((resolve)=>{
		//ms秒后Promise状态由pending变为fuldilled状态
		setTimeout(resolve,ms)
	})
}
const pr1=delay(1000).then(()=>{
	console.log('pr1完成了')
	return 'pr1'
	//return Promise.reject("pr1错误")
})
const pr2=delay(2000).then(()=>{
	console.log("pr2完成了")
	// return "pr2";
    return Promise.reject("pr2错误")
})

//Promise.race()
const pro=Promise.race([pr1,pr2])
pro.then(data=>{
	console.log("所有Promise对象状态第一个为fulfilled的",data)
},err=>{
	console.log("传入的Promise对象状态第一个为rejected",err)
})

//运行结果
//pr1完成了
//传入的Promise对象第一个为rejected pr1错误
//pr2完成了

5.Promise.allSettled()【关注多个Promise对象的状态变化,无论怎样都成功,记录多个Promise对象表现】

Promise.allSettled()的状态与传入的Promise状态无关,永远都是成功,永远都是成功,它只会忠实的记录各个Promise的表现

const delay=ms=>{
	return new Promise((resolve)=>{
		//ms秒后Promise状态由pending变为fuldilled状态
		setTimeout(resolve,ms)
	})
}
const pr1=delay(1000).then(()=>{
	console.log('pr1完成了')
	return 'pr1'
	//return Promise.reject("pr1错误")
})
const pr2=delay(2000).then(()=>{
	console.log("pr2完成了")
	// return "pr2";
    return Promise.reject("pr2错误")
})

//Promise.allSettled()
const pro=Promise.allSettled([pr1,pr2])
pro.then(data=>{
	console.log("记录所有Promise对象",data)
})

//运行结果
//pr1完成了
//pr2完成了
记录所有Promise对象 (2) [{…}, {…}]
	0: {status: 'fulfilled', value: 'pr1'}
	1: {status: 'rejected', reason: 'pr2错误'}
	length: 2

10.4 Promise的注意事项和应用

1.Promise的注意事项

1.1 resolve或reject执行后的代码

推荐在调用resolve或reject函数的时候加上return,不再执行它们后面的代码

new Promise((resolve,reject)=>{
	resolve(123)
	console.log('hahaha')//可以执行
	
	//这里建议
	//return resolve()
	//return reject()
})
1.2 Promise.all/arce/allSettled的参数问题

参数如果不是Promise数组,会将不是Promise的数组元素转变成Promise对象;不只是数组,任何可遍历的都可以作为参数:(数组,字符串,Set,Map,arguments,NodeList...)

Promise.all([1,2,3]).then(data=>{
	console.log(data)
})
//等价于
Promise.all([
	Promise.resolve(1),
	Promise.resolve(2),
	Promise.resolve(3)
]).then((data)=>{
	console.log(data)
})
//[1,2,3]

Promise.all(new Set([1,2,3])).then(data=>{
	console.log(data)
})
//[1,2,3]
1.3 Promise.all/race/allSettled的错误处理

Promise.all()如果传入的Promise对象都是错误,失败,则执行完第一个Promise的时候执行Promise.all()对应的错误,然后再执行别的下一个Promise

const delay=ms=>{
    return new Promise((resolve)=>{
        //ms秒后Promise状态由pending变为fuldilled状态
        setTimeout(resolve,ms)
    })
}
const pr1=delay(1000).then(()=>{
    console.log('pr1完成了')
    //return 'pr1'
    return Promise.reject("pr2错误")
})
//或
const pr1=delay(1000).then(()=>{
    console.log('pr1完成了')
    //return 'pr1'
    return Promise.reject("pr1错误xxx")
}).catch(err=>{
    console.log("pr1的错误",err)
})
​
const pr2=delay(2000).then(()=>{
    console.log("pr2完成了")
    // return "pr2";
    return Promise.reject("pr2错误")
})
​
//Promise.all()
const pro=Promise.all([pr1,pr2])
pro.then(data=>{
    console.log("所有Promise对象状态都为fulfilled的",data)
},err=>{
    console.log("传入的Promise对象有一个为rejected",err)
})
​
//或整个
pro.then(data=>{
    console.log("所有Promise对象状态都为fulfilled的",data)
}).catch(err=>{
    console.log("错误",err)
})

2.Promise的应用【1.异步加载图片】

 <img
   id="img"
   src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-d0fd619d-b424-42b7-9eb4-12d4070b76d1/1dde7feb-fb8b-4580-b951-d7ba40824f4d.jpg"
   alt=""/>
   
<script>
//1.异步加载图片
const loadImagAsync=url=>{
    return new Promise((resolve,reject)=>{
        //不执行具体的逻辑。只要决定什么时候resolve和reject就行
        const img=new Image()
        //加载成功
        img.onload=()=>{
            resolve(url)
        }
​
        //加载失败
        img.onerror=()=>{
            reject(new Error(`Could not load image at ${url}`))
        }
​
        //当对象的src赋值之后,图片开始加载
        img.src=url
    })
}
const imgDOM=document.getElementById("img")
​
loadImagAsync("https://vkceyugu.cdn.bspapp.com/VKCEYUGU-d0fd619d-b424-42b7-9eb4-12d4070b76d1/948d1efa-d8f3-45c9-a148-1275cc98d66f.jpg").then(img=>{
    console.log(img)
    imgDOM.src=img
})
</script>

11.Class 类

1.初始Class

1.1 Class是什么

1.认识Class

人类:类,抽象,对象是具体;类可以看作是对象的模板,用一个类可以创建出许多不同的对象

2.Class的基本使用

类名一般首字母大写,声明一个类是不需要分号的;所有的类必须有构造方法constructor(){},实例化时执行构造方法,所以必须有构造方法,但可以不写出来

类里面的this指向这个类它实例化的对象

class Person{
	//必须由有构造方法
	constructor(name,age){
		//这里一般用作初始化属性
		this.name=name;
		this.age=age
		
		//这里不推荐写方法,因为方法可以共用
		this.speak=()=>{}
	}
	//各实例对象共有的方法
	run(){}
}
//实例化时执行constructor函数
const p=new Person('ZS',17)
console.log(p.name,p.age,p.run())//ZS 17 undefined
3.Class与构造函数

Class初始化属性一般写在constructor构造函数中(可以保证每个实例化对象属性不同,方法不是共用的),方法写在与constructor同级上(实例化对象共用方法)相当于以前的构造函数在原型上添加方法

//Class类
class Person{
	//必须由有构造方法
	constructor(name,age){
		//这里一般用作初始化属性
		this.name=name;
		this.age=age
		
		//这里不推荐写方法,因为方法可以共用
		this.speak=()=>{}
	}
	//各实例对象共有的方法
	run(){}
}
console.log(typeof Person)//function
console.log(Person.prototype.run)




//构造函数
function Person1(name,age){
	this.name=name;
	this.age=age
	//构造函数写方法,没实例化一个对象就多一份,不是相同的
	//不建议写方法
}
//构造函数一般方法写在构造函数的原型上,这样实例化的对象共用的是一个方法
Perosn.prototype.speak=function(){
	console.log("人在说话")
}

1.2 Class的两种定义形式

1.声明新形式

class Person{}

class 类名{
	constructor(){}
}
2.表达式形式

const Person=class {}

new (class {})()

//构造函数的表达式形式
//function Person(){}
//const Person=function(){}//类表达式形式
const Person=class{
    constructor(){
        console.log("constructor")
    }
}
​
//立即执行的匿名函数
(function(){
    console.log('func')
})()
​
//立即执行的匿名类
new (class{
    constructor(){
        console.log("constructor")
    }
})()

2.Class的属性和方法

2.1 实例属性,静态方法和静态属性

1.实例属性【实例的】

constructor里面添加的属性为实例属性,也可以在constructor同级下添加属性相当于在constructor里面用this添加属性

class Person2{
    //实例属性h和方法,这里一般为默认值
    age=18;
    sex="male"
    
    //实例方法,方法就是值为函数的特殊属性
    getSex=function(){
        return this.sex
    }
    getName=function(){
        return this.name
    }
    
    //想修改可以在这里面传进来的值
    constructor(name){
        //实例属性
        this.name=name
        //this.age=18//this指向实例对象
    }
}
const p2=new Person("lisi")
console.log(p2.name)
2.静态方法【类的方法】

static speak(){}

this指向类

class Person2{
    //实例属性,这里一般为默认值
    age=18;
    sex="male"
    //实例方法,方法就是值为函数的特殊属性
    getSex=function(){
        return this.sex
    }
    
    //想修改可以在这里面传进来的值
    constructor(name){
        //实例属性
        this.name=name
        //this.age=18
    }
    //实例对象的方法
    speak(){
        console.log("speak")
        console.log(this)//Perosn2 {...}
    }
    
    //静态方法【类的方法】
    static speak(){
        console.log("人类可以说话")
        console.log(this)//class Perosn2
    }
}
//直接添加静态方法----不推荐
Person2.run=function(){
    console.log("人类跑")
    console.log(this)
}
​
​
const p2=new Person("lisi")
console.log(p2.name)//实例的属性。通过实例对象调用
​
p2.speak()//实例方法的调用【实例对象的this指向实例对象】
Person.speak()//类静态方法的调用【静态方法的this指向这个类】
3.静态属性【类的属性】
class Person2{
    //实例属性,这里一般为默认值
    age=18;
    sex="male"
    //实例方法,方法就是值为函数的特殊属性
    getSex=function(){
        return this.sex
    }
    
    //想修改可以在这里面传进来的值
    constructor(name){
        //实例属性
        this.name=name
        //this.age=18
    }
    
    
    //静态属性---不推荐写,目前只是提案,有兼容性问题
    static version=1.0
    //可以把属性变为方法
    static getVersion(){
        return "1.0"
    }
}
//类外部添加静态属性---也是不推荐
Person2.version=1.0
​
​
const p2=new Person("lisi")
console.log(p2.name)//lis
console.log(Person.version)//1.0
console.log(Person.getVersion())//1.0

2.2 私有属性和方法【类的内部使用,不能再外部使用】

1.为什么需要私有属性和方法

一般情况下,类的属性和方法都是公开的,公有的属性和方法可以被外界修改,造成意想不到的错误

class Person3{
    constructor(name){
        this.name=name
    }
    
    //通过方法返回属性,外面也是只能通过方法访问属性了
    getName(){
        return this.name
    }
    
    speak(){
        console.log("speak")
    }
}
const p3=new Person3('alen')
console.log(p3.name)
p.speak()
2.模拟私有属性和方法
2.1._开头表示私有
class Person3{
    constructor(name){
        this._name=name
    }
    
    //通过方法返回属性,外面也是只能通过方法访问属性了
    getName(){
        return this._name
    }
    
    speak(){
        console.log("speak")
    }
}
const p3=new Person3('alen')
console.log(p3.name)//undefined
console.log(p3.getName())//alen
p3.speak()
2.2 将私有属性和方法移出去
;(function(){
    //将属性或方法移出来
    let name=""
    
    class Person3{
        constructor(username){
            //this.name=name
            name=username
        }
    
        //通过方法返回属性,外面也是只能通过方法访问属性了
        getName(){
            return this.name
        }
    
        speak(){
            console.log("speak")
        }
    }
    
    window.Perosn3=Person3
})()
​
;(function(){
    const p3=new Person3('alen')
    console.log(p3.name)//undefined
    console.log(p3.getName())//alen
    p3.speak()
})()
​
​

3.Class的继承

3.1 extends【继承关键字】

1.子类继承父类【extends和super】
class Person{
    constructor(name,sex){
        //Person实例对象的属性
        this.name=name;
        this.sex=sex;
        
        this.say=function(){//每一次实例化都会创建一份
            console.log("说话")
        }
    }
    //实例共享的
    speak(){
        console.log("Peerson类实例对象共享方法speak")
    }
    //静态方法
    static speak(){
        console.log("Person类的speak静态方法")
    }
}
//Person类的静态属性
Person.version='1.0'class Programmer extends Person{
    //子类也必须要有构造方法
    constructor(name,sex){
        //子类必须调用父类的构造方法
        super(name,sex)
    }
}
​
//实例化子类
const zs=new Programmer('zs','男')
console.log(zs.name)
console.log(zs.sex)
zs.say()
zs.speak()
Programmer.speak()
console.log(Programmer.version)
​
2.改写继承的属性或方法【同名覆盖】

子类同名属性或方法会覆盖父类的属性或方法【同名覆盖】

class Person{
	constructor(name,sex){
		//Person实例对象的属性
		this.name=name;
		this.sex=sex;
		
		this.say=function(){//每一次实例化都会创建一份
			console.log("说话")
		}
	}
	//实例共享的
	speak(){
		console.log("Peerson类实例对象共享方法speak")
	}
	//静态方法
	static speak(){
		console.log("Person类的speak静态方法")
	}
}
//Person类的静态属性
Person.version='1.0'

class Programmer extends Person{
	//子类也必须要有构造方法
	constructor(name,sex,feature){
		//子类必须调用父类的构造方法
		super(name,sex)
		
		//子类添加实例属性--【this不能写在super()前面】
		this.feature=feature
	}
	 //不是同名的相当于自己的方法或者属性
     test(){
       console.log("子类Programmer自己的方法")
     }
	
	
	//同名覆盖实例属性
	speak(){
		console.log("子类Programmer的实例方法speak")
	}
	//同名覆盖静态方法
	static speak(){
        console.log("子类Programmer的类的方法")
     }
}
//同名覆盖静态属性
Programmer.version='2.0'
 
 
//实例化子类
const zs=new Programmer('zs','男','秃头')
console.log(zs.name)
console.log(zs.sex)
console.log(zs.feature)
zs.test()
zs.say()
zs.speak()
Programmer.speak()
console.log(Programmer.version)

3.2 super【作为函数使用,代表父类,super虽然代表了父类的构造方法,但是内部的this指向子类的实例】

1.作为函数调用【代表父类的构造函数,只能用在子类的构造方法中】

代表父类的构造函数,只能用在子类的构造方法中,用在其他地方就会报错

super虽然代表了父类的构造方法,但是内部的this指向子类的实例

class Person{
    constructor(name){
        this.name=name
        //这里子类继承后,构造方法调用super()之后的this指向子类
        console.log(this)
    }
}
class Programmer extends Person{
    constructor(name,sex){
        //作为函数调用
        super(name)
        this.sex=sex
    }
}
new Person()
new Programmer()
2.作为对象使用【可以在构造方法或者一般方法使用,代表父类的原型对象,指向子类实例】【在静态调用,代表父类,指向子类而不是子类实例】
2.1 在构造方法或者一般方法使用super

super代表父类的原型对象Person.prototype

定义在父类实例上的方法和属性,是无法通过super调用的

通过super调用父类的方法时,方法内部的this指向当前的子类实例

2.2 在静态方法使用super

指向父类,而不是父类的原型对象

通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例

class Person{
    constructor(name){
        this.name=name
        //这里子类继承后,构造方法调用super()之后的this指向子类
        console.log(this)
        
        //实例的方法
        this.test=function(){
            console.log("Person实例的方法test")
        }
        
    }
    //Person原型添加方法speak
    speak(){
        console.log("相当于在Person的原型上添加方法speak")
        console.log("super作为对象调用此方法",this)
    }
    
    //父类静态方法
    static speak(){
        console.log("Person父类的静态方法speak")
        console.log(this)
    }
}
​
​
class Programmer extends Person{
    constructor(name,sex){
        //作为函数调用
        super(name)
        this.sex=sex
        
        console.log(super.name)//undefined
        super.test()//
        
        //作为对象使用---代表父类原型对象
        super.speak()
    }
    
    speak(){
        //作为对象使用---代表父类原型对象
        super.speak()
        console.log('子类原型的方法speak')
    }
    
    //静态方法里调用super
    static speak(){
        //调用父类的静态方法
        super.speak()
        console.log("Programmer子类的静态方法speak")
    }
}
//实例化父类
new Person()
//实例化子类
new Programmer()
​
//子类静态方法的调用
Programmer.speak()
3.注意事项

使用super的时候,必须显示指定是作为函数还是作为对象使用,否则会报错

class Person{
    constructor(name){
        this.name=name
    }
    
    speak(){
        console.log("相当于在Person的原型上添加方法speak")
    }
    
}
​
​
class Programmer extends Person{
    constructor(name,sex){
        super(name,sex) 
        //console.log(super)//报错,需要指定是函数还是对象
        console.log(super())
        console.log(super.name)
    }
}
​

4.Class的应用

4.1 幻灯片

//主要代码
class Slider extends BaseSlider{
    constructor(el,opptions){
        super(el,options)
        this._bindEvent()
    }
    _bindEvent(){
        document.addEventListener("keyup",ev=>{
            //console.log(ev,keyCode)
            if(ev.keyCode===37){
                //左
                this.prev()
            }else if(ev,keyCode===39){
                //右
                this.next()
            }
        })
    }
}
new Slider(document.querySelector(".slider"),{
    initialIndex:1,
    animation:true,
    speed:1000
})

12.Module模块系统

12.1 初始Module【一个js文件就是一个模块,模块是局部的】

ES6 模块化规范中定义:

  • 每个 js 文件都是一个独立的模块
  • 导入其它模块成员使用 import 关键字
  • 向外共享模块成员使用 export 关键字

ES6的模块化主要包含如下3种用法:

  • 默认导出于默认导入
  • 按需导出于按需导入
  • 直接导入并执行模块中的代码

1.Module是什么

1.1 什么是模块【模块的东西是局部的了】

模块:一个一个的局部作用域的代码块;以前是使用立即执行函数模仿模块的,将需要暴露出去的暴露在全局上

1.2 什么是模块系统

模块系统需要解决的问题:1.模块化问题 2.消除全局变量 3.管理加载顺序

RequireJS seaJS

ES Module

2.Module的基本用法

2.1使用Module模块化之前的例子
//导出父类
//base.js文件
export default BaseSlider;//局部作用域

//slider.js文件
//导入父类到子类的文件
import BaseSlider from './base.js'
export default Slider

//index.js
//导入子类文件
import Slider from './slider.js'
2.2使用script标签加载模块化【type="module"】

只要你会用到import 或 export ,在使用script标签加载的时候,就要加上type="module"

<script src='./index.js' type="module"></script>
2.3分析Module解决的问题

1.模块化问题

2.消除全局变量

3.管理加载顺序

12.2 Module的导入和导出【重点】

NodeJS中使用模块

CommonJS,reqiure引入,module.exports导出

1.export default 和对应的 import...from...【一个文件只能使用一次】

1.1 认识导出和导出

导出的东西可以被导入(import),并访问到

没有导出,可以导入(import),被导入的代码都会执行一遍,也仅会执行一遍

1.2 基本用法

导入的时候可以随意命名

//mudule.js
const age=18
const sex='male'

//导出一个--【只能有一个】
//export default age
//export default 20
//export default {}
//export default function(){}
//export default class {}

//index.html
<script type="module" src="./mudule.js">
	import a from "./module.js"
	console.log(a)
</script>

2.export 和对应的 import {} from...【export 声明或语句】

2.1 基本用法

导入的时候不可以随意命名的

//mudule.js
const age=18


//可以多次使用
export const age=18

//也可以对应导入的
export {
	age
}

//index.html
<script type="module" src="./mudule.js">
	import {age} from "./module.js"
	console.log(age)
</script>
2.2 多个导出
//mudule.js
const age=18
function fn(){
	console.log("mudule.js文件的方法fn")
}
class Person{
	constructor(name){
		this.name=name
	}
}


//可以多次使用-【匿名不可以  必须有名字】
export function fn(){
	console.log("mudule.js文件的方法fn")
}
export class Person{
	constructor(name){
		this.name=name
	}
}
export const age=18



//或者使用下面这个对象导出---也可以对应导入的
export {
	fn,
	Person,
	age
}




//index.html
<script type="module" src="./mudule.js">
	import {fn,Person,age} from "./module.js"
	console.log(fn,Person,age)
</script>
2.3 导出导入时起别名【as】
//mudule.js
const age=18
function fn(){
	console.log("mudule.js文件的方法fn")
}
class Person{
	constructor(name){
		this.name=name
	}
}


//可以多次使用-【匿名不可以  必须有名字】
export function fn(){
	console.log("mudule.js文件的方法fn")
}
export class Person{
	constructor(name){
		this.name=name
	}
}
export const age=18



//或者使用下面这个对象导出---也可以对应导入的
export {
	fn as func,
	Person,
	age
}




//index.html
<script type="module" src="./mudule.js">
	import {func,Person as p,age} from "./module.js"
	console.log(func,p,age)
</script>
2.4 整体导入【* as x】
//mudule.js
const age=18
function fn(){
    console.log("mudule.js文件的方法fn")
}
class Person{
    constructor(name){
        this.name=name
    }
}
​
​
//可以多次使用-【匿名不可以  必须有名字】
export function fn(){
    console.log("mudule.js文件的方法fn")
}
export class Person{
    constructor(name){
        this.name=name
    }
}
export const age=18
​
​
​
//或者使用下面这个对象导出---也可以对应导入的
export {
    fn as func,
    Person,
    age
}
​
​
​
​
//index.html
<script type="module" src="./mudule.js">
    import * as obj from "./module.js"
    console.log(obj.func,obj.Person,obj.age)
</script>
2.5 同时导入

一定时export default在前面

//mudule.js
const age=18
function fn(){
    console.log("mudule.js文件的方法fn")
}
class Person{
    constructor(name){
        this.name=name
    }
}
​
​
//可以多次使用-【匿名不可以  必须有名字】
export function fn(){
    console.log("mudule.js文件的方法fn")
}
export class Person{
    constructor(name){
        this.name=name
    }
}
export const age=18
export default 18
​
​
//或者使用下面这个对象导出---也可以对应导入的
export {
    fn as func,
    Person,
    age
}
​
​
​
​
//index.html
<script type="module" src="./mudule.js">
    import {func,Person as p,age} from "./module.js"
    import age2 from "./module.js"
    
    //可以写成下面这个方式
    //export default在前面
    import age2,{func,Person as p,age} from "./module.js"
</script>

12.3 Module的注意事项和应用

1. Module的注意事项

1.1 模块顶层的this指向【undefined】

在模块中,顶层的this指向undefined

//mudule.js
console.log(this)
if(typeof this !=='undefined'){
	throw new Error("请使用模块方式加载")
}


//index.html
<script type="module" src="./mudule.js">
	import "./module.js"//undefined
</script>
<script  src="./mudule.js">
	import "./module.js"//window
</script>
1.2 import 关键字 和 import()函数【import 提升效果到模块头部】

import 命令具有提升效果,会提升到整个模块的头部,率先执行

import 执行的时候,代码还没执行

import 和 export命令只能在模块的顶层,不能在代码块中执行

import()可以按条件导入,返回的是一个Promise对象

//import()可以按条件导入
//举个例子
if(PC){
	import('pc.js').then().catch()
}else if(Mobile){
	import('mobile.js').then().catch()
}
1.3 导入导出的复合写法【export 】

复合写法没法在当前模块使用

//mudule.js
const age=18
export const age=18


//index.html
<script type="module" src="./mudule.js">
	export {age} from"./module.js"
	//等价于
	import {age} from "./module.js"
	export {age} from './module.js'
</script>

2. Module的应用

//default.js默认参数模块文件
//默认参数
const DEFAULTS={
    //初始索引
    initialIndex:0,
    //切换时是否有动画
    animation:true,
    //切换速度
    speed:300
}
//export default  DEFAULTS
export default {
    //初始索引
    initialIndex:0,
    //切换时是否有动画
    animation:true,
    //切换速度
    speed:300
}
​
​
​
//constants.js常量模块文件
//base
const ELEMENT_NODE=1
const SLIDER_ANIMATION_CLASSNAME='slider-animation'const LEFT_KEYCODE=37
const RIGHT_KEYCODE=39export {
    ELEMENT_NODE,
    SLIDER_ANIMATION_CLASSNAME,
    LEFT_KEYCODE,
    RIGHT_KEYCODE
}
​
​
​
//base.js模块文件
//引入默认参数模块
import DEFAULTS from './default.js'
//引入常量文件模块
import {ELEMENT_NODE,SLIDER_ANIMATION_CLASSNAME} from "./constants.js"
//父类
class BaseSlider{
    constructor(el,options){
        if(el.nodeType!==ELEMENT_NODE){}
            throw new Error("实例化的时候,请传入DOM元素")
            const slider=el
            const sliderContent=slider.querySelectorAll('.slider-content')
            const sliderItems=slider.querySelectorAll('.slider-item')
            
            this._slider=slider
            this._sliderContent=sliderContent
            this._sliderItems=sliderItems
            this._options={
                ...DEFAULTS,
                ...options
            }
    }
}
export default BaseSlider
​
​
​
​
//keyboard.js键盘控制模块文件
//引入常量文件模块
import {LEFT_KEYCODE,RIGHT_KEYCODE} from "./constants.js"
const keyboard={
    bindEvent(slider){
        document.addEventListener("keyup",ev=>{
            if(ev.keyCode==LEFT_KEYCODE){
                slider.prev()
            }else if(ev.keyCode==RIGHT_KEYCODE){
                slider.next()
            }
        },false)
    }
}
export default keyboard
​
​
​
​
//slider.js模块文件
//引入父类文件模块
import BaseSlider from "./base.js"
//引入操控键盘方法的模块文件
import Keyboard from "./keyboard.js"
//import Mouse from "./keyboard.js"class Silder extends BaseSlider{
    constructor(el,options){
        super(el,options)
        this._bindEvent()
    }
    _bindEvent(){
        Keyboard.bindEvent(this) 
        //Mouse,bindEvent(this)
    }
}
export default Slider
​
​
​
​
//index.js
import Slider from "./slider.js"
new Slider(document.querySelector(".slider"))

13.Babel与Webpack

13.1 Babel

1.Babel是什么

1.1 认识Babel

官网:babeljs.io/

Babel是Javascript的编译器,用来将ES6的代码,转换成ES6之前的代码

1.2 使用Babel

在线编译:babeljs.io/repl

1.3 解释编译结果

Babel本身可以编译ES6的大部分语法,比如let const 箭头函数 类

但是对于ES6新增的API,比如Set,Map,Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign()/Array.from()...)都不能直接编译,需要借助其他模块

Babel一般需要配合Webpack来编译模板语法

2.Babel的使用方式

2.1 Babel的哪些使用方式
2.2 在命令行工具中使用Babel

babeljs.io/setup

3.使用Babel前的准备工作

3.1 什么是Node.js和npm

Node.js是一个平台或者工具,对应浏览器

后端的Javascript = ECMAScript + IO + File + ...等服务器端操作

npm:node包管理工具

3.2 安装Node.js

blog.csdn.net/qq_51066068…

3.3 初始化项目

切换到项目目录下执行:npm init -y

3.4 安装Babel需要的包

package.json记录安装包,执行npm install 直接可以安装里面所有的安装包

切换下载包安装镜像:npm config set registry register.npm.taobao.org

npm install --save-dev @babel/core@7.11.0 @babel/cli@7.10.5

安装转换成那种版本的js代码:npm install @babel/preset-env@7.11.0 --save-dev

4.使用Babel编译ES6代码

4.1 执行编译的命令

在package.json文件中添加执行babel的命令;babel src -d dist / babel src --out-dir dist

npm run build

4.2 Babel的配置文件

.babelrc

npm install @babel/preset-env@7.11.0 --save-dev

创建配置文件.babelrc 并配置

{ "presets": ["@babel/preset-env"] }

"scripts": {
    "build":"babel src -d dist"
    //"build":"babel src --out-dir dist"
 },
 
 //配置babel转换成那种js代码
安装:npm install @babel/preset-env@7.11.0 --save-dev
配置:.babelrc文件
{
  "presets": ["@babel/preset-env"]
}

13.2 Webpack入门

安装:npm install webpack@4.44.1 webpack-cli@3.3.12 -D

package.json配置:

"scripts": {

"build": "webpack --config webpack.config.js"

},

1.Webpack是什么

1.1 认识Webpack

webpack是静态模块打包器,当webpack处理应用程序时,会将所有这些模块打包成一个或多个文件

1.2 理解Webpack

什么是Webpack:

模块:webpack可以处理js/css/图片,图标字体等单位

静态:开发过程中存在本地的js/css/图片/图标字体等文件,就是静态的

动态内容,webpack没办法处理,只能处理静态的

2.Webpack初体验

13.3 Webpack的核心概念【webpack.config.js文件】

1.entry和output

1.1 entry【单入口(字符串)和多入口(对象)】

指定入口文件

//单个入口【字符串】
entry: "./src/index.js",
​
​
//多个入口【对象】
entry: {
    main:"./src/index.js",
    search:"./src/search.js"
},
1.2 output【单出口和多出口】

指定出口

const path = require("path")
//单个出口【对象】
output: {
    path: path.resolve(__dirname, 'dist'),//path要求绝对路径,__dirname表示当前文件所在目录
    filename: "bundle.js"
}
​
//多个出口【filaname文件名为字符串[name].js】name对应入口文件名
output: {
    path: path.resolve(__dirname, 'dist'),//path要求绝对路径,__dirname表示当前文件所在目录
    filename: "[name].js"
}

2.loader【模块(对象),规则(数组),对象(test,loader,use,exclude)】

网址:www.webpackjs.com/loaders/

2.1 什么是loader

webpack js/css/图片,webpack本身是处理js的,loader让webpack能够处理哪些非JS文件的模块

2.2 babel-loader

安装:npm install babel-loader @babel/core【babel核心包,负责调度】 @babel/preset-env 【babel指定转js的配置文件】-D

配置babel文件:

配置:.babelrc文件
{
  "presets": ["@babel/preset-env"]
}
//配置babel-loader
module: {
    rules: [
      {
        test:/.js$/,
        exclude:/node_modules/,
        loader:"babel-loader"
      }
    ]
  }

编译新的API

安装:npm install -D core-js@3.6.5

引入core-js,import "core-js/stable"

3.plugin【数组】

3.1 什么是plugins

插件;loader被用于帮助webpack处理各种模块,而插件则可以用于执行范围更广的任务

www.webpackjs.com/plugins/

3.2 html-webpack-plugin

安装:npm install html-webpack-plugin@4.3.0 -D

1.引入:const HtmlWebpackPlugin=require("html-webpack-plugin")

1.引入:const HtmlWebpackPlugin=require("html-webpack-plugin")
​
2.使用:plugins:[
    new HtmlWebpackPlugin({
        template:"./index.html"
    })
]

2.多页面时html-webpack-plugin插件的配置

有几个实例化几个new HtmlWebpackPlugin({})

1.引入:const HtmlWebpackPlugin=require("html-webpack-plugin")
​
2.使用: plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
      filename:'index.html',//打包后的多html文件的名字
      excludeChunks:["search"],//,不要那个js入口文件
      // chunks:["main"],
      minify:{
        //删除index.html的注释
        removeComments:true,
        //删除index的空格
        collapseWhitespace:true,
        //删除各种html标签属性值的双引号
        removeAttributeQuotes:true
      }
    }),
    
    new HtmlWebpackPlugin({
      template: "./search.html",
      filename:'search.html',//打包后的多html文件的名字
      chunks:["search"]//要哪个js入口文件
    })
  ]

13.4 Webpack的应用

1.处理CSS文件【js文件引入css文件】

1.1 下载loader【css-loader让webpack识别css文件,style-loader处理css文件】

npm install --save-dev css-loader@4.1.1

npm install --save-dev style-loader@1.2.1

1.2 配置【多个loader使用use后是数组或对象,loader处理的是只使用一个时后是字符串】

多个处理的loader使用use后即可是个对象也可是一个数组,执行顺序是从右往左

//处理css文件的loader
{
    test:/.css$/,
    // loader:"css-loader",
    // 多个处理的loader使用use后即可是个对象也可是一个数组,执行顺序是从右往左
    use:["style-loader","css-loader"]
}

2.使用file-loader 处理CSS中的图片【本地的需要处理,远程不需要处理】

2.1 下载loader【file-loader认识图片,路径处理mini-css-extract-plugin插件】

npm install --save-dev file-loader@6.0.0

npm install -D mini-css-extract-plugin@0.9.0

2.2 配置

如果是外部资源,是不需要考虑webpack的,只有本地的图片才需要被webpack处理

const MiniCssExtractPlugin=require("mini-css-extract-plugin")
​
new MiniCssExtractPlugin({
    filename:"css/[name].css"
})
//处理css文件的loader
{
    test:/.css$/,
    // loader:"css-loader",
    // 多个处理的loader使用use后即可是个对象也可是一个数组,执行顺序是从右往左
    // use:["style-loader","css-loader"],
    use:[{
        loader:MiniCssExtractPlugin.loader,
        options:{
            publicPath:'../'
        }
        },"css-loader"],
​
},
// 处理css文件的图片
{
    test:/.(jpg|jpeg|png|webp|gif)$/,
    // loader:"file-loader",
    use:{
        loader:"file-loader",
        options:{
            name:"img/[name].[ext]"
        }
    }
​
}

3.使用file-loader 处理JS中的图片

3.1 安装【file-loader】

npm install --save-dev file-loader@6.0.0

3.2 配置
{
    test:/.(jpg|jpeg|png|webp|gif)$/,
    // loader:"file-loader",
    use:{
        loader:"file-loader",
        options:{
            name:"img/[name].[ext]",//打包后的路径
            esModule:false//html的图片不按照es6模块导出只是拿值
    }
}
},

4.使用html-withimg-loader 处理HTML中的图片

4.1安装loader【html-withimg-loader解析html文件,处理图片的具体还是使用file-laoder】

npm install --save-dev html-withimg-loader@0.1.16

npm install --save-dev file-loader@6.0.0

4.2配置
{
     test:/.(jpg|jpeg|png|webp|gif)$/,
     // loader:"file-loader",
     use:{
       loader:"file-loader",
       options:{
         name:"img/[name].[ext]",
         esModule:false//html的图片不按照es6模块导出只是拿值
       }
     }
},
{
     test:/.(html|htm)$/,
     loader:"html-withimg-loader"
}

5.使用url-loader处理图片

file-loader ===>css===>img

html-withimg-loader===>html===>img

file-loader===>js===>img

url-loader依赖于file-loader

5.1 安装

npm install --save-dev url-loader@4.1.0

5.2 配置
{
    test:/.(jpg|jpeg|png|webp|gif)$/,
    // loader:"file-loader",
    use:{
    loader:"url-loader",
        options:{
            name:"img/[name].[ext]",
            esModule:false,//,html的图片不按照es6模块导出只是拿值
            limit:10000//小于10k转base64格式
        }
    }
},

6.使用webpack-dev-server搭建开发环境

6.1 安装

npm install --save-dev webpack-dev-server@3.11.0

6.2 配置

devServer配置查看网址:www.webpackjs.com/configurati…

"scripts": {
    "build": "webpack --config webpack.config.js",
    "dev":"webpack-dev-server --open chrome"
  },
  
devServer:{
    //所有来自 dist/ 目录的文件都做 gzip 压缩和提供为服务
    contentBase: path.join(__dirname, "dist"),
    compress: true,
    port: 9000
}
6.3 整体webpack.config.js文件
//package.json文件
{
  "name": "webpacks",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "dev":"webpack-dev-server --open chrome"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@babel/cli": "^7.10.5",
    "@babel/core": "^7.11.0",
    "@babel/preset-env": "^7.11.0",
    "babel-loader": "^8.2.5",
    "core-js": "^3.6.5",
    "css-loader": "^4.1.1",
    "file-loader": "^6.0.0",
    "html-webpack-plugin": "^4.3.0",
    "html-withimg-loader": "^0.1.16",
    "mini-css-extract-plugin": "^0.9.0",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}
​
​
​
//webpack.config.js文件
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
​
​
//模式,入口,出口,模块,插件
module.exports = {
  mode: "development",
​
  // entry: "./src/index.js",//单入口
  entry: {
    main: "./src/index.js",
    search: "./src/search.js"
  },//多入口output: {
    path: path.resolve(__dirname, 'dist'),
    // filename: "bundle.js"//,这是单文件出口
    filename: "js/[name].js"//,这是多文件出口
  },
​
  module: {
    rules: [
      //处理es6语法的loader
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      //处理css文件的loader
      {
        test: /.css$/,
        // loader:"css-loader",
        // 多个处理的loader使用use后即可是个对象也可是一个数组,执行顺序是从右往左
        // use:["style-loader","css-loader"],
        use: [{
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '../'
          }
        }, "css-loader"],
​
      },
      // 处理css文件的图片
      // {
      //   test:/.(jpg|jpeg|png|webp|gif)$/,
      //   // loader:"file-loader",
      //   use:{
      //     loader:"file-loader",
      //     options:{
      //       name:"img/[name].[ext]",
      //       esModule:false//html的图片不按照es6模块导出只是拿值
      //     }
      //   }
      // },
      {
        test: /.(jpg|jpeg|png|webp|gif)$/,
        // loader:"file-loader",
        use: {
          loader: "url-loader",
          options: {
            name: "img/[name].[ext]",
            esModule: false,//,html的图片不按照es6模块导出只是拿值
            limit: 10000//小于10k转base64格式
          }
        }
      },
      //处理html的loader
      {
        test: /.(html|htm)$/,
        loader: "html-withimg-loader"
      }
    ]
  },
​
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
      filename: 'index.html',//打包后的多html文件的名字
      excludeChunks: ["search"],//,不要那个js入口文件
      // chunks:["main"],
      minify: {
        //删除index.html的注释
        removeComments: true,
        //删除index的空格
        collapseWhitespace: true,
        //删除各种html标签属性值的双引号
        removeAttributeQuotes: true
      }
    }),
    new HtmlWebpackPlugin({
      template: "./search.html",
      filename: 'search.html',//打包后的多html文件的名字
      chunks: ["search"]//要哪个js入口文件
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css"
    })
  ],
  devServer: {
    //所有来自 dist/ 目录的文件都做 gzip 压缩和提供为服务
    contentBase: path.join(__dirname, "dist"),
    compress: true,
    port: 9000
  }
​
}

\