From e9f7c5186ad24cdc25c8f31b82950aff824697a1 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 3 Nov 2015 09:14:22 +0800 Subject: [PATCH] edit array, set-map --- docs/array.md | 71 ++++++++++++++++++++++++++++++++----------------- docs/module.md | 2 +- docs/set-map.md | 32 ++++++++++++++-------- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/docs/array.md b/docs/array.md index e9dd294..569d835 100644 --- a/docs/array.md +++ b/docs/array.md @@ -4,6 +4,8 @@ `Array.from`方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。 +下面是一个类似数组的对象,`Array.from`将它转为真正的数组。 + ```javascript let arrayLike = { '0': 'a', @@ -19,34 +21,41 @@ var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c'] let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'] ``` -下面是更多的例子。 +实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的`arguments`对象。`Array.from`都可以将它们转为真正的数组。 + +```javascript +// NodeList对象 +let ps = document.querySelectorAll('p'); +Array.from(ps).forEach(function (p) { + console.log(p); +}); + +// arguments对象 +function foo() { + var args = Array.from(arguments); + // ... +} +``` + +上面代码中,`querySelectorAll`方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用`forEach`方法。 + +只要是部署了Iterator接口的数据结构,`Array.from`都能将其转为数组。 ```javascript 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); -}); ``` -上面代码中,`querySelectorAll`方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用forEach方法。 +上面代码中,字符串和Set结构都具有Iterator接口,因此可以被`Array.from`转为真正的数组。 -`Array.from`方法可以将函数的`arguments`对象,转为数组。 +如果参数是一个真正的数组,`Array.from`会返回一个一模一样的新数组。 ```javascript -function foo() { - var args = Array.from(arguments); -} - -foo('a', 'b', 'c'); +Array.from([1, 2, 3]) +// [1, 2, 3] ``` 值得提醒的是,扩展运算符(`...`)也可以将某些数据结构转为数组。 @@ -61,16 +70,14 @@ function foo() { [...document.querySelectorAll('div')] ``` -扩展运算符背后调用的是遍历器接口(`Symbol.iterator`),如果一个对象没有部署这个接口,就无法转换。`Array.from`方法就不存在这个问题,比如下面的这个例子,扩展运算符就无法转换。 - -所谓类似数组的对象,本质特征只有一点,即必须有`length`属性。因此,任何有`length`属性的对象,都可以通过`Array.from`方法转为数组。 +扩展运算符背后调用的是遍历器接口(`Symbol.iterator`),如果一个对象没有部署这个接口,就无法转换。`Array.from`方法则是还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有`length`属性。因此,任何有`length`属性的对象,都可以通过`Array.from`方法转为数组,而此时扩展运算符就无法转换。 ```javascript Array.from({ length: 3 }); // [ undefined, undefined, undefinded ] ``` -上面代码中,`Array.from`返回了一个具有三个成员的数组,每个位置的值都是`undefined`。 +上面代码中,`Array.from`返回了一个具有三个成员的数组,每个位置的值都是`undefined`。扩展运算符转换不了这个对象。 对于还没有部署该方法的浏览器,可以用`Array.prototype.slice`方法替代。 @@ -80,7 +87,7 @@ const toArray = (() => )(); ``` -`Array.from`还可以接受第二个参数,作用类似于数组的`map`方法,用来对每个元素进行处理。 +`Array.from`还可以接受第二个参数,作用类似于数组的`map`方法,用来对每个元素进行处理,将处理后的值放入返回的数组。 ```JavaScript Array.from(arrayLike, x => x * x); @@ -91,6 +98,18 @@ Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9] ``` +下面的例子是取出一组DOM节点的文本内容。 + +```javascript +let spans = document.querySelectorAll('span.name'); + +// map() +let names1 = Array.prototype.map.call(spans, s => s.textContent); + +// Array.from() +let names2 = Array.from(spans, s => s.textContent) +``` + 下面的例子将数组中布尔值为`false`的成员转为`0`。 ```javascript @@ -110,7 +129,7 @@ typesOf(null, [], NaN) 如果`map`函数里面用到了`this`关键字,还可以传入`Array.from`的第三个参数,用来绑定`this`。 -`Array.from()`可以将各种值转为真正的数组,并且还提供`map`功能。这实际上意味着,你可以在数组里造出任何想要的值。 +`Array.from()`可以将各种值转为真正的数组,并且还提供`map`功能。这实际上意味着,只要有一个原始的数据结构,你就可以先对它的值进行处理,然后转成规范的数组结构,进而就可以使用数量众多的数组方法。 ```javascript Array.from({ length: 2 }, () => 'jack') @@ -240,9 +259,9 @@ i32a.copyWithin(0, 2); }) // 2 ``` -这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。 +这两个方法都可以接受第二个参数,用来绑定回调函数的`this`对象。 -另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。 +另外,这两个方法都可以发现`NaN`,弥补了数组的`IndexOf`方法的不足。 ```javascript [NaN].indexOf(NaN) @@ -252,7 +271,7 @@ i32a.copyWithin(0, 2); // 0 ``` -上面代码中,`indexOf`方法无法识别数组的NaN成员,但是`findIndex`方法可以借助`Object.is`方法做到。 +上面代码中,`indexOf`方法无法识别数组的`NaN`成员,但是`findIndex`方法可以借助`Object.is`方法做到。 ## 数组实例的fill() @@ -275,6 +294,8 @@ new Array(3).fill(7) // ['a', 7, 'c'] ``` +上面代码表示,`fill`方法从1号位开始,向原数组填充7,到2号位之前结束。 + ## 数组实例的entries(),keys()和values() ES6提供三个新的方法——`entries()`,`keys()`和`values()`——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用`for...of`循环进行遍历,唯一的区别是`keys()`是对键名的遍历、`values()`是对键值的遍历,`entries()`是对键值对的遍历。 diff --git a/docs/module.md b/docs/module.md index 294433a..309916d 100644 --- a/docs/module.md +++ b/docs/module.md @@ -438,7 +438,7 @@ console.log(counter); // 4 上面代码说明,ES6模块输入的变量`counter`是活的,完全反映其所在模块`lib.js`内部的变化。 -还是举本章开头时的例子。 +再举一个出现在`export`一节中的例子。 ```javascript // m1.js diff --git a/docs/set-map.md b/docs/set-map.md index e573d07..22a0534 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -212,17 +212,23 @@ set = new Set([...set].filter(x => (x % 2) == 0)); // 返回Set结构:{2, 4} ``` -因此使用Set,可以很容易地实现并集(Union)和交集(Intersect)。 +因此使用Set,可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。 ```javascript let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); +// 并集 let union = new Set([...a, ...b]); // [1, 2, 3, 4] +// 交集 let intersect = new Set([...a].filter(x => b.has(x))); // [2, 3] + +// 差集 +let difference = new Set([...a].filter(x => !b.has(x))); +// [1] ``` Set结构的实例的forEach方法,用于对每个成员执行某种操作,没有返回值。 @@ -321,7 +327,7 @@ ws.forEach(function(item){ console.log('WeakSet has ' + item)}) 上面代码试图获取size和forEach属性,结果都不能成功。 -WeakSet不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保存成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。 +WeakSet不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。 下面是WeakMap的另一个例子。 @@ -385,7 +391,7 @@ map.has("title") // true map.get("title") // "Author" ``` -上面代码在新建Map实例时,就指定了两个键name和title。 +上面代码在新建Map实例时,就指定了两个键`name`和`title`。 Map构造函数接受数组作为参数,实际上执行的是下面的算法。 @@ -402,14 +408,17 @@ items.forEach(([key, value]) => map.set(key, value)); ```javascript let map = new Map(); -map.set(1, 'aaa'); -map.set(1, 'bbb'); + +map +.set(1, 'aaa') +.set(1, 'bbb'); + map.get(1) // "bbb" ``` -上面代码对键1连续赋值两次,后一次的值覆盖前一次的值。 +上面代码对键`1`连续赋值两次,后一次的值覆盖前一次的值。 -如果读取一个未知的键,则返回undefined。 +如果读取一个未知的键,则返回`undefined`。 ```javascript new Map().get('asfddfsasadf') @@ -425,7 +434,7 @@ map.set(['a'], 555); map.get(['a']) // undefined ``` -上面代码的set和get方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined。 +上面代码的`set`和`get`方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此`get`方法无法读取该键,返回`undefined`。 同理,同样的值的两个实例,在Map结构中被视为两个键。 @@ -435,14 +444,15 @@ var map = new Map(); var k1 = ['a']; var k2 = ['a']; -map.set(k1, 111); -map.set(k2, 222); +map +.set(k1, 111) +.set(k2, 222); map.get(k1) // 111 map.get(k2) // 222 ``` -上面代码中,变量k1和k2的值是一样的,但是它们在Map结构中被视为两个键。 +上面代码中,变量`k1`和`k2`的值是一样的,但是它们在Map结构中被视为两个键。 由上可知,Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。