1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-28 21:32:20 +00:00
This commit is contained in:
Ruan Yifeng 2015-06-16 15:33:36 +08:00
parent f69843a8f6
commit 6bf087c771
5 changed files with 60 additions and 81 deletions

View File

@ -413,10 +413,8 @@ const [first, ...middle, last] = [1, 2, 3, 4, 5];
JavaScript的函数只能返回一个值如果需要返回多个值只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。 JavaScript的函数只能返回一个值如果需要返回多个值只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。
```javascript ```javascript
var dateFields = readDateFields(database); var dateFields = readDateFields(database);
var d = new Date(...dateFields); var d = new Date(...dateFields);
``` ```
上面代码从数据库取出一行数据通过扩展运算符直接将其传入构造函数Date。 上面代码从数据库取出一行数据通过扩展运算符直接将其传入构造函数Date。
@ -424,10 +422,8 @@ var d = new Date(...dateFields);
除了展开数组,扩展运算符还可以将一个数值扩展成数组。 除了展开数组,扩展运算符还可以将一个数值扩展成数组。
```javascript ```javascript
[...5] [...5]
// [0, 1, 2, 3, 4, 5] // [0, 1, 2, 3, 4, 5]
``` ```
扩展运算符还可以将字符串转为真正的数组。 扩展运算符还可以将字符串转为真正的数组。

View File

