mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-28 21:32:20 +00:00
edit set
This commit is contained in:
parent
f69843a8f6
commit
6bf087c771
@ -413,10 +413,8 @@ const [first, ...middle, last] = [1, 2, 3, 4, 5];
|
||||
JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。
|
||||
|
||||
```javascript
|
||||
|
||||
var dateFields = readDateFields(database);
|
||||
var d = new Date(...dateFields);
|
||||
|
||||
```
|
||||
|
||||
上面代码从数据库取出一行数据,通过扩展运算符,直接将其传入构造函数Date。
|
||||
@ -424,10 +422,8 @@ var d = new Date(...dateFields);
|
||||
除了展开数组,扩展运算符还可以将一个数值扩展成数组。
|
||||
|
||||
```javascript
|
||||
|
||||
[...5]
|
||||
// [0, 1, 2, 3, 4, 5]
|
||||
|
||||
```
|
||||
|
||||
扩展运算符还可以将字符串转为真正的数组。
|
||||
|
@ -7,7 +7,6 @@
|
||||
在形式上,Generator是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
||||
|
||||
```javascript
|
||||
|
||||
function* helloWorldGenerator() {
|
||||
yield 'hello';
|
||||
yield 'world';
|
||||
@ -15,7 +14,6 @@ function* helloWorldGenerator() {
|
||||
}
|
||||
|
||||
var hw = helloWorldGenerator();
|
||||
|
||||
```
|
||||
|
||||
上面代码定义了一个Generator函数helloWorldGenerator,它的遍历器有两个成员“hello”和“world”。调用这个函数,就会得到遍历器。
|
||||
@ -127,7 +125,6 @@ for (var f of flat(arr)){
|
||||
上面代码也会产生句法错误,因为forEach方法的参数是一个普通函数,但是在里面使用了yield语句。一种修改方法是改用for循环。
|
||||
|
||||
```javascript
|
||||
|
||||
var arr = [1, [[2, 3], 4], [5, 6]];
|
||||
|
||||
var flat = function* (a){
|
||||
@ -146,7 +143,6 @@ for (var f of flat(arr)){
|
||||
console.log(f);
|
||||
}
|
||||
// 1, 2, 3, 4, 5, 6
|
||||
|
||||
```
|
||||
|
||||
## next方法的参数
|
||||
@ -154,7 +150,6 @@ for (var f of flat(arr)){
|
||||
yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
|
||||
|
||||
```javascript
|
||||
|
||||
function* f() {
|
||||
for(var i=0; true; i++) {
|
||||
var reset = yield i;
|
||||
@ -167,7 +162,6 @@ var g = f();
|
||||
g.next() // { value: 0, done: false }
|
||||
g.next() // { value: 1, done: false }
|
||||
g.next(true) // { value: 0, done: false }
|
||||
|
||||
```
|
||||
|
||||
上面代码先定义了一个可以无限运行的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
|
||||
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) {
|
||||
var y = 2 * (yield (x + 1));
|
||||
var z = yield (y / 3);
|
||||
@ -192,7 +203,6 @@ it.next(12)
|
||||
// { value:8, done:false }
|
||||
it.next(13)
|
||||
// { value:42, done:true }
|
||||
|
||||
```
|
||||
|
||||
上面代码第一次调用next方法时,返回`x+1`的值6;第二次调用next方法,将上一次yield语句的值设为12,因此y等于24,返回`y / 3`的值8;第三次调用next方法,将上一次yield语句的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。
|
||||
@ -249,15 +259,12 @@ for (let n of fibonacci()) {
|
||||
Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。
|
||||
|
||||
```javascript
|
||||
|
||||
var g = function* () {
|
||||
while (true) {
|
||||
try {
|
||||
yield;
|
||||
} catch (e) {
|
||||
if (e != 'a') {
|
||||
throw e;
|
||||
}
|
||||
if (e != 'a') throw e;
|
||||
console.log('内部捕获', e);
|
||||
}
|
||||
}
|
||||
@ -274,7 +281,6 @@ try {
|
||||
}
|
||||
// 内部捕获 a
|
||||
// 外部捕获 b
|
||||
|
||||
```
|
||||
|
||||
上面代码中,遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获,然后Generator函数执行完成,于是第二个错误被函数体外的catch捕获。
|
||||
@ -287,9 +293,7 @@ var g = function* () {
|
||||
try {
|
||||
yield;
|
||||
} catch (e) {
|
||||
if (e != 'a') {
|
||||
throw e;
|
||||
}
|
||||
if (e != 'a') throw e;
|
||||
console.log('内部捕获', e);
|
||||
}
|
||||
}
|
||||
@ -436,15 +440,15 @@ try {
|
||||
|
||||
上面代码中,第二个next方法向函数体内传入一个参数42,数值是没有toUpperCase方法的,所以会抛出一个TypeError错误,被函数体外的catch捕获。
|
||||
|
||||
一旦Generator执行过程中抛出错误,就不会再执行下去了。如果此后还调用next方法,将一直返回发生错误前的那个值。
|
||||
一旦Generator执行过程中抛出错误,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象,即JavaScript引擎认为这个Generator已经运行结束了。
|
||||
|
||||
```javascript
|
||||
|
||||
function* g() {
|
||||
yield 1;
|
||||
console.log('throwing an exception');
|
||||
throw new Error('generator broke!');
|
||||
yield 2;
|
||||
yield 3;
|
||||
}
|
||||
|
||||
function log(generator) {
|
||||
@ -452,36 +456,35 @@ function log(generator) {
|
||||
console.log('starting generator');
|
||||
try {
|
||||
v = generator.next();
|
||||
console.log('got back', v);
|
||||
console.log('第一次运行next方法', v);
|
||||
} catch (err) {
|
||||
console.log('fixing generator', v);
|
||||
console.log('捕捉错误', v);
|
||||
}
|
||||
try {
|
||||
v = generator.next();
|
||||
console.log('got back', v);
|
||||
console.log('第二次运行next方法', v);
|
||||
} catch (err) {
|
||||
console.log('fixing generator', v);
|
||||
console.log('捕捉错误', v);
|
||||
}
|
||||
try {
|
||||
v = generator.next();
|
||||
console.log('got back', v);
|
||||
console.log('第三次运行next方法', v);
|
||||
} catch (err) {
|
||||
console.log('fixing generator', v);
|
||||
console.log('捕捉错误', v);
|
||||
}
|
||||
console.log('caller done');
|
||||
}
|
||||
|
||||
log(g());
|
||||
// starting generator
|
||||
// got back { value: 1, done: false }
|
||||
// 第一次运行next方法 { value: 1, done: false }
|
||||
// throwing an exception
|
||||
// fixing generator { value: 1, done: false }
|
||||
// fixing generator { value: 1, done: false }
|
||||
// 捕捉错误 { value: 1, done: false }
|
||||
// 第三次运行next方法 { value: undefined, done: true }
|
||||
// caller done
|
||||
|
||||
```
|
||||
|
||||
上面代码在Generator函数g抛出错误以后,再调用next方法,就不再执行下去了,一直停留在上一次的状态。
|
||||
上面代码一共三次运行next方法,第二次运行的时候会抛出错误,然后第三次运行的时候,Generator函数就已经结束了,不再执行下去了。
|
||||
|
||||
## yield*语句
|
||||
|
||||
|
12
docs/let.md
12
docs/let.md
@ -7,7 +7,6 @@
|
||||
ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
|
||||
|
||||
```javascript
|
||||
|
||||
{
|
||||
let a = 10;
|
||||
var b = 1;
|
||||
@ -15,7 +14,6 @@ ES6新增了let命令,用来声明变量。它的用法类似于var,但是
|
||||
|
||||
a // ReferenceError: a is not defined.
|
||||
b // 1
|
||||
|
||||
```
|
||||
|
||||
上面代码在代码块之中,分别用let和var声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。这表明,let声明的变量只在它所在的代码块有效。
|
||||
@ -23,12 +21,10 @@ b // 1
|
||||
for循环的计数器,就很合适使用let命令。
|
||||
|
||||
```javascript
|
||||
|
||||
for(let i = 0; i < arr.length; i++){}
|
||||
|
||||
console.log(i)
|
||||
//ReferenceError: i is not defined
|
||||
|
||||
```
|
||||
|
||||
上面代码的计数器i,只在for循环体内有效。
|
||||
@ -36,7 +32,6 @@ console.log(i)
|
||||
下面的代码如果使用var,最后输出的是10。
|
||||
|
||||
```javascript
|
||||
|
||||
var a = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
a[i] = function () {
|
||||
@ -44,13 +39,11 @@ for (var i = 0; i < 10; i++) {
|
||||
};
|
||||
}
|
||||
a[6](); // 10
|
||||
|
||||
```
|
||||
|
||||
如果使用let,声明的变量仅在块级作用域内有效,最后输出的是6。
|
||||
|
||||
```javascript
|
||||
|
||||
var a = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
a[i] = function () {
|
||||
@ -58,7 +51,6 @@ for (let i = 0; i < 10; i++) {
|
||||
};
|
||||
}
|
||||
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命令声明的全局变量,不属于全局对象的属性。
|
||||
|
||||
```javascript
|
||||
|
||||
var a = 1;
|
||||
// 如果在node环境,可以写成global.a
|
||||
// 或者采用通用方法,写成this.a
|
||||
@ -383,7 +374,6 @@ window.a // 1
|
||||
|
||||
let b = 1;
|
||||
window.b // undefined
|
||||
|
||||
```
|
||||
|
||||
上面代码中,全局变量a由var命令声明,所以它是全局对象的属性;全局变量b由let命令声明,所以它不是全局对象的属性,返回undefined。
|
||||
|
@ -165,19 +165,32 @@ for (let x of set) {
|
||||
|
||||
```
|
||||
|
||||
Set结构的forEach方法,用于对每个成员执行某种操作,返回修改后的Set结构。
|
||||
Set结构的forEach方法,用于对每个成员执行某种操作,不影响原来的Set结构。
|
||||
|
||||
```javascript
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
set.forEach((value, key) => value*2 )
|
||||
// 返回Set结构{2, 4, 6}
|
||||
|
||||
set.forEach((value, key) => value * 2 )
|
||||
// set的值还是1, 2, 3
|
||||
```
|
||||
|
||||
上面代码说明,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方法,这时每个值的键名就是键值。
|
||||
|
||||
```javascript
|
||||
@ -293,7 +306,6 @@ WeakSet结构有以下三个方法。
|
||||
下面是一个例子。
|
||||
|
||||
```javascript
|
||||
|
||||
var ws = new WeakSet();
|
||||
var obj = {};
|
||||
var foo = {};
|
||||
@ -306,24 +318,16 @@ ws.has(foo); // false
|
||||
|
||||
ws.delete(window);
|
||||
ws.has(window); // false
|
||||
|
||||
ws.clear();
|
||||
|
||||
```
|
||||
|
||||
WeakSet没有size属性,没有办法遍历它的成员。
|
||||
|
||||
```javascript
|
||||
|
||||
ws.size
|
||||
// undefined
|
||||
ws.size // undefined
|
||||
ws.forEach // undefined
|
||||
|
||||
ws.forEach(function(item){ console.log('WeakSet has ' + item)})
|
||||
// TypeError: undefined is not a function
|
||||
|
||||
ws.forEach
|
||||
// undefined
|
||||
|
||||
```
|
||||
|
||||
上面代码试图获取size和forEach属性,结果都不能成功。
|
||||
|
@ -304,10 +304,9 @@ repeat()返回一个新字符串,表示将原字符串重复n次。
|
||||
|
||||
## 正则表达式的y修饰符
|
||||
|
||||
除了u修饰符,ES6还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。它的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始,不同之处在于,g修饰符只确保剩余位置中存在匹配,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
|
||||
除了u修饰符,ES6还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。它的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始,不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
|
||||
|
||||
```javascript
|
||||
|
||||
var s = "aaa_aa_a";
|
||||
var r1 = /a+/g;
|
||||
var r2 = /a+/y;
|
||||
@ -317,7 +316,6 @@ r2.exec(s) // ["aaa"]
|
||||
|
||||
r1.exec(s) // ["aa"]
|
||||
r2.exec(s) // null
|
||||
|
||||
```
|
||||
|
||||
上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是“_aa_a”。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。
|
||||
@ -325,13 +323,11 @@ r2.exec(s) // null
|
||||
如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。
|
||||
|
||||
```javascript
|
||||
|
||||
var s = "aaa_aa_a";
|
||||
var r = /a+_/y;
|
||||
|
||||
r.exec(s) // ["aaa_"]
|
||||
r.exec(s) // ["aa_"]
|
||||
|
||||
```
|
||||
|
||||
上面代码每次匹配,都是从剩余字符串的头部开始。
|
||||
@ -339,10 +335,8 @@ r.exec(s) // ["aa_"]
|
||||
进一步说,y修饰符号隐含了头部匹配的标志ˆ。
|
||||
|
||||
```javascript
|
||||
|
||||
/b/y.exec("aba")
|
||||
// null
|
||||
|
||||
```
|
||||
|
||||
上面代码由于不能保证头部匹配,所以返回null。y修饰符的设计本意,就是让头部匹配的标志ˆ在全局匹配中都有效。
|
||||
@ -350,10 +344,8 @@ r.exec(s) // ["aa_"]
|
||||
与y修饰符相匹配,ES6的正则对象多了sticky属性,表示是否设置了y修饰符。
|
||||
|
||||
```javascript
|
||||
|
||||
var r = /hello\d/y;
|
||||
r.sticky // true
|
||||
|
||||
```
|
||||
|
||||
## Regexp.escape()
|
||||
@ -663,20 +655,16 @@ HelloWorldApp.main();
|
||||
模板处理函数的第一个参数,还有一个raw属性。它也是一个数组,成员与处理函数的第一个参数完全一致,唯一的区别是字符串被转义前的原始格式,这是为了模板函数处理的方便而提供的。
|
||||
|
||||
```javascript
|
||||
|
||||
tag`First line\nSecond line`
|
||||
|
||||
```
|
||||
|
||||
上面代码中,tag函数的第一个参数是一个数组`["First line\nSecond line"]`,这个数组有一个raw属性,等于`["First line\\nSecond line"]`,两者唯一的区别就是斜杠被转义了。
|
||||
|
||||
```javascript
|
||||
|
||||
function tag(strings) {
|
||||
console.log(strings.raw[0]);
|
||||
// "First line\\nSecond line"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## String.raw()
|
||||
@ -684,13 +672,11 @@ function tag(strings) {
|
||||
String.raw方法,往往用来充当模板字符串的处理函数,返回字符串被转义前的原始格式。
|
||||
|
||||
```javascript
|
||||
|
||||
String.raw`Hi\n${2+3}!`;
|
||||
// "Hi\\n5!"
|
||||
|
||||
String.raw`Hi\u000A!`;
|
||||
// 'Hi\\u000A!'
|
||||
|
||||
```
|
||||
|
||||
String.raw方法也可以正常的函数形式使用。这时,它的第一个参数,应该是一个具有raw属性的对象,且raw属性的值应该是一个数组。
|
||||
|
Loading…
x
Reference in New Issue
Block a user