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

edit iterator && string

This commit is contained in:
Ruan Yifeng 2015-05-29 11:26:11 +08:00
parent 81fe014df6
commit 1352e36b18
5 changed files with 181 additions and 48 deletions

View File

@ -59,7 +59,7 @@ var [bar, foo] = [1];
```
以上几种情况都属于解构不成功foo的值都会等于undefined。
以上几种情况都属于解构不成功foo的值都会等于undefined。这是因为原始类型的值会自动转为对象比如数值1转为`new Number(1)`从而导致foo取到undefined。
另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
@ -122,6 +122,22 @@ a // "a"
事实上只要某种数据结构具有Iterator接口都可以采用数组形式的解构赋值。
```javascript
function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
var [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
```
上面代码中fibs是一个Generator函数原生具有Iterator接口。解构赋值会依次从这个接口获取值。
## 对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
@ -190,6 +206,9 @@ x // 3
var {x, y = 5} = {x: 1};
console.log(x, y) // 1, 5
var { message: msg = "Something went wrong" } = {};
console.log(msg); // "Something went wrong"
```
对象解构可以与函数参数的默认值一起使用。

View File

@ -69,19 +69,19 @@ var p = new Point();
```javascript
fetch(url, { body='', method='GET', headers={} }){
fetch(url, { body = '', method = 'GET', headers = {} }){
console.log(method);
}
```
上面代码中传入函数fetch的第二个参数是一个对象调用的时候可以为它的三个属性设置默认值。这个例子也说明,不仅函数定义时,可以设置参数默认值,而且函数调用时,也可以设置参数默认值。
上面代码中传入函数fetch的第二个参数是一个对象调用的时候可以为它的三个属性设置默认值。
甚至还可以设置双重默认值。
```javascript
fetch(url, { method='GET' } = {}){
fetch(url, { method = 'GET' } = {}){
console.log(method);
}
@ -95,10 +95,10 @@ fetch(url, { method='GET' } = {}){
// 以下两种写法都是错的
function f(x=5, y) {
function f(x = 5, y) {
}
function f(x, y=5, z) {
function f(x, y = 5, z) {
}
```
@ -107,7 +107,7 @@ function f(x, y=5, z) {
```javascript
function foo(x=5, y=6){
function foo(x = 5, y = 6){
console.log(x,y);
}
@ -123,8 +123,8 @@ foo(undefined, null)
```javascript
(function(a){}).length // 1
(function(a=5){}).length // 0
(function(a, b, c=5){}).length // 2
(function(a = 5){}).length // 0
(function(a, b, c = 5){}).length // 2
```
@ -249,7 +249,7 @@ push(a, 1, 2, 3)
```
注意rest参数之后不能再有其他参数否则会报错。
注意rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
```javascript

View File

@ -8,7 +8,7 @@ JavaScript原有的数据结构主要是数组Array和对象Object
遍历器Iterator就是这样一种机制。它属于一种接口规格任何数据结构只要部署这个接口就可以完成遍历操作即依次处理该结构的所有成员。它的作用有两个一是为各种数据结构提供一个统一的、简便的接口二是使得数据结构的成员能够按某种次序排列。在ES6中遍历操作特指for...of循环即Iterator接口主要供for...of消费。
遍历器的遍历过程是这样的它提供了一个指针默认指向当前数据结构的起始位置。第一次调用遍历器的next方法可以将指针指向到第一个成员第二次调用就指向第二个成员直至指向数据结构的结束位置。每一次调用都会返回当前成员的信息具体来说就是返回一个包含value和done两个属性的对象。其中value属性是当前成员的值done属性是一个布尔值表示遍历是否结束。
遍历器的遍历过程是这样的:它提供了一个指针,默认指向当前数据结构的起始位置。也就是说,遍历器返回一个内部指针。第一次调用遍历器的next方法可以将指针指向到第一个成员第二次调用next方法就指向第二个成员直至指向数据结构的结束位置。每一次调用都会返回当前成员的信息具体来说就是返回一个包含value和done两个属性的对象。其中value属性是当前成员的值done属性是一个布尔值表示遍历是否结束。
下面是一个模拟next方法返回值的例子。
@ -33,9 +33,15 @@ it.next() // { value: undefined, done: true }
```
上面代码定义了一个makeIterator函数它的参数是一个数组。调用该函数就会返回一个对象。这个对象具有一个next方法每次调用next方法它的内部指针就会指向数组的下一个成员并返回一个该成员信息的对象。请特别注意next方法的返回值的构造一个具有value和done两个属性的对象。通过这个返回值我们就可以知道当前成员的值是什么以及遍历是否结束。在这个例子中makeIterator函数用来生成遍历器它返回的那个具有next方法的对象就是遍历器调用遍历器的next方法就可以遍历事先给定的数组
上面代码定义了一个makeIterator函数它的作用就是返回数组的遍历器。对数组`['a', 'b']`执行这个函数就会返回该数组的遍历器it
因为遍历器的作用,只是把接口规格加到数据结构之上。所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器,或者说用遍历器模拟出数据结构。下面是一个无限运行的遍历器例子。
遍历器it的next方法用来移动指针。开始时指针指向数组的开始位置。然后每次调用next方法指针就会指向数组的下一个成员。第一次调用指向a第二次调用指向b。
next方法返回一个对象表示当前位置的信息。这个对象具有value和done两个属性value属性返回当前位置的成员done属性是一个布尔值表示遍历是否结束即是否还有必要再一次调用next方法。
总之遍历器是一个对象具有next方法。调用next方法就可以遍历事先给定的数据结构。
由于遍历器只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器,或者说用遍历器模拟出数据结构。下面是一个无限运行的遍历器例子。
```javascript
@ -60,9 +66,9 @@ it.next().value // '2'
上面的例子中idMaker函数返回的对象就是遍历器但是并没有对应的数据结构或者说遍历器自己描述了一个数据结构出来。
总之所谓Iterator接口就是指调用这个接口会返回一个遍历器对象。该对象具备next方法每次调用该方法会返回一个具有value和done两个属性的新对象指向部署了Iterator接口的数据结构的一个成员
在ES6中有些数据结构原生提供遍历器比如数组即不用任何处理就可以被for...of循环遍历有些就不行比如对象。原因在于这些数据结构部署了System.iterator属性详见下文。凡是部署了System.iterator属性的数据结构就称为部署了遍历器接口。调用这个接口就会返回一个遍历器
如果使用TypeScript的写法遍历器接口、遍历器和遍历器返回值的规格可以描述如下。
如果使用TypeScript的写法遍历器接口、遍历器和next方法返回值的规格可以描述如下。
```javascript
@ -83,9 +89,9 @@ interface IterationResult {
### 默认的Iterator接口
Iterator接口的开发目的就是为所有数据结构提供了一种统一的访问机制即for...of循环见后文的介绍。当使用for...of循环遍历某种数据结构时该循环会自动去寻找Iterator接口。
Iterator接口的目的就是为所有数据结构提供了一种统一的访问机制即for...of循环详见下文。当使用for...of循环遍历某种数据结构时该循环会自动去寻找Iterator接口。
ES6规定默认的Iterator接口部署在数据结构的Symbol.iterator属性或者一个数据结构只要具有Symbol.iterator属性就可以认为是“可遍历的”iterable。也就是说调用Symbol.iterator方法就会得到当前数据结构的默认遍历器。Symbol.iterator是一个表达式返回Symbol对象的iterator属性这是一个预定义好的、类型为Symbol的特殊值所以要放在方括号内请参考Symbol一节
ES6规定默认的Iterator接口部署在数据结构的Symbol.iterator属性或者一个数据结构只要具有Symbol.iterator属性就可以认为是“可遍历的”iterable。也就是说调用Symbol.iterator方法就会得到当前数据结构的默认遍历器。Symbol.iterator本身是一个表达式返回Symbol对象的iterator属性这是一个预定义好的、类型为Symbol的特殊值所以要放在方括号内请参考Symbol一节
在ES6中有三类数据结构原生具备Iterator接口数组、某些类似数组的对象、Set和Map结构。
@ -111,17 +117,38 @@ iter.next() // { value: undefined, done: true }
```javascript
class MySpecialTree {
// ...
[Symbol.iterator]() {
// ...
return theIterator;
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
} else {
return {done: true, value: undefined};
}
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value);
}
```
上面代码是一个类部署Iterator接口的写法。Symbol.iterator属性对应一个函数执行后返回当前对象的遍历器。下面是一个例子。
上面代码是一个类部署Iterator接口的写法。Symbol.iterator属性对应一个函数执行后返回当前对象的遍历器。
下面是通过遍历器实现指针结构的例子。
```javascript
@ -159,6 +186,7 @@ Obj.prototype[Symbol.iterator] = function(){
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;

View File

@ -349,14 +349,25 @@ a = ["Dave"]; // 报错
如果真的想将对象冻结应该使用Object.freeze方法。
```javascript
const foo = Object.freeze({});
foo.prop = 123; // 不起作用
```
上面代码中常量foo指向一个冻结的对象所以添加新属性不起作用。
除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。
```javascript
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, value) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
```
## 全局对象的属性
全局对象是最顶层的对象在浏览器环境指的是window对象在Node.js指的是global对象。ES5规定所有全局变量都是全局对象的属性。

View File

@ -364,9 +364,9 @@ r.sticky // true
## 模板字符串
在ES6,输出模板通常是这样写的。
以前的JavaScript语言,输出模板通常是这样写的。
```js
```javascript
$("#result").append(
"There are <b>" + basket.count + "</b> " +
"items in your basket, " +
@ -377,7 +377,7 @@ $("#result").append(
上面这种写法相当繁琐不方便ES6引入了模板字符串解决这个问题。
```js
```javascript
$("#result").append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
@ -385,7 +385,7 @@ $("#result").append(`
`);
```
模板字符串template string是增强版的字符串用反引号`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串template string是增强版的字符串用反引号&#96;)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
```javascript
@ -405,15 +405,40 @@ var name = "Bob", time = "today";
```
上面代码中的字符串,都是用反引号表示。如果在模板字符串中嵌入变量,需要将变量名写在`${}`之中。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
上面代码中的字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
```javascript
var greeting = `\`Yo\` World!`;
```
大括号内部可以进行运算,以及引用对象属性。
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
```javascript
$("#warning").html(`
<h1>Watch out!</h1>
<p>Unauthorized hockeying can result in penalties
of up to ${maxPenalty} minutes.</p>
`);
```
模板字符串中嵌入变量,需要将变量名写在`${}`之中。
```javascript
function authorize(user, action) {
if (!user.hasPrivilege(action)) {
throw new Error(
// 传统写法为
// 'User '
// + user.name
// + ' is not authorized to do '
// + action
// + '.'
`User ${user.name} is not authorized to do ${action}.`);
}
}
```
大括号内部可以放入任意的JavaScript表达式可以进行运算以及引用对象属性。
```javascript
@ -445,34 +470,25 @@ console.log(`foo ${fn()} bar`);
```
如果大括号中的值不是字符串将按照一般的规则转为字符串。比如默认会调用对象的toString方法。
如果模板字符串中的变量没有声明,将报错。
```javascript
var msg = `Hello, ${place}`;
// throws error
```
模板字符串使得字符串与变量的结合,变得容易。下面是一个例子。
## 标签模板
```javascript
if (x > MAX) {
throw new Error(`Most ${MAX} allowed: ${x}!`);
// 传统写法为'Most '+MAX+' allowed: '+x+'!'
}
```
模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。
模板字符串的功能不仅仅是上面这些。它可以紧跟在一个函数名后面该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能tagged template
```javascript
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b}`;
tag`Hello ${ a + b } world ${ a * b }`;
```
@ -546,7 +562,66 @@ msg
上面这个例子展示了,如何将各个参数按照原来的位置拼合回去。
处理函数的第一个参数还有一个raw属性。它也是一个数组成员与处理函数的第一个参数完全一致唯一的区别是字符串被转义前的原始格式这是为了模板函数处理的方便而提供的。
“标签模板”的一个重要应用就是过滤HTML字符串防止用户输入恶意内容。
```javascript
var message =
SaferHTML`<p>${sender} has sent you a message.</p>`;
function SaferHTML(templateData) {
var s = templateData[0];
for (var i = 1; i < arguments.length; i++) {
var arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
```
上面代码中经过SaferHTML函数处理HTML字符串的特殊字符都会被转义。
标签模板的另一个应用,就是多语言转换(国际化处理)。
```javascript
i18n`Hello ${name}, you have ${amount}:c(CAD) in your bank account.`
// Hallo Bob, Sie haben 1.234,56 $CA auf Ihrem Bankkonto.
```
模板字符串并不能取代Mustache之类的模板函数因为没有条件判断和循环处理功能但是通过标签函数你可以自己添加这些功能。
```javascript
// 下面的hashTemplate函数
// 是一个自定义的模板处理函数
var libraryHtml = hashTemplate`
<ul>
#for book in ${myBooks}
<li><i>#{book.title}</i> by #{book.author}</li>
#end
</ul>
`;
```
除此之外你甚至可以使用标签模板在JavaScript语言之中嵌入其他语言。
```javascript
java`
class HelloWorldApp {
public static void main(String[] args) {
System.out.println(“Hello World!”); // Display the string.
}
}
`
HelloWorldApp.main();
```
模板处理函数的第一个参数还有一个raw属性。它也是一个数组成员与处理函数的第一个参数完全一致唯一的区别是字符串被转义前的原始格式这是为了模板函数处理的方便而提供的。
```javascript