mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
修改string/模板字符串
This commit is contained in:
parent
ed562a470f
commit
78b4c2f282
@ -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。
|
||||
|
@ -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这个词是“生成器”的意思)。
|
||||
|
@ -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`方法。
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
118
docs/string.md
118
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);
|
||||
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user