diff --git a/docs/class.md b/docs/class.md index f8832e9..32a0af0 100644 --- a/docs/class.md +++ b/docs/class.md @@ -153,6 +153,24 @@ import { lastName as surname } from './profile'; ``` +ES6支持多重加载,即所加载的模块中又加载其他模块。 + +```javascript + +import { Vehicle } from './Vehicle'; + +class Car extends Vehicle { + move () { + console.log(this.name + ' is spinning wheels...') + } +} + +export { Car } + +``` + +上面的模块先加载Vehicle模块,然后在其基础上添加了move方法,再作为一个新模块输出。 + **(2)模块的整体输入,module命令** export命令除了输出变量,还可以输出方法或类(class)。下面是一个circle.js文件,它输出两个方法area和circumference。 diff --git a/docs/generator.md b/docs/generator.md index f2b9cf3..34c84d9 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -52,6 +52,21 @@ hw.next() 总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。 +由于Generator函数本身就能生成遍历器,所以它的Symbol.iterator属性指向自身。 + +```javascript + +function* gen(){ + // some code +} + +gen[Symbol.iterator]() === gen +// true + +``` + +上面代码中,gen是一个Generator函数,它的Symbol.iterator属性就指向它自己。 + 由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个成员,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。另一方面,由于yield后面的表达式,直到调用next方法时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。 yield语句与return语句有点像,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。 diff --git a/docs/iterator.md b/docs/iterator.md index d63d61c..ebe3589 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -2,6 +2,8 @@ ## Iterator(遍历器) +### 语法 + 遍历器(Iterator)是一种接口规格,任何对象只要部署这个接口,就可以完成遍历操作。它的作用有两个,一是为各种数据结构,提供一个统一的、简便的接口,二是使得对象的属性能够按某种次序排列。在ES6中,遍历操作特指for...of循环,即Iterator接口主要供for...of循环使用。 遍历器提供了一个指针,指向当前对象的某个属性,使用next方法,就可以将指针移动到下一个属性。next方法返回一个包含value和done两个属性的对象。其中,value属性是当前遍历位置的值,done属性是一个布尔值,表示遍历是否结束。下面是一个模拟next方法返回值的例子。 @@ -51,9 +53,15 @@ it.next().value // '2' ``` -上面的例子,只是为了说明next方法返回值的结构。Iterator接口返回的遍历器,原生具备next方法,不用自己部署。所以,真正需要部署的是Iterator接口,让其返回一个遍历器。在ES6中,有三类数据结构原生具备Iterator接口:数组、类似数组的对象、Set和Map结构。除此之外,其他数据结构(主要是对象)的Iterator接口都需要自己部署。 +上面的例子,说明了next方法返回值的结构:value和done两个属性。 -下面就是如何部署Iterator接口。一个对象如果要有Iterator接口,必须部署一个@@iterator方法(原型链上的对象具有该方法也可),该方法部署在一个键名为`Symbol.iterator`的属性上,对应的键值是一个函数,该函数返回一个遍历器对象。 +### Iterator接口的部署 + +具有Iterator接口的对象,都能被for...of循环遍历(见后文的介绍)。所谓Iterator接口,就是指它会返回一个遍历器对象,该对象具备next方法,每次调用该方法,会依次返回一个具有上节提到的value和done两个属性的新对象,指向原对象的一个成员。 + +在ES6中,有三类数据结构原生具备Iterator接口:数组、类似数组的对象、Set和Map结构。除此之外,其他数据结构(主要是对象)的Iterator接口都需要自己部署。其他对象需要手动部署Iterator接口,让其返回一个遍历器。 + +一个对象如果要有Iterator接口,必须部署一个@@iterator方法(原型链上的对象具有该方法也可),该方法部署在一个键名为`Symbol.iterator`的属性上,对应的键值是一个函数,该函数返回一个遍历器对象。 ```javascript @@ -69,7 +77,59 @@ class MySpecialTree { 上面代码是一个类部署Iterator接口的写法。`Symbol.iterator`是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一节)。这里要注意,@@iterator的键名是`Symbol.iterator`,键值是一个方法(函数),该方法执行后,返回一个当前对象的遍历器。 -下面是为对象添加Iterator接口的例子。 +下面是一个例子。 + +```javascript + +function O(value){ + this.value = value; + this.next = null; +} + +O.prototype[Symbol.iterator] = function(){ + + var iterator = { + next: next + }; + + var current = this; + + function next(){ + if (current){ + var value = current.value; + var done = current == null; + current = current.next; + return { + done: done, + value: value + } + } else { + return { + done: true + } + } + } + return iterator; +} + +var one = new O(1); +var two = new O(2); +var three = new O(3); +one.next = two; +two.next = three; + +for (var i of one){ + console.log(i) +} +// 1 +// 2 +// 3 + +``` + +上面代码首先在构造函数的原型链上部署Symbol.iterator方法,调用该方法会返回遍历器对象iterator,调用该对象的next方法,在返回一个值的同时,自动将内部指针移到下一个实例。 + +下面是另一个为对象添加Iterator接口的例子。 ```javascript @@ -95,6 +155,22 @@ let obj = { ``` +如果`Symbol.iterator`方法返回的不是遍历器,解释引擎将会报错。 + +```javascript + +var obj = {}; + +obj[Symbol.iterator] = () => 1; + +[...obj] // TypeError: [] is not a function + +``` + +上面代码中,变量obj的@@iterator方法返回的不是遍历器,因此报错。 + +### 原生具备iterator接口的数据结构 + 《数组的扩展》一章中提到,ES6对数组提供entries()、keys()和values()三个方法,就是返回三个遍历器。 ```javascript @@ -159,6 +235,8 @@ str // "hi" 上面代码中,字符串str的`Symbol.iterator`方法被修改了,所以扩展运算符(...)返回的值变成了bye,而字符串本身还是hi。 +### Iterator接口与Generator函数 + 部署`Symbol.iterator`方法的最简单实现,还是使用下一节要提到的Generator函数。 ```javascript @@ -189,20 +267,6 @@ for (let x of obj) { ``` -如果`Symbol.iterator`方法返回的不是遍历器,解释引擎将会报错。 - -```javascript - -var obj = {}; - -obj[Symbol.iterator] = () => 1; - -[...obj] // TypeError: [] is not a function - -``` - -上面代码中,变量obj的@@iterator方法返回的不是遍历器,因此报错。 - ## for...of循环 ES6中,一个对象只要部署了@@iterator方法,就被视为具有iterator接口,就可以用for...of循环遍历它的值。也就是说,for...of循环内部调用是原对象的`Symbol.iterator`方法。 diff --git a/docs/reference.md b/docs/reference.md index bd957fd..80b0589 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -19,6 +19,8 @@ - Luke Hoban, [ES6 features](https://github.com/lukehoban/es6features) - Traceur-compiler, [Language Features](https://github.com/google/traceur-compiler/wiki/LanguageFeatures): Traceur文档列出的一些ES6例子 - Axel Rauschmayer, [ECMAScript 6: what’s next for JavaScript?](https://speakerdeck.com/rauschma/ecmascript-6-whats-next-for-javascript-august-2014): 关于ES6新增语法的综合介绍,有很多例子 +- Toby Ho, [ES6 in io.js](http://davidwalsh.name/es6-io) + ## 语法点 @@ -31,7 +33,12 @@ - Dmitry Soshnikov, [ES6 Notes: Default values of parameters](http://dmitrysoshnikov.com/ecmascript/es6-notes-default-values-of-parameters/): 介绍参数的默认值 - Mozilla Developer Network, [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet):介绍WeakSet数据结构 - Axel Rauschmayer, [ECMAScript 6: maps and sets](http://www.2ality.com/2015/01/es6-maps-sets.html): Set和Map结构的详细介绍 + +## 字符串 + - Mathias Bynens, [Unicode-aware regular expressions in ES6](https://mathiasbynens.be/notes/es6-unicode-regex): 详细介绍正则表达式的u修饰符 +- Nicholas C. Zakas, [A critical review of ECMAScript 6 quasi-literals](http://www.nczonline.net/blog/2012/08/01/a-critical-review-of-ecmascript-6-quasi-literals/) +- Mozilla Developer Network, [Template strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings) ## Object diff --git a/docs/set-map.md b/docs/set-map.md index 2ec638c..51023e4 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -256,6 +256,16 @@ WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set 首先,WeakSet的成员只能是对象,而不能是其他类型的值。其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。 +```javascript + +var ws = new WeakSet(); +ws.add(1) +// TypeError: Invalid value used in weak set + +``` + +上面代码试图向WeakSet添加一个数值,结果报错。 + WeakSet是一个构造函数,可以使用new命令,创建WeakSet数据结构。 ```javascript @@ -303,6 +313,25 @@ ws.clear(); ``` +WeakSet没有size属性,没有办法遍历它的成员。 + +```javascript + +ws.size +// undefined + +ws.forEach(function(item){ console.log('WeakSet has ' + item)}) +// TypeError: undefined is not a function + +ws.forEach +// undefined + +``` + +上面代码试图获取size和forEach属性,结果都不能成功。 + +WeakSet不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保存成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。 + ## Map **(1)基本用法** @@ -611,24 +640,36 @@ WeakMap的设计目的在于,键名是对象的弱引用(垃圾回收机制 ```javascript -var map = new WeakMap(); +var wm = new WeakMap(); var element = document.querySelector(".element"); -map.set(element, "Original"); +wm.set(element, "Original"); -var value = map.get(element); +var value = wm.get(element); console.log(value); // "Original" element.parentNode.removeChild(element); element = null; -value = map.get(element); +value = wm.get(element); console.log(value); // undefined ``` WeakMap与Map在API上的区别主要是两个,一是没有遍历操作(即没有key()、values()和entries()方法),也没有size属性;二是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。 +```javascript + +var wm = new WeakMap(); + +wm.size +// undefined + +wm.forEach +// undefined + +``` + WeakMap的一个用处是部署私有属性。 ```javascript diff --git a/docs/string.md b/docs/string.md index 3f4c042..3eb18af 100644 --- a/docs/string.md +++ b/docs/string.md @@ -402,6 +402,15 @@ console.log(`${obj.x + obj.y}`) ``` +如果模板字符串中的变量没有声明,将报错。 + +```javascript + +var msg = `Hello, ${place}`; +// throws error + +``` + 模板字符串使得字符串与变量的结合,变得容易。下面是一个例子。 ```javascript @@ -412,3 +421,112 @@ if (x > MAX) { } ``` + +模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。 + +```javascript + +var a = 5; +var b = 10; + +tag`Hello ${ a + b } world ${ a * b}`; + +``` + +上面代码中,模板字符串前面有一个函数tag,整个表达式将返回tag处理模板字符串后的返回值。 + +函数tag依次接受三个参数。第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。第一个参数之后的参数,都是模板字符串各个变量被替换后的值。 + +- 第一个参数:['Hello ', ' world '] +- 第二个参数: 15 +- 第三个参数:50 + +```javascript + +var a = 5; +var b = 10; + +function tag(s, v1, v2) { + console.log(s[0]); // "Hello " + console.log(s[1]); // " world " + console.log(v1); // 15 + console.log(v2); // 50 + + return "OK"; +} + +tag`Hello ${ a + b } world ${ a * b}`; +// "OK" + +``` + +下面是一个更复杂的例子。 + +```javascript + +var total = 30; +var msg = passthru`The total is ${total} (${total*1.05} with tax)`; + +function passthru(literals) { + var result = ""; + var i = 0; + + while (i < literals.length) { + result += literals[i++]; + if (i < arguments.length) { + result += arguments[i]; + } + } + + return result; + +} + +msg +// "The total is 30 (31.5 with tax)" + +``` + +上面这个例子展示了,如何将各个参数按照原来的位置拼合回去。 + +处理函数的第一个参数,还有一个raw属性。它也是一个数组,成员与处理函数的第一个参数完全一致,唯一的区别是字符串被转义前的原始格式,这是为了模板函数处理的方便而提供的。 + +```javascript + +tag`First line\nSecond line` + +``` + +上面代码中,tag函数的第一个参数是一个数组`["First line\nSecond line"]`,这个数组有一个raw属性,等于`["First line\\nSecond line"]`,两者唯一的区别就是斜杠被转义了。 + +function tag(strings) { + console.log(strings.raw[0]); + // "First line\\nSecond line" +} + +## String.raw() + +String.raw方法,往往用来充当模板字符串的处理函数,返回字符串被转义前的原始格式。 + +```javascript + +String.raw`Hi\n${2+3}!`; +// "Hi\\n5!" + +String.raw`Hi\u000A!`; +// 'Hi\\u000A!' + +``` + +String.raw方法也可以正常的函数形式使用。这时,它的第一个参数,应该是一个具有raw属性的对象,且raw属性的值应该是一个数组。 + +```javascript + +String.raw({ raw: 'test' }, 0, 1, 2); +// 't0e1s2t' + + +// 等同于 +String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2); + +```