ES6
1 ES6入门
阮一峰书籍推荐:https://es6.ruanyifeng.com/#docs/reference
1.1 简介
ECMAScript标准简称为ES6也称为ES2015,规定以后的ECMAScript标准每一年一更,统一以年份命名(每年的六月份)
1.2 块级作用域
通过块级声明的变量无法被代码块外部访问,这种就称为块级作用域,也成为语法作用域
块级作用域可以在函数内部和代码块{}内部创建
1.3 let和const
{
var a=2;
let b=10;
变量b只能在代码块内访问
console.log(a)//2
console.log(b)//10
}
console.log(a)//2
console.log(b)//b is not defined
let特点:
- let和const不存在变量提升,原因let和const之前的区域叫做暂存性死区,var的变量存在变量提升
- let和const声明的变量不再属于window,不能通过window.变量名访问,var声明的变量属于window
- 使用块级作用域不能在同一级中重复声明变量(let声明的变量不能重复)
const特点:
- const和let,除了const是声明常量外,其他的特点和let一样
- const声明的常量只能在声明的时候赋值
- const声明的常量不可以修改
练习:实现点击输出当前下标
*{margin: 0;padding: 0;}
ul{list-style: none; margin: 50px auto; width: 200px;}
li{
width: 200px;
height: 50px;
background-color: rgb(126, 235, 116);
margin: 10px 0;
}
<ul>
<li></li>
<li></li>
<li></li>
</ul>
ES5:
var li=document.getElementsByTagName('li');
for(var i=0;i<li.length;i++){
li[i].index=i;
li[i].onclick=function(){
console.log(this.index)
}
}
ES6:
//普通函数版
var li=document.getElementsByTagName('li');
console.log(li)
for(let i=0;i<li.length;i++){
li[i].onclick=function(){
console.log(i)
}
}
//箭头函数版
var li=document.getElementsByTagName('li');
console.log(li)
for(let i=0;i<li.length;i++){
li[i].onclick=()=>{
console.log(i)
}
}
2 ES6函数新增特性
2.1带默认值的函数
JavaScript函数最大的特性就是在传递参数时,参数的个数不受限制,为了函数的健壮性,一般在函数内部做默认值处理
ES6中从语法层面新增了‘默认值’的支持(默认值可以为数字、字符串、表达式甚至为函数)
ES5:
function fun(a,b){
a= a || 10;
b= b || 30;
console.log(a,b)//10 30
}
// fun(10,30)//10 30
fun(0,0)//出现bug,得到10,30
ES6:
function demo(a=10,b=30){
console.log(a,b)//10 30
}
// fun(10,30)//10 30
demo(0,0)//0,0
2.2 默认值影响arguments
ES5中arguments用于接收所有传入的实参,返回的是一个数组
ES5:
//非严格模式下,形参重新赋值,会影响arguments里面的值
function fun1(a,b){
console.log(arguments)//[3,6]
console.log(arguments[0]===a)//true
console.log(arguments[1]===b)//true
a=33;
b=20;
console.log(arguments[0]===a)//true
console.log(arguments[1]===b)//true
console.log(arguments)
}
fun1(3,6)
//严格模式
function fun2(a,b){
//使用严格模式,形参重新赋值,不会影响arguments里面的值
'use strict'
console.log(arguments)
console.log(arguments[0]===a)//true
console.log(arguments[1]===b)//true
a=33;
b=20;
console.log(arguments[0]===a)//false
console.log(arguments[1]===b)//fasle
console.log(arguments)
}
fun2(3,6)
fun1()结果:
fun2()结果:
ES6:
//在ES6中若有参数默认值,不管是在严格模式还是非严格模式,都和ES5中的严格模式相同
function fun3(a,b=1){
console.log(arguments)
console.log(arguments[0]===a)//true
console.log(arguments[1]===b)//true
a=33;
b=20;
console.log(arguments[0]===a)//false
console.log(arguments[1]===b)//false
}
fun3(3,6)
fun3()结果:
注:
“use strict” 的目的是指定代码在严格条件下执行。
严格模式下你不能使用未声明的变量。
2.3 默认参数为表达式
参数的默认值可以是一个表达式或者函数的调用
function getValue(){
return 5;
}
function add(first,seconed=getValue()){
return first+seconed;
}
var res = add(1)
console.log(res)//6
//注意:getValue()只会在调用add函数且不传入第二个参数时才会去调用
//由于默认值可以是表达式,所以我们可以使用前面的参数作为后面参数的默认值
//反之则不成立(不能采用后面的参数定义表达式作为参数默认值)
function add(first=2,seconed=first+1){
return first + seconed;
}
var res = add(3);
console.log(res)//7
var res = add();
console.log(res);//5
2.4 剩余参数(重点)
ES6中提供了一种更加便捷处理未命名参数的方法,叫做剩余参数
语法:
function fun(a,...b){
[剩余参数用三个点接收,多余的参数则被放到数组b中]
}
function run(a,b,...c){
//arguments接收的是所有传入的实参
console.log(arguments)//[1, 2, 3, 4, 5, 6, 7, 8]
//...剩余参数只接收剩余的形参
console.log(a,b)//1 2
console.log(c)// [3, 4, 5, 6, 7, 8]
}
run(1,2,3,4,5,6,7,8)
注意:
- 函数最多只能有一个剩余参数,且该参数必须放置在所有形参的最后
- 虽然有剩余参数,arguments仍然存在
- 剩余参数是在**“函数声明的时候”**出现
2.5 扩展运算符(重点)
1 扩展运算符拆散数组或对象
var arr=[1,2,3,4,5,6]
//Math.max()只能接收一个一个的数
var res=Math.max(1,2,3,4,5)
console.log(res)//6
//ES5中需要通过apply切换对象取得最大值
var res =Math.max.apply(Math,arr)
console.log(res)//6
//ES6中...扩展运算符可以获得数组中的一个一个的数,相当于拆散了数组或对象
console.log(...arr)//1 2 3 4 5 6
console.log(Math.max(...arr))//6
2 扩展运算符连接两个数组
var arr1=['胡桃','七七'];
var arr2=[1,2,3,4,5]
//ES5
var newArr=arr2.concat(arr1)
console.log(newArr)//[1, 2, 3, 4, 5, "胡桃", "七七"]
//ES6
var arr3=[1,2,3,4,5,...arr1]
console.log(arr3)//[1, 2, 3, 4, 5, "胡桃", "七七"]
3 扩展运算符继承的使用
var People={
nation:'中国',
city:'北京'
}
var student={
//继承父类
...People
}
console.log(student)//{nation: "中国", city: "北京"}
People.city='上海'
console.log(People)//{nation: "中国", city: "上海"}
console.log(student)//{nation: "中国", city: "北京"}
var People={
nation:'中国',
city:'北京',
foods:['烤鸭','抄手','小面']
}
var student={
//继承父类
...People
}
People.foods.push('刀削面')
console.log(People.foods)// ["烤鸭", "抄手", "小面", "刀削面"]
console.log(student.foods)// ["烤鸭", "抄手", "小面", "刀削面"]
通过上面的实例可以看出,…扩展运算符的继承是实现的浅拷贝操作
2.6 结构赋值(重点)
ES6允许我们按照一定的模式从数组或者对象中提取变量进行赋值,被称为结构赋值
本质上,结构赋值属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
//ES5的赋值,分别取出数组的值,赋值给对应的变量
var arr=[1,2,3];
var a=arr[0];
var b=arr[1];
var c=arr[2];
console.log(a,b,c)
//ES6数组的结构赋值主要依据相同下标进行赋值
var [a,b,c]=[10,20,30];
console.log(a,b,c);//10 20 30
var [d,,e]=[10,20,30]
console.log(d,e);//10 30
//对象的结构赋值主要查看属性名和方法名
//只有当变量名和属性名同名时,才能取到正确的值,和先后顺序无关
var People={
nation:'中国',
city:'北京',
foods:['烤鸭','抄手','小面']
}
var {nation,city}=People
console.log(nation,city)//中国 北京
var {city,nation}=People
console.log(nation,city)//中国 北京
//结构赋值常见的使用方式
function demo({city}){
//相当于{city}=People
console.log(city)//北京
}
demo(People)
3 箭头函数(重点)
3.1 语法
ECMAScript6 允许使用箭头 =>定义函数,语法如下
(arg1,arg2...)=>{
函数体
}
示例1:
- 当箭头函数的形参只有一个时,包含形参的()可以省略,其他情况都不能省略
- 当箭头函数后只有一行代码且不加大括号{},则自带return 功能
//普通函数
//ES5
var fun=function(){
console.log(111)
}
//ES6
var fun=()=>{
console.log(111)
}
fun()
//带参数的函数
var fun1=function(a){
return a
}
var fun1=(a)=>{
return a
}
//当箭头函数的形参只有一个时,包含形参的()可以省略,其他情况都不能省略
//当箭头函数后只有一行代码且不加大括号{},则自带return 功能
var fun1=a=>a
var res=fun1(3)
console.log(res)//3
示例2:
特殊情况:return返回值为一个对象
//特殊情况:return返回值为一个对象
//ES5
var fun2=function(){
// var obj={
// name:'胡桃',
// age:16
// }
return {
name:'胡桃',
age:16
};
}
//ES6
var fun2=()=>{
// var obj={
// name:'胡桃',
// age:16
// }
return {
name:'胡桃',
age:16
};
}
//特殊情况,得在对象{}之外再加一个()
var fun2=()=>({ name:'胡桃',age:16})
var res=fun2();
console.log(res);//{name: "胡桃", age: 16}
示例3:
使用箭头函数自执行
//使用箭头函数自执行
//ES5
(function(){
console.log('匿名函数')
})();
//ES6
(()=>{
console.log('匿名函数')
})();
3.2 箭头函数中无this绑定
在ES5中this的绑定是一个比较麻烦的问题,稍微不注意就达不到想要的效果,因为在ES5中this的绑定和定义的位置无关,只和其调用的位置有关
ES5中this指代问题:
- 在普通函数中,this指代全局对象window
- 在普通对象中,this指代当前对象
- 在构造函数中,this指代实例化对象
- 在apply、call、bind切换对象时,this指代切换后的对象
箭头函数中没有this的绑定(并不是说ES6没有this),this的定义只和定义的作用域有关,和调用的位置无关
// ES5的写法
btn.onclick = function(){
// 定时器 2s后输出
setTimeout(function(){
console.log(this)//window
},2000)
// ES6 箭头函数
btn.onclick = function(){
setTimeout(()=>{
console.log(this);//btn按钮
},2000)
}
箭头函数可以让this指向固定化,这样有利于面向对象封装回调函数
//构造函数
function Dog(name,color){
this.name = name;
this.color = color;
}
Dog.prototype.doSomething = function(){
document.onclick =()=>{
console.log(this)//Dog
console.log(this.color)//yellow
}
}
var dog = new Dog('大黄','yellow');
console.log(dog)
dog.doSomething()//yellow
总结:
1、注意箭头函数中this指向固定化,并不是因为箭头函数内部有绑定this,实际原因时箭头函数内根本没有绑定this,导致函数内部的this就是外部代码块的this
2、箭头函数作为一个用完就扔的函数,不能作为构造函数使用,也就是说不能使用new的方式来使用箭头函数
3、由于箭头函数中的this与函数的作用域无关,所以不能使用call、apply、bind来重新绑定this
4、箭头函数内没有自己的arguments对象,但是箭头函数内可以使用外部的arguments对象
4 对象的扩展功能
在js中,几乎所有的类型都是对象,所以使用好对象,对提高js性能很重要
4.1 对象类别(了解)
普通对象(ordinary object)拥有js所有的默认行为
特异对象(exotic object)某些内部行为与默认有所差异
标准对象(stabdard object)是ES6中定义的对象,例如Array Date等,他们可能是普通对象也可能是特异对象
内置对象(built-in object)指js的运行环境开始运行时已存在的对象,标准对象均为内置对象
4.2 字面量语法扩展(重点)
4.2.1 简化属性的初始化
var name ='七七'
var age=16
var obj={
name:name,
age:age
}
console.log(obj)//{name: "七七", age: 16}
//改进
var name ='七七'
var age=16
var obj={
name,
age
}
console.log(obj)//{name: "七七", age: 16}
注意:在ES6中,若对象属性名和本地变量名相同时,赋值可以省略冒号和值,直接写属性
当对象字面量中属性只有属性名时,js引擎会在其作用域之内寻找是否有和属性名同名的变量,简写可以让对象的字面量更加简洁。
4.2.2 对象方法的简写
var name ='七七'
var age=16
var obj={
name,
age,
//对象方法简写
play(){
console.log('僵尸')
}
}
obj.play()//僵尸
4.2.3 属性名提前
var key1='adas_sd'
var key2='aacc_a'
var obj={
key1:12312,
key2:123456,
//变量名作为属性名 需要加上中括号
[key1]:12312,
[key2]:123456,
}
console.log(obj)//{key1: 12312, key2: 123456, adas_sd: 12312, aacc_a: 123456}
4.3 对象中新增的方法
4.3.1 Object.is()
Object.is()判断两个对象是否相等,该方法是对全等(===)的怪异行为的补偿,只有在两者的类型和值都相等时才会判断为相等
console.log(+0 == -0);//true
console.log(+0 === -0);//true
console.log(Object.is(+0,-0));//false
console.log(NaN == NaN)//false
console.log(NaN === NaN)//false
console.log(Object.is(NaN,NaN))//true
一般情况下Object.is()的效果和===(全等)是相同,他们的区别就是在怪异情况的判断 +0 -0 NaN
4.3.2 Object.assign()
使用Object.assign()主要是为了简化对象的混合(mixin),混合指的将一个对象引入到另一个对象的属性或者方法(拷贝)
var obj={
name:'胡桃',
age:16,
love:['七七','钟离','烟绯']
}
var newObj={}
//obj对象复制给newObj对象
Object.assign(newObj,obj)
console.log(newObj)//{name: "胡桃", age: 16, love: Array(3)}
obj.love.push('凝光')
console.log(obj.love)//["七七", "钟离", "烟绯", "凝光"]
console.log(newObj.love)//["七七", "钟离", "烟绯", "凝光"]
//以上说明Object.assign() 方法为浅拷贝
//Object.assign()可以同时拷贝多个对象
var obj2={
name:'甘雨',
love:'雷军'
}
//newObj有多个提供者
Object.assign(newObj,obj,obj2)
console.log(obj2)//{name: "甘雨", love: "雷军"}
console.log(newObj)//{name: "甘雨", age: 16, love: "雷军"}
多个提供者,意味着后面的提供者若同名则覆盖前面的提供者的属性
注意:
Object.assign()的这种copy(拷贝)是浅拷贝
浅拷贝指的是拷贝的属性的属性值不是基本数据类型(数组\对象)时
拷贝只是copy该对象的内存地址
5 字符串新增功能
5.1 查找字符串
在ES5中字符串的查找,都是使用indexOf() lastIndexOf()
ES6新增了三个方法来查找字符串
includes(text,index) 查找字符串是否在文本中,返回boolean值。存在->true 不存在->false
startsWith(text,index)文本是否以指定的字符开头,返回boolean值
endsWith(text,length)文本是否以指定的字符结尾,返回boolean值
注意:
length代表总长度从1开始计算
index代表下标从0开始计算
var str ='大丘丘病了二丘丘瞧,三丘丘采药,四丘丘熬';
var res=str.includes('大丘丘');
console.log(res)//true
var res=str.includes('大丘丘',2);//从2下标开始查找
console.log(res)//false
var res=str.startsWith('大丘丘');
console.log(res)//true
var res=str.startsWith('二丘丘',5);
console.log(res)//true
var res=str.endsWith('大丘丘');
console.log(res)//false
var res=str.endsWith('大丘丘',3);
console.log(res)//true
5.2 repeat()方法
//repeat(n) 重复前面字符串的次数
var str='胡桃';
var res=str.repeat(3)
console.log(res)//胡桃胡桃胡桃
5.3 字符串的模板字面量(重点)
模板字面量主要是ES6针对js的如下功能的改进:
多行字符串
基本的字符串格式化:将字符串中的变量转换为值的能力
转义HTML:将字符串进行转义并使用安全得插入Html的能力
1.语法
使用一对反括号 `` (Tab键正上方) 来表示模板字面量
2.多行字符串
//字符串的模板字面量 ``
//报错
// var str='胡桃
// 甘雨
// 钟离'
var str=`胡桃
甘雨
钟离`
console.log(str)//按原文本格式输出
3.字符串的置换(重点)
//字符串的置换
var obj={
name:'胡桃',
age:16,
area:'璃月'
}
//ES5 字符串拼接
var str=obj.name+'今年'+obj.age+'岁,住在'+obj.area
console.log(str)//胡桃今年16岁,住在璃月
//ES6 模板字面量
var str=`${obj.name}今年${obj.age}岁,住在${obj.area}`
console.log(str)//胡桃今年16岁,住在璃月
4.模板标签
模板字面量真正的强大之处在于模板标签,一个模板标签可以被转换为模板字面量并作为最终值返回(标签必须在模板之前指定)
模板标签其实并不是模板而是一种函数的调用的特殊形式,标签指的就是函数,紧跟在后面的模板字符串就是他的参数
//模板标签
alert `璃月人`//等价于 alert('璃月人')
5.自义定模板标签
function myTag(str,...value){
//str 拿到除了变量以外的字符
console.log(str)//["", "今年", "", raw: Array(3)]
//剩余参数拿到所有字符串中的变量,以数组的方式返回
console.log(value)//["胡桃", 18]
}
var name = '胡桃'
var age = 18
myTag`${name}今年${age}`;
6 新的基本类型 Symbol
ES5中有六种数据类型:
- number数值类型
- string字符串类型
- boolean布尔类型
- undefined未定义类型
- null空类型
- object对象类型(function)
ES6新增了一种数据类型Symbol
解决:在ES5中我们对象的属性为字符串,容易被重写。没有办法创建私有属性,只能通过封装。Symbol创建一个独一无二的值
6.1 创建Symbol
Symbol在基本数据类型中比较特殊,需要通过全局函数Symbol创建
//创建Symbol
var names= Symbol();
console.log(typeof name)//symbol
var obj={
[names]:'胡桃',
age:16
}
console.log(obj)//{age: 16, Symbol(): "胡桃"}
var a1=Symbol();
var a2=Symbol();
console.log(a1==a2)//false
console.log(a1===a2)//false
var obj={
[Symbol()]:'独一无二'
}
console.log(obj)//{Symbol(): "独一无二"}
//此时Symbol()重新创建了另一个独一无二的值,则obj内该属性未被赋值,则为undefined
console.log(obj[Symbol()])//undefined
在创建symbol时,括号内允许传入字符串,该字符串仅为了区分symbol,没有任何实际作用
var s1 = Symbol('七七')
var s2 = Symbol('七七')
console.log(s1 === s2)//false
注意:s1 s2都是一个独一无二的值
若属性名使用symbol类型,可以保证属性名都是独一无二的,不会与其他属性名冲突
6.2 识别Symbol
symbol属于基本数据类型,可以使用typeof操作符辨别是否为symbol。
如果在控制台直接输出symbol变量,那么不好区分,建议在创建symbol变量时,添加参数(字符串),用于描述创建的symbol作为区分
6.3 Symbol作为对象属性(重点)
//symbol作为对象的属性
var user=Symbol('user');
var age=Symbol('age');
//方法一
var person={
// 变量名作为属性 需要添加中括号
[user]:'胡桃'
}
console.log(person)//{Symbol(user): "胡桃"}
//方法二
person[age]=16
console.log(person)//{Symbol(user): "胡桃", Symbol(age): 16}
var sex;
person[sex]='女'
for(var key in person){
console.log(person[key])//女
console.log(`属性:${person[key]},属性值:${person[key]}`)//属性:女,属性值:女
}//以上例子说明:Symbol作为属性名时,不能采用for...in 或者for...of遍历
注意:
1、symbol作为对象的属性时,只能使用[]去添加或访问,不能使用点语法
2、symbol作为对象属性名使用时,该属性还是公有属性,不是私有属性。但此时无法通过for…in 或者for…of遍历
6.4 Symbol属性名的遍历
//遍历Symbol Object.getOwnPropertySymbols():将所有对象中的symbol属性以数组方式返回
var res=Object.getOwnPropertySymbols(person)
console.log(res)//[Symbol(user), Symbol(age)]
console.log(res[0])//Symbol(user)
console.log(person[res[0]])//胡桃
//Reflect.ownKeys():将所有对象中的属性以数组方式返回
var keys=Reflect.ownKeys(person);
console.log(keys)//["undefined", Symbol(user), Symbol(age)]
说明:由于Symbol的值作为属性名时,不会被常规方法遍历到(for…in for…of),我们利用该特性,为对象创建一些非私有但是希望只用于内部的方法
6.5 Symbol.for()和Symbol.keyFor()的使用
Symbol.for(字符串):每次创建时会在全局环境中搜索以该字符串作为参数的Symbol的值,若搜到则返回该Symbol,若搜索不到才创建一个Symbol,并把它注册到全局环境中
//Symbol和Symbol.for的区别
var a1=Symbol('胡桃')
var a2=Symbol('胡桃')
console.log(a1==a2)//false
//Symbol.for()每次创建都会在全局环境中搜索以该字符串作为参数的Symbol是否存在,若存在则直接返回已创建的Symbol,不存在则重新创Symbol,并向全局进行注册
var b1=Symbol.for('七七')
var b2=Symbol.for('七七')
console.log(b1==b2)//true
//Symbol.keyFor(symbol)返回一个已注册到全局的symbol的key
//只有注册的Symbol才会返回字符串
var res=Symbol.keyFor(b1)
console.log(res)//七七
Symbol.for()和Symbol()的区别:
1、Symbol.for()对同样的字符串每次得到的结果肯定一样,Symbol()则不会有相同的结果,每次都会新创建。
2、Symbol()每次都会创建一个全新的Symbol,且不会向全局注册;Symbol.for()只有在全局找不到时才创建新的Symbol,并注册到全局
6.6 Symbol总结
//1.创建独一无二的值
//2.给对象属性添加唯一值,先创建并赋值,加上[]
//3.作为对象属性,不能被for ...in遍历到
//4.Symbol每次创建不会注册到全局,Symbol.for()先查找再注册
7 Set
set本身是一个构造函数
//创建
var set =new Set()
//添加
set.add('胡桃')
//长度
console.log(set.size)
7.1 创建Set集合并添加元素
var set=new Set();
console.log(set)//Set(0)
console.log(typeof set)//object
//添加元素 add()
set.add('胡桃')
console.log(set)//Set(1) {"胡桃"}
//set集合长度
console.log(set.size)//1
7.2 Set不能添加重复元素
set.add('a');
console.log(set);//{"a", "b"}
console.log(set.size);//2
注意:set是通过Object.is()方法判断两个元素是否相等(但是判断+0 -0的时候用===判断)
set.add(+0);
set.add(-0);
console.log(set)//{"a", "b", "c", 0}
特殊情况:
// 特殊情况,数组、对象这种引用类型 每次添加都是指向不同的内存空间
console.log(set.size)//1
set.add([])
console.log(set.size)//2
set.add([])
console.log(set.size)//3
7.3 数组去重
var arr=[2,3,5,6,2,5,3]
var set =new Set(arr);
console.log(set)//[2,3,5,6]
7.4 判断某值是否在Set中
// 判断Set中是否存在某一个值 has()
var arrSet = new Set([2,3,4])
console.log(arrSet.has(5))//false
console.log(arrSet.has(2))//true
7.5 删除Set中的值
delete(需要删除的值)
clear()清空所有的值
var set= new Set([1,2,5,3,6,4])
//删除指定的值
set.delete(1)
console.log(set)//{2,5,3,6,4}
//清空所有的值
set.clear()
console.log(set)//{}
7.6 Set遍历
Set遍历智能使用隐式迭代,也就是forEach
补充知识点:
数组遍历(迭代):
显示迭代 for循环
隐式迭代 forEach
语法:
遍历的数组.forEach(function(val,key,ownerArr){
//forEach中的形参不需要手动传入
参数一:val代表遍历到的元素的值
参数二:数组的键(下标) 但是对于Set类型,第二个参数和第一个的值相同
参数三:当前遍历的整个对象
})
set.forEach(function(val,key,ownARR){
console.log(key,val,ownARR)
})//输出的结果没有下标,所以不能通过for遍历
//改为箭头函数
set.forEach((val,key)=>{
console.log(`key为:${key},值为:${val}`)
})//输出的结果没有下标,所以不能通过for遍历
7.7 将Set转为数组(重点)
数组转换为Set:创建Set时把数组当作一个参数传入
Set转换为数组:扩展运算符 …
//数组去重
var arr=[1,2,2,3,5,4,2,6]
var set=new Set(arr)
//将Set集合转为数组
// 方法一
var newArr=[...set]
console.log(newArr)// [1, 2, 3, 5, 4, 6]
// 方法二
console.log(Array.from(set))// [1, 2, 3, 5, 4, 6]
8 Map
在ES5中,对象的属性只能字符串
ES6中的map数据类型包含一组有序的键值对,其中键和值可以为任意的数据类型
8.1 创建Map
1、Map对象的创建同样使用Map构造函数
2、Map的存储键值对使用set(key,value)
3、通过get(key),来获取指定的key对应的value
var map=new Map();
console.log(map)//Map(0)
set(key,val)添加指定的键值对
//set()添加属性和属性值
map.set(5,50)
map.set(true,'满分')
console.log(map)//Map(2) {5 => 50, true => "满分"}
get(key)获取指定key对应的value值
//get()获取指定属性的值
console.log(map.get(true))//满分
// 若key不存在则返回undefined
console.log(map.get(liyue));//undefined
8.2 Map和Set类似的三个方法
has(key) 判断给定key是否在map中存在
delete(key) 删除map中指定的key及其对应的值
clear() 移除map中所有的键值对
size map对象的长度
8.3 初始化Map(了解)
map可以像Set一样传入一个数组,但是传入的数组必须为两两是一个子数组(二维数组)
var initMap = new Map([
['name','甘雨'],
['age','18'],
[4,false]
])
console.log(initMap)//Map(3) {"name" => "甘雨", "age" => "18", 4 => false}
console.log(initMap.size)//3
8.4 Map遍历
var mapper=new Map();
mapper.set(1,'胡桃')
mapper.set(2,'甘雨')
mapper.set(3,'七七')
mapper.forEach((val,key)=>{
console.log(`key:${key},val:${val}`)
})
for(var key of mapper){
console.log(key)
}
9 Promise
9.1入门问题
1 什么是promise?
promise是异步编程的一种解决方案,传统的解决方案为:回调函数和事件,promise比传统的解决方案更合理。promise是由社区提出的。
- 简单来说,promise就是一个容器,里面装的有某个未来才会结束的事件,一般为异步事件。
- promise是一个对象,可以从对象中获取异步操作的消息,promise提供了同意的API,各种异步操作都可以采用同样的方法进行处理。
异步同步?
同步:代码从上到下执行,必须等前面的代码执行完了才执行后面的代码。JS中大部分代码都是同步的。
异步:代码执行到异步代码片时,异步代码片后面的代码可以和异步代码片同时执行。
JS常见的异步:
- 定时器 setTimeout,setInterval
- Ajax的异步请求
- jQuery中的animate()动画
- ES6中的Promise
2 promise三状态有哪些?
进行中(Pending),已完成(Resolved),已失败(Rejected)
3 promise优点?
- 有了promise对象,可以把异步操作以同步操作的流程表现出来,避免层层嵌套的回调函数。
- promise提供了统一的API,使得异步操作更加容易。
4 promise缺点?
- 无法取消,一旦建立则立即执行,无法中途取消。
- 如果不设置回调函数,promise内部抛出错误,不会反应到外部。
- pending(进行中)状态下时,无法得知目前进行到那个阶段了。
5 promise有哪些特点?
- 对象的状态不受外界的影响,promise代表一个异步操作。
- 有三种状态:Pending(进行中)、Resolved(已完成)和Rejected(已失败)。
- 只有异步的结果可以决定当前是哪一种状态,任何操作都不能改变结果。
- 一旦状态改变,就不会再变。任何时候你都可以得到该结果。
- Promise的状态只有两种可能:pending->resolved或者pending->rejected。只要这两两种情况发生了状态改变,该结果则凝固,不会再改变,一直保留该结果。
9.2 promise 的基本用法
1 语法
ES6规定Promise对象是一个构造函数,用于生成Promise实例
promise的基本使用语法:
const promise = new Promise(function(resolve,reject){
...(some code)
//判断异步操作是否成功
if(/*异步操作成功*/){
resolve(value)
}else{
reject(error)
}
})
说明:promise接受一个函数作为参数,该函数两个参数分别为resolve和reject
他们两个函数(resolve和reject)由js引擎提供,不需要自己部署
'resolve'函数的作用:将Promise状态从'未完成'变成'完成',在异步操作时调用
并将异步调用的结果作为参数传递出去
'reject'函数的作用:将Promise状态从'未完成'变成'失败',在异步操作时调用
并将失败的错误信息作为参数传递出去
promise.then(function(value){
//成功时函数
},function(error){
//失败时函数
})
说明:'then'方法可以接收两个回调函数作为参数
第一个回调函数是Promise对象变成resolved状态时调用
第二个回调函数是Promise对象编程rejected状态时调用
其中第二个回调的参数是可选值,不一定会提供
Promise.prototype.catch()接收promise抛出的错误
promise.catch(function(error){
console.log(error)
})
2 随机数案例
const promise=new Promise(function(resolve,reject){
//执行相关的异步操作
let num=Math.random()*2;
// var str=num>1 ? '调用成功': '调用失败';
let flag=num>1 ? true: false;
if(flag){
// resolve('随机数大于1')
setTimeout(resolve, 1000,'随机数大于1');
}else{
// reject('随机数小于1')
setTimeout(reject, 1000,'随机数小于1');
}
})
promise.then(
function(data){
//当resolve方法被调用时执行
console.log(data)
},
function(err){
//当reject方法被调用时执行
console.error(err)
})
3 在线视频加载案例
<div class="wrap"></div>
//异步请求视频
let url='https://f.video.weibocdn.com/002akCN7gx07PPZAb9cX01041201dcf50E010.mp4?label=mp4_1080p&template=1920x1080.25.0&trans_finger=d88af6227b88881484d2b59dfbafa136&media_id=4681647124316211&tp=8x8A3El:YTkl0eM8&us=0&ori=1&bf=3&ot=h&ps=3lckmu&uid=34tsO1&ab=3915-g1,5178-g1,3663-g0,5786-g0,966-g1,1493-g0,1192-g0,1191-g0,3601-g17,1258-g0&Expires=1631935806&ssig=fFWuNnPpa5&KID=unistore,video'
const promise=new Promise((resolve,reject)=>{
let video=document.createElement('video');
video.src=url
video.controls='controls'
//onloadedmetadata当视频加载元数据成功后执行
video.onloadedmetadata=function(){
resolve(video)
}
video.onerror=function(){
reject('视频加载失败')
}
})
promise.then((data)=>{
console.log(data)
let wrap=document.querySelector('.wrap');
wrap.appendChild(data)
},(err)=>{
console.error(err);
})
//将上面代码封装
function loadVideoSync(url){
return new Promise((resolve,reject)=>{
let video=document.createElement('video');
video.src=url
video.controls='controls'
//onloadedmetadata当时拍加载元数据成功后执行
video.onloadedmetadata=function(){
resolve(video)
}
video.onerror=function(){
reject('视频加载失败')
}
})
}
let promise=loadVideoSync(url)
promise.then((data)=>{
console.log(data)
let wrap=document.querySelector('.wrap');
wrap.appendChild(data)
},(err)=>{
console.error(err);
})
结果:
成功:
失败:
4 请求本地json案例
function AjaxSync(url){
return new Promise((resovle,reject)=>{
//1.创建ajax对象
let ajax=new XMLHttpRequest()
//3.监听状态改变
ajax.onreadystatechange=function(){
if(ajax.readyState==4 && ajax.status==200){
//4.成功则调用resolve
// console.log(ajax.responseText)
resovle(ajax.responseText)
}
}
//2.发起get请求
ajax.open('get',url,true)
ajax.send(null)
})
}
const promise =AjaxSync('wearth.json')
promise.then((data)=>{
var data =JSON.parse(data)
console.log(data)
},(error)=>{
console.error(error)
})
wearth.json数据:
{"desc":"OK","status":1000,"data":{"wendu":"22","ganmao":"风较大,较易发生感冒,注意防护。","forecast":[{"fengxiang":"北风","fengli":"5-6级","high":"高温 24℃","type":"晴","low":"低温 11℃","date":"3日星期六"},{"fengxiang":"北风","fengli":"4-5级","high":"高温 19℃","type":"晴","low":"低温 8℃","date":"4日星期日"},{"fengxiang":"无持续风向","fengli":"微风","high":"高温 21℃","type":"晴","low":"低温 9℃","date":"5日星期一"},{"fengxiang":"无持续风向","fengli":"微风","high":"高温 21℃","type":"多云","low":"低温 10℃","date":"6日星期二"},{"fengxiang":"无持续风向","fengli":"微风","high":"高温 24℃","type":"晴","low":"低温 12℃","date":"7日星期三"},{"fengxiang":"无持续风向","fengli":"微风","high":"高温 23℃","type":"晴","low":"低温 11℃","date":"8日星期四"}]}}
该代码需要在服务器上运行,我使用的WampServer,把代码拷贝到WampServer的www目录下,在浏览器url路径中输入localhost,再进入对应的代码文件。输出结果为:
10 async异步
ES2017 标准引入了async函数,使异步操作更加方便
async语法中,可以指定函数返回一个then方法添加的回调函数,当函数执行时,一旦遇到await就先返回等待异步的操作完成之后,再去执行await后面的语句
// async的使用
async function getData(){
// let promise=new Promise(function(resolve,reject){
// setTimeout(resolve,2000,'网路请求成功');
// })
return new Promise((resolve,reject)=>{
setTimeout(resolve,2000,'网路请求成功');
})
}
async function showAsync(){
let promise=getData();
//await等待异步执行的结果后调用
await promise.then((data)=>{
console.log(data)
},(error)=>{
console.log(error)
})
//如果上面不加await,则会直接输出'111',过两秒后才输出'网络请求成功'
//如果上面加了await,则输出的'111'要等待上面的'网络请求成功'完成后才执行
console.log(111)
}
showAsync();
async的多种函数使用函数
//标准函数
async funtion foo(){
}
//函数表达式
var foo = async function(){
}
//对象内的方法
let obj = {
async foo(){}
}
//箭头函数
const foo = async ()=>{}
async的错误处理机制
有时前一个异步操作失败也不要中断后面的异步操作,则可以把await放在try…catch结构。那么不管该异步操作是否成功,第二个await都会执行
主要功能:使用该方法用来防止出错的写法
语法:
try{
需要执行的代码
}catch(error){
//error自动获取失败的错误信息
console.error(error)
}
async function foo(){
try{
let value = await new Promise(fun)
let data = await value.getData();
}catch(error){
console.error(error)
}
}
11 class类
什么是class(类)?
对某一种事物的总称
js其实不是严格意义上的面向对象的语言,因为js中没有类的概念。
在ES5以前生成实例对象主要通过构造函数,(构造函数就是一个普通函数),这种代码风格和传统的面向对象语言差异较大。
ES6提供了一种更接近于传统语言的写法,引入Class这个概念,作为模板定义构造函数。ES6中class只能被认为是一个语法糖,他的绝大部分功能,ES5都可以完成。只是class让对象原型模式更加清晰,更加面向对象。
11.1 基本使用
//ES5中定义一个构造函数
function Animal(name,color){
this.name=name;
this.color=color;
this.say=()=>{
console.log('喵~')
}
}
let dog=new Animal('小米','白色')
console.log(dog)//Animal {name: "小米", color: "白色", say: ƒ}
//ES6中定义一个类
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`你好我是${this.name},我今年${this.age}岁了!`)
}
//注意这里面有简写比如:
// constructor:function(){}直接省略了':function'
// say:function(){}直接省略了':function'
}
let ming=new Person('小名',16)
ming.say()//你好我是小名,我今年16岁了!
11.2 class中的私有化
私有化方法:只能在内部访问
公有方法:在外部可以正常访问
常见的几种方式:
方式一:在方法名前加_
注意这种方法只是行业规则,程序员自己标记这个方法为私有方法,希望其他人使用的时候不要调用。但是我们还是可以在外部访问到
//ES6
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`你好我是${this.name},我今年${this.age}岁了!`)
}
//定义私有方法
//方法一,方法名前添加_
_goHome(){
console.log(`${this.name}准备回家!`)
}
}
方法二:将私有方法移除
//ES6
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`你好我是${this.name},我今年${this.age}岁了!`)
//方法二:将私有方法移除,在say()方法中调用
//通过apply,call,bind切换为当前实例化对象
goHome.call(this)
}
}
//方法二:将私有方法移除
function goHome(){
console.log(`${this.name}准备回家!`)
}
let ming=new Person('小名',16)
ming.say()//你好我是小名,我今年16岁了!
//小名准备回家!
方式三:使用Symbol(推荐)
//ES6
let gooHome=Symbol()
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`你好我是${this.name},我今年${this.age}岁了!`)
this[gooHome]()//?
}
[gooHome](){
console.log('内部方法')
}
}
let ming=new Person('小名',16)
//调用symbol()创建的私有方法
ming[gooHome]()//内部方法
11.3 class中this指向(了解)
class方法内部如果含有this,默认指向类的实例(new实例化出来的对象),一旦单独使用该方法,则可能报错
class Demo{
constructor(name){
this.name = name;
}
print(){
console.log(this.name)
}
}
var demo = new Demo('胡桃');
demo.print()//
// 结构赋值
let {print} = demo
// console.log(print)
// print()//报错
print中的this默认指向demo实例,如果将其单独使用,则this指代当前的运行环境
解决方案:箭头函数
class Demo{
constructor(name){
this.name = name;
this.print = ()=>{
console.log(this.name)
}
}
}
var demo = new Demo('胡桃');
console.log(demo)
demo.print()
// 结构赋值
let {print} = demo
print()
11.4 class中的继承(重点)
class的继承主要通过’extends’关键字继承
class Animal{}
class Dog extends Animal{}
继承父类Person的所有属性
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`我的名字是${this.name},我今年${this.age}岁了!`)
}
}
//继承父类Person的所有属性
class stu extends Person{
}
var ming=new stu('小明',16)
ming.say()//我的名字是小明,我今年16岁了!
继承父类Person的所有属性,并且还加新的属性
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`我的名字是${this.name},我今年${this.age}岁了!`)
}
}
class stu extends Person{
// 若子类不需要添加新的属性,则不需要写构造函数
constructor(name,age,score){
super(name,age);
this.score=score;
}
sayScore(){
console.log(`${this.name}的成绩为${this.score}分`)
}
toSay(){
//调用父类的方法使用super和this
super.say()
}
}
var ming=new stu('小明',16,98)
ming.toSay()//我的名字是小明,我今年16岁了!
ming.sayScore()//小明的成绩为98分
12 导入导出
export导出文件
import引入文件