ES6 全称ECMAScript 6 也叫ECMAScript2015
ES6的主要技术
1、变量、常量
2、取值赋值、解构
3、运算符的扩展、展开运算符、指数运算符
4、字符串的扩展
5、数组的扩展
6、函数的扩展
7、Set单值集合
8、Map键值对集合
9、对象的扩展
10、生成器和迭代器与迭代器接口
11、反射Reflect
12、代理Proxy
13、Promise异步处理、async与await(ES7)
14、ES6的模块化开发ESModule
15、CommonJS模块化
之前我们在ES5里面,我们定义变量使用的是var关键字,这个关键字有几个特点
1、var 没有数据类型
2、var 是有一个建立阶段
3、var 是没有作用域的,它要通过函数才能形成作用域
var a = 1; //没有数据类型的,所以任何类型的数据都可以赋值
//var没有作用域
{
var a = "哈哈"
}
console.log(a);
//var有一个建立阶段
console.log(c);
var c = 123;
var定义的变量是不够严谨的
ES6推出了let用于定义变量
1、let定义的变量也没有数据类型
2、let没有建立阶段,必须先定义再使用
3、let定义的变量是有作用域的,以大括号作为作用域
let定义变量是有作用域的,并且不能再定义之前调用
let a = "hello";
{
console.log(a); //这里是没有问题的,因为内部的可以调用外部的变量
}
let b = "world";
{
console.log(b); //这里就形成了一个暂时性死区,b变量调不出来
let b = "hello";
}
常量里面的数据是不能改变的,比如,Math.PI
const关键字声明常量,它具备上面的let的所有特点,同时不能改变值
const a = "hahaha";
a = "hehehehe"; //报错
首先,我们先要知道内存中的数据结构分为 栈、堆、表、链
如果const声明的常量里面保存的是一个引用数据,那么根据锁栈不锁堆的原则,我们是可以改变数据中的属性值的,但是无法覆盖之前的整个数据
const常量定义的时候一定记得初始化,不然没有任何意义
var btn = document.querySelectorAll("button");
for(var i = 0; i < btn.length;i++){
btn[i].addEventListener("click",function(){
console.log(i);
})
}
ES5闭包解决
for(var i = 0; i < btn.length;i++){
btn[i].addEventListener("click",(function(j){
var j = i;
return function(){
console.log(j)
}
})(i))
}
ES6闭包解决
var btn = document.querySelectorAll("button");
for(let i = 0; i < btn.length;i++){
btn[i].addEventListener("click",function(){
console.log(i);
})
}
一种特殊的取值赋值的方式
解构取值:
1、数组解构取值
var arr = ["张三","李四"];
// arr[0];
// arr[1];
let [a,b] = arr;
console.log(a);
console.log(b);
对于复杂的数组可以实现深度解构
var arr = ["张三","李四",["王五","赵六"]];
// arr[0];
// arr[1];
let [a,b,[c,d]] = arr;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
解构实现变量置换
let a = 10;
let b = 20;
[a,b] = [b,a];
console.log(a,b);
var userInfo = {
userName:"张三",
age:19,
sex:"男"
}
let {userName,age,sex} = userInfo;
let userName = "张三";
let age = 18;
let userInfo = {
userName,
age
}
在ES6里面,新增了展开运算符,所有实现了Iterable接口的都可以使用展开运算符,目前来讲在系统中ES里面实现了这个接口的数据如下
1、数组
2、NodeList
3、HTMLCollection
4、Set
5、Map
6、arguments
7、字符串
展开运算符使用 … 来完成运算
var arr = [1,2,3,4,5,6,7];
console.log(...arr);
//展开运算获取最多的数字
var max1 = Math.max(...arr);
console.log(max1);
//数组的深拷贝
var a = 1;
var b;
b = a;
var newArr = [...arr];
//实现数组的合并
var arr1 = ["A","b","c","d"];
var arr2 = [...arr,...arr1];
let obj = {
userName:"张三",
age:18
}
let obj1 = {
hobby:"sleep"
}
//Object.assgin()
//实现对象的合并
let obj2 = {
...obj,
...obj1
}
模板字符串,模板字符串自带格式,你格式什么样子打印出来就是什么样子
var str = `亲爱的张三:
你好!
恭喜你,已中本公司大奖,价值9999元的大礼包直接抗回家!
你只需要缴纳1999元的保证金就可以领取大奖
电话:1234566789
联系人:李四
XXX公司
XXXX年XX月XX日`
console.log(str);
模板字符串另外一个特点可以快速实现字符串拼接
let nickName = "张三";
let tel = 110;
let connectPerson = "李四"
let company = "画大饼公司"
var str = `亲爱的${nickName}:
你好!
恭喜你,已中本公司大奖,价值9999元的大礼包直接抗回家!
你只需要缴纳1999元的保证金就可以领取大奖
电话:${tel}
联系人:${connectPerson}
${company}公司
XXXX年XX月XX日`
console.log(str);
模板字符串内部可以使用 ${ } 在大括号里面可以写任意的JS代码
function abc(){
console.log("haha")
}
abc`` //可以替代函数调用时使用的()
1、include() 判断当前字符串中是否包含某一个字符串,以前可以使用indexOf方法
2、repeat() 生成一个重复字符串
3、trimStart() / trimEnd() 去除开始与结束的空格
1、Array.of()
声明数组的方法,它可以解决一个问题 new Array() 所存在的问题
var arr = new Array(2); //代表这个数组的长度是2
var arr1 = Array.of(2);
2、Array.from() 把类数组转成数组
var lis = document.querySelectorAll("li");
let arr = Array.from(lis);
3、fill() 填充数组的方法
参数1:需要填充的数据
参数2:需要从索引几开始填充
参数3:填充到索引几
4、flat(steps)拍平一个多维数组
以前我们需要通过递归+遍历,现在有这个方法就简单多了
参数steps:表示拍平到第几个维度,也可以传入Infinity,不管几维都给拍平
5、find() 在数组中查找一个元素,如果找到返回找到的第一个元素,如果找不到返回undefined
let list = [
{
age:18
userName:"张三",
},
{
userName:"李四",
age:20
},
{
userName:"王五",
age:21
}
]
let obj = list.find(function(item,index,_arr){
//返回后面可以写一个匹配条件
return item.userName == "张三"
})
6、findIndex() 查找某一个元素的索引,这个方法的用法和上面find一样
of的遍历与in的遍历是相同的,但是for…in是一个有序遍历(有索引的时候依靠索引遍历),而for…of是一个无序遍历(遍历的时候不需要索引)
for(var i of list){
console.log(i);
}
注意:
for…of是基于属性值来遍历,所以并不是所有的对象都可以用它来遍历,如果要使用for…of来遍历,则这个对象必须要实现Iterable接口
Set是ES6中新增的一种数据结构,它是一个单值集合,没有属性名(没有索引,没有key),并且是一个不重合的集合
let s1 = new Set();
let s2 = new Set(["a","b","c","d"]);
let s3 = new Set(["a","b","a","c","d"])
1、add() 给集合添加元素
s3.add("haha").add("hehe")
2、delete() 向Set集合中删除一个元素,返回布尔值
3、has() 判断set集合中有没有这个元素,返回布尔值
4、size属性,表示当前set集合的元素个数
5、clear() 清空set集合
6、values() 它是一个生成器方法,返回当前set集合中的迭代器
7、keys()和values方法一样,但是set没有键,所以返回的依然是values的结果
let arr1 = [1,2,3,4,5,5,6]
let s1 = new Set();
let s2 = new Set(["a","b","c","d"]);
let s3 = new Set(["a","b","a","c","d"])
let s4 = new Set(arr1)
for(let i of s3){
console.log(i);
}
//将set集合的数据转换成数组的数据,来进行调用,同时还可以达到去重的操作
let arr = [...s1];
map是键值对形式的一个储存,它结合set的特点(储存速度快),也解决了set集合取值慢的特性
map集合当中的键不允许重复的,但是值可以
let m1 = new Map([
["a","张三"],
["b","李四"],
["c","王五"],
[3,{
userName:"哈哈"
}],
[4,function(){
console.log("hello")
}]
])
1、键不能重复,值可以
2、键与值可以是任意数据类型
1、size属性
2、set(key,value)向当前map集合新增元素
3、get(key)通过一个键获取map集合当中的一个值
4、delete() 向Map集合中删除一个元素,返回布尔值
5、clear() 清空Map集合
6、values() 它是一个生成器方法,返回当前Map集合中的迭代器
7、keys()生成器方法,返回当前map集合的key-value的集合
for(let i [k,v] of m1){
console.log(k,v)
}
之前在ES5中我们学习了哪些函数
以上是我们在ES5中接触和学习到函数形式,但是它们本质上来讲没有区别,之所以有那么多种叫法完全出于调用方式的不同
官方叫法成员函数,属性函数
在之前的ES5当中,所有通过function定义的东西都叫函数,而所以通过function声明的函数都具备构造函数的特点,也就是可以被new调用
let obj = {
name:"haha",
sayHello:function(){
console.log("hello")
}
}
obj.sayHello(); //普通调用
new obj.sayHello(); //构造函数调用
在上面的代码中,我们可以看到sayHello方法本是obj对象的,它就应该通过obj.sayHello() 来调用,而不应该用new来调用
上面这种情况就会产生一些不必要的歧义,ES6就改变了这种函数的定义方式,直接抛弃了关键字function
let obj = {
name:"haha",
sayHello(){
console.log("hello")
}
}
obj.sayHello(); //普通调用
new obj.sayHello(); //报错
抛弃了function关键字来声明的一个函数声明方式
//无参箭头函数
const sayHello1 = () => {
console.log("hello");
}
//有一个参数的箭头函数
const sayHello2 = userName => {
console.log(userName);
}
//有多个参数的箭头函数
const sayHello2 = (userName,age) => {
console.log(userName,age);
}
//箭头函数的返回值
const a = () => {
return "hello"
}
//如果只有一句话要执行,可以省略大括号
const sayHello2 = (userName,age) => console.log(userName,age);
//省略return关键字返回数据
const b = (x,y) => x + y
省略return,举例:
let newArr = arr.filter(item => item % 2 == 0)
1、箭头函数不具备构造函数的特点,不能用new调用
2、箭头函数是没有arguments
3、箭头函数内部没有this,如果写了this它绑定的是外边的this
之前我们声明函数可以放置参数
function sayHello(userName,age){
age = age || 18;
console.log(age);
}
在上面的代码种,我们可以看到,如果要设置一个参数的默认值,我们需要使用逻辑运算在函数内部进行一个计算才行,但是ES6可以直接设置参数的默认值
function sayHello(userName,age=18){
console.log(age);
}
多个参数也行
const sayHello = (userName="张三",age=18) => {
console.log(age);
}
在ES6里面,我们的箭头函数没有arguments,但是我们向实现类似于实参列表的功能怎么办?
const sayHello = (userName,hobby) => {
console.log(`大家好,我叫${userName}`);
console.log(`我的爱好是${hobby}`)
}
sayHello("张三","看书","睡觉")
代码分析:
上面的代码我们实现了打印用户爱好的功能,但是如果有多个爱好怎么办?
这里使用剩余参数
const sayHello = (userName,...hobby) => {
console.log(`大家好,我叫${userName}`);
console.log(`我的爱好是${hobby.join("-")}`)
}
sayHello("张三","看书","睡觉","打游戏","吃饭")
简单来说,剩余参数就是利用展开运算符在参数中声明了一个没有长度的数组
在上面的函数扩展当中,无论是新增的成员函数还是箭头函数,都不能使用构造函数的new去调用了,那么如果我们要在ES6的新语法中定义一个构造函数怎么办?ES6推出了class关键字
class Student{
//constructor是在new调用的一瞬间,自动执行
constructor(userName,age,sex){
this.userName = userName;
this.age = age;
this.sex = sex;
}
sayHello(){
console.log(this.userName)
}
}
let stu1 = new Student("张三",20,"男");
let stu2 = new Student("李四",18,"女")
在ES6中访问器属性的功能得到的了扩展
class Person {
constructor(firstName,lastName){
this.firstName = firstName;
this.lastName = lastName;
}
userName(){
return this.firstName + this.lastName;
}
}
let p1 = new Person("张","三");
console.log(p1.userName())
上面的代码,有个问题,我们的userName表现出来的还是一个方法,不是一个属性
class Person {
constructor(firstName,lastName){
this.firstName = firstName;
this.lastName = lastName;
}
get userName(){
return this.firstName + this.lastName;
}
set userName(v){
this.firstName = v[0];
this.lastName = v.slice(1)
}
}
let p1 = new Person("张","三");
分析:
get / set 共同修改了访问器属性,get在取值的时候自动调用,set在赋值的时候自动调用,set的参数就赋的值
let obj = {
firstName:"张",
lastName:"三",
get userName(){
return this.firstName + this.lastName;
}
set userName(v){
this.firstName = v[0];
this.lastName = v.slice(1)
}
}
class Student{
//constructor是在new调用的一瞬间,自动执行
constructor(userName,age,sex){
this.userName = userName;
this.age = age;
this.sex = sex;
}
static sayHello(){
console.log(this.userName)
}
}
代码分析:
对于class里面的普通方法,我们需要先new一个对象出来,然后通过这个对象来调用里面的方法
对于class里面的静态方法,我们可以直接使用构造函数名来调用
静态的方法不能使用非静态的属性和方法
这个是通过extends关键字来直接实现class的继承
class Person{
sayHello(){
console.log("我是一个人")
}
}
class Student extends Person {
study(){
console.log("我在学习")
}
}
let s1 = new Student();
分析:
上面的代码中,我们让Student继承自Person,同时两个构造函数内部都没有参数,所以可以把构造器省略掉
class Person{
constructor(userName,age){
this.userName = userName;
this.age = age;
}
sayHello(){
console.log("我是一个人")
}
}
class Student extends Person {
constructor(userName,age,sex){
super(userName,age);
this.sex = sex;
}
study(){
console.log("我在学习")
}
}
let s1 = new Student("张三",20,"男");
代码分析:
如果要构造函数接收参数,则构造器一定要手写,写的时候注意,super一定要先写出来,this要在super后面
之前我们说过,class里面除了普通属性和方法之外,还有static静态属性和方法,静态的是否可以被继承?
class Person{
constructor(userName,age){
this.userName = userName;
this.age = age;
}
static hobby = "睡觉";
static sayHello(){
console.log("我是一个人")
}
}
class Student extends Person {
constructor(userName,age,sex){
super(userName,age);
this.sex = sex;
}
study(){
console.log("我在学习")
}
}
let s1 = new Student("张三",20,"男");
代码分析:
1、静态方法和属性是可以被继承的
2、静态与非静态之间还是隔离的,就算被继承了也是隔离的
3、静态方法可以调用静态属性
class Person{
constructor(userName,age){
this.userName = userName;
this.age = age;
}
static hobby = "睡觉";
static sayHello(){
console.log("我是一个人")
}
abc(){
console.log("Person")
}
}
class Student extends Person {
constructor(userName,age,sex){
super(userName,age);
this.sex = sex;
}
study(){
console.log("我在学习")
}
abc(){
console.log("Student");
}
parentAbc(){
super.abc();
}
}
let s1 = new Student("张三",20,"男");
代码分析:
1、当父级类的方法满足不了子类的要求的时候,我们可以考虑重写方法
2、重写只需要在子类当中建立一个同名的方法即可
3、在重写子方法的时候,如果要调用父级的同名方法可以使用super指向父级调用