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:1,1:2,2: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)//false
const 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'))//xabab
console.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=39
export {
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
3.使用Babel前的准备工作
3.1 什么是Node.js和npm
Node.js是一个平台或者工具,对应浏览器
后端的Javascript = ECMAScript + IO + File + ...等服务器端操作
npm:node包管理工具
3.2 安装Node.js
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)】
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处理各种模块,而插件则可以用于执行范围更广的任务
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
}
}
\