ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
JavaScript 与 ECMAScript
JavaScript 诞生于1995年,设计者是就职于 Netscape 公司的工程师 Brendan Eich。它是一门仅用10天就完成设计的编程语言,但至今为止已对业界保持26年的影响,且势头愈发强劲
1996年 Netscape 将 JavaScript 提交给ECMA,希望它可以成为“标准化一个通用的、跨平台的、中立于厂商的脚本语言的语法和语义标准”,1997年 ECMA 确定将 JavaScript 作为浏览器脚本语言的标准,并为之重命名为 ECMAScript,所以通常来讲我们将 ECMAScript 视为 JavaScript 的标准,而 JavaScript 则是 ECMAScript 的实现及扩展
1997年-1999年连续发布了ES1-ES3发布,2000年开始酝酿新版本的升级内容,中间因标准委员会意见未能达成一致,只做了部分功能的小范围升级及支持,于2009年12月发布了过渡版 ECMAScript 5.0,2011年6月发布 ECMAScript 5.1 并成为 ISO 国际标准
2013年3月 ECMAScript 6 草案冻结,2013年12月 ECMAScript 草案发布,2015年6月 ECMAScript 6 正式通过,成为新的国际标准。ES6 泛指自2015年升级为 ECMAScript 6.0 后的所有子版本,如:ES2015-ES2020,等同于ES6.0-ES6.5,这是 JavaScript 走向企业级编程语言的强势升级
不断升级的标准与实现,对于开发效率及产品质量起到强有力的支撑,接下来我们开始梳理ES6的新特性吧!
什么是异步操作
打个生活中的例子,异步就是你去买饭,告诉厨师开始做饭,然后做饭的时候你去旁边超市买饮料,等饭做好了,厨师给你打了个电话,然后你回到饭店开始吃饭。在告诉厨师开始做饭和吃饭的这个过程中,你去买了瓶饮料。
将一个任务分成了两节,开始你做了一节,然后去做别的事情了,等做好了准备,再回头执行第二节。
那么相比较同步就很明显了,就是你告诉厨师开始做饭以后,你就在饭店等着,等着吃饭,吃完了饭,你再去买的饮料。连续的执行就叫做同步,由于是连续执行,不能插入其他任务,所以你从做饭到吃饭的这段时间,你什么也做不了,只能等着。
回调函数
JavaScript 对异步编程的实现,就是回调函数。
回调函数,就是把任务分成两端,把第二段单独写在一个函数里,当任务执行成功后,就直接调用这个函数。英文名叫“callback”,中文意思就是“重新调用”。
比如,有一个需求,我们要在一个异步操作完成之后,进行一些其他操作,应该怎么办呢?(这里用定时器作为异步操作来举例)
setTimeout(() => {
var data = 'hello'
},1000)
在上面代码里,我们想要在定时器结束以后,输出这个 data 的值,应该怎么做呢?
// 错误做法
setTimeout(() => {
var data = 'hello'
},1000)
console.log(data)
// Uncaught ReferenceError: data is not defined
程序会优先执行同步操作,也就是执行第5行,但是当执行这行的时候,data还没有定义呢,所以程序报错。
// 正确做法
setTimeout(() => {
var data = 'hello'
console.log(data)
},1000)
// hello
上面代码将 console 语句移到了定时器内部,在1秒后会输出 data 的值。
但是,如果我们要进行的操作是不确定的呢?比如我们想在拿到这个 data 的时候,拼接上 ‘world’ 字符串怎么办呢?这时候就需要回调函数了。
// 回调函数写法
function fn(callback){
setTimeout(() => {
var data = 'hello'
callback(data)
},1000)
}
// 调用fn函数
fn(function(data){
console.log(data + 'world')
})
// helloworld
上面代码,调用 fn() 的时候,参数是一个函数,这个函数里面就是我们对异步操作结果 data 进行的操作。
Promise
本系列前面已经介绍过 Promise,这里我们直接用 Promise 来对上面的回调函数进行改造。
// promise 方式
var pro = new Promise(function(resolve,reject){
setTimeout(()=>{
var data = 'hello'
resolve(data)
},1000)
})
pro.then((data)=>{
console.log(data + 'world')
})
// helloworld
有关Promise的详细介绍,请移步ES6 新特性梳理系列丨Promise 对象
Generator
本系列前面已经介绍过 Generator,这里我们也是直接用 Generator 来对上面的回调函数进行改造,并且加上额外的需求,模拟获取接口请求,获取用户数据、订单数据、商品数据,但是是有先后顺序的,要先获取用户数据,才能获取订单数据,先获取订单数据,才可以获取商品数据。
// Generator 形式
// 获取用户数据
function getUsers(){
setTimeout(()=>{
var data = '用户数据'
// 调用next方法,并且将数据传入
iterator.next(data) // data 将会作为第一个yield语句的返回结果
},1000)
}
// 获取订单数据
function getOrders(){
setTimeout(()=>{
var data = '订单数据'
// 调用next方法,并且将数据传入
iterator.next(data) // data 将会作为第二个yield语句的返回结果
},1000)
}
// 获取商品数据
function getGoods(){
setTimeout(()=>{
var data = '商品数据'
// 调用next方法,并且将数据传入
iterator.next(data) // data 将会作为第三个yield语句的返回结果
},1000)
}
// 生成器函数
function * gen(){
let users = yield getUsers()
console.log(users) // 用户数据
let orders = yield getOrders()
console.log(orders) // 订单数据
let goods = yield getGoods()
console.log(goods) // 商品数据
}
// 调用生成器函数
let iterator = gen()
iterator.next()
// 运行第一个 yield语句(第27行),并且将结果返回,
// 本例中,此处返回结果是undefined
有关 Generator 的详细介绍,请移步ES6 新特性梳理系列丨Generator 函数
async
async 和 await 是ES8 的新特性,可以让异步代码像同步代码一样。
先来说 async 函数,async 函数的返回结果是一个 promise 对象,promise对象的结果由 async 函数执行的返回值决定。
async function fn(){
return 'hello'
}
console.log(fn())
// Promise {: "hello"}
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "hello"
上面返回的 Promise 结果是 fulfilled,成功的值是 hello。如果我们不返回值呢?
async function fn(){
return
}
console.log(fn())
// Promise {: undefined }
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: undefined
上面返回的 Promise 结果是 fulfilled,成功的值是 undefined。如果我们抛出一个错误呢?
async function fn(){
throw new Error('出错了')
}
console.log(fn())
// Promise {: Error: 出错了 }
// __proto__: Promise
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: Error: 出错了
上面返回的 Promise 结果是 rejected,失败的值是失败的 Error 对象。
现在让我们在 async 函数中写入一个 promise 对象。
async function fn(){
return new Promise((resolve,reject)=>{
resolve('成功')
})
}
console.log(fn())
// Promise {: "成功"}
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "成功"
async function foo(){
return new Promise((resolve,reject)=>{
reject('失败')
})
}
console.log(foo())
// Promise {: "失败"}
// __proto__: Promise
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: "失败"
可见,内部 promise 返回的值和状态,决定了 async 返回的 Promise 对象的值和状态。
await
现在我们再来看 await。
await 函数必须写在 async 函数内部,但是 async 内部不一定要有 await 函数;await 函数右侧是一个 Promise 对象,await 返回的是右侧 Promise 成功的值,如果 await 右边的 Promise 失败了,就会抛出一个异常,我们需要使用 try...catch 捕获处理。
var pro = new Promise((resolve,reject=>{
resolve('成功')
}))
async function fn(){
let data = await pro
console.log(data)
// 成功
}
var promise= new Promise((resolve,reject=>{
reject('失败')
}))
async function fn(){
try{
// promise 成功走这里
let data = await promise
console.log(data)
}catch(e){
// promise 失败走这里
console.log(e)
// 失败
}
}
最后,我们将 await 和 async 结合的方式,来对上一个获取用户数据、订单数据、商品数据的例子进行改造。
// 获取用户数据
function getUsers(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var data = '用户数据'
resolve(data)
},1000)
})
}
// 获取订单数据
function getOrders(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var data = '订单数据'
resolve(data)
},1000)
})
}
// 获取商品数据
function getGoods(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var data = '商品数据'
resolve(data)
},1000)
})
}
// 定义async函数
async function fn(){
let users = await getUsers()
let orders = await getOrders()
let goods = await getGoods()
console.log(users)
console.log(orders)
console.log(goods)
}
// 调用fn()
fn()
// '用户数据'
// '订单数据'
// '商品数据'
ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩:
ES6 新特性梳理系列丨Var + Let + Const 的区别与作用域
ES6 新特性梳理系列丨变量的解构赋值
ES6 新特性梳理系列丨Symbol - 新的数据类型
ES6 新特性梳理系列丨新的数据结构-Set
ES6 新特性梳理系列丨新的数据结构-Map
ES6 新特性梳理系列丨Proxy 和 Reflect
ES6 新特性梳理系列丨Iterator 和 for...of 循环
ES6 新特性梳理系列丨Generator 函数
ES6 新特性梳理系列丨Promise 对象
ES6 新特性梳理系列丨Module