ECMAScript6.0简介
什么是ECMAScript
为了避免各大浏览器厂商在各自的JavaScript标准上越走越远,1997年在ECMA的提议协调下,最终确定了统一的脚本语言标准规范——ECMA-262,也就是ECMAScript。
两者区别
完整的JavaScript由三部分组成:文档对象模型DOM、浏览器对象模型BOM、核心ECMAScript
- DOM:多层节点结构,借助DOM提供的API可以添加、删除、修改、替换任何节点
- BOM:提供和浏览器交互的接口
- ECMAScript:在设计实现JavaScript脚本语言的语法和基本对象的核心内容时遵循的标准
let
- 作用域
声明变量,类似于var,但let声明的变量只在let所在的代码块有效
{
var name = "zhangsan";
let age = 18;
}
console.log(name) //张三
console.log(age) //age is not defined
当在代码块外部定义var正常输出,let不行
2. 变量提升
var 会变量提升,即可以在声明前调用,值是undefined
按一般规律变量应该在声明后用,所以为了纠正这种现象,let必须先声明才能使用,否则报错
console.log(a); //undefined
var a = 2;
------------------------------
console.log(b); // 报错ReferenceError: b is not defined
let b = 2;
3.不允许重复声明
let不能在相同作用域内,重复声明同一个变量
function fun(){
let a = 123;
var a = 456;
}
// SyntaxError: Identifier 'a' has already been declared
function fun(){
let a = 123;
let a = 456;
}
// SyntaxError: Identifier 'a' has already been declared
声明的参数不能与形参同名
function func(arg){
let arg
}
//SyntaxError: Identifier 'arg' has already been declared 报错,因为在同一个作用域内
function func(arg){
{
let arg
}
}
//不报错 因为不在同一个作用域内
4.for循环中var和let的父子作用域对比
<div id="varCount">var循环</div>
<div id="letCount">let循环</div>
---------------------------------------------
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<script>
var myVar = document.getElementById("varCount");
var myLet = document.getElementById("letCount");
for (var i = 0; i < 10; i++) {
setTimeout(function(){
myVar.innerHTML += i+" "; //10 10 10 10 10 10 10 10 10 10
})
}
for (let j = 0; j < 10; j++) {
setTimeout(function(){
myLet.innerHTML += j+" "; //0 1 2 3 4 5 6 7 8 9
})
}
-------------------------------------
var btns=document.querySelectorAll('button');
for(let i=0; i<btns.length; i++){
btns[i].onclick=function(){
alert('这是第'+(i+1)+'个按钮!') //1 2 3 4
}
}
</script>
变量 i 是var声明,在全局内有效 ,即全局只有一个变量i,每次循环变量i值会改变,使用的变量 i 指向的都是同一个变量i,导致运行时输出的最后一轮的变量i值也是10。
let声明的变量只在块级作用域内有效,在本轮循环内有效,所以每次循环都是一个新的变量,最后输出0-9。
如果每一轮循环的变量j 都是重新声明的,那么如何知道上一轮循环的值?因为ECMAScript引擎内部会记住上一轮循环的值来初始化本轮的变量j,就是在上一轮循环的基础上进行计算。
const
声明一个只读的常量,一旦声明就不改变,所以一旦声明要立刻初始化,只声明不赋值就报错
const PI = 3.1415; //对
PI = 3.14; //错 因为pi已经又3.14 不能再改变
const m; //错 没赋值
console.log(PI);
console.log(m);
const作用域与let命令相同,只声明所在的块级作用域内有效,且不能重复声明。
变量解构赋值
数组解构
逐个拆分现有的对象或数组来提取所需要的数据
ES6允许按照一定模式从数组和对象中提取值再对变量赋值,这被称为解构。
数组解构按照等号左边=等号右边的匹配进行
let [var1,var2,...varn] = array;
数组解构按照次序排列,变量取值由位置决定
1. 模式匹配
let [a,b,c] = [1,2,3] ;//a=1,b=2,c=3
2. 嵌套方式
let [aaa,[[bbb],ccc]] = [1,[[2],3]]; //aaa=1,bbb=2,ccc=3
3. 不完全解构
let[x,,y] = [1,2,3]; //x=1,y=3
4. 省略号解构,带有省略号修饰符的变量必须放到最后,否则是无效解构
let [x,...y] = [1,2,3,4]; //x=1,y=[2,3,4]
let [x,y] = [1]; //x=1,y=undefined
5. 有默认值的解构
let [a=0,b=1,c=2] = [1,undefined]; //a=1,b=1,c=2
当右侧数组中是undefined或没有左侧对应的值时,左侧会用默认值给变量进行赋值
6. 字符串解构处理
var [a,b,c] = 'hello'; //a = 'h',b='e',c='l'
当右侧是字符串时,把字符串的每一个字符解构到相对应等号左边的变量中
对象解构
和数组类似,数组按次序匹配,对象按照属性名称进行匹配不一定按属性先后次序
1. 基本形式
let {foo,bar} = {foo:'aaa',bar:'bbb'}; //解构后foo ="aaa",bar="bbb"
let {bar,foo} = {foo:'aaa',bar:'bbb'}; //解构后foo ="aaa",bar="bbb"
let {foo} = {bar:'bbb'}; //解构后foo = undefined
2. 左边变量key:value的形式
let {foo:qwe} = {foo:'aaa',bar:'bbb'}; //解构后qwe ="aaa"
let obj = {first:'hello',last:'world'};
let {first:f,last:l} = obj; //f='hello',l='world'
3. 解构正常情况
let {foo:foo,bar:bar} = {foo:'aaa',bar:'bbb'}; //foo='aaa',bar = 'bbb'
简化:
let {foo,bar} = {foo:'aaa',bar:'bbb'}; //foo='aaa',bar = 'bbb'
即对象的解构赋值是先找到同名属性,后赋值给对应变量。
解构赋值应用
函数只返回一个值,如果返回多个值用数组或对象返回,通过数组或对象的解构赋值就可以很方便取出这些值。
// function fn(a,b){
// return a+b
// }
// var a = fn(1,2);
// console.log(a);
function fn(){
return [1,2,3] //数组
}
let [a,b,c] = fn(); //对函数的返回值进行解构
console.log(a,b,c); //1 2 3
function fn1(){
return{ //对象
foo:1,
bar:2
}
}
let {foo,bar} = fn1();
console.log(foo,bar); //1 2
解构赋值提取JSON
<p id="p"></p>
<script>
let jsonData = {
name:'zhangsan',
age:18,
hobbies:['上课','睡觉']
};
// console.log(jsonData.name,jsonData.age,jsonData.hobbies);
let {name,age,hobbies} = jsonData; // 对JSON进行解构
console.log(name,age,hobbies);
var p = document.getElementById("p");
p.innerHTML = `姓名:${name}<br>年龄:${age}<br>爱好:${hobbies}`
</script>
解构赋值遍历Map解构
任何部署了Iterator接口的对象都可以用 for…of 循环遍历
【Iterator是一种接口,为各种不同的数据结构提供统一的访问机制。
什么意思呢,你想用for …of遍历你的数据结构, 就得部署Iterator接口。
原生提供这个接口的数据结构有:数组、类数组对象(如argument)、Map和Set结构。】
什么是Map
之前 ES5 中是没有 Map 这种数据集合的,ES6才把它添加进来了。
Map 是 key-value 的集合,key 可以是任意类型的数据,类似于对象,但对象的 key 只能是字符串。
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
如果你需要“键值对”的数据结构,Map 比 Object 更合适。
案例
先定义和赋值Map变量,再循环访问变量,访问同时用解构赋值遍历Map
<p id="p"></p>
<script>
const map = new Map();
map.set('name','zhangsan')
map.set('age',18)
map.set('gendar','男')
// console.log(map); //Map(2) {'name' => 'zhangsan', 'age' => 18}
var p = document.getElementById("p");
for(let [key,value] of map){
p.innerHTML +=`键名:${key},键值:${value}<br><br>`
}
// 单独获取键名
for(let [key] of map){
p.innerHTML +=`键名:${key}<br>`
}
// 单独获取键值
for(let [,value] of map){
p.innerHTML +=`<br>键值:${value}<br>`
}
</script>
箭头函数
写法:
function fn(a,b){
return a+b;
}
可以简化成:
(a,b)=>{
return a+b
}
简化的方法主要有两种:
var fn = ()=>{} //无参
var fn = a=>{} //单个参数a
var fn = (a,b)=>{} //多个参数a、b
var fn = (a,b,...args)=>{} //可变参数
()=>'hello' //返回字符串'hello'
(a,b)=>a+b //返回a+b的和
(a)=>{
a=a+1;
return a; //返回a+1的值
}
如下:
<p id="p"></p>
<script>
var p = document.getElementById("p");
let show1 = function(a){
return a*2
}
let show2 = a=>{
return a*3
}
let show3 = a=>a*4
p.innerHTML = show1(8) //16
p.innerHTML = show2(8) //24
p.innerHTML = show3(8) //32
箭头函数解构赋值
二者相结合可以简化对箭头函数的调用
const yushu = ([i,j])=>i%j;
console.log(yushu([8,3])); //余数是2
const max = (...args)=>Math.max(...args);
console.log(max(...[12,2,33,5,2222,10000])); //最大值10000
const min = (...args)=>Math.min(...args);
console.log(min(...[1,33,44,2,50])); //最小值1
数组方法
ES6中新增了数组的方法
1. map() 方法
遍历数组中每个元素,让其作为参数执行一个指定的函数,并将每个返回值形参一个新数组
此方法不改变原数组的值
let newArr = arr.map(function(参数){
})
简化:
let newArr = arr.map((参数)=>{
})
案例:
// 让数组中的每个值*2生成新数组
let arr = [1,2,3];
let newArr = arr.map(item =>item*2);
console.log(arr); //[1,2,3]
console.log(newArr); //[2,4,6]
// 根据成绩生成评价
let score = [95,80,50];
let newScore = score.map((item)=>item>=60?item>90?"优秀":"及格":"不及格")
console.log(score);
console.log(newScore);
- forEach() 方法
从头到尾遍历数组,为每个元素调用指定函数。此方法改变原数组本身。
函数参数分别是:数组元素、元素索引、数组本身
arr.forEach(function(数组元素,元素索引,数组本身){
})
简化:
arr.forEach((数组元素,元素索引,数组本身)=>{
})
案例:
let arr = [1,2,3];
console.log(arr); //[1,2,3] 原数组
arr.forEach((element,index,arr)=>{
console.log(element,index,arr); //1 0 (3) [1, 2, 3]
//2 1 (3) [1, 2, 3]
//3 2 (3) [1, 2, 3]
// 把数组每个数加2,并分别显示修改前后数组的值
arr[index] = element +2
})
console.log(arr); //[3,4,5] 新数组
- filter() 方法
过滤方法,对数组元素执行特定函数后返回一个子集。入口参数是执行逻辑判断的函数,函数返回值是true或false
方法的结果是所执行逻辑判断函数返回true的元素。即过滤掉不满足条件的值。
返回一个新数组,不改变原数组的值。
arr.filter((参数列表)=>{})
案例1:
// 过滤掉不能被3整除的元素形成一个新数组
let arr = [60,70,80,87,90];
let res = arr.filter(temp=>temp%3==0)
console.log(res); // [60, 87, 90]
案例2:
let arrJSON = [
{book:'HTML',price:40},
{book:'Java',price:60},
{book:'JS',price:100},
]
// 将书本价格大于50过滤并形成新数组
let res = arrJSON.filter(item=>item.price>50);
console.log(res); //(2) [{…}, {…}]
for(var key in arrJSON){
console.log(arrJSON[key].book,arrJSON[key].price);
}
- every() 方法 some() 方法
都是对数组进行指定函数的逻辑判断,入口参数是指定函数,方法返回值是true或false
every() 一假即假 some()一真即真
一般用来判断是否数组中所有数都满足某一条件或是否存在某些数满足条件
var person = [
{name:'lisi',gender:'男'},
{name:'wangwu',gender:'男'},
{name:'haha',gender:'女'},
];
console.log(person);
var res = person.every(person=>person.gender == '男')
console.log("里面所有性别都为男:"+res); //false
var some = person.some(person=>person.gender == '男');
console.log("里面含有性别女:"+some); //true
- reduce() 方法
接收一个函数作为累加器,使用数组的每个元素依次执行回调函数,不包括被删除或未被赋值的元素,接收4个参数:
上一次调用时的返回值或初始值init、当前正在处理的数组元素、正在处理的数组元素的索引(提供init索引为0,否则索引为1)、原数组
arr.reduce((prev,cur,index,arr)=>{
},init) //init表示初始值
- 数组求和
const arr = [1,2,3,4,5]
// 数组求和
const sum = arr.reduce((prev,item)=>{
return prev+item
},0)
console.log(sum); //15
- 求数组最大值
const arr = [1,2,3,4,5]
// 求数组最大值
const max = arr.reduce((prev,cur)=>Math.max(prev,cur));
console.log(max); //5
因为没传初始值,所以开始时prev的值为数组的第一个元素1,cur为第二个值2,取二者中最大值进入下一轮回调
- 数组去重
const arr = [1,2,3,4,4]
// 数组去重
const newArr = arr.reduce((prev,cur)=>{
prev.indexOf(cur)===-1 && prev.push(cur);
return prev;
},[]) //[]是空数组
console.log(newArr); //[1, 2, 3, 4]
在初始化数组中查找需要去重处理的数组中的第n个元素,如果找不到,就将该项继续添加到初始化数组中
案例:
const str = 'qweqwer';
const obj = str.split('').reduce((prev,cur)=>{
prev[cur]?prev[cur]++ :prev[cur] = 1
return prev
},{}); //{}初始值是空对象
console.log(obj); //{q: 2, w: 2, e: 2, r: 1}
字符串扩展
模板字符串
let name = "zhangsan ";
console.log("姓名" + name);
console.log(`姓名${name}`);
空格和缩进都会保存在输出中
字符串新增方法
查找
传统JS有indexOf() lastIndexOf()
- includes(String,index) 返回布尔值
- startsWith(String,index) 返回布尔值
- endsWith(String,index) 返回布尔值
案例:
let str = 'http://www.baidu.com';
if(str.startsWith('http://')){
console.log('普通网址');
}else if(str.startsWith('https://')){
console.log("加密网址");
}
let fileName = '1.jpg';
if(fileName.endsWith('.txt')){
console.log("是文本文件");
}else if(fileName.endsWith('.jpg')){
console.log("是图片文件");
}
重复
repeat() 将字符串重复几次并返回一个新的字符串,如果输入小数则向下取整;NaN看作0
let str = 'zhangsan';
console.log(str.repeat(3)); //zhangsanzhangsanzhangsan
console.log(str.repeat(1.94)); //zhangsan
console.log(str.repeat(0.5)); //控制台无显示
console.log(str.repeat(NaN));//控制台无显示
补全
padStart() padEnd()字符串补全长度的方法
某个字符串不够指定长度,在头或尾补全。参数:补全后的字符最大长、要补的字符串
console.log('7'.padStart(2,'0')); //07 时间的补全
console.log('7'.padEnd(2,'0')); //70
console.log('hello'.padStart(4,'h')); //hello
console.log('hello'.padEnd(9,'qw')); //helloqwqw
console.log('hi'.padStart(5)); // hi 前面空三空格
console.log('hello'.padEnd(9,'qwert')); //helloqwer 超出部分被截取
Module语法
ES6之前,JS语言一直没有模块体系。ES6模块设计思想:尽量静态化,即在词法分析、语法分析、语义分析时就能确定模块的依赖关系,以及输入和输出的变量。好处:在编译时可优化;缺点:不能进行条件加载。
ES6不是对象,而是通过export命令显示指定输出代码,再通过import命令输入。
import {reactive,toRefs,computed} from 'vue'
export命令
export用于规定模块的对外接口,import用于输入其他模块提供的功能
一个模块是一个独立的文件,文件内部的所有变量从外部无法获取,如果想要外部文件读取模块内部变量要使用export。
export var m =1;
等价于:
var m =1;
export {m}
也可以取别名:
var n = 1;
export {n as m}; //变量n别名m
规定对外接口m后,其他脚本可以通过这个接口获取的m值为1。
import命令
export定义模块对外接口后,其他js文件可以通过import导入加载模块
import {firstName,lastNamem,year} from './profile'
function setName(element){
element.textContent = firstName + "" + lastName;
}
export default命令
使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
console.log('foo');
}
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字
import customName from './export-default';
customName(); //'foo'
import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。
使用export defautl命令对应import语句不需要用大括号,不使用export default对应的import要用大括号。
export default命令用于指定模块的默认输出,一个模块只有一个默认输出,所以export default只能使用一次。
JSON与Map⭐
JSON:一种轻量级数据交换格式,即一个字符串,元素用特定符合标注。键值对用 : 分隔,数据用 , 分隔,对象用{}
{}:对象
[]:数组
“”:属性或值
: :后者是前者的值(数字、字符串、数组、对象)
格式:对象中字符串必须是双引号 且最后一个成员不能加逗号
let student = {
"name":"zhangsan",
"age":18,
"address":{
"city":'武汉',
"local":"10楼"
}
}
JSON可以将JavaScript对象中的一组数据转为字符串,就可在网络或程序之间传递这个字符串,在需要时还原为所支持的数据格式。
在Ajax中,如果要用数组传值,要用JSON将数组转为字符串,获取JSON数据格式语法:
JSON对象.键名
JSON对象[“键名”]
数组对象[索引]
如:
student.name
student.address.city
也可以直接修改数据: student.name = 'lisi'
字符串转对象JSON.parse()
var obj = JSON.parse('{"a":"Hello","b":"World"}');
console.log(obj); //{a: 'Hello', b: 'World'}
对象转字符串JSON.stringify()
var json = JSON.stringify({a:'Hello',b:'World'});
console.log(json); //{"a":"Hello","b":"World"}
JSON数据操作
// 定义对象
var myJson = {"name":"zhangsan","age":18};
for(var key in myJson){
// console.log(key,myJson[key]); //遍历对象
}
// 定义数组,成员是JSON对象
var arrJson = [
{"name":"李四","age":"20"},
{"name":"王五","age":"18"},
{"name":"赵四","age":"20"}
]
// 遍历数组
for(var i= 0;i<arrJson.length;i++){
for(var j in arrJson[i]){
console.log(j+":"+arrJson[i][j]);
}
}
Map数据结构特点
是ES6提供的一种字典数据结构,字典就是存储不重复键的Hash结构
Object提供"字符串”-- 值 Map提供 值-- 值 的对应结构
创建Map和设计方法
const myMap = new Map();
myMap.set("name","liis");
console.log(myMap.get('name')); //lisi
常用属性和方法
1. size
console.log(myMap.size);
2. set(key,value)
const myMap = new Map();
myMap.set("age",20)
myMap.set(999,"感冒灵")
myMap.set(undefined,'hhh');
console.log(myMap);
// set方法返回的是当前的Map对象,可以采用链式写法
let m = new Map().set(666,"厉害").set(233,"aaa").set(50,'疯狂星期四');
console.log(m);
3. get(key)
读取key对应的值,没有key返回undefined
const m = new Map();
const hello = function(){console.log("hello"); };
m.set(hello,'ES6');
m.get(hello)
console.log(m); //Map(1) {ƒ => 'ES6'}
4.has(key)
返回布尔值,某个键是否存在当前Map对象中
const m = new Map();
m.set('qq',123);
m.set(886,'再见');
m.set(undefined,'hhh');
console.log(m.has('qq')); //true
console.log(m.has('aa')); //false
5. delete(key)
删除某个键,删成功返回true
const m = new Map();
m.set('qq',123);
m.set(886,'再见');
m.set(undefined,'hhh');
m.delete(886)
console.log(m); //剩两个
6. clear()
清除数据
m.clear()
console.log(m); //Map(0) {size: 0}
7. Map循环遍历
原生提供三个遍历生成器函数和一个遍历方法
keys 返回键名的遍历器
values 返回键值的遍历器
entires返回所有成员的遍历器
forEach遍历Map的所有成员
const m = new Map([[1,'one'],[2,'two'],[3,'three']]);
console.log([...m.keys()]); // [1, 2, 3]
console.log([...m.values()]); // ['one', 'two', 'three']
console.log([...m.entries()]); // [Array(2), Array(2), Array(2)]
// 遍历输出
m.forEach((value, key, m) => console.log(value, key)); //one 1
// two 2
// three 3
Map与JSON相互转换⭐
const map = new Map();
map.set("name", 18);
function mapToJson(map) {
return JSON.stringify([...map]);
}
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
// map转json
console.log(mapToJson(map)); //[["name",18]]
// json转map
console.log(jsonToMap('[["name",18]]')); //{'name' => 18}