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

edit function/spread

This commit is contained in:
ruanyf 2015-09-02 09:00:16 +08:00
parent 2dc06a3c79
commit 0012c4f1eb
4 changed files with 110 additions and 78 deletions

View File

@ -5,8 +5,16 @@
Array.from方法用于将两类对象转为真正的数组类似数组的对象array-like object和可遍历iterable的对象包括ES6新增的数据结构Set和Map
```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) {
console.log(p);
});
@ -45,6 +53,9 @@ Array.from()还可以接受第二个参数作用类似于数组的map方法
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
```
下面的例子将数组中布尔值为false的成员转为0。
@ -54,7 +65,16 @@ Array.from([1, , 2, , 3], (n) => n || 0)
// [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
function countSymbols(string) {
@ -95,8 +115,8 @@ function ArrayOf(){
数组实例的find方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数所有数组成员依次执行该回调函数直到找出第一个返回值为true的成员然后返回该成员。如果没有符合条件的成员则返回undefined。
```javascript
var found = [1, 4, -5, 10].find((n) => n < 0);
console.log("found:", found);
[1, 4, -5, 10].find((n) => n < 0)
// -5
```
上面代码找出数组中第一个小于0的成员。
@ -129,7 +149,7 @@ console.log("found:", found);
// 0
```
上面代码中indexOf方法无法识别数组的NaN成员但是findIndex方法可以借助Object.is方法做到。
上面代码中,`indexOf`方法无法识别数组的NaN成员但是`findIndex`方法可以借助`Object.is`方法做到。
## 数组实例的fill()
@ -156,10 +176,9 @@ fill()还可以接受第二个和第三个参数,用于指定填充的起始
## 数组实例的entries()keys()和values()
ES6提供三个新的方法——entries()keys()和values()——用于遍历数组。它们都返回一个遍历器可以用for...of循环进行遍历唯一的区别是keys()是对键名的遍历、values()是对键值的遍历entries()是对键值对的遍历。
ES6提供三个新的方法——`entries()``keys()``values()`——用于遍历数组。它们都返回一个遍历器对象详见《Iterator》一章,可以用`for...of`循环进行遍历,唯一的区别是`keys()`是对键名的遍历、`values()`是对键值的遍历,`entries()`是对键值对的遍历。
```javascript
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
@ -177,7 +196,16 @@ for (let [index, elem] of ['a', 'b'].entries()) {
}
// 0 "a"
// 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()

View File

@ -34,7 +34,6 @@ if (arguments.length === 1) {
ES6允许为函数的参数设置默认值即直接写在参数定义的后面。
```javascript
function log(x, y = 'World') {
console.log(x, y);
}
@ -42,13 +41,11 @@ function log(x, y = 'World') {
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
```
可以看到ES6的写法比ES5简洁许多而且非常自然。下面是另一个例子。
```javascript
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
@ -56,7 +53,6 @@ function Point(x = 0, y = 0) {
var p = new Point();
// p = { x:0, y:0 }
```
除了简洁ES6的写法还有两个好处首先阅读代码的人可以立刻意识到哪些参数是可以省略的不用查看函数体或文档其次有利于将来的代码优化即使未来的版本彻底拿到这个参数也不会导致以前的代码无法运行。
@ -64,11 +60,9 @@ var p = new Point();
默认值的写法非常灵活,下面是一个为对象属性设置默认值的例子。
```javascript
fetch(url, { body = '', method = 'GET', headers = {} }){
console.log(method);
}
```
上面代码中传入函数fetch的第二个参数是一个对象调用的时候可以为它的三个属性设置默认值。
@ -76,11 +70,9 @@ fetch(url, { body = '', method = 'GET', headers = {} }){
甚至还可以设置双重默认值。
```javascript
fetch(url, { method = 'GET' } = {}){
console.log(method);
}
```
上面代码中调用函数fetch时如果不含第二个参数则默认值为一个空对象如果包含第二个参数则它的method属性默认值为GET。
@ -88,7 +80,6 @@ fetch(url, { method = 'GET' } = {}){
定义了默认值的参数,必须是函数的尾部参数,其后不能再有其他无默认值的参数。这是因为有了默认值以后,该参数可以省略,只有位于尾部,才可能判断出到底省略了哪些参数。
```javascript
// 以下两种写法都是错的
function f(x = 5, y) {
@ -96,20 +87,17 @@ function f(x = 5, y) {
function f(x, y = 5, z) {
}
```
如果传入undefined将触发该参数等于默认值null则没有这个效果。
```javascript
function foo(x = 5, y = 6){
console.log(x,y);
}
foo(undefined, null)
// 5 null
```
上面代码中x参数对应undefined结果触发了默认值y参数等于null就没有触发默认值。
@ -117,11 +105,9 @@ foo(undefined, null)
指定了默认值以后函数的length属性将返回没有指定默认值的参数个数。也就是说指定了默认值后length属性将失真。
```javascript
(function(a){}).length // 1
(function(a = 5){}).length // 0
(function(a, b, c = 5){}).length // 2
```
上面代码中length属性的返回值等于函数的参数个数减去指定了默认值的参数个数。
@ -129,7 +115,6 @@ foo(undefined, null)
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
```javascript
function throwIfMissing() {
throw new Error('Missing parameter');
}
@ -140,7 +125,6 @@ function foo(mustBeProvided = throwIfMissing()) {
foo()
// Error: Missing parameter
```
上面代码的foo函数如果调用的时候没有参数就会调用默认值throwIfMissing函数从而抛出一个错误。
@ -222,7 +206,6 @@ const sortNumbers = (...numbers) => numbers.sort();
rest参数中的变量代表一个数组所以数组特有的方法都可以用于这个变量。下面是一个利用rest参数改写数组push方法的例子。
```javascript
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
@ -232,33 +215,41 @@ function push(array, ...items) {
var a = [];
push(a, 1, 2, 3)
```
注意rest参数之后不能再有其他参数即只能是最后一个参数否则会报错。
```javascript
// 报错
function f(a, ...b, c) {
// ...
}
```
函数的length属性不包括rest参数。
```javascript
(function(a) {}).length // 1
(function(...a) {}).length // 0
(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
function push(array, ...items) {
@ -275,12 +266,6 @@ add(...numbers) // 42
上面代码中,`array.push(...items)``add(...numbers)`这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
下面是Date函数的参数使用扩展运算符的例子。
```javascript
const date = new Date(...[2015, 1, 1]);
```
由于扩展运算符可以展开数组所以不再需要apply方法将数组转为函数的参数了。
```javascript
@ -295,14 +280,6 @@ var args = [0, 1, 2];
f(...args);
```
扩展运算符与正常的函数参数可以结合使用,非常灵活。
```javascript
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
```
下面是扩展运算符取代apply方法的一个实际的例子应用Math.max方法简化求出一个数组最大元素的写法。
```javascript
@ -316,7 +293,7 @@ Math.max(...[14, 3, 77])
Math.max(14, 3, 77);
```
上面代码表示由于JavaScript不提供求数组最大元素的函数所以只能套用Math.max函数将数组转为一个参数序列然后求最大值。有了扩展运算符以后就可以直接用Math.max了。
上面代码表示由于JavaScript不提供求数组最大元素的函数所以只能套用`Math.max`函数,将数组转为一个参数序列,然后求最大值。有了扩展运算符以后,就可以直接用`Math.max`了。
另一个例子是通过push函数将一个数组添加到另一个数组的尾部。
@ -334,22 +311,36 @@ arr1.push(...arr2);
上面代码的ES5写法中push方法的参数不能是数组所以只好通过apply方法变通使用push方法。有了扩展运算符就可以直接将数组传入push方法。
扩展运算符还可以用于数组的赋值
扩展运算符与正常的函数参数可以结合使用,非常灵活
```javascript
var a = [1];
var b = [2, 3, 4];
var c = [6, 7];
var d = [0, ...a, ...b, 5, ...c];
d
// [0, 1, 2, 3, 4, 5, 6, 7]
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
```
上面代码其实也提供了,将一个数组拷贝进另一个数组的便捷方法。
扩展运算符可以简化很多种ES5的写法。
```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函数运行后返回一个遍历器对象因此也可以使用扩展运算符。
```javascript
var go = function*(){
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的指向。

View File

@ -6,9 +6,11 @@
Generator函数是ES6提供的一种异步编程解决方案语法行为与传统函数完全不同。本章详细介绍Generator函数的语法和API它的异步编程应用请看《异步操作》一章。
Generator函数有多种理解角度。从语法上首先可以把它理解成一个函数的内部状态的遍历器也就是说Generator函数是一个状态机。它每调用一次就进入下一个内部状态。Generator函数可以控制内部状态的变化依次遍历这些状态。
Generator函数有多种理解角度。从语法上首先可以把它理解成Generator函数是一个状态机封装了多个内部状态。
形式上Generator函数是一个普通函数但是有两个特征。一是function命令与函数名之间有一个星号二是函数体内部使用yield语句定义遍历器的每个成员即不同的内部状态yield语句在英语里的意思就是“产出”
执行Generator函数会返回一个遍历器对象也就是说Generator函数除了状态机还是一个遍历器对象生成函数。返回的遍历器对象可以依次遍历Generator函数内部的每一个状态。
形式上Generator函数是一个普通函数但是有两个特征。一是`function`命令与函数名之间有一个星号;二是,函数体内部使用`yield`语句定义不同的内部状态yield语句在英语里的意思就是“产出”
```javascript
function* helloWorldGenerator() {
@ -20,11 +22,11 @@ function* helloWorldGenerator() {
var hw = helloWorldGenerator();
```
上面代码定义了一个Generator函数helloWorldGenerator它内部有两个yield语句“hello”和“world”即该函数有三个状态helloworld和return语句结束执行
上面代码定义了一个Generator函数`helloWorldGenerator`它内部有两个yield语句“hello”和“world”即该函数有三个状态helloworld和return语句结束执行
然后Generator函数的调用方法与普通函数一样也是在函数名后面加上一对圆括号。不同的是调用Generator函数后该函数并不执行返回的也不是函数运行结果而是一个指向内部状态的指针对象也就是上一章介绍的遍历器对象Iterator Object
下一步必须调用遍历器对象的next方法使得指针移向下一个状态。也就是说每次调用next方法内部指针就从函数头部或上一次停下来的地方开始执行直到遇到下一个yield语句或return语句为止。换言之Generator函数是分段执行的yield命令是暂停执行的标记而next方法可以恢复执行。
下一步必须调用遍历器对象的next方法使得指针移向下一个状态。也就是说每次调用`next`方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个`yield`语句(或`return`语句为止。换言之Generator函数是分段执行的`yield`语句是暂停执行的标记,而`next`方法可以恢复执行。
```javascript
hw.next()
@ -40,33 +42,33 @@ hw.next()
// { value: undefined, done: true }
```
上面代码一共调用了四次next方法。
上面代码一共调用了四次`next`方法。
第一次调用Generator函数开始执行直到遇到第一个yield语句为止。next方法返回一个对象它的value属性就是当前yield语句的值hellodone属性的值false表示遍历还没有结束。
第一次调用Generator函数开始执行直到遇到第一个`yield`语句为止。`next`方法返回一个对象,它的`value`属性就是当前`yield`语句的值hello`done`属性的值false表示遍历还没有结束。
第二次调用Generator函数从上次yield语句停下的地方一直执行到下一个yield语句。next方法返回的对象的value属性就是当前yield语句的值worlddone属性的值false表示遍历还没有结束。
第二次调用Generator函数从上次`yield`语句停下的地方,一直执行到下一个`yield`语句。`next`方法返回的对象的`value`属性就是当前`yield`语句的值world`done`属性的值false表示遍历还没有结束。
第三次调用Generator函数从上次yield语句停下的地方一直执行到return语句如果没有return语句就执行到函数结束。next方法返回的对象的value属性就是紧跟在return语句后面的表达式的值如果没有return语句则value属性的值为undefineddone属性的值true表示遍历已经结束。
第三次调用Generator函数从上次`yield`语句停下的地方,一直执行到`return`语句如果没有return语句就执行到函数结束`next`方法返回的对象的`value`属性,就是紧跟在`return`语句后面的表达式的值(如果没有`return`语句,则`value`属性的值为undefined`done`属性的值true表示遍历已经结束。
第四次调用此时Generator函数已经运行完毕next方法返回对象的value属性为undefineddone属性为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语句
由于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
function* gen{
@ -74,11 +76,11 @@ function* gen{
}
```
上面代码中yield后面的表达式`123 + 456`不会立即求值只会在next方法将指针移到这一句时才会求值。
上面代码中yield后面的表达式`123 + 456`,不会立即求值,只会在`next`方法将指针移到这一句时,才会求值。
yield语句与return语句既有相似之处也有区别。相似之处在于都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield函数暂停执行下一次再从该位置继续向后执行而return语句不具备位置记忆的功能。一个函数里面只能执行一次或者说一个return语句但是可以执行多次或者说多个yield语句。正常函数只能返回一个值因为只能执行一次returnGenerator函数可以返回一系列的值因为可以有任意多个yield。从另一个角度看也可以说Generator生成了一系列的值这也就是它的名称的来历在英语中generator这个词是“生成器”的意思
`yield`语句与`return`语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到`yield`,函数暂停执行,下一次再从该位置继续向后执行,而`return`语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)`return`语句,但是可以执行多次(或者说多个)`yield`语句。正常函数只能返回一个值,因为只能执行一次`return`Generator函数可以返回一系列的值因为可以有任意多个`yield`。从另一个角度看也可以说Generator生成了一系列的值这也就是它的名称的来历在英语中generator这个词是“生成器”的意思
Generator函数可以不用yield语句这时就变成了一个单纯的暂缓执行函数。
Generator函数可以不用`yield`语句,这时就变成了一个单纯的暂缓执行函数。
```javascript
function* f() {

View File

@ -20,7 +20,7 @@ import { stat, exists, readFile } from 'fs';
所以ES6可以在编译时就完成模块编译效率要比CommonJS模块高。
需要注意的是ES6的模块自动采用严格模,不管你有没有在模块头部加上`"use strict"`
需要注意的是ES6的模块自动采用严格模,不管你有没有在模块头部加上`"use strict"`
## export命令