1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-28 21:32:20 +00:00

edit symbol

This commit is contained in:
ruanyf 2015-10-27 10:04:10 +08:00
parent 7c51ca6cf2
commit 7b97e0abfe
3 changed files with 64 additions and 14 deletions

View File

@ -12,13 +12,13 @@ Iterator的遍历过程是这样的。
1创建一个指针对象指向当前数据结构的起始位置。也就是说遍历器对象本质上就是一个指针对象。 1创建一个指针对象指向当前数据结构的起始位置。也就是说遍历器对象本质上就是一个指针对象。
2第一次调用指针对象的next方法可以将指针指向数据结构的第一个成员。 2第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。
3第二次调用指针对象的next方法指针就指向数据结构的第二个成员。 3第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。
4不断调用指针对象的next方法直到它指向数据结构的结束位置。 4不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。
每一次调用next方法都会返回数据结构的当前成员的信息。具体来说就是返回一个包含value和done两个属性的对象。其中value属性是当前成员的值done属性是一个布尔值表示遍历是否结束。 每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value``done`两个属性的对象。其中,`value`属性是当前成员的值,`done`属性是一个布尔值,表示遍历是否结束。
下面是一个模拟next方法返回值的例子。 下面是一个模拟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只是把接口规格加到数据结构之上所以遍历器与它所遍历的那个数据结构实际上是分开的完全可以写出没有对应数据结构的遍历器对象或者说用遍历器对象模拟出数据结构。下面是一个无限运行的遍历器对象的例子。 由于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方法返回值的规格可以描述如下。 如果使用TypeScript的写法遍历器接口Iterable、指针对象Iterator和next方法返回值的规格可以描述如下。

View File

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

View File

@ -2,7 +2,7 @@
## 概述 ## 概述
ES5的对象属性名都是字符串这容易造成属性名的冲突。比如你使用了一个他人提供的对象但又想为这个对象添加新的方法新方法的名字就有可能与现有方法产生冲突。如果有一种机制保证每个属性的名字都是独一无二的就好了这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。 ES5的对象属性名都是字符串这容易造成属性名的冲突。比如你使用了一个他人提供的对象但又想为这个对象添加新的方法mixin模式新方法的名字就有可能与现有方法产生冲突。如果有一种机制保证每个属性的名字都是独一无二的就好了这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。
ES6引入了一种新的原始数据类型Symbol表示独一无二的值。它是JavaScript语言的第七种数据类型前六种是Undefined、Null、布尔值Boolean、字符串String、数值Number、对象Object ES6引入了一种新的原始数据类型Symbol表示独一无二的值。它是JavaScript语言的第七种数据类型前六种是Undefined、Null、布尔值Boolean、字符串String、数值Number、对象Object
@ -72,6 +72,21 @@ String(sym) // 'Symbol(My symbol)'
sym.toString() // '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值都是不相等的这意味着Symbol值可以作为标识符用于对象的属性名就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用能防止某一个键被不小心改写或覆盖。 由于每一个Symbol值都是不相等的这意味着Symbol值可以作为标识符用于对象的属性名就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用能防止某一个键被不小心改写或覆盖。
@ -145,6 +160,26 @@ log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info 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值作为属性名时该属性还是公开属性不是私有属性。 还有一点需要注意Symbol值作为属性名时该属性还是公开属性不是私有属性。
## 属性名的遍历 ## 属性名的遍历