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的新特性吧!
什么是 Generator 函数
在传统函数中,函数一旦开始执行,就会运行到结束或者遇到 return 语句提前结束,期间不会有其他的语句能够终止函数的执行,而 Generator 函数可以在函数运行期间暂停,也就是打破函数的完整运行。
实际上他是 ES6 提出的一种异步编程解决方案,旨在实现在JS中优雅的实现异步编程,避免回调地狱现象。
创建一个 Generator
function * test() {
yield 'a';
yield 'b';
return 'c';
}
var gene = test()
gene.next()
// {value:'a',done:false}
gene.next()
// {value:'b',done:false}
gene.next()
// {value:'c',done:true}
gene.next()
// {value:undefined,done:true}
上面代码中,通过 function * ,创建一个 Generator 函数,在函数体内使用yield 语句,为 Generator 函数定义了不同的内部状态,a,b和c三个状态。
Generator 函数的执行返回的是一个 iterator 对象,我们通过 next() 方法可以依次遍历 generator 中的多个状态,yield 语句后面的表达式的值,作为next 语句返回的 value 值输出。
可以这样认为,yield 语句像是在 generator 函数里防止了很多个暂停点,而next 语句可以恢复暂停点的执行。
funtion 关键字和函数名之间的星号,放在哪里都可以,也就是下面这四种写法都正确。
function* test() {...}
function * test() {...}
function *test() {...}
function*test() {...}
有关 iterator 遍历器的详细信息,请移步本系列文章ES6 新特性梳理系列丨Iterator 和 for...of 循环
yield语句
我们来回顾一下上面的 test 函数。
当第一次调用 next() 时,遇到了第一个 yield 语句,将第一个 yield 语句后面的 'a' 作为返回的对象的 value 属性值;
第二次调用 next() 遇到了第二个 yield 语句,将第一个 yield 语句后面的 'b' 作为返回的对象的 value 属性值;
第三次调用 next() ,没有遇到 yield 语句,我们遇到了 return 语句,将 return 语句后面的 'c' 作为返回的对象的 value 值,并且将 done 设为 true,表示 generator 中的状态已经遍历结束了;
第四次调用 next(),此时后面已经没有 yield 和 return 了,我们返回的对象的 value 为 undefined,done 为 true。
值得注意的是,yield 后面的表达式,并不会立即执行,只有当手动调用next() 函数时才会执行。一个 generator 函数中,可以包含多个 yield 语句,但只能包含一个 return 语句。
next()
需要注意,yield 语句并没有返回值,或者说总是返回 undefined。注意不要和上面所说的弄混,yield 语句后面的表达式将会作为 next() 函数返回的对象的 value 属性值,和这里 yield 语句本身并无返回值是两个概念。
next() 方法可以包含一个参数,这个参数将会作为上一个 yield 语句的返回值。
我们引入一个例子进行说明:
function * test(x) {
var y = yield(x+1)
var z = yield(y+1)
return x+y+z
}
var a = test(2);
a.next() // Object{value:3, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = test(2);
b.next() // { value:3, done:false }
b.next(5) // { value:6, done:false }
b.next(10) // { value:17, done:true }
上面代码中,先说上半部分。var a = test(2),为x赋值2,那么返回的值就是2+1,为3;再次调用 next(),执行第二个 next(),此时由于yield语句返回的值为 undefined,所以 y+1,等同于 undefined + 1,返回 NaN ;再次调用next(),执行 return 语句,由于第二个yield语句返回值为 undefined,所以x+y+z,等同于 3+undefined+undefined,返回 NaN。
再说下半部分,var b = test(2),为x赋值2,第一次调用,那么返回的值就是2+1,为3;第二次调用 next(),参数5作为第一个 yield 语句的返回结果,也就相当于 var y = 5,则第二个 yield 语句中的表达式就变成 yield(5+1),返回6;第三次调用 next(),执行 return 语句,参数10作为第二个 yield语句的返回结果,也就相当于 var z = 10 ,最终执行结果相当于 return 2+5+10,结果为17。
注意,由于 next 方法的参数作为上一个 yield 语句的返回值,所以第一个next 方法不能带有参数,即使带了,JS 也会自动忽略第一个 next 方法的参数。
for...of循环
我们上一篇讲过,for...of 循环专门为 iterator 遍历器而生,而 generator 函数相当于一个 iterator 生成器,所以 for...of 循环可以自动遍历 generator 函数,并且不需要调用 next 方法。
function * test() {
yield 1;
yield 2;
return 3;
}
for (let i of test()) {
console.log(i);
}
// 1 2
需要注意的是,generator 函数中的 return 语句执行时,返回对象中的 done 为 true,而 for...of 循环遇到返回对象的 done 为 true,就会立即终止循环,所以遍历返回结果并不包含 return 语句中的3。
yield* 语句
当我们在一个 generator 函数内部调用另一个 generator 函数时,就要使用yield* 语句。
function * test1() {
yield '1';
yield '2';
}
function * test2() {
yield * test1();
yield '3';
yield '4';
}
for (let i of test2()){
console.log(i);
}
// 1 2 3 4
ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩: