ES6简介:
ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。所以又称ECMAScript 2015。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现(另外的ECMAScript方言还有Jscript和ActionScript)。日常场合,这两个词是可以互换的。、在我们正式讲解ES6语法之前,我们得先了解下Babel。
Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。大家可以选择自己习惯的工具来使用使用Babel,具体过程可直接在Babel官网查看。
1.作用域
被let和const命令声明的变量,只在let命令所在的代码块内生效,代码块外无法访问;
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
2.变量提升
与var不同,let和const声明的变量,不会发生“变量提升”现象,即变量不能在声明之前使用,一旦使用就会报错。
3.暂时性死区
在代码块内,使用let和const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
4.重复声明
被let和const声明的变量不可重复声明。
在ES6之前,JavaScript只有全局作用域和函数作用域,let命令实际上增加了块级作用域。块级作用域的应用场景:
1.在函数作用域内声明变量
2.在for循环中声明循环变量const声明一个只读的常量。一旦声明,常量的值就不能改变。
注意:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
这可能是前端程序员处理异步最长用,用的最多的吧。哈哈哈。。。
背景:在Javascript的世界里,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现,但是如果代码中异步代码比较多,也就意味着需要写很多回调函数,这样的代码难以读懂,维护困难,而且不利于复用。
我们希望不用写太多回调,而是也可以像同步代码一样链式执行,而Promise就是为了这种情况而生的。暂且不谈await、async。
Promise本身并不能实现任何业务,我们之所以要使用Promise只是为了不写回调,为了把本来异步的代码写成同步的形式,我们先来看一个简单的Promise处理异步的例子:承诺一秒之后生成的随机数大于0.5
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.random()
if (num > 0.5) resolve(num)
else reject(num)
}, 1000)
})
pro.then((n) => {
console.log(n + '大于0.5,承诺兑现')
}).catch((n) => {
console.log(n + '不大于0.5,承诺失信')
})
变量pro是一个Promise对象,它负责执行参数里的函数,函数带有 resolve 和 reject 两个参数,resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
Promise内部只负责判断和修改状态,并不需要执行具体的逻辑,承诺兑现的逻辑在then里执行,承诺失信的逻辑在catch里执行。
先来看个采用Promise后ajax的写法:
getData(method, url){
var promise = new Promise(function(resolve, reject){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open(method, url);
xmlHttp.send();
xmlHttp.onload = function () {
if (this.status == 200 ) {
resolve(this.response);
} else {
reject(this.statusText);
}
};
xmlHttp.onerror = function () {
reject(this.statusText);
};
})
return promise;
}
getData('get','www.xxx.com').then(successFun, failFun)
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。Promise 实例生成以后,可以用then 方法分别指定成功状态和失败状态的回调函数。
先提前准备几个后端接口
var express = require("express");
var app = express();
//设置跨域访问(设置在所有的请求前面即可)
app.all("*", function (req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
//允许的header类型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method == 'OPTIONS')
res.sendStatus(200); //让options尝试请求快速结束
else
next();
});
// 第一个接口
app.get("/one", function (req, res) {
res.send({
state: true,
des: "第一个接口请求成功,允许请求第二个接口"
})
})
// 第二个接口
app.get("/two", function (req, res) {
res.send({
state: true,
des: "第二个接口请求成功,允许请求第三个接口"
})
})
// 第三个接口
app.get("/three", function (req, res) {
res.send({
state: true,
des: "第三个接口请求成功!"
})
})
app.listen(8081, function () {
console.log("应用实例,访问地址为 http://127.0.0.1:8081");
});
//这里是对位传参,只要参数名可以随意取,第一个参数可以理解为成功后的回调,第二个参数可以理解为失败后的回调
const p = new Promise(function(resolve,reject){
//发起异步操作,ajax请求
......
if(/*异步操作成功*/){
resolve(value); //在异步操作成功时调用,返回异步操作结果;
}
else{
reject(error); //在异步操作失败时调用,返回异步操作结果;
}
});
Var p1=p.then(function(data){
console.info(data);
//发起第二次请求
//返回请求结果
}
);
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise 对象的状态变为resolved时调用,第二个回调函数是Promise 对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise 对象传出的值作为参数。
var p1 = new Promise(function (reslove, reject) {
$.getJSON(
"http://10.1.0.39:8081/check?key=123456",
function (data) {
if (data.mes != "正确") reject(data);
else reslove(data);
}
);
});
var p2 = p1.then(
// 成功的执行函数
function (data) {
console.log("成功", "数据为:", data);
},
// 失败的执行函数
function (data) {
console.log("失败", "数据为:", data);
}
);
正常情况then方法会接受两个参数,分别表示成功的处理函数和失败的处理函数。
但是很多时候我们可能含有多个then方法,这时如果要每个then里面都写上失败处理函数就太麻烦了,这时我们就可以使用catch方法。
我们在最末尾加上catch方法,那么他就可以捕获前面任何地方产生的错误,如下:
注意:
一旦在catch前的某个then方法中写了第二个参数用于捕获错误,那么当前错误就会到此停止,后面就是正确的流程了。
如果在后面的流程中没产生其他错误,那么catch方法也不会再执行,如果出错了,那么catch仍然会继续执行。
(1)对象的状态不受外界影响。
Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise 对象的状态改变,只有两种可能:从进行中变为已成功和从进行中变为已失败。只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于进行中状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
all / race
all和race两个函数都是并发执行promise的方法,他们的返回值也是promise,all会等所有的promise都决议之后决议,而race是只要有一个决议就会决议。
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
//注意:如果参数为空,all方法会立刻决议,而race方法会挂住
先来看个列子:
// 假设后端有三个接口,
// 第一个接口验证口令是否正确,
// 如果口令正确就发起第二次请求获取当前用户名
// 如果第二个接口成功获取到了用户名了,就发起第三次请求:通过用户名获取用户的vip等级
// http://10.1.0.39:8081/check?key=123456
// http://10.1.0.39:8081/user
// http://10.1.0.39:8081/leave?username=张三
var p1 = new Promise(function (reslove, reject) {
$.getJSON('http://10.1.0.39:8081/check?key=123456', function (data) {
if (data.mes == '正确') {
reslove(data)
}else {
reject(data)
}
})
})
var p2 = p1.then(function (preData) {
return new Promise(function (resolve, reject) {
$.getJSON('http://10.1.0.39:8081/user', function (data) {
if (data.username == '张三') {
resolve(data)
}else {
reject(data)
}
})
})
})
var p3 = p2.then(function (preData) {
$.getJSON('http://10.1.0.39:8081/leave?username=张三', function (data) {
console.log(data)
})
})
是不是觉得不优雅,在追求优雅的道路上永无止境 !(ovo)这时候promise.all就派上用场了。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
你学费了吗。。。。。。
Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。
需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
面试题:封装一个promise.all方法(重点)
Promise.prototype.all = function(promises) {
let results = [];
let promiseCount = 0;
let promisesLength = promises.length;
return new Promise(function(resolve, reject) {
for (let val of promises) {
Promise.resolve(val).then(function(res) {
promiseCount++;
// results.push(res);
results[i] = res;
// 当所有函数都正确执行了,resolve输出所有返回结果。
if (promiseCount === promisesLength) {
return resolve(results);
}
}, function(err) {
return reject(err);
});
}
});
};
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
原理是挺简单的,但是在实际运用中还没有想到什么的使用场景会使用到.自己也还没用过!
表示重复多少次。如果参数是正小数,会取整。如果参数是负数,会报错。如果参数是0,会得到空串。如果参数是字符串、布尔型或其他类型,会先换算成整数。
这两个是ES8(也就是2017)新增方法,注意打开实验性JavaScript。
用于在开头补全字符和在末尾补全字符,padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。
如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。
有一个问题是,如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,js会截去右边超出位数的补全字符串,而原字符串是不会碰的。即使是padEnd也是截去右边超出的位数。
'abc'.padStart(10, '0123456789')
// '0123456abc'
'abc'.padEnd(10, '0123456789')
// 'abc0123456'
如果没有缺省第二个参数,默认填充空格。
顶不住了,今天先这样,跑步睡觉了。你的点赞是我坚持的动力,爱你哦~~~