@ -7,7 +7,6 @@
在形式上Generator是一个普通函数但是有两个特征。一是function命令与函数名之间有一个星号二是函数体内部使用yield语句定义遍历器的每个成员即不同的内部状态yield语句在英语里的意思就是“产出” 在形式上Generator是一个普通函数但是有两个特征。一是function命令与函数名之间有一个星号二是函数体内部使用yield语句定义遍历器的每个成员即不同的内部状态yield语句在英语里的意思就是“产出”
```javascript ```javascript
function* helloWorldGenerator() { function* helloWorldGenerator() {
yield 'hello'; yield 'hello';
yield 'world'; yield 'world';
@ -15,7 +14,6 @@ function* helloWorldGenerator() {
} }
var hw = helloWorldGenerator(); var hw = helloWorldGenerator();
``` ```
上面代码定义了一个Generator函数helloWorldGenerator它的遍历器有两个成员“hello”和“world”。调用这个函数就会得到遍历器。 上面代码定义了一个Generator函数helloWorldGenerator它的遍历器有两个成员“hello”和“world”。调用这个函数就会得到遍历器。
@ -127,7 +125,6 @@ for (var f of flat(arr)){
上面代码也会产生句法错误因为forEach方法的参数是一个普通函数但是在里面使用了yield语句。一种修改方法是改用for循环。 上面代码也会产生句法错误因为forEach方法的参数是一个普通函数但是在里面使用了yield语句。一种修改方法是改用for循环。
```javascript ```javascript
var arr = [1, [[2, 3], 4], [5, 6]]; var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a){ var flat = function* (a){
@ -146,7 +143,6 @@ for (var f of flat(arr)){
console.log(f); console.log(f);
} }
// 1, 2, 3, 4, 5, 6 // 1, 2, 3, 4, 5, 6
``` ```
## next方法的参数 ## next方法的参数
@ -154,7 +150,6 @@ for (var f of flat(arr)){
yield语句本身没有返回值或者说总是返回undefined。next方法可以带一个参数该参数就会被当作上一个yield语句的返回值。 yield语句本身没有返回值或者说总是返回undefined。next方法可以带一个参数该参数就会被当作上一个yield语句的返回值。
```javascript ```javascript
function* f() { function* f() {
for(var i=0; true; i++) { for(var i=0; true; i++) {
var reset = yield i; var reset = yield i;
@ -167,7 +162,6 @@ var g = f();
g.next() // { value: 0, done: false } g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false } g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false } g.next(true) // { value: 0, done: false }
``` ```
上面代码先定义了一个可以无限运行的Generator函数f如果next方法没有参数每次运行到yield语句变量reset的值总是undefined。当next方法带一个参数true时当前的变量reset就被重置为这个参数即true因此i会等于-1下一轮循环就会从-1开始递增。 上面代码先定义了一个可以无限运行的Generator函数f如果next方法没有参数每次运行到yield语句变量reset的值总是undefined。当next方法带一个参数true时当前的变量reset就被重置为这个参数即true因此i会等于-1下一轮循环就会从-1开始递增。
@ -177,7 +171,24 @@ g.next(true) // { value: 0, done: false }
再看一个例子。 再看一个例子。
```javascript ```javascript
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:false}
```
上面代码中第二次运行next方法的时候不带参数导致y的值等于`2 * undefined`即NaN除以3以后还是NaN因此返回对象的value属性也等于NaN。第三次运行Next方法的时候不带参数所以z等于undefined返回对象的value属性等于`5 + NaN + undefined`即NaN。
如果向next方法提供参数返回结果就完全不一样了。
```javascript
function* foo(x) { function* foo(x) {
var y = 2 * (yield (x + 1)); var y = 2 * (yield (x + 1));
var z = yield (y / 3); var z = yield (y / 3);
@ -192,7 +203,6 @@ it.next(12)
// { value:8, done:false } // { value:8, done:false }
it.next(13) it.next(13)
// { value:42, done:true } // { value:42, done:true }
``` ```
上面代码第一次调用next方法时返回`x+1`的值6第二次调用next方法将上一次yield语句的值设为12因此y等于24返回`y / 3`的值8第三次调用next方法将上一次yield语句的值设为13因此z等于13这时x等于5y等于24所以return语句的值等于42。 上面代码第一次调用next方法时返回`x+1`的值6第二次调用next方法将上一次yield语句的值设为12因此y等于24返回`y / 3`的值8第三次调用next方法将上一次yield语句的值设为13因此z等于13这时x等于5y等于24所以return语句的值等于42。
@ -249,15 +259,12 @@ for (let n of fibonacci()) {
Generator函数还有一个特点它可以在函数体外抛出错误然后在函数体内捕获。 Generator函数还有一个特点它可以在函数体外抛出错误然后在函数体内捕获。
```javascript ```javascript
var g = function* () { var g = function* () {
while (true) { while (true) {
try { try {
yield; yield;
} catch (e) { } catch (e) {
if (e != 'a') { if (e != 'a') throw e;
throw e;
}
console.log('内部捕获', e); console.log('内部捕获', e);
} }
} }
@ -274,7 +281,6 @@ try {
} }
// 内部捕获 a // 内部捕获 a
// 外部捕获 b // 外部捕获 b
``` ```
上面代码中遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获然后Generator函数执行完成于是第二个错误被函数体外的catch捕获。 上面代码中遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获然后Generator函数执行完成于是第二个错误被函数体外的catch捕获。
@ -287,9 +293,7 @@ var g = function* () {
try { try {
yield; yield;
} catch (e) { } catch (e) {
if (e != 'a') { if (e != 'a') throw e;
throw e;
}
console.log('内部捕获', e); console.log('内部捕获', e);
} }
} }
@ -436,15 +440,15 @@ try {
上面代码中第二个next方法向函数体内传入一个参数42数值是没有toUpperCase方法的所以会抛出一个TypeError错误被函数体外的catch捕获。 上面代码中第二个next方法向函数体内传入一个参数42数值是没有toUpperCase方法的所以会抛出一个TypeError错误被函数体外的catch捕获。
一旦Generator执行过程中抛出错误就不会再执行下去了。如果此后还调用next方法一直返回发生错误前的那个值 一旦Generator执行过程中抛出错误就不会再执行下去了。如果此后还调用next方法返回一个value属性等于undefined、done属性等于true的对象即JavaScript引擎认为这个Generator已经运行结束了
```javascript ```javascript
function* g() { function* g() {
yield 1; yield 1;
console.log('throwing an exception'); console.log('throwing an exception');
throw new Error('generator broke!'); throw new Error('generator broke!');
yield 2; yield 2;
yield 3;
} }
function log(generator) { function log(generator) {
@ -452,36 +456,35 @@ function log(generator) {
console.log('starting generator'); console.log('starting generator');
try { try {
v = generator.next(); v = generator.next();
console.log('got back', v); console.log('第一次运行next方法', v);
} catch (err) { } catch (err) {
console.log('fixing generator', v); console.log('捕捉错误', v);
} }
try { try {
v = generator.next(); v = generator.next();
console.log('got back', v); console.log('第二次运行next方法', v);
} catch (err) { } catch (err) {
console.log('fixing generator', v); console.log('捕捉错误', v);
} }
try { try {
v = generator.next(); v = generator.next();
console.log('got back', v); console.log('第三次运行next方法', v);
} catch (err) { } catch (err) {
console.log('fixing generator', v); console.log('捕捉错误', v);
} }
console.log('caller done'); console.log('caller done');
} }
log(g()); log(g());
// starting generator // starting generator
// got back { value: 1, done: false } // 第一次运行next方法 { value: 1, done: false }
// throwing an exception // throwing an exception
// fixing generator { value: 1, done: false } // 捕捉错误 { value: 1, done: false }
// fixing generator { value: 1, done: false } // 第三次运行next方法 { value: undefined, done: true }
// caller done // caller done
``` ```
上面代码在Generator函数g抛出错误以后再调用next方法就不再执行下去了一直停留在上一次的状态 上面代码一共三次运行next方法第二次运行的时候会抛出错误然后第三次运行的时候Generator函数就已经结束了不再执行下去了
## yield*语句 ## yield*语句

View File

@ -7,7 +7,6 @@
ES6新增了let命令用来声明变量。它的用法类似于var但是所声明的变量只在let命令所在的代码块内有效。 ES6新增了let命令用来声明变量。它的用法类似于var但是所声明的变量只在let命令所在的代码块内有效。
```javascript ```javascript
{ {
let a = 10; let a = 10;
var b = 1; var b = 1;
@ -15,7 +14,6 @@ ES6新增了let命令用来声明变量。它的用法类似于var但是
a // ReferenceError: a is not defined. a // ReferenceError: a is not defined.
b // 1 b // 1
``` ```
上面代码在代码块之中分别用let和var声明了两个变量。然后在代码块之外调用这两个变量结果let声明的变量报错var声明的变量返回了正确的值。这表明let声明的变量只在它所在的代码块有效。 上面代码在代码块之中分别用let和var声明了两个变量。然后在代码块之外调用这两个变量结果let声明的变量报错var声明的变量返回了正确的值。这表明let声明的变量只在它所在的代码块有效。
@ -23,12 +21,10 @@ b // 1
for循环的计数器就很合适使用let命令。 for循环的计数器就很合适使用let命令。
```javascript ```javascript
for(let i = 0; i < arr.length; i++){} for(let i = 0; i < arr.length; i++){}
console.log(i) console.log(i)
//ReferenceError: i is not defined //ReferenceError: i is not defined
``` ```
上面代码的计数器i只在for循环体内有效。 上面代码的计数器i只在for循环体内有效。
@ -36,7 +32,6 @@ console.log(i)
下面的代码如果使用var最后输出的是10。 下面的代码如果使用var最后输出的是10。
```javascript ```javascript
var a = []; var a = [];
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
a[i] = function () { a[i] = function () {
@ -44,13 +39,11 @@ for (var i = 0; i < 10; i++) {
}; };
} }
a[6](); // 10 a[6](); // 10
``` ```
如果使用let声明的变量仅在块级作用域内有效最后输出的是6。 如果使用let声明的变量仅在块级作用域内有效最后输出的是6。
```javascript ```javascript
var a = []; var a = [];
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
a[i] = function () { a[i] = function () {
@ -58,7 +51,6 @@ for (let i = 0; i < 10; i++) {
}; };
} }
a[6](); // 6 a[6](); // 6
``` ```
### 不存在变量提升 ### 不存在变量提升
@ -370,12 +362,11 @@ var constantize = (obj) => {
## 全局对象的属性 ## 全局对象的属性
全局对象是最顶层的对象在浏览器环境指的是window对象在Node.js指的是global对象。ES5规定,所有全局变量都是全局对象的属性。 全局对象是最顶层的对象在浏览器环境指的是window对象在Node.js指的是global对象。在JavaScript语言中,所有全局变量都是全局对象的属性。
ES6规定var命令和function命令声明的全局变量属于全局对象的属性let命令、const命令、class命令声明的全局变量不属于全局对象的属性。 ES6规定var命令和function命令声明的全局变量属于全局对象的属性let命令、const命令、class命令声明的全局变量不属于全局对象的属性。
```javascript ```javascript
var a = 1; var a = 1;
// 如果在node环境可以写成global.a // 如果在node环境可以写成global.a
// 或者采用通用方法写成this.a // 或者采用通用方法写成this.a
@ -383,7 +374,6 @@ window.a // 1
let b = 1; let b = 1;
window.b // undefined window.b // undefined
``` ```
上面代码中全局变量a由var命令声明所以它是全局对象的属性全局变量b由let命令声明所以它不是全局对象的属性返回undefined。 上面代码中全局变量a由var命令声明所以它是全局对象的属性全局变量b由let命令声明所以它不是全局对象的属性返回undefined。

View File

@ -64,7 +64,7 @@ Set数据结构有以下方法。
```javascript ```javascript
s.add(1).add(2).add(2); s.add(1).add(2).add(2);
// 注意2被加入了两次 // 注意2被加入了两次
s.size // 2 s.size // 2
@ -165,19 +165,32 @@ for (let x of set) {
``` ```
Set结构的forEach方法用于对每个成员执行某种操作返回修改后的Set结构。 Set结构的forEach方法用于对每个成员执行某种操作不影响原来的Set结构。
```javascript ```javascript
let set = new Set([1, 2, 3]); let set = new Set([1, 2, 3]);
set.forEach((value, key) => value * 2 )
set.forEach((value, key) => value*2 ) // set的值还是1, 2, 3
// 返回Set结构{2, 4, 6}
``` ```
上面代码说明forEach方法的参数就是一个处理函数。该函数的参数依次为键值、键名、集合本身上例省略了该参数。另外forEach方法还可以有第二个参数表示绑定的this对象。 上面代码说明forEach方法的参数就是一个处理函数。该函数的参数依次为键值、键名、集合本身上例省略了该参数。另外forEach方法还可以有第二个参数表示绑定的this对象。
如果想在遍历操作中同步改变原来的Set结构目前没有直接的方法但有两种变通方法。一种是利用原Set结构映射出一个新的结构然后赋值给原来的Set结构另一种是利用Array.from方法。
```javascript
// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6
// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
```
上面代码提供了两种方法直接在遍历操作中改变原来的Set结构。
为了与Map结构保持一致Set结构也有keys和entries方法这时每个值的键名就是键值。 为了与Map结构保持一致Set结构也有keys和entries方法这时每个值的键名就是键值。
```javascript ```javascript
@ -204,7 +217,7 @@ for ( let [key, value] of set.entries() ){
```javascript ```javascript
let set = new Set(['red', 'green', 'blue']); let set = new Set(['red', 'green', 'blue']);
let arr = [...set]; let arr = [...set];
// ['red', 'green', 'blue'] // ['red', 'green', 'blue']
``` ```
@ -293,7 +306,6 @@ WeakSet结构有以下三个方法。
下面是一个例子。 下面是一个例子。
```javascript ```javascript
var ws = new WeakSet(); var ws = new WeakSet();
var obj = {}; var obj = {};
var foo = {}; var foo = {};
@ -304,26 +316,18 @@ ws.add(obj);
ws.has(window); // true ws.has(window); // true
ws.has(foo); // false ws.has(foo); // false
ws.delete(window); ws.delete(window);
ws.has(window); // false ws.has(window); // false
ws.clear();
``` ```
WeakSet没有size属性没有办法遍历它的成员。 WeakSet没有size属性没有办法遍历它的成员。
```javascript ```javascript
ws.size // undefined
ws.size ws.forEach // undefined
// undefined
ws.forEach(function(item){ console.log('WeakSet has ' + item)}) ws.forEach(function(item){ console.log('WeakSet has ' + item)})
// TypeError: undefined is not a function // TypeError: undefined is not a function
ws.forEach
// undefined
``` ```
上面代码试图获取size和forEach属性结果都不能成功。 上面代码试图获取size和forEach属性结果都不能成功。
@ -418,7 +422,7 @@ map.get(k2) // 222
```javascript ```javascript
let map = new Map(); let map = new Map();
map.set(NaN, 123); map.set(NaN, 123);
map.get(NaN) // 123 map.get(NaN) // 123
@ -492,12 +496,12 @@ m.get("edition") // 6
let map = new Map(); let map = new Map();
map.set('foo', true); map.set('foo', true);
map.set('bar', false); map.set('bar', false);
map.size // 2 map.size // 2
map.clear() map.clear()
map.size // 0 map.size // 0
``` ```
**3遍历** **3遍历**

View File

@ -304,10 +304,9 @@ repeat()返回一个新字符串表示将原字符串重复n次。
## 正则表达式的y修饰符 ## 正则表达式的y修饰符
除了u修饰符ES6还为正则表达式添加了y修饰符叫做“粘连”sticky修饰符。它的作用与g修饰符类似也是全局匹配后一次匹配都从上一次匹配成功的下一个位置开始不同之处在于g修饰符只确保剩余位置中存在匹配而y修饰符确保匹配必须从剩余的第一个位置开始这也就是“粘连”的涵义。 除了u修饰符ES6还为正则表达式添加了y修饰符叫做“粘连”sticky修饰符。它的作用与g修饰符类似也是全局匹配后一次匹配都从上一次匹配成功的下一个位置开始不同之处在于g修饰符只要剩余位置中存在匹配就可而y修饰符确保匹配必须从剩余的第一个位置开始这也就是“粘连”的涵义。
```javascript ```javascript
var s = "aaa_aa_a"; var s = "aaa_aa_a";
var r1 = /a+/g; var r1 = /a+/g;
var r2 = /a+/y; var r2 = /a+/y;
@ -317,7 +316,6 @@ r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"] r1.exec(s) // ["aa"]
r2.exec(s) // null r2.exec(s) // null
``` ```
上面代码有两个正则表达式一个使用g修饰符另一个使用y修饰符。这两个正则表达式各执行了两次第一次执行的时候两者行为相同剩余字符串都是“_aa_a”。由于g修饰没有位置要求所以第二次执行会返回结果而y修饰符要求匹配必须从头部开始所以返回null。 上面代码有两个正则表达式一个使用g修饰符另一个使用y修饰符。这两个正则表达式各执行了两次第一次执行的时候两者行为相同剩余字符串都是“_aa_a”。由于g修饰没有位置要求所以第二次执行会返回结果而y修饰符要求匹配必须从头部开始所以返回null。
@ -325,13 +323,11 @@ r2.exec(s) // null
如果改一下正则表达式保证每次都能头部匹配y修饰符就会返回结果了。 如果改一下正则表达式保证每次都能头部匹配y修饰符就会返回结果了。
```javascript ```javascript
var s = "aaa_aa_a"; var s = "aaa_aa_a";
var r = /a+_/y; var r = /a+_/y;
r.exec(s) // ["aaa_"] r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"] r.exec(s) // ["aa_"]
``` ```
上面代码每次匹配,都是从剩余字符串的头部开始。 上面代码每次匹配,都是从剩余字符串的头部开始。
@ -339,10 +335,8 @@ r.exec(s) // ["aa_"]
进一步说y修饰符号隐含了头部匹配的标志&#710; 进一步说y修饰符号隐含了头部匹配的标志&#710;
```javascript ```javascript
/b/y.exec("aba") /b/y.exec("aba")
// null // null
``` ```
上面代码由于不能保证头部匹配所以返回null。y修饰符的设计本意就是让头部匹配的标志&#710;在全局匹配中都有效。 上面代码由于不能保证头部匹配所以返回null。y修饰符的设计本意就是让头部匹配的标志&#710;在全局匹配中都有效。
@ -350,10 +344,8 @@ r.exec(s) // ["aa_"]
与y修饰符相匹配ES6的正则对象多了sticky属性表示是否设置了y修饰符。 与y修饰符相匹配ES6的正则对象多了sticky属性表示是否设置了y修饰符。
```javascript ```javascript
var r = /hello\d/y; var r = /hello\d/y;
r.sticky // true r.sticky // true
``` ```
## Regexp.escape() ## Regexp.escape()
@ -663,20 +655,16 @@ HelloWorldApp.main();
模板处理函数的第一个参数还有一个raw属性。它也是一个数组成员与处理函数的第一个参数完全一致唯一的区别是字符串被转义前的原始格式这是为了模板函数处理的方便而提供的。 模板处理函数的第一个参数还有一个raw属性。它也是一个数组成员与处理函数的第一个参数完全一致唯一的区别是字符串被转义前的原始格式这是为了模板函数处理的方便而提供的。
```javascript ```javascript
tag`First line\nSecond line` tag`First line\nSecond line`
``` ```
上面代码中tag函数的第一个参数是一个数组`["First line\nSecond line"]`这个数组有一个raw属性等于`["First line\\nSecond line"]`,两者唯一的区别就是斜杠被转义了。 上面代码中tag函数的第一个参数是一个数组`["First line\nSecond line"]`这个数组有一个raw属性等于`["First line\\nSecond line"]`,两者唯一的区别就是斜杠被转义了。
```javascript ```javascript
function tag(strings) { function tag(strings) {
console.log(strings.raw[0]); console.log(strings.raw[0]);
// "First line\\nSecond line" // "First line\\nSecond line"
} }
``` ```
## String.raw() ## String.raw()
@ -684,13 +672,11 @@ function tag(strings) {
String.raw方法往往用来充当模板字符串的处理函数返回字符串被转义前的原始格式。 String.raw方法往往用来充当模板字符串的处理函数返回字符串被转义前的原始格式。
```javascript ```javascript
String.raw`Hi\n${2+3}!`; String.raw`Hi\n${2+3}!`;
// "Hi\\n5!" // "Hi\\n5!"
String.raw`Hi\u000A!`; String.raw`Hi\u000A!`;
// 'Hi\\u000A!' // 'Hi\\u000A!'
``` ```
String.raw方法也可以正常的函数形式使用。这时它的第一个参数应该是一个具有raw属性的对象且raw属性的值应该是一个数组。 String.raw方法也可以正常的函数形式使用。这时它的第一个参数应该是一个具有raw属性的对象且raw属性的值应该是一个数组。