ECMAScript 6
之前我们学习过ES5,它是JavaScript中的一部分,主要讲的都是变量,数据类型,控制流程,函数,面向对象等等
ES6 全称ECMAScript 6,也叫ECMAScript 2015,因为它是2015年定型发布的,它是新一代的js的语法标准,我们只涉及到ES不涉及DOM与BOM
ES6主要技术如下:
1、变量、常量
2、取值与赋值,解构
3、运算符的扩展,展开运算符,指数运算符
4、字符串的扩展
5、数组的扩展
6、函数的扩展
7、Set单值集合
8、Map键值对集合
9、对象的扩展与class关键字
10、生成器与迭代器与迭代器接口
11、反射Reflect
12、代理Proxy
13、Promise异步处理,async 及 await
14、ES6的模块话 ESModule
15、CommonJS模块化
let定义变量
之前在ES5里面,我们定义变量使用 var
这个关键字有几个特点说明一下
1、var
没有数据类型
2、var
是由一个建立阶段
3、var
是没有作用域的,它要通过函数才能形成作用域
//var没有数据类型
var a = 1;
//var没有作用域
{
var b = "haha"
}
console.log(b);
//var有一个建立阶段,可以在定义之前调用
console.log(c);
var c = 123;
var在定义变量的过程中其实并不严谨
为了让变量声明的过程更加严谨,ES6就推出了 let
用于定义变量
1、let定义的变量也没有数据类型
2、let没有建立阶段,必须先定义后使用
let aa = "hello";
console.log(aa); //没有问题的
console.log(bb); //这里会报错,因为let没有建立阶段
let bb = "world";
3、let定义的变量是有作用域的,以花括号作为作用域
{
let c = "haha";
console.log(c); //没有问题的
}
console.log(c); //会报错,告诉你c is not defined
4、let在同一作用域下不允许重复定义
let a = 123;
let a = 456; //这里是会报错
{
let a = "haha"; //这里没有问题
}
暂时性死区
let有它自身的特点,所以在使用的时候有一些现象需要注意,这种现象叫暂时性死区
let定义的变量是有作用域的,并且不能再定义之前调用
let a = "hello";
{
console.log(a); //这里是没有问题的,因为内部可以使用外部的变量
}
但是如果把代码改成如下的情况
let a = "hello";
{
console.log(a); //这里就会形成一个暂时性死区
let a = "world";
}
const常量
- 变量:可以变化的数据叫变量
- 常量:不会变化的数据叫常量,如
Math.PI
const
是定义常量的关键字,它具备上面的let的所有特点,同时又不能改变值
let a = "hello";
console.log(a);
a = "world";
console.log(a);
const b = "yoxi";
console.log(b);
b = "heihei"; //这里会报错
总结:
1、没有建立阶段
2、会形成块级作用域,以花括号为区域
3、只能初始化赋值,后期不能改变值
const的锁栈与锁堆原理
const常量,只锁栈,不锁堆
如果const声明的常量里面报存的是一个引用类型,那么根据只锁栈,不锁堆的原则,我们是可以改变数据中的属性值,但是无法整体覆盖掉之前的数据
const常量定义的时候一定要赋予初始化,不然没有任何意义
const a; //毫无意义
let与const的闭包特性
<button type="button" class="btn">按钮0</button>
<button type="button" class="btn">按钮1</button>
<button type="button" class="btn">按钮2</button>
<button type="button" class="btn">按钮3</button>
<button type="button" class="btn">按钮4</button>
<script type="text/javascript">
var btns = document.querySelectorAll(".btn");
for(var i = 0; i < btns.length;i++){
btns[i].addEventListener("click",function(){
console.log(i);
})
}
</script>
根据我们的分析,我峨嵋你认为按钮点击的时候应该是第0个按钮打印0,最后一个按钮打印4,结果实际情况是每个按钮都打印5,这是为什么?
分析原理: 在进行循环的时候,i是0-4进行的,所以会把所有的按钮都遍历一遍,然后添加事件绑定,当i = 5结束循环,事件绑定结束。
直接去点击click事件的时候,我们 要打印是i,但是这个时候 i 已经是5了 ,因为var定义的变量没有区域性,打印的console.log(i) 其实还是for徐娜混定的 var i的变量
ES5闭包解决
var btns = document.querySelectorAll(".btn");
for(var i = 0; i < btns.length;i++){
btns[i].addEventListener("click",(function(j){
var j = i
return function(){
console.log(j)
}
})(i))
}
ES6的let解决
var btns = document.querySelectorAll(".btn");
for(let i = 0; i < btns.length;i++){
btns[i].addEventListener("click",function(){
console.log(i);
})
}
分析:因为let定义的变量是具有区域性的,所以上面的代码相当于在循环内,定义了5个let i;
{ let i = 0; } { let i = 1; } //....... //依次类推,每个花括号都是一个作用域 ,不影响外边
解构
ES6里面一大特色就是解构,它是一种特殊的取值赋值的方式
解构取值
1、数组的解构取值
let arr = ["张三","李四"];
// let a = arr[0];
// lat b = arr[1];
let [a,b] = arr;
console.log(a);
console.log(b);
对于复杂的数组也可以是现实深度解构
let arr = ["张三","李四",["王五","赵六"]];
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);
2、对象的解构取值
var userInfo = {
userName: "张三",
age: 20
}
let {userName,age} = userInfo;
console.log(userName,age);
对象也可以进行深度的解构取值
var userInfo = {
userName: "张三",
age: 20,
phone:{
price:2999,
brand:"xiaomi"
}
}
let {
userName,
phone:{
price
}
} = userInfo
console.log(userName,price);
解构赋值
解构赋值其实就是解构取值的反向操作,这个过程只发生在对象里面,所以也叫对象的解构赋值
let userName = "张三";
let age = 18;
let userInfo = {
userName: userName,
age: age
}
在上面的代码当中,我们发现属性名和属性值是相同的,这个时候要注意,这种情况完全可以简化成解构赋值
let userName = "张三";
let age = 18;
let userInfo = {
userName,
age
}
console.log(userInfo);
展开运算符
它是ES6里面新出的运算符,所有实现了 Iterable
这个接口的都可以使用展开运算符,目前系统ES里里面实现这个接口的数据类型如下
1、数组
2、NodeList
3、HTMLCollection
4、Set
5、Map
6、arguments
展开运算符在ES6里面使用 ...
来完成
let arr = ["a","b","c","d","e"];
console.log(arr);
console.log(...arr);
通过展开运算符得到数组中最大的数字
let arr1 = [1,5,8,2,4,9];
var max1 = Math.max(...arr1);
console.log(max1);
展开运算符实现数组的拷贝
var arrStr = ["张三","李四","王五"];
//创建一个arrStr2
//arrStr2与arrStr相同
//两个数组之间互不影响
//var arrStr2 = arrStr.concat();
//var arrStr2 = arrStr.slice();
var arrStr2 = [...arrStr];
console.log(arrStr2);
还可以将类数组转换成数组
var lis = document.querySelectorAll(".ul1>li");
//$(lis).toArray();
//$.makeArray(lis);
//var arr1 = Array.prototype.slice.call(lis);
var arr2 = [...lis];
使用展开运算符合并数组
var arr1 = ["a","b","c"];
var arr2 = [1,2,3];
var arr3 = ["张三","李四"];
var arr4 = arr1.concat(arr2).concat(arr3);
let arr5 = [...arr1,...arr2,...arr3];
console.log(arr5);
使用展开运算符实现对象的拷贝
let obj1 = {
userName:"张三",
age:18
}
// Object.assign(obj3,obj1);
let obj3 = {
...obj1
}
console.log(obj3);
使用展开运算符实现对象的合并
let obj1 = {
userName:"张三",
age:18
}
let obj2 = {
hobby:"睡觉"
}
// Object.assign(obj3,obj1,obj2);
let obj3 = {
...obj1,
...obj2
}
console.log(obj3);
字符串扩展
模板字符串,模板字符串的一大特征就是自带格式,你格式是什么样子的,你输出的时候就长什么样
var str = `
亲爱的XXX:
你好!
恭喜你!你已中了本公司的特等奖,价值9999元的礼包直接领回家!
您只需要交纳1999元的保证金即可领取大奖,具体详情欢迎咨询我们的客户人员
电话:123456678
联系人:XXXX
XXX公司
XXXX年XX月XX日
`
console.log(str);
怎么书写就怎么生成,很方便
模板字符串另外一个特点就是可以快捷的实现字符串拼接
let nickName = "张三";
let tel = "027-110"
let connectPerson = "李四";
let company = "皮皮虾公司"
let str = `
亲爱的${nickName}:
你好!
恭喜你!你已中了本公司的特等奖,价值9999元的礼包直接领回家!
您只需要交纳1999元的保证金即可领取大奖,具体详情欢迎咨询我们的客户人员
电话:${tel}
联系人:${connectPerson}
${company}公司
${new Date().toLocaleString()}
`
console.log(str);
模板字符串里面 ${} 这里面可以写任何的js代码
模板字符串的注意事项
function abc(){
console.log("hello");
}
abc``;
模板字符串的反引号可以当成方法的括号去执行
let userName = "张三";
let age = 18;
let sex = "男"
function abc(){
console.log(arguments);
}
abc`xxxx${userName}yyyy${age}zzzzz${sex}`;
以上的写法会把字符串当前实参传入,同时 符号作为分隔符,将字符串的部分进行数组存放,并且 {} 符号作为分隔符,将字符串的部分进行数组存放,并且 符号作为分隔符,将字符串的部分进行数组存放,并且{}内部的js语句的执行结果分别作为单个实参传入
字符串扩展方法
1、includes()
判断当前字符串当中是否包含某个字符串,以前使用 indexOf
或者是 lastIndexOf
2、repeat()
生成一个重复的字符串
3、trimStart()
去除开始的空格
4、trimEnd()
去除结束的空格
数组的扩展方法
ES6又多了很多方法
1、Array.of()
方法
它是一个数组定义的方法,解决了之前 new Array()
所存在的问题
let arr = new Array(2); //代表这个数组的长度是2
let arr2 = Array.of(2); //代表给数组添加了一个2的元素
let arr3 = [2]; //上面的写法等价于这句
Array.of() 等价于 [],所以肯定也可以这么写
let arr2 = Array.of(2,3,5,7);
2、Array.from()
把类数组转换成数组
var lis = document.querySelectorAll(".ul1>li");
//$(lis).toArray();
//$.makeArray(lis);
//var arr1 = Array.prototype.slice.call(lis);
var arr2 = [...lis];
let arr3 = Array.from(lis);
3、Array.prototype.fill()
填充数组的方法
这个方法需要传参
参数一:需要填充的数据
参数二:需要从索引几开始填充
参数三:填充到索引几
let arr1 = new Array(100);
arr1.fill("abc");
console.log(arr1);
let arr2 = new Array(100);
arr2.fill("张三",0,20);
console.log(arr2);
let arr3 = new Array(100);
arr3.fill("李四",15,30);
console.log(arr3);
4、Array.prototype.flat(steps)
拍平一个多维数组
以前我们如果需要将多维数组拍平,我们要使用递归遍历,现在又这个方法就简单多了
let arr = [1,2,["z","y",["123",[true,false],"222"]]]
let arr2 = arr.flat(Infinity);
console.log(arr2);
参数 steps
代表要拍平多少层,如果想把整个多维数组转换成一维数组,可以直接传一个参数 Infinity
即可
5、Array.prototype.find()
let list = [
{
userName:"张三",
age:18
},
{
userName:"李四",
age:18
},
{
userName:"王五",
age:18
},
{
userName:"张三",
age:20
}
]
var obj = list.find(function(item,index,_arr){
//返回匹配条件的对象
return item.userName == "张三";
})
console.log(obj);
找到了就返回第一次找到的元素,如果找不到返回 undefined
6、Array.prototype.findIndex()
查找某一个元素的索引,这个方法的使用于上面的一样
var index = list.findIndex(function(item,index,_arr){
return item.userName == "张三";
})
//返回的是匹配条件的对象的索引
for…of遍历
of的遍历于之前 的in遍历是相同的,只是 for…in遍历时一个有序遍历(有索引的时候依靠索引),而for…of称之为无序遍历(遍历的时候不需要索引)
for…of是基于属性值的遍历,但是并不是所有的对象都可以使用它来遍历,如果要使用for…of来遍历,则这个对象必须要实现
Iterable
接口
let arr = ["a","b","c","d"];
//基于属性名遍历,相当于是基于索引遍历
for(let i in arr){
console.log(arr[i]);
}
//for...of是基于属性值遍历的,也就是不需要索引
for(let i of arr){
console.log(arr[i]);
}
Set单值集合
Set是ES6里面新增的一个数据解构,它是一种单值集合,没有属性名(没有索引,没有key),并且是一个不重复的集合
Set创建
let s1 = new Set(); //创建了一个空的set单值集合
let s2 = new Set(["a","b","c","d"]);
let s3 = new Set(["a","b","c","c","d"]); //不能重复粗,所以得到的结果是a,b,c,d
Set方法
1、add()
向set集合里面添加新的元素
s3.add("haha");
s3.add("haha").add("hehe");
2、delete()
向set集合中删除某个元素,返回布尔值结果
let result = s3.delete("a");
3、has()
判断set集合当中有没有指定元素,返回布尔值
let result = s3.has("a") //true
4、size
属性,表示set集合中的元素个数
5、clear()
,清空当前集合
6、values()
,它是一个生成器方法,返回当前Set集合中值的迭代器 Iterator
7、keys()
它也是一个生成器方法,返回当前set集合中键的迭代器,但是set没有键,所以返回依然是values的结果
set的遍历
set因为是单值的无序集合,所以没有索引,那么就不能使用 for…in 进行遍历,而for…of是基于属性值遍历,同时set又实现了 iterable
接口,所以它完全可以使用for…of进行遍历
let s2 = new Set(["a","b","c","d"]);
for(let i of s2){
console.log(i);
}
set集合扩展,使用set对数组去重
let arr = ["a","b","c","d","d","c"];
//请将上面数组去重
let s4 = new Set(arr);
let arr1 = [...s4];
console.log(arr1);
Map键值对集合
map集合相对于上面的set集合的单值存在,map是键值对形式的一种储存,它结合set的储存特点(存取速度快),也解决了获取的缺点(获取速度慢)
Map集合当中的键是不允许重复的,但是值可以重复
let m1 = new Map();
//直接静态初始化
let m2 = new Map([
["a","张三"],
["b","李四"],
[1,"王五"]
])
//键是不会重复的,值可以重复,键同名的时候,后面出现的值会覆盖掉前面出现的值
let m3 = new Map([
["a","张三"],
["b","李四"],
["c","哈哈"],
["d","呵呵"],
["b",123],
[1,"王五"],
[2,{
userName: "yoxi"
}],
["e",function(){
console.log("hello");
}]
])
1、键不能重复,值可以重复
2、键与值可以是任意数据类型
Map的方法
1、size
属性,用于表示有多少对元素
2、set(k,v)
,向Map集合内部添加一对元素
let m1 = new Map();
m1.set("a","张三");
m1.set("b","lisi").set(1,"王五")
3、get(key)
通过一个键向Map集合当中取一个值
var x = m1.get("a");
var y = m1.get(1);
4、delete(key)
通过一个键删除一个元素
m1.delete("b");
5、clear()
清空当前Map集合
6、values()
,它是一个生成器方法,返回当前Map集合中值的迭代器 Iterator
7、keys()
它也是一个生成器方法,返回当前Map集合中键的迭代器 Iterator
8、entries()
它是一个生成器方法,返回当前Map集合中的 key-value集合
Map的遍历
Map是实现了迭代器接口的,所以可以用for…of 来遍历,但是 Map 散列储存,所以没有索引的说法,所以不能用for…in
let m1 = new Map();
m1.set("a","张三");
m1.set("b","lisi").set(1,"王五")
for(let i of m1){
console.log(i)
}
这个时候 i 代表是 key和value的组合,这个时候我们可以直接使用解构来进行遍历
for(let [k,v] of m1){
console.log(k,v);
}