ES6
一、变量
1、let
以前,我们使用var
关键字来声明变量,但是var
并不完美,因为会存在变量提升的问题。
console.log(a); //输出defined
var a=10;
console.log(b); //报错:ReferenceError: b is not defined
var b=5;
再来看var
和let
的作用域范围,甚至于:
for (var i = 0; i < 10; i++) {}
document.write(i); //输出10
for (let i = 0; i < 10; i++) {}
document.write(i); //报错:ReferenceError: i is not defined
用var
声明的变量,会存在内存泄漏的风险,因为此变量并不会被及时销毁;
而let
就不一样了,只要出了这个代码块,就会及时的被销毁。
2、const
const
用来声明常量,如果值是数字或字符串,那么值不可以被改变。
如果声明的是对象,那么对象中的属性的值是可以被改变的,因为内存地址并没有发生变化。
二、模板语言
//过去,我们要这样做,使用加号拼接
let name="Vito";
console.log("I'm "+ name);
//现在,这样就可以了,这就叫做模板语言
let name="Vito";
console.log(`I'm ${name}`);
在` `中,你可以随意的去写格式,比如换行。它会保存你固有的格式。
三、默认参数
function add(flag=true){
//给参数设定一个默认值
}
四、箭头函数
比如,有一个数组,我们去遍历每一个值,在回调中,给其加2并返回,得到一个新的数组:
//过去的方法
let arr1=[1,2,3];
let newArr1=arr1.map(function(data){
return data+2;
})
console.log(newArr1);
//箭头函数
let arr2=[5,6,7];
let newArr2=arr2.map(data=>{
return data+2;
})
console.log(newArr2);
//如果回调中只有一行代码,还可以去掉{}和return,简写为:
let arr3=[5,6,7];
let newArr3=arr3.map(data=>data+2)
console.log(newArr3);
而且,箭头函数不会改变作用域,箭头里面的this
,指向的也是外层的作用域。
五、数组、字符串、对象解构
//对数组的结构
let [a,b,c]=[1,2,3];
console.log(`a=${a}, b=${b}, c=${c}`);
//对字符串的结构
let [x,y,z]="ES6";
console.log(`x=${x}, y=${y}, z=${z}`);
//对对象的解构(这里也是用对象接收,通过对象的key对其拆分,但是可以调整n和m的顺序)
let {n,m}={n:10,m:20};
console.log(`n=${n}, m=${m}`);
六、for循环
function sum(){
let arr=[3,4,5,6];
for(let a of arr){
console.log(a);
}
}
七、函数的Rest参数
//过去的做法,并不确定用户实际会传几个参数过来,因此只能先做非空判断
function sum1(x,y,z){
let total=0;
if(x) total+=x;
if(y) total+=y;
if(z) total+=z;
console.log(total);
}
sum1(1,2,3);
//动态接收参数
function sum2(...m){
let total=0;
for(let a of m){
total+=a;
}
console.log(total);
}
sum2(1,2,3,4,5);
...
有很多的含义,但是如果放在形参当中,并且和m
这样的一个变量组合,就叫Rest参数,表示动态的,不确定的,不知道会传多少个参数进来。
而在ES6中,又有了不同的做法:
let sum3=(...m)=>{ //括号即代表一个function,一个函数
let total=0;
for(let a of m){
total+=a;
}
console.log(total);
}
sum3(1,2,3,4,5);
八、扩展
- 数组的扩展
...
和数组结合,就称之为函数的扩展。因为它是一种运算符,就会进行运算,把数组拆解。
比如,要想把两个数组合并,过去有一种数组.concat()的方法:
//两个数组合并,过去的方法
let arr1=[1,2];
let arr2=[3,4]
let arr3=(arr1.concat(arr2));
console.log(arr3);
//使用数组扩展的方法
console.log([...arr1,...arr2]);
...arr1,...arr2
就表示对数组arr1和arr2进行了解构,同时在外层又包了一层[]
,就表示最终形成了一个新的数组。
在五、数组、字符串、对象解构也提到了数组的结构,对号入座。其实还有一种扩展的形式,比如,当不确定数组的长度时,可以将接收的变量设置为动态的:
//数组结构,对号入座
let [x,y]=[1,2];
//Rest和解构放在一起
let [x,...y]=[1,2,3,4,5];
console.log(y); //输出:[2, 3, 4, 5]
对字符串的扩展同理。
- 函数的扩展
九、Promise
其实在ES5中就已经大量去使用了,比如jQuery,经常会通过.then
来进行回调,它实际上就是一种Promise的结构。它是通过对Promise这种结构的封装,使方法成为这种调用方式。
ES6中,原生JS已经支持Promise,我们可以在函数中new 一个 Promise对象。
而Promise,就是解决以前不断的层层回调,现在直接.then
就可以了,通过链式调用,减少冗余代码。
let checkLogin = () => { //前面有提到,()即代表一个function,同:function(){}
//Promise是一个对象,这个对象接收一个回调,这个回调有两个参数。
//resolve:接口调用成功,执行的回调。 reject:接口报错,执行的回调。
return new Promise((resolve, reject) => {
let flag = document.cookie.indexOf("userId") > -1 ? true : false;
if (flag) {
resolve({
status: 0,
result: "登录成功"
})
} else {
reject("登录失败");
//reject实际上是一种报错,登录失败并不属于报错,因为登录失败表示的是接口和服务器都没有问题,只是用户名和密码输错了。
//所谓的报错,应该是服务器请求挂了,或者说代码错误导致请求失败。
//所以reject不应该写在else里面,这里应该还是使用resolve正常返回,这里只是作为演示。
}
})
}
checkLogin().then(data => {
if (data.status == 0) {
console.log(data.result);
}
}).catch(error => console.log(`errors:${error}`))
再演示一个链式调用,比如判断登录成功之后,获取到用户id
let checkLogin = () => {
return new Promise((resolve, reject) => {
let flag = document.cookie.indexOf("userId") > -1 ? true : false;
if (flag=true) {
resolve({
status: 0,
result: "登录成功"
})
} else {
reject("登录失败");
}
})
}
let getUserInfo=()=>{
return new Promise((resolve,reject)=>{
let userInfo={
userId:101
}
resolve(userInfo); //这里只是演示,直接把userInfo装进去
})
}
checkLogin().then(data => {
if (data.status == 0) {
console.log(data.result);
return getUserInfo(); //如果登录成功,直接返回用户信息。
//因为返回的是Promise, 所以可以继续使用Promise来调用,在函数后.then链式调用
}
}).catch(error => console.log(`errors:${error}`)).then(data2=>{
console.log(data2.userId);
})
除此之外,还有一种方式:Promise.all()
可以同时调用多个接口和请求。
let checkLogin = () => {
return new Promise((resolve, reject) => {
let flag = document.cookie.indexOf("userId") > -1 ? true : false;
if (flag=true) {
resolve({
status: 0,
result: "登录成功"
})
} else {
reject("登录失败");
}
})
}
let getUserInfo=()=>{
return new Promise((resolve,reject)=>{
let userInfo={
userId:101
}
resolve(userInfo); //这里只是演示,直接把userInfo装进去
})
}
Promise.all([checkLogin(),getUserInfo()]).then(([data1,data2])=>{ //回调中对数组进行解构
console.log(`result1: ${data1.result}, result2: ${data2.userId}`);
})
十、ES6模块化开发
因为要结合案例,这里先使用vue-cli构建一个SPA应用,将项目跑起来。
随后,在src目录下创建一个新文件:exportdemo.js
export let sum=(x,y)=>{
return x+y;
}
export let minus=(x,y)=>{
return x-y;
}
export
进行导出,import
进行导入。
之后打开src目录下的main.js,添加以下代码:
//一旦暴露出了名字,就需要使用花括号来接收
import {sum,minus} from './exportdemo'
//sum这个变量本身是个函数,因此可以直接执行
console.log(`sum=${sum(1,2)}`); //输出:sum=3
console.log(`minus=${minus(2,1)}`); //输出:minus=1
然后根据$ npm run dev
得到的『Project is running at...』打开页面,得到输出结果。
main.js中import语句还可以换种写法:
import * as util from './exportdemo'
console.log(`sum=${util.sum(1,2)}`); //输出:sum=3
console.log(`minus=${util.minus(2,1)}`); //输出:minus=1
1、import方法
import方法可以按需加载js文件。过去的SPA,打包以后会生成一个js文件,这个js文件会非常庞大,有了import之后,就可以按需加载。使用了import方法,那么在一开始,就不会把相应的js文件打包进去,直到程序执行到某一步操作的时候,才会加载对应的js文件。
语法:
import("js文件路径");