1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 10:22:23 +00:00

修改string/模板字符串

This commit is contained in:
Ruan Yifeng 2015-01-27 21:52:28 +08:00
parent ed562a470f
commit 78b4c2f282
6 changed files with 284 additions and 21 deletions

View File

@ -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。

View File

@ -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语句。正常函数只能返回一个值因为只能执行一次returnGenerator函数可以返回一系列的值因为可以有任意多个yield。从另一个角度看也可以说Generator生成了一系列的值这也就是它的名称的来历在英语中generator这个词是“生成器”的意思

View File

@ -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`方法。

View File

@ -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: whats 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

View File

@ -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

View File

@ -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);
```