mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
edit function/spread
This commit is contained in:
parent
2dc06a3c79
commit
0012c4f1eb
@ -5,8 +5,16 @@
|
|||||||
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
|
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
let ps = document.querySelectorAll('p');
|
Array.from('hello')
|
||||||
|
// ['h', 'e', 'l', 'l', 'o']
|
||||||
|
|
||||||
|
Array.from([1, 2, 3])
|
||||||
|
// [1, 2, 3]
|
||||||
|
|
||||||
|
let namesSet = new Set(['a', 'b'])
|
||||||
|
Array.from(namesSet) // ['a', 'b']
|
||||||
|
|
||||||
|
let ps = document.querySelectorAll('p');
|
||||||
Array.from(ps).forEach(function (p) {
|
Array.from(ps).forEach(function (p) {
|
||||||
console.log(p);
|
console.log(p);
|
||||||
});
|
});
|
||||||
@ -45,6 +53,9 @@ Array.from()还可以接受第二个参数,作用类似于数组的map方法
|
|||||||
Array.from(arrayLike, x => x * x);
|
Array.from(arrayLike, x => x * x);
|
||||||
// 等同于
|
// 等同于
|
||||||
Array.from(arrayLike).map(x => x * x);
|
Array.from(arrayLike).map(x => x * x);
|
||||||
|
|
||||||
|
Array.from([1, 2, 3], (x) => x * x)
|
||||||
|
// [1, 4, 9]
|
||||||
```
|
```
|
||||||
|
|
||||||
下面的例子将数组中布尔值为false的成员转为0。
|
下面的例子将数组中布尔值为false的成员转为0。
|
||||||
@ -54,7 +65,16 @@ Array.from([1, , 2, , 3], (n) => n || 0)
|
|||||||
// [1, 0, 2, 0, 3]
|
// [1, 0, 2, 0, 3]
|
||||||
```
|
```
|
||||||
|
|
||||||
Array.from()的一个应用是,将字符串转为数组,然后返回字符串的长度。这样可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。
|
`Array.from()`可以将各种值转为真正的数组,并且还提供map功能。这实际上意味着,你可以在数组里造出任何想要的值。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Array.from({ length: 2 }, () => 'jack')
|
||||||
|
// ['jack', 'jack']
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码中,`Array.from`的第一个参数指定了第二个参数运行的次数。这种特性可以让该方法的用法变得非常灵活。
|
||||||
|
|
||||||
|
`Array.from()`的另一个应用是,将字符串转为数组,然后返回字符串的长度。这样可以避免JavaScript将大于`\uFFFF`的Unicode字符,算作两个字符的bug。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function countSymbols(string) {
|
function countSymbols(string) {
|
||||||
@ -95,8 +115,8 @@ function ArrayOf(){
|
|||||||
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
|
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var found = [1, 4, -5, 10].find((n) => n < 0);
|
[1, 4, -5, 10].find((n) => n < 0)
|
||||||
console.log("found:", found);
|
// -5
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码找出数组中第一个小于0的成员。
|
上面代码找出数组中第一个小于0的成员。
|
||||||
@ -129,7 +149,7 @@ console.log("found:", found);
|
|||||||
// 0
|
// 0
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。
|
上面代码中,`indexOf`方法无法识别数组的NaN成员,但是`findIndex`方法可以借助`Object.is`方法做到。
|
||||||
|
|
||||||
## 数组实例的fill()
|
## 数组实例的fill()
|
||||||
|
|
||||||
@ -156,10 +176,9 @@ fill()还可以接受第二个和第三个参数,用于指定填充的起始
|
|||||||
|
|
||||||
## 数组实例的entries(),keys()和values()
|
## 数组实例的entries(),keys()和values()
|
||||||
|
|
||||||
ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
|
ES6提供三个新的方法——`entries()`,`keys()`和`values()`——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用`for...of`循环进行遍历,唯一的区别是`keys()`是对键名的遍历、`values()`是对键值的遍历,`entries()`是对键值对的遍历。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
for (let index of ['a', 'b'].keys()) {
|
for (let index of ['a', 'b'].keys()) {
|
||||||
console.log(index);
|
console.log(index);
|
||||||
}
|
}
|
||||||
@ -177,7 +196,16 @@ for (let [index, elem] of ['a', 'b'].entries()) {
|
|||||||
}
|
}
|
||||||
// 0 "a"
|
// 0 "a"
|
||||||
// 1 "b"
|
// 1 "b"
|
||||||
|
```
|
||||||
|
|
||||||
|
如果不使用`for...of`循环,可以手动调用遍历器对象的`next`方法,进行遍历。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let letter = ['a', 'b', 'c'];
|
||||||
|
let entries = letter.entries();
|
||||||
|
console.log(entries.next().value); // [0, 'a']
|
||||||
|
console.log(entries.next().value); // [1, 'b']
|
||||||
|
console.log(entries.next().value); // [2, 'c']
|
||||||
```
|
```
|
||||||
|
|
||||||
## 数组实例的includes()
|
## 数组实例的includes()
|
||||||
|
102
docs/function.md
102
docs/function.md
@ -34,7 +34,6 @@ if (arguments.length === 1) {
|
|||||||
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
|
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function log(x, y = 'World') {
|
function log(x, y = 'World') {
|
||||||
console.log(x, y);
|
console.log(x, y);
|
||||||
}
|
}
|
||||||
@ -42,13 +41,11 @@ function log(x, y = 'World') {
|
|||||||
log('Hello') // Hello World
|
log('Hello') // Hello World
|
||||||
log('Hello', 'China') // Hello China
|
log('Hello', 'China') // Hello China
|
||||||
log('Hello', '') // Hello
|
log('Hello', '') // Hello
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可以看到,ES6的写法比ES5简洁许多,而且非常自然。下面是另一个例子。
|
可以看到,ES6的写法比ES5简洁许多,而且非常自然。下面是另一个例子。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function Point(x = 0, y = 0) {
|
function Point(x = 0, y = 0) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
@ -56,7 +53,6 @@ function Point(x = 0, y = 0) {
|
|||||||
|
|
||||||
var p = new Point();
|
var p = new Point();
|
||||||
// p = { x:0, y:0 }
|
// p = { x:0, y:0 }
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
除了简洁,ES6的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本彻底拿到这个参数,也不会导致以前的代码无法运行。
|
除了简洁,ES6的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本彻底拿到这个参数,也不会导致以前的代码无法运行。
|
||||||
@ -64,11 +60,9 @@ var p = new Point();
|
|||||||
默认值的写法非常灵活,下面是一个为对象属性设置默认值的例子。
|
默认值的写法非常灵活,下面是一个为对象属性设置默认值的例子。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
fetch(url, { body = '', method = 'GET', headers = {} }){
|
fetch(url, { body = '', method = 'GET', headers = {} }){
|
||||||
console.log(method);
|
console.log(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,传入函数fetch的第二个参数是一个对象,调用的时候可以为它的三个属性设置默认值。
|
上面代码中,传入函数fetch的第二个参数是一个对象,调用的时候可以为它的三个属性设置默认值。
|
||||||
@ -76,11 +70,9 @@ fetch(url, { body = '', method = 'GET', headers = {} }){
|
|||||||
甚至还可以设置双重默认值。
|
甚至还可以设置双重默认值。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
fetch(url, { method = 'GET' } = {}){
|
fetch(url, { method = 'GET' } = {}){
|
||||||
console.log(method);
|
console.log(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,调用函数fetch时,如果不含第二个参数,则默认值为一个空对象;如果包含第二个参数,则它的method属性默认值为GET。
|
上面代码中,调用函数fetch时,如果不含第二个参数,则默认值为一个空对象;如果包含第二个参数,则它的method属性默认值为GET。
|
||||||
@ -88,7 +80,6 @@ fetch(url, { method = 'GET' } = {}){
|
|||||||
定义了默认值的参数,必须是函数的尾部参数,其后不能再有其他无默认值的参数。这是因为有了默认值以后,该参数可以省略,只有位于尾部,才可能判断出到底省略了哪些参数。
|
定义了默认值的参数,必须是函数的尾部参数,其后不能再有其他无默认值的参数。这是因为有了默认值以后,该参数可以省略,只有位于尾部,才可能判断出到底省略了哪些参数。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// 以下两种写法都是错的
|
// 以下两种写法都是错的
|
||||||
|
|
||||||
function f(x = 5, y) {
|
function f(x = 5, y) {
|
||||||
@ -96,20 +87,17 @@ function f(x = 5, y) {
|
|||||||
|
|
||||||
function f(x, y = 5, z) {
|
function f(x, y = 5, z) {
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
如果传入undefined,将触发该参数等于默认值,null则没有这个效果。
|
如果传入undefined,将触发该参数等于默认值,null则没有这个效果。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function foo(x = 5, y = 6){
|
function foo(x = 5, y = 6){
|
||||||
console.log(x,y);
|
console.log(x,y);
|
||||||
}
|
}
|
||||||
|
|
||||||
foo(undefined, null)
|
foo(undefined, null)
|
||||||
// 5 null
|
// 5 null
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,x参数对应undefined,结果触发了默认值,y参数等于null,就没有触发默认值。
|
上面代码中,x参数对应undefined,结果触发了默认值,y参数等于null,就没有触发默认值。
|
||||||
@ -117,11 +105,9 @@ foo(undefined, null)
|
|||||||
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
|
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
(function(a){}).length // 1
|
(function(a){}).length // 1
|
||||||
(function(a = 5){}).length // 0
|
(function(a = 5){}).length // 0
|
||||||
(function(a, b, c = 5){}).length // 2
|
(function(a, b, c = 5){}).length // 2
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
|
上面代码中,length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
|
||||||
@ -129,7 +115,6 @@ foo(undefined, null)
|
|||||||
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
|
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function throwIfMissing() {
|
function throwIfMissing() {
|
||||||
throw new Error('Missing parameter');
|
throw new Error('Missing parameter');
|
||||||
}
|
}
|
||||||
@ -140,7 +125,6 @@ function foo(mustBeProvided = throwIfMissing()) {
|
|||||||
|
|
||||||
foo()
|
foo()
|
||||||
// Error: Missing parameter
|
// Error: Missing parameter
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误。
|
上面代码的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误。
|
||||||
@ -222,7 +206,6 @@ const sortNumbers = (...numbers) => numbers.sort();
|
|||||||
rest参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用rest参数改写数组push方法的例子。
|
rest参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用rest参数改写数组push方法的例子。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function push(array, ...items) {
|
function push(array, ...items) {
|
||||||
items.forEach(function(item) {
|
items.forEach(function(item) {
|
||||||
array.push(item);
|
array.push(item);
|
||||||
@ -232,33 +215,41 @@ function push(array, ...items) {
|
|||||||
|
|
||||||
var a = [];
|
var a = [];
|
||||||
push(a, 1, 2, 3)
|
push(a, 1, 2, 3)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
|
注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// 报错
|
// 报错
|
||||||
function f(a, ...b, c) {
|
function f(a, ...b, c) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
函数的length属性,不包括rest参数。
|
函数的length属性,不包括rest参数。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
(function(a) {}).length // 1
|
(function(a) {}).length // 1
|
||||||
(function(...a) {}).length // 0
|
(function(...a) {}).length // 0
|
||||||
(function(a, ...b) {}).length // 1
|
(function(a, ...b) {}).length // 1
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 扩展运算符
|
## 扩展运算符
|
||||||
|
|
||||||
扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。该运算符主要用于函数调用。
|
扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.log(...[1, 2, 3])
|
||||||
|
// 1 2 3
|
||||||
|
|
||||||
|
console.log(1, ...[2, 3, 4], 5)
|
||||||
|
// 1 2 3 4 5
|
||||||
|
|
||||||
|
[...document.querySelectorAll('div')]
|
||||||
|
// <- [<div>, <div>, <div>]
|
||||||
|
```
|
||||||
|
|
||||||
|
该运算符主要用于函数调用。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function push(array, ...items) {
|
function push(array, ...items) {
|
||||||
@ -275,12 +266,6 @@ add(...numbers) // 42
|
|||||||
|
|
||||||
上面代码中,`array.push(...items)`和`add(...numbers)`这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
|
上面代码中,`array.push(...items)`和`add(...numbers)`这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
|
||||||
|
|
||||||
下面是Date函数的参数使用扩展运算符的例子。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const date = new Date(...[2015, 1, 1]);
|
|
||||||
```
|
|
||||||
|
|
||||||
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
|
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -295,14 +280,6 @@ var args = [0, 1, 2];
|
|||||||
f(...args);
|
f(...args);
|
||||||
```
|
```
|
||||||
|
|
||||||
扩展运算符与正常的函数参数可以结合使用,非常灵活。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function f(v, w, x, y, z) { }
|
|
||||||
var args = [0, 1];
|
|
||||||
f(-1, ...args, 2, ...[3]);
|
|
||||||
```
|
|
||||||
|
|
||||||
下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
|
下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -316,7 +293,7 @@ Math.max(...[14, 3, 77])
|
|||||||
Math.max(14, 3, 77);
|
Math.max(14, 3, 77);
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码表示,由于JavaScript不提供求数组最大元素的函数,所以只能套用Math.max函数,将数组转为一个参数序列,然后求最大值。有了扩展运算符以后,就可以直接用Math.max了。
|
上面代码表示,由于JavaScript不提供求数组最大元素的函数,所以只能套用`Math.max`函数,将数组转为一个参数序列,然后求最大值。有了扩展运算符以后,就可以直接用`Math.max`了。
|
||||||
|
|
||||||
另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。
|
另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。
|
||||||
|
|
||||||
@ -334,22 +311,36 @@ arr1.push(...arr2);
|
|||||||
|
|
||||||
上面代码的ES5写法中,push方法的参数不能是数组,所以只好通过apply方法变通使用push方法。有了扩展运算符,就可以直接将数组传入push方法。
|
上面代码的ES5写法中,push方法的参数不能是数组,所以只好通过apply方法变通使用push方法。有了扩展运算符,就可以直接将数组传入push方法。
|
||||||
|
|
||||||
扩展运算符还可以用于数组的赋值。
|
扩展运算符与正常的函数参数可以结合使用,非常灵活。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var a = [1];
|
function f(v, w, x, y, z) { }
|
||||||
var b = [2, 3, 4];
|
var args = [0, 1];
|
||||||
var c = [6, 7];
|
f(-1, ...args, 2, ...[3]);
|
||||||
var d = [0, ...a, ...b, 5, ...c];
|
|
||||||
|
|
||||||
d
|
|
||||||
// [0, 1, 2, 3, 4, 5, 6, 7]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码其实也提供了,将一个数组拷贝进另一个数组的便捷方法。
|
扩展运算符可以简化很多种ES5的写法。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const arr2 = [...arr1];
|
// ES5
|
||||||
|
[1, 2].concat(more)
|
||||||
|
// ES6
|
||||||
|
[1, 2, ...more]
|
||||||
|
|
||||||
|
// ES5
|
||||||
|
list.push.apply(list, [3, 4])
|
||||||
|
// ES6
|
||||||
|
list.push(...[3, 4])
|
||||||
|
|
||||||
|
// ES5
|
||||||
|
a = list[0], rest = list.slice(1)
|
||||||
|
// ES6
|
||||||
|
[a, ...rest] = list
|
||||||
|
|
||||||
|
// ES5
|
||||||
|
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
|
||||||
|
// ES6
|
||||||
|
new Date(...[2015, 1, 1]);
|
||||||
```
|
```
|
||||||
|
|
||||||
扩展运算符也可以与解构赋值结合起来,用于生成数组。
|
扩展运算符也可以与解构赋值结合起来,用于生成数组。
|
||||||
@ -425,7 +416,6 @@ let arr = [...map.keys()]; // [1, 2, 3]
|
|||||||
|
|
||||||
Generator函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
|
Generator函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var go = function*(){
|
var go = function*(){
|
||||||
yield 1;
|
yield 1;
|
||||||
@ -569,7 +559,19 @@ var handler = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码的init方法中,使用了箭头函数,这导致this绑定handler对象,否则回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。
|
上面代码的init方法中,使用了箭头函数,这导致this绑定handler对象,否则回调函数运行时,`this.doSomething`这一行会报错,因为此时this指向全局对象。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function Timer () {
|
||||||
|
this.seconds = 0
|
||||||
|
setInterval(() => this.seconds++, 1000)
|
||||||
|
}
|
||||||
|
var timer = new Timer()
|
||||||
|
setTimeout(() => console.log(timer.seconds), 3100)
|
||||||
|
// 3
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码中,`Timer`函数内部的`setInterval`调用了`this.seconds`属性,通过箭头函数将`this`绑定在Timer的实例对象。否则,输出结果是0,而不是3。
|
||||||
|
|
||||||
由于this在箭头函数中被绑定,所以不能用call()、apply()、bind()这些方法去改变this的指向。
|
由于this在箭头函数中被绑定,所以不能用call()、apply()、bind()这些方法去改变this的指向。
|
||||||
|
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍Generator函数的语法和API,它的异步编程应用请看《异步操作》一章。
|
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍Generator函数的语法和API,它的异步编程应用请看《异步操作》一章。
|
||||||
|
|
||||||
Generator函数有多种理解角度。从语法上,首先可以把它理解成一个函数的内部状态的遍历器(也就是说,Generator函数是一个状态机)。它每调用一次,就进入下一个内部状态。Generator函数可以控制内部状态的变化,依次遍历这些状态。
|
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
|
||||||
|
|
||||||
形式上,Generator函数是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
|
||||||
|
|
||||||
|
形式上,Generator函数是一个普通函数,但是有两个特征。一是,`function`命令与函数名之间有一个星号;二是,函数体内部使用`yield`语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function* helloWorldGenerator() {
|
function* helloWorldGenerator() {
|
||||||
@ -20,11 +22,11 @@ function* helloWorldGenerator() {
|
|||||||
var hw = helloWorldGenerator();
|
var hw = helloWorldGenerator();
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码定义了一个Generator函数helloWorldGenerator,它内部有两个yield语句“hello”和“world”,即该函数有三个状态:hello,world和return语句(结束执行)。
|
上面代码定义了一个Generator函数`helloWorldGenerator`,它内部有两个yield语句“hello”和“world”,即该函数有三个状态:hello,world和return语句(结束执行)。
|
||||||
|
|
||||||
然后,Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
|
然后,Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
|
||||||
|
|
||||||
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。换言之,Generator函数是分段执行的,yield命令是暂停执行的标记,而next方法可以恢复执行。
|
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用`next`方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个`yield`语句(或`return`语句)为止。换言之,Generator函数是分段执行的,`yield`语句是暂停执行的标记,而`next`方法可以恢复执行。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
hw.next()
|
hw.next()
|
||||||
@ -40,33 +42,33 @@ hw.next()
|
|||||||
// { value: undefined, done: true }
|
// { value: undefined, done: true }
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码一共调用了四次next方法。
|
上面代码一共调用了四次`next`方法。
|
||||||
|
|
||||||
第一次调用,Generator函数开始执行,直到遇到第一个yield语句为止。next方法返回一个对象,它的value属性就是当前yield语句的值hello,done属性的值false,表示遍历还没有结束。
|
第一次调用,Generator函数开始执行,直到遇到第一个`yield`语句为止。`next`方法返回一个对象,它的`value`属性就是当前`yield`语句的值hello,`done`属性的值false,表示遍历还没有结束。
|
||||||
|
|
||||||
第二次调用,Generator函数从上次yield语句停下的地方,一直执行到下一个yield语句。next方法返回的对象的value属性就是当前yield语句的值world,done属性的值false,表示遍历还没有结束。
|
第二次调用,Generator函数从上次`yield`语句停下的地方,一直执行到下一个`yield`语句。`next`方法返回的对象的`value`属性就是当前`yield`语句的值world,`done`属性的值false,表示遍历还没有结束。
|
||||||
|
|
||||||
第三次调用,Generator函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。
|
第三次调用,Generator函数从上次`yield`语句停下的地方,一直执行到`return`语句(如果没有return语句,就执行到函数结束)。`next`方法返回的对象的`value`属性,就是紧跟在`return`语句后面的表达式的值(如果没有`return`语句,则`value`属性的值为undefined),`done`属性的值true,表示遍历已经结束。
|
||||||
|
|
||||||
第四次调用,此时Generator函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
|
第四次调用,此时Generator函数已经运行完毕,`next`方法返回对象的`value`属性为undefined,`done`属性为true。以后再调用`next`方法,返回的都是这个值。
|
||||||
|
|
||||||
总结一下,调用Generator函数,返回一个部署了Iterator接口的遍历器对象,用来操作内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
|
总结一下,调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的`next`方法,就会返回一个有着`value`和`done`两个属性的对象。`value`属性表示当前的内部状态的值,是`yield`语句后面那个表达式的值;`done`属性是一个布尔值,表示是否遍历结束。
|
||||||
|
|
||||||
### yield语句
|
### yield语句
|
||||||
|
|
||||||
由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。
|
由于Generator函数返回的遍历器对象,只有调用`next`方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。`yield`语句就是暂停标志。
|
||||||
|
|
||||||
遍历器next方法的运行逻辑如下。
|
遍历器对象的`next`方法的运行逻辑如下。
|
||||||
|
|
||||||
(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
|
(1)遇到`yield`语句,就暂停执行后面的操作,并将紧跟在`yield`后面的那个表达式的值,作为返回的对象的`value`属性值。
|
||||||
|
|
||||||
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。
|
(2)下一次调用`next`方法时,再继续往下执行,直到遇到下一个`yield`语句。
|
||||||
|
|
||||||
(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
|
(3)如果没有再遇到新的`yield`语句,就一直运行到函数结束,直到`return`语句为止,并将`return`语句后面的表达式的值,作为返回的对象的`value`属性值。
|
||||||
|
|
||||||
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
|
(4)如果该函数没有`return`语句,则返回的对象的`value`属性值为`undefined`。
|
||||||
|
|
||||||
需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
需要注意的是,`yield`语句后面的表达式,只有当调用`next`方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function* gen{
|
function* gen{
|
||||||
@ -74,11 +76,11 @@ function* gen{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,yield后面的表达式`123 + 456`,不会立即求值,只会在next方法将指针移到这一句时,才会求值。
|
上面代码中,yield后面的表达式`123 + 456`,不会立即求值,只会在`next`方法将指针移到这一句时,才会求值。
|
||||||
|
|
||||||
yield语句与return语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。
|
`yield`语句与`return`语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到`yield`,函数暂停执行,下一次再从该位置继续向后执行,而`return`语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)`return`语句,但是可以执行多次(或者说多个)`yield`语句。正常函数只能返回一个值,因为只能执行一次`return`;Generator函数可以返回一系列的值,因为可以有任意多个`yield`。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。
|
||||||
|
|
||||||
Generator函数可以不用yield语句,这时就变成了一个单纯的暂缓执行函数。
|
Generator函数可以不用`yield`语句,这时就变成了一个单纯的暂缓执行函数。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function* f() {
|
function* f() {
|
||||||
|
@ -20,7 +20,7 @@ import { stat, exists, readFile } from 'fs';
|
|||||||
|
|
||||||
所以,ES6可以在编译时就完成模块编译,效率要比CommonJS模块高。
|
所以,ES6可以在编译时就完成模块编译,效率要比CommonJS模块高。
|
||||||
|
|
||||||
需要注意的是,ES6的模块自动采用严格模块,不管你有没有在模块头部加上`"use strict"`。
|
需要注意的是,ES6的模块自动采用严格模式,不管你有没有在模块头部加上`"use strict"`。
|
||||||
|
|
||||||
## export命令
|
## export命令
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user