摘要:下例实现了一个数组的迭代器在中,可迭代数据结构比如数组都必须实现一个名为的方法,该方法返回一个该结构元素的迭代器。原话是还可以传递返回值。
前记
按照规划,明年年中,ECMAScript 6(ES6)就要正式发布了。
最近抽空看了Dr. Axel Rauschmayer的几篇文章和演讲PPT,对新特性有了些了解。
趁没忘,抓紧记录下,夹杂自己的感受。
计划分三部分:
新语法
面对对象和模块化
标准库扩充
参考了以下文章/PPT:
Use ECMAScript 6 today
Ecmascript 6 Whats next for Javascript
es6 features
ECMAScript 6: arrow functions and method definitions
Callable entities in ECMAScript 6
Iterators and generators in ECMAScript 6
其他文章:
ECMAScript 6新特性印象之二:面对对象和模块化
总体印象的确是「design by champions」。各种为了代码书写效率进行的优化,借鉴了近年各种「新」语言的优秀特性,灵活性大大提升,阅读难度也提升了……
不过,熟悉Ruby的看了这些会放心不少吧。
新语法1.块级作用域 关键字let, const
function order(x, y) { if (x > y) { let tmp = x; x = y; y = tmp; } console.log(tmp === x); // 引用错误:tmp此时未定义 return [x,y]; }
JS终于有了块级作用域变量。虽然在代码结构层面没有太大的作用(以前没有时也活得很好么,虽然不怎么舒服),但会让代码更加准确,更易于阅读。
今年夏天发布的Swift中也增加了let关键字,虽然有些许区别,但目的应该是差不多——提升代码可读性。
2.对象字面量的属性赋值简写 property value shorthand
let first = "Bob"; let last = "Dylan"; let singer = { first, last }; console.log(singer.first + " " + singer.last); // Bob Dylan
这对于经常使用对象作为配置属性参数的苦主来说,算个小小的抚慰了。估计重复添加同一属性会报错吧,没有验证。
3.方法定义 Method definitions
let obj = { myMethod(arg0, arg1) { ... } };
避免了在对象定义中出现function关键字,更加清晰明确地分离出函数的三种用途。
4.赋值解构 Destructuring
let singer = { first: "Bob", last: "Dylan" }; let { first: f, last: l } = singer; // 相当于 f = "Bob", l = "Dylan"
依然是为了方便。以后代码头部的「变量定义区域」不会有太多行了。
数组也是可以的,下面这个例子特别棒:
let [all, year, month, day] = /^(ffffdd)-(dd)-(dd)$/.exec("2014-08-31"); let [x, y] = [1, 2, 3]; // x = 1, y = 2
当然也可以这样,但有些……:
function f([x]) {...} // 参数定义 f(["Blonde on Blonde"]);
下面是几种错误用法(Refutable):
let { a: x, b: y } = {a: 3}; // TypeError let [x, y] = ["a"]; // TypeError
更重要的是,支持默认值,在形式不匹配或目标值undefined时有效:
let { a: x, b: y=5 } = {a: 3, b: undefined }; // x = 3, y = 5 let [x, y="b"] = ["a"]; // x = "a", y = "b"
5.函数的多项返回值 Multiple return values
function findSong(songs, songTitle) { for (let trackNumber = 0; trackNumber < songs.length; trackNumber++) { let song = songs[trackNumber]; if(songTitle ===song.title) { return {song, trackNumber}; } } return {song: undefined, trackNumber: -1} } let songList = ["Tombstone blues", "Don"t think twice", "North country girl"]; let {song, trackNumber} = findSong(songList, "North country girl"); // song = "North country girl", trackNumber = 2;
因为赋值解构,所以也可以这样:
let {song} = findSong(...); let {trackNumber} = findSong(...); let {trackNumber, song} = findSong(...); // 变量顺序不重要
其实就是返回个对象。
但也有个问题,变量名一定要与函数返回对象的属性名相同,这可以会是一个别扭点。
6.函数参数 - 默认值
function findArtist(name="", genre="") { ... }
没什么好说的,以后不用再写var option = option || {}了。
7.函数参数 - 参数打包 Rest parameters
function createArtistProfile(name, ...details) { .. // details是个数组 }
所以,以后也不需要arguments了。不过,看例子只是「1,rest」,不知可不可以「1,2,3,rest」。
8.函数参数 - 数组展开 Spread parameters
Math.max(...[1,11,111]); // 111
算是参数打包的逆操作,以后不用写[1,2,3].apply(Math.max)这类代码了。
9.函数参数 - 指名参数 Named parameters
function func(arg0, {opt1, opt2}) { return [opt1, opt2]; } func(0, {opt1: "a", opt2: "b"}) // ["a", "b"]
同样是通过对象带来的变化。有个复杂点的例子:
class Entries { // ... selectEntries({ from = 0, to = this.length } = {}) { // Long: { from: from=0, to: to=this.length } // Use `from` and `to` } } let entries = new Entries(); entries.selectEntries({ from: 5, to: 15 }); entries.selectEntries({ from: 5 }); entries.selectEntries({ to: 15 });
指名参数+赋值解构+默认参数,看着反而有点混乱了……自由度大自然带来阅读难度的上升,这又是一个权衡点。
10.胖箭头函数 Arrow functions
let bob = { name: "Bob Dylan", holdConcert: function (songList) { songList.forEach(song => { console.log(this.name + " sang " + song) }); } }
这里形式上借鉴了CoffeeScript里「fat arrow」(ES6对执行和内存上有优化)。Arrow functions主要做了两件事:
简化了代码形式,默认return表达式结果。
自动绑定语义this,即定义函数时的this。如上面例子中,forEach的匿名函数参数中用到的this。
来看几个例子:
let squares = [ 1, 2, 3 ].map(x => x * x); x => x + this.y // 相当于 function(x) { return x + this.y }.bind(this) // 但胖箭头在执行效率上会更高
胖箭头函数与正常函数的区别:
胖箭头在创建时即绑定this(lexical this);正常函数的this是在执行时动态传入的(dynamic this)。
胖箭头没有内部方法[[Construct]]和属性原型,所以new (() => {})是会报错的。
胖箭头没有arguments变量。
这样,以后在定义方法/函数时,就有了清晰的选择:
定义子程序(subroutine),用胖箭头,自动获得语义this。
定义方法(method),用正常函数,动态this。而且可以用方法定义特性简写代码,避免function关键字出现。
11.字符串模板 Template strings
templateHandler`Hello ${first} ${last}!`
${first}这样的结构在Ruby的字符串处理很常见,first是动态替换的部分。templateHandler是替换后的处理函数。
当然也可以不要handler,那就仅仅是模板替换了:
if(x > MAX) { throw new Error(`At most ${MAX} allowed: $(x)!`); }
Template strings支持多行,其间的文本也不会被转码:
var str = String.raw`This is a text with multiple lines. Escapes are not interpreted, is not a newline.`;
结合不同的handler,用法多样,比如正则:
let str = "Bob Dylan - 2009 - Together Through Life"; let albumInfo = str.match(XRegExp.rx` ^(?[^/]+ ) - (? d{4}) - (? [^/]+)$ `); console.log(albumInfo.year); // 2009
12.迭代器 Iterators
稍微熟悉函数式编程(Python,Ruby也可以)的朋友对着这个概念应该都不陌生。ES6参考了Python的设计,迭代器有个next方法,调用会返回:
返回迭代对象的一个元素:{ done: false, value: elem }
如果已到迭代对象的末端:{done: true[, value: retVal] }
上面第二种情况中的条件返回部分是为了递归调用生成器而设计的(迭代器其实是生成器的应用之一),具体说明参见这篇文章的对应部分。
下例实现了一个数组的迭代器:
function createArrayIterator(arr) { let index = 0; return { next() { if (index < arr.length) { return { done: false, value: arr[index++] }; else { return { done: true } } } } } let arr = [1,2,3]; let iter = createArrayIterator(arr); console.log(iter.next()); // 1 console.log(iter.next()); // 2
在ES6中,可迭代数据结构(比如数组)都必须实现一个名为Symbol.iterator的方法,该方法返回一个该结构元素的迭代器。注意,Symbol.iterator是一个Symbol,Symbol是ES6新加入的原始值类型。
针对可迭代的数据结构,ES6还引入了一个新的遍历方法 for-of。再举个例子,改造下上例中的createArrayIterator:
function createArrayIterator(arr) { let index = 0; return { [Symbol.iterator]() { return this; // 因为本身就是个迭代器 }, next() { ... } } } let arr = [1, 2, 3]; for(x of createArrayIterator(arr)) { // 注意看 console.log(x); }
当然,ES6中的数组本身就是可迭代的,上例仅仅是为了展示而已。
13.生成器 Generators
ES6的生成器同样借鉴了Python,通过操作符yield来挂起、继续。
生成器的写法比较怪异,使用了关键字function*:
function* generatorFunction() { yield 1; yield 2; }
生成器返回一个对象,用来控制生成器执行,这个对象是可迭代的:
let genObj = generatorFunction(); genObj.next(); // { done: false, value: 1 } genObj.next(); // { done: false, value: 2 } genObj.next(); // { done: true }
下面这个例子演示了可递归调用的生成器,用到了操作符yield*:
function* iterTree(tree) { if (Array.isArray(tree)) { for (let i = 0; i < tree.length; i++) { yield* iterTree(tree[i]); // (*) } } else { yield tree; } }
yield*会交出(yield)全部迭代对象,而不仅仅是一个元素值。原话是「yield* in line (*) yields everything that is yielded by the iterable that is its operand. 」
yield*还可以传递返回值。如:
let result1 = yield* step(); // step也是个generator
这个例子不太好,或者说,ES6的这部分实现有点繁琐,需要更多示例才能理解这个特性。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/87626.html
摘要:本文参考了以下文章之前的文章新特性印象之一新语法面对对象关键字看上面例子就能明白。定义类的,配合创建新对象。继承非构造器对象的原型是。错误检查继承的目标一定要是个对象或者。的构造器是可改写,但不可枚举。引入了一个标签,负责载入模块。 本文参考了以下文章/PPT: Use ECMAScript 6 today Ecmascript 6 Whats next for Javascrip...
摘要:正大力推进,网景通讯公司即将与他们达成一项协议,让可以用在浏览器上。年月,网景通讯公司和达成协议将被重新命名为,它将会作为浏览器中小型客户端任务的一种脚本语言,同时将会被提升为一种更大的开发富组件的专业工具。 本文转载自:众成翻译译者:网络埋伏纪事审校: 为之漫笔链接:http://www.zcfy.cc/article/2389原文:https://auth0.com/blog/a-...
摘要:移动端缓存失效是我印象最深的一个之一,为啥呢,因为这个问题导致我加班到很晚。的生命周期是仅在当前会话下有效。引入了一个浏览器窗口的概念,是在同源的窗口中始终存在的数据。无bug,不程序:作为程序员的我,不是修bug就是在写bug的路上。 移动端sessionStorage缓存失效是我印象最深的一个bug之一,为啥呢,因为这个问题导致我加班到很晚。在现在看来就是一个简单的概念问题。在我刚工作...
摘要:下一步我们将结果输出到文件。这是我们用编写的第一个非常简单的组建。使用将创建的组建导出以便在其它地方能够正常导入使用。 这是React和ECMAScript6结合使用系列文章的第一篇。 本文出自从零到壹全栈部落 下面是所有系列文章章节的链接: React 、 ES6 - 介绍(第一部分) React类、ES7属性初始化(第二部分) React类,方法绑定(第三部分) ES6中Reac...
摘要:随后,它出现在公司之后的浏览器,以及从微软从起发布的所有浏览器上。标准的第版在年月的大会上被表决接受。第版在年月底大会上被采纳。 前言 本系列译文的初衷旨在希望更多人能够了解关于JS的一些基本概念,遇到原理性的问题时多去翻翻文档,而不是在社区无休止的重复提出某些在文档中能够非常方便快捷就能找到的东西。 精力和水平有限,所以暂时只打算尝试翻译前面几章概括性的介绍,同时后面的章节大...
阅读 2744·2021-11-19 09:40
阅读 5293·2021-09-27 14:10
阅读 2099·2021-09-04 16:45
阅读 1461·2021-07-25 21:37
阅读 2993·2019-08-30 10:57
阅读 2980·2019-08-28 17:59
阅读 1054·2019-08-26 13:46
阅读 1406·2019-08-26 13:27