Skip to content

Commit 7b97e0a

Browse files
committed
edit symbol
1 parent 7c51ca6 commit 7b97e0a

File tree

3 files changed

+64
-14
lines changed

3 files changed

+64
-14
lines changed

docs/iterator.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ Iterator的遍历过程是这样的。
1212

1313
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
1414

15-
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
15+
(2)第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。
1616

17-
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
17+
(3)第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。
1818

19-
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
19+
(4)不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。
2020

21-
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
21+
每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value``done`两个属性的对象。其中,`value`属性是当前成员的值,`done`属性是一个布尔值,表示遍历是否结束。
2222

2323
下面是一个模拟next方法返回值的例子。
2424

@@ -41,13 +41,28 @@ function makeIterator(array){
4141
}
4242
```
4343

44-
上面代码定义了一个makeIterator函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。对数组`['a', 'b']`执行这个函数,就会返回该数组的遍历器对象(即指针对象)it
44+
上面代码定义了一个`makeIterator`函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。对数组`['a', 'b']`执行这个函数,就会返回该数组的遍历器对象(即指针对象)`it`
4545

46-
指针对象的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向a;第二次调用,指向b
46+
指针对象的`next`方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用`next`方法,指针就会指向数组的下一个成员。第一次调用,指向`a`;第二次调用,指向`b`
4747

48-
next方法返回一个对象,表示当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法
48+
`next`方法返回一个对象,表示当前数据成员的信息。这个对象具有`value``done`两个属性,`value`属性返回当前位置的成员,`done`属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用`next`方法
4949

50-
总之,调用指针对象的next方法,就可以遍历事先给定的数据结构。
50+
总之,调用指针对象的`next`方法,就可以遍历事先给定的数据结构。
51+
52+
对于遍历器对象来说,`done: false``value: undefined`属性都是可以省略的,因此上面的`makeIterator`函数可以简写成下面的形式。
53+
54+
```javascript
55+
function makeIterator(array){
56+
var nextIndex = 0;
57+
return {
58+
next: function(){
59+
return nextIndex < array.length ?
60+
{value: array[nextIndex++]} :
61+
{done: true};
62+
}
63+
}
64+
}
65+
```
5166

5267
由于Iterator只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器对象,或者说用遍历器对象模拟出数据结构。下面是一个无限运行的遍历器对象的例子。
5368

@@ -70,9 +85,9 @@ function idMaker(){
7085
}
7186
```
7287

73-
上面的例子中,遍历器生成函数idMaker,返回一个遍历器对象(即指针对象)。但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
88+
上面的例子中,遍历器生成函数`idMaker`,返回一个遍历器对象(即指针对象)。但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
7489

75-
在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
90+
在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被`for...of`循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了`Symbol.iterator`属性(详见下文),另外一些数据结构没有。凡是部署了`Symbol.iterator`属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
7691

7792
如果使用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下。
7893

docs/set-map.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ map.set(Symbol(), 2)
785785
// TypeError: Invalid value used as weak map key
786786
```
787787
788-
上面代码中,如果将1和`Symbol`作为WeakMap的键名,都会报错。
788+
上面代码中,如果将`1``Symbol`作为WeakMap的键名,都会报错。
789789
790790
`WeakMap`的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,`WeakMap`自动移除对应的键值对。典型应用是,一个对应DOM元素的`WeakMap`结构,当某个DOM元素被清除,其所对应的`WeakMap`记录就会自动被移除。基本上,`WeakMap`的专用场合就是,它的键所对应的对象,可能会在将来消失。`WeakMap`结构有助于防止内存泄漏。
791791
@@ -832,7 +832,7 @@ myElement.addEventListener('click', function() {
832832
}, false);
833833
```
834834
835-
上面代码中,myElement是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在WeakMap里,对应的键名就是myElement。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险。
835+
上面代码中,`myElement`是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在WeakMap里,对应的键名就是`myElement`。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险。
836836
837837
WeakMap的另一个用处是部署私有属性。
838838
@@ -863,4 +863,4 @@ c.dec()
863863
// DONE
864864
```
865865
866-
上面代码中,Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。
866+
上面代码中,Countdown类的两个内部属性`_counter``_action`,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。

docs/symbol.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 概述
44

5-
ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。
5+
ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。
66

77
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
88

@@ -72,6 +72,21 @@ String(sym) // 'Symbol(My symbol)'
7272
sym.toString() // 'Symbol(My symbol)'
7373
```
7474

75+
另外,Symbol值也可以转为布尔值,但是不能转为数值。
76+
77+
```javascript
78+
var sym = Symbol();
79+
Boolean(sym) // true
80+
!sym // false
81+
82+
if (sym) {
83+
// ...
84+
}
85+
86+
Number(sym) // TypeError
87+
sym + 2 // TypeError
88+
```
89+
7590
## 作为属性名的Symbol
7691

7792
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
@@ -145,6 +160,26 @@ log(log.levels.DEBUG, 'debug message');
145160
log(log.levels.INFO, 'info message');
146161
```
147162

163+
下面是另外一个例子。
164+
165+
```javascript
166+
const COLOR_RED = Symbol();
167+
const COLOR_GREEN = Symbol();
168+
169+
function getComplement(color) {
170+
switch (color) {
171+
case COLOR_RED:
172+
return COLOR_GREEN;
173+
case COLOR_GREEN:
174+
return COLOR_RED;
175+
default:
176+
throw new Error('Undefined color');
177+
}
178+
}
179+
```
180+
181+
常量使用Symbol值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的`switch`语句会按设计的方式工作。
182+
148183
还有一点需要注意,Symbol值作为属性名时,该属性还是公开属性,不是私有属性。
149184

150185
## 属性名的遍历

0 commit comments

Comments
 (0)