mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-28 21:32:20 +00:00
edit symbol
This commit is contained in:
parent
7c51ca6cf2
commit
7b97e0abfe
@ -12,13 +12,13 @@ Iterator的遍历过程是这样的。
|
||||
|
||||
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
|
||||
|
||||
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
|
||||
(2)第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。
|
||||
|
||||
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
|
||||
(3)第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。
|
||||
|
||||
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
|
||||
(4)不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。
|
||||
|
||||
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
|
||||
每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value`和`done`两个属性的对象。其中,`value`属性是当前成员的值,`done`属性是一个布尔值,表示遍历是否结束。
|
||||
|
||||
下面是一个模拟next方法返回值的例子。
|
||||
|
||||
@ -41,13 +41,28 @@ function makeIterator(array){
|
||||
}
|
||||
```
|
||||
|
||||
上面代码定义了一个makeIterator函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。对数组`['a', 'b']`执行这个函数,就会返回该数组的遍历器对象(即指针对象)it。
|
||||
上面代码定义了一个`makeIterator`函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。对数组`['a', 'b']`执行这个函数,就会返回该数组的遍历器对象(即指针对象)`it`。
|
||||
|
||||
指针对象的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向a;第二次调用,指向b。
|
||||
指针对象的`next`方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用`next`方法,指针就会指向数组的下一个成员。第一次调用,指向`a`;第二次调用,指向`b`。
|
||||
|
||||
next方法返回一个对象,表示当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。
|
||||
`next`方法返回一个对象,表示当前数据成员的信息。这个对象具有`value`和`done`两个属性,`value`属性返回当前位置的成员,`done`属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用`next`方法。
|
||||
|
||||
总之,调用指针对象的next方法,就可以遍历事先给定的数据结构。
|
||||
总之,调用指针对象的`next`方法,就可以遍历事先给定的数据结构。
|
||||
|
||||
对于遍历器对象来说,`done: false`和`value: undefined`属性都是可以省略的,因此上面的`makeIterator`函数可以简写成下面的形式。
|
||||
|
||||
```javascript
|
||||
function makeIterator(array){
|
||||
var nextIndex = 0;
|
||||
return {
|
||||
next: function(){
|
||||
return nextIndex < array.length ?
|
||||
{value: array[nextIndex++]} :
|
||||
{done: true};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
由于Iterator只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器对象,或者说用遍历器对象模拟出数据结构。下面是一个无限运行的遍历器对象的例子。
|
||||
|
||||
@ -70,9 +85,9 @@ function idMaker(){
|
||||
}
|
||||
```
|
||||
|
||||
上面的例子中,遍历器生成函数idMaker,返回一个遍历器对象(即指针对象)。但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
|
||||
上面的例子中,遍历器生成函数`idMaker`,返回一个遍历器对象(即指针对象)。但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
|
||||
|
||||
在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
|
||||
在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被`for...of`循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了`Symbol.iterator`属性(详见下文),另外一些数据结构没有。凡是部署了`Symbol.iterator`属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
|
||||
|
||||
如果使用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下。
|
||||
|
||||
|
@ -785,7 +785,7 @@ map.set(Symbol(), 2)
|
||||
// TypeError: Invalid value used as weak map key
|
||||
```
|
||||
|
||||
上面代码中,如果将1和`Symbol`作为WeakMap的键名,都会报错。
|
||||
上面代码中,如果将`1`和`Symbol`作为WeakMap的键名,都会报错。
|
||||
|
||||
`WeakMap`的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,`WeakMap`自动移除对应的键值对。典型应用是,一个对应DOM元素的`WeakMap`结构,当某个DOM元素被清除,其所对应的`WeakMap`记录就会自动被移除。基本上,`WeakMap`的专用场合就是,它的键所对应的对象,可能会在将来消失。`WeakMap`结构有助于防止内存泄漏。
|
||||
|
||||
@ -832,7 +832,7 @@ myElement.addEventListener('click', function() {
|
||||
}, false);
|
||||
```
|
||||
|
||||
上面代码中,myElement是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在WeakMap里,对应的键名就是myElement。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险。
|
||||
上面代码中,`myElement`是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在WeakMap里,对应的键名就是`myElement`。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险。
|
||||
|
||||
WeakMap的另一个用处是部署私有属性。
|
||||
|
||||
@ -863,4 +863,4 @@ c.dec()
|
||||
// DONE
|
||||
```
|
||||
|
||||
上面代码中,Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。
|
||||
上面代码中,Countdown类的两个内部属性`_counter`和`_action`,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## 概述
|
||||
|
||||
ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。
|
||||
ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。
|
||||
|
||||
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
|
||||
|
||||
@ -72,6 +72,21 @@ String(sym) // 'Symbol(My symbol)'
|
||||
sym.toString() // 'Symbol(My symbol)'
|
||||
```
|
||||
|
||||
另外,Symbol值也可以转为布尔值,但是不能转为数值。
|
||||
|
||||
```javascript
|
||||
var sym = Symbol();
|
||||
Boolean(sym) // true
|
||||
!sym // false
|
||||
|
||||
if (sym) {
|
||||
// ...
|
||||
}
|
||||
|
||||
Number(sym) // TypeError
|
||||
sym + 2 // TypeError
|
||||
```
|
||||
|
||||
## 作为属性名的Symbol
|
||||
|
||||
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
|
||||
@ -145,6 +160,26 @@ log(log.levels.DEBUG, 'debug message');
|
||||
log(log.levels.INFO, 'info message');
|
||||
```
|
||||
|
||||
下面是另外一个例子。
|
||||
|
||||
```javascript
|
||||
const COLOR_RED = Symbol();
|
||||
const COLOR_GREEN = Symbol();
|
||||
|
||||
function getComplement(color) {
|
||||
switch (color) {
|
||||
case COLOR_RED:
|
||||
return COLOR_GREEN;
|
||||
case COLOR_GREEN:
|
||||
return COLOR_RED;
|
||||
default:
|
||||
throw new Error('Undefined color');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
常量使用Symbol值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的`switch`语句会按设计的方式工作。
|
||||
|
||||
还有一点需要注意,Symbol值作为属性名时,该属性还是公开属性,不是私有属性。
|
||||
|
||||
## 属性名的遍历
|
||||
|
Loading…
x
Reference in New Issue
Block a user