ES6
模板字符串
- 模板字符串格式
var age = 33;
var str= = `我叫陈哥`
console.log(`我叫陈哥,今年${age}`)
- 模板字符串特点
let
-
什么是let?
let用于代替var,用于声明变量的新关键词,不会存在变量提升 -
var的两个缺点
- 变量声明提前,打乱了程序正常的执行顺序
- 没有块级作用域,代码块内的变量,会影响块外的变量
- 变量提升举例
for(let i = 0;i<10;i++){
console.log(i)//0-9
}
console.log(i)//报错
//以后写程序都用let不用var 只要声明变量都用let代替var
-
let原理
-
let会被自动翻译为匿名函数自调
-
双保险,let为了进一步避免可能的冲突将变量悄悄改了名字:let t = 0 —> let _t = 0;
-
let特点
-
全局写let t=0,用window.t却找不到
-
同一级作用域内,不能在 let t=0 前,提前使用t。即使外层作用域有t变量,也不行
-
相同作用域内,不允许重复let同一个变量
-
注意事项:let有个兄弟const,专门用于声明一个值不能修改的常量。同时也具有let的特点,不会被声明提前,添加了块级作用域
-
案例:
//第一步获得button按钮
var btn = document.getElementsByTagName("button");
// 用var
//{}并不是块级作用域,全局依然可以找到
//使用var声明的变量时在全局下,当我们开始找的时候,就已经在全局之下执行完成for循环了
for(var i=0;i<btn.length;i++){
btn[i].οnclick=function(){
console.log(`点击了第${i+1}个按钮`)
}
}
// 用let
for(let j=0;j<btn.length;j++){
btn[j].οnclick=function(){
console.log(`点击了第${j+1}个按钮`)
}
}
箭头函数
- 箭头函数如何使用
- 去掉 function,在 () 和 {} 之间加 =>
- 当形参只有一个的时候可以省略()
- 当函数内部是return时,可以省略return不写
- 当函数内部只有一条语句时,可以省略{}(就像if语句省略{}一个道理)
- 数组排序
//funtion(){}
var arr = [1,2,5,3,4,6];
arr.sort((a,b)=>{
return a-b
})
//当只有一个参数的时候可以省略()
console.log(arr)
- 简化:
//箭头函数
function show(a){
a(1)
}
// show(function(b){
// console.log(b)
// })
//简化
// show((b)=>{
// console.log(b)
// })
//简化
show(a=>console.log(b))
- 什么时候使用?
几乎所有回调函数都可以用 => 来简化
注意:一定要记得去掉结尾的分号 - 箭头函数的问题:
箭头函数是一把双刃剑,箭头函数内外this是相通的。如果希望函数内外的this保持一致时,可以不用bind()换,而直接改为箭头函数即可。如果不希望函数内外的this一致,就是希望内部的this和外部的this不一样时,不能改为箭头函数
//this->window
var lilei={ //不是作用域
sname: "Li Lei",
friends:["欣欣","峰峰","丁丁"],
// say:function(){
say(){
// 今后ES6中所有对象的方法不用加":function"
// 但是这个简写不改变this,绝不等于箭头函数
// Li Lei认识欣欣
// Li Lei认识峰峰
// Li Lei认识丁丁
// 遍历lilei好友列表中每个朋友
this.friends.forEach(
// function(f){console.log(`${this.sname}认识${f}`)}
//函数内的this指向window,用bind改变this的指向
f=>console.log(`${this.sname}认识${f}`)
// 希望this和say中的this保持一致都指lilei
)
}
}
lilei.say();
var button = document.getelementbyid("button")
button.addEventListener(functon(){
console.log(this)
}//this指向button
button.addEventListener(()=>{
console.log(this)
}//this指向window
for of
var li = document.querySelectorAll("#ul>li");
var str = "hello word";
var arr = [2,4,5,9,3];
var obj={
name:"kiki",
age:12
}
for(var key of arr){
console.log(key)
}
- 对比循环
- for循环
- arr.forEach()
- for of 局限:无法获得当前遍历的位置;是ES6的功能,不是所有浏览器,所有数组和类数组对象都支持
for of和for循环一样的,可以遍历数组、类数组对象、字符串,但是不能遍历对象和关联数组 - for in 只能遍历关联数组和对象,in其实不仅遍历当前对象自己的成员,且会延着原型链向父对象继续查找所有可用的成员名
-
for循环、for of、forEach三种遍历,哪种效率高?
答:一样高。其实只有一种遍历,就是for循环。其他的都是封装的for循环而已。所以,ES5的其他遍历函数 indexOf、every、some、forEach、 map、filter 和 reduce,都没有效率的提升,因为内部都是for循环而已,只是语法简单而已 -
是不是有了ES6,就不用ES5了?
答:要看浏览器是否兼容ES6。如果使用框架和微信开发,基本上可以使用最新的语法。因为框架中和微信开发中都自带翻译工具(Babel),在项目打包时,会将ES6代码翻译成ES5代码,保证浏览器兼容性。如果没使用框架,使用原生或jquery开发,则尽量不要使用ES6
参数增强
- 参数默认值
- 什么是参数默认值(default)?
ES6允许为函数定义中形参列表的最后一个参数定义默认值,如果没有传递实参,则自动调用形参的默认值
function fn(a,b,c=0){…} - 使用
定义:function 函数名(形参1,形参2,…,形参n=默认值)
提示:如果调用函数时,没有提供最后一个实参,就自动用默认值代替
//形参默认值
function add(a,b,c=10){
console.log(a+b+c)
}
add(1,2,3)//6
add(1,2)//13
调用函数时,如果没有传入最后一个实参值,则自动使用默认值代替
兼容性写法:
function add(a,b,c){
//兼容性写法
c=c||10
//或者写 c === undefined && (c=10)
console.log(a+b+c)
}
add(1,2,3)//6
add(1,2)//13
案例
//点餐案例
function order(zhushi,xiaochi,yinliao="可乐"){
console.log(`您点的套餐为:
主食:${zhushi}
小吃:${xiaochi}
饮料:${yinliao}
`)
}
order("米饭","鸡腿")
- 剩余参数
专门代替arguments的语法,代替arguments处理不确定参数个数的情况
- 定义函数时
从头到尾获得所有实参值
function fun(…数组名){…}
提示:…的意思是”收集”,把传给函数的所有实参值都收集到…后的数组中保存
//剩余参数
var arr=[]
function order(...arr){
console.log(arr)
}
order("套餐1","套餐2","套餐3")
剩余参数可以只收集部分实参值
function 函数名(形参1,形参2,…数组名){…}
提示:…数组名 收集的是除了之前形参1和形参2对应的实参值之外的所有剩余的实参值
注意:保存剩余实参值的结构是一个纯正的数组,可用数组家全部APi
var arr=[]
function order(taocan1,taocan2,...arr){
arr.forEach(function(elem,i){
console.log(elem+i)//元素值+下标
})
}
order("套餐1","套餐2","套餐3","套餐4")//套餐32套餐43
案例1:使用剩余参数语法求和
var arr=[];
function add(...arr){
console.log(arr)
var result = arr.reduce(function(prev,elem,i,arr){
return elem+prev;//prev是前一个参数,elem是当前参数值
})
return result;
}
console.log(add(1,2,3,4))//10
案例2:计算员工薪资,包含姓名和基本工资(bonus),以及其他来源工资(送外卖,跑滴滴,兼职…),使用剩余参数来实现
var arr=[];
function add(name,jibengongzi,...arr){
var result = arr.reduce(function(prev,elem){
return prev+elem
})
var sum=result+jibengongzi
// return sum;
console.log(`${name}的总工资是${sum}`)
// return `${name}的总工资是${sum}`
}
// console.log(add("lili",2000,1,2,3,4))
add("lili",2000,1,2,3,4)
//用for循环做
var arr=[];
function add(name,jibengongzi,...arr){
// var result = arr.reduce(function(prev,elem){
// return prev+elem
// })
// var sum=result+jibengongzi
// //用for循环做
for(var i=0;i<arr.length;i++){
var result=result+arr[i]
}
var sum = result+jibengongzi;
// return sum;
console.log(`${name}的总工资是${sum}`)
// return `${name}的总工资是${sum}`
}
// console.log(add("lili",2000,1,2,3,4))
add("lili",2000,1,2,3,4)
add("bibi",3000,1,2,3,4,5)
-
打散数组
什么是打散数组?将一个数组打散为多个元素值,依次传给函数的多个形参变量,代替apply
为什么使用打散数组?
apply()也能打散数组参数,再传给要调用的函数。但是,apply()的本职工作是修改this,然后顺便帮忙打散数组。如果今后我们只想单纯的打散数组,跟this无关时。apply()就不好用了。如果用apply()单纯打散数组,必须给定一个无意义的对象作为第一个参数
Math.max.apply( ? ,arr)
var arr=[4,6,2]
console.log(Math.max.apply(null,arr))//(this指针的方向,数组)
什么时候使用打散数组?
当一个函数不支持数组参数,而给定的实参值却是放在一个数组中的时候,都要先打散数组,再使用多个值
Math.max(…arr)
…先将数组打散为单个值,再传入前边的函数。好处是,…和this无关,只是专门单纯的打散数组
使用打散数组
(要考)
函数名(…数组)
复制数组:
第一种:var arr1 = arr.slice();
第二种: var arr1=[…arr]
合并两个数组
第一种: var arr3=[].concat(arr1,arr2)
第二种: var arr3=[…arr1,…arr3]
浅克隆一个对象
var obj1 = {…obj}
合并两个对象
第一种: var obj3 = Object.assign({},obj1,obj2)
将obj1和obj2打散后,所有的属性,都放入第一个参数空对象中返回
Object.assign方法用于对象的合并,第一个参数是目标对象,后面的参数都是源对象
第二种: var obj3={…obj1,…obj2}
笔试题:
-
…不是打散的意思吗?这里怎么成了收集?
定义函数时,形参列表中的…是收集不确定个数的实参值的意思。调用函数时,实参值列表中的…是打散数组实参为多个实参值,分别传入的意思
-
…作为打散数组放在实参列表中时,不一定放在结尾?
是的,可以和其他单独的实参值混搭
解构
-
什么是解构(destruct)?
将一个大的对象或数组中集中存储的成员中的个别成员,取出来单独使用 -
为什么要使用解构?
将来从服务器端返回的对象或数组可能很复杂 -
什么时候使用解构?
凡是获得结构嵌套非常复杂的对象,都要先解构成多个小的成员,再分别单独使用
- 数组解构:
同时声明多个变量,下标对下标,去数组中找到和当前变量所在下标一致的元素值,保存到对应位置的变量中,从此变量1和变量2就可代表数组中的单个元素,单独使用
var [cg,zx,qg]=[[1,2,3],[4,5,6],[7,8,9]]
// 0 1 2
console.log(cg)//[1,2,3]
//不声明第三个变量,交换两个变量的值
var change = [1,2];
var [a1,b1]=change;//[a,b]
change = [2,1];
var [a1,b1]=change;
console.log(a1)//2
- 对象解构
var {name:name,sex:sex}={
name="cg",
sex="女"
}
- 什么是对象解构?
仅提取出复杂对象中的个别成员属性或方法,保存在变量中,单独使用 - 简写
简写形式:如果属性名和变量名相同,只写一个即可
var {sname:sname, signin:signin}=lilei
var {sname, signin}=lilei
var obj={
name:"陈小玲玲",
sex:"女",
say1:function(){
console.log(`我是${name}`)
},
say2:function(){
console.log(`我是个${sex}的`)
}
}
var {sex:sex,say1:say1,name:name}=obj//对象解构顺序无限制
console.log(sex)
say1()//我是陈小玲玲
- 参数解构
function xiaomeng({
name:name,
age:age//当属性名和形参名一致时才可以简写:name,age,简写只能出现在函数定义中,函数调用时必须使用函数参数解构 的完整形式
}){
console.log(name,age)
}
//调用
xiaomeng({
name:"叶梦",
age:18,
})
//点餐系统
function order({zhushi="小龙虾鸡肉卷",xiaochi="香辣鸡翅",yinliao="可乐"}){
console.log(`
您点的套餐为:
主食:${zhushi}
小吃:${xiaochi}
饮料:${yinliao}
`)
}
//默认情况下,一个都不选择,直接选择套餐
order({})
//想要换掉饮料
order({
yinliao:"奶茶"
})
//想要换掉小吃和饮料
order({
xiaochi:"鸡米花",
yinliao:"奶茶"
})
order({
zhushi:"米饭",
xiaochi:"鸡腿",
yinliao:"雪碧"
})
其实就是对象解构在函数传参时的应用而已
class封装
- 使用
// 1. 用class{}包裹构造函数和原型对象方法
class Student{
// 2. 构造函数名提升为整个类型class名,所有构造函数统一更名为constructor
constructor(sname,sage){
this.sname=sname;
this.sage=sage;
}
// 3. 所有原型对象方法可省略一切前缀和=function。定义在class中的普通函数,默认就是保存在原型对象中的
intr(){
console.log(`我是 ${this.sname}, 我今年 ${this.sage} 岁`)
}
}
var stu=new Student("lili",22)
//class封装
class Student{
//构造函数的名以class进行提升
//所有构造函数中的变量,统一保存在constructor中
constructor(name,sex){
this.name=name,
this.sex=sex
}
//所有原型对象中的属性和方法都省略 xxx.prorotype.xxx=function
init(){
console.log(`${this.name}是个大镁铝,今年${this.sex}岁`)
}
}
var stu = new Student("陈小玲玲",19)
stu.init()
注意:在clas中强烈不推荐共有属性
但是我们还是写一下
//class封装
class Student{
constructor(name,age){
this.name=name;
this.age=age;
if(Student.prototype.sex==undefined){
Student.prototype.sex="女"//所有构造函数里的属性,将来一律都会成为每个子对象的自有属性
}//在构造函数中,向原型对象中添加共有属性。强调一点,要先判断原型对象中如果没有这个共有属性,才强行添加。如果已经有这个共有属性了,则不要再重复添加
Student.prototype.ClassName="大四2班"//在原型上添加,但是,我们每次执行的时候都会重复执行
}
}
console.log(stu.ClassName)
var stu1 = new Student("cxll",20)
stu1.ClassName="高一6班"
console.log(stu1.age)//20
console.log(stu1.ClassName)//高一6班
- 访问器属性(用来保护子对象)
"use strict";
class Emp{
constructor(eid,ename,eage){
this.eid=eid;
this.ename=ename;
this.eage=eage;
// 要保护的是将来要创建的子对象
Object.defineProperties(this,{
eid:{ writable:false },
// 1. 定义一个隐姓埋名的半隐藏的数据属性
_eage:{
value:eage,
writable:true,
enumerable:false
}
})
// 密封将来要出生的子对象
Object.seal(this);
}
// 向该类型的原型对象中添加一个访问器属性eage,包含两个保镖函数get和set
get eage(){
return this._eage;
}
set eage(value){
if(value>=18&&value<=60){
this._eage=value
}else{
throw Error("年龄必须介于18~60之间")
}
}
}
var lili=new Emp(1001, "lili", 25);
// 试图修改eid属性
// lili.eid=0;
// 试图删除ename属性
// delete lili.ename
// 试图修改eage为-2
// lili.eage=-2;
console.log(lili);
var xinxin=new Emp(1002,"xinxin",26);
// 试图修改eid属性
// xinxin.eid=0;
// 试图删除ename属性
// delete xinxin.ename
// 试图修改eage为-2
// xinxin.eage=-2;
console.log(xinxin);
清楚案例:
class Student{
constructor(name){
this.name=name;
this.age=20;
//添加年龄
Object.defineProperties(this,{
_age:{
value:this.age,
writeable:true,
configurable:false
}
})
if(Student.prototype.sex==undefined){
Student.prototype.sex="女"
}
}
//访问
get age(){
return this._age;
}
//设置
set age(value){
if(value>=18&&value<=80){
return this._age = value
}else{
throw Error("超出范围")
}
}
init(){
console.log(`${this.name}是个大镁铝,今年${this.age}岁`)
}
}
var stu1 = new Student("cxll")
console.log(stu1.age)
- class继承
//1.我们得有一个父对象,父对象包汉子对象想要的东西(相同的属性和结构)
class Father{
constructor(money,house){
this.money=money;
this.house=house
}
say(){
console.log(`我是你爸爸,我真伟大`)
}
}
//2.继承
class Child extends Father{
}
var child1 = new Child(1000,"you");
console.log(child1.money)
飞机大战
super:通过super把X,Y的值传递给父对象,而super本身相当于调用父对象的构造函数
extends附赠了一个关键词super,super指向父类型的构造函数。调用super,就等效于调用父类型的构造函数了
//飞机大战数据结构
//敌军或奖励的位置
class Diren{
constructor(x,y){
this.x = x;
this.y = y;
}
fly(){
console.log(`敌军位于(${this.x},${this.y})的位置`)
}
}
//击落敌军得分
class Score extends Diren{
constructor(x,y,score){
super(x,y)
//通过super把X,Y的值传递给父对象,而super本身相当于调用父对象的构造函数
//extends附赠了一个关键词super,super指向父类型的构造函数。调用super,就等效于调用父类型的构造函数了
this.score = score;
}
show(){
console.log(`敌军在(${this.x},${this.y})位置被击落,获得${this.score}分`)
}
}
//奖励
class Award extends Diren{
constructor(x,y,award){
super(x,y)//指向
this.award=award;
}
show(){
console.log(`在(${this.x},${this.y})的位置获得${this.award}的奖励`)
}
}
// var dijun1 = new Diren(100,100)
// dijun1.fly()
//开始进行击落
var jiluo = new Score(100,200,10)
console.log(jiluo.show())
//奖励得分
var jiangli=new Award(200,200,"激光炮")
jiangli.show()
jiangli.fly()