Skip to content

Commit 4fe4539

Browse files
committed
feat: 2020.07.18
1 parent 3998ac0 commit 4fe4539

1 file changed

Lines changed: 279 additions & 7 deletions

File tree

Cute-JavaScript/Cute-ES/1.ES6.md

Lines changed: 279 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ let f = function (){return 1};
10781078

10791079
**箭头函数与变量结构结合使用**
10801080
```js
1081-
// 正常函数写法
1081+
// 普通函数写法
10821082
function f (p) {
10831083
return p.a + ':' + p.b;
10841084
}
@@ -1089,7 +1089,7 @@ let f = ({a, b}) => a + ':' + b;
10891089

10901090
**简化回调函数**
10911091
```js
1092-
// 正常函数写法
1092+
// 普通函数写法
10931093
[1, 2, 3].map(function (x){
10941094
return x * x;
10951095
})
@@ -1463,7 +1463,7 @@ typeof Object.assign(2); // 'object'
14631463
```
14641464
* 由于`undefined``NaN`无法转成对象,所以做为参数会报错。
14651465
```js
1466-
Object.assign(undefined) // 报错
1466+
Object.assign(undefined); // 报错
14671467
Object.assign(NaN); // 报错
14681468
```
14691469
* `Object.assign()`实现的是浅拷贝。
@@ -1486,7 +1486,7 @@ Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]
14861486
## 9 Symbol
14871487
### 9.1 介绍
14881488
ES6引入`Symbol`作为一种新的**原始数据类型**,表示**独一无二**的值,主要是为了**防止属性名冲突**
1489-
ES6之后,JavaScript一共有其中数据类型`Symbol``undefined``null``Boolean``String``Number``Object`
1489+
ES6之后,JavaScript一共有 7 种数据类型`Symbol``undefined``null``Boolean``String``Number``Object`
14901490
简单实用:
14911491
```js
14921492
let a = Symbol();
@@ -1762,14 +1762,15 @@ class P {
17621762
```
17631763

17641764
* **5.Symbol.replace**
1765+
17651766
当该对象被`String.prototype.replace`方法调用时,会返回该方法的返回值。
17661767
```js
17671768
let a = {};
17681769
a[Symbol.replace] = (...s) => console.log(s);
17691770
'Hello'.replace(a , 'World') // ["Hello", "World"]
17701771
```
17711772

1772-
* **6.Symbol.hasInstance**
1773+
* **6.Symbol.search**
17731774
当该对象被`String.prototype.search`方法调用时,会返回该方法的返回值。
17741775
```js
17751776
class P {
@@ -1871,6 +1872,26 @@ let x = new Collection();
18711872
Object.prototype.toString.call(x) // "[object xxx]"
18721873
```
18731874

1875+
ES6 新增内置对象的Symbol.toStringTag属性值如下。
1876+
1877+
- JSON[Symbol.toStringTag]:'JSON'
1878+
- Math[Symbol.toStringTag]:'Math'
1879+
- Module 对象M[Symbol.toStringTag]:'Module'
1880+
- ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
1881+
- DataView.prototype[Symbol.toStringTag]:'DataView'
1882+
- Map.prototype[Symbol.toStringTag]:'Map'
1883+
- Promise.prototype[Symbol.toStringTag]:'Promise'
1884+
- Set.prototype[Symbol.toStringTag]:'Set'
1885+
- %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
1886+
- WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
1887+
- WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
1888+
- %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
1889+
- %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
1890+
- %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
1891+
- Symbol.prototype[Symbol.toStringTag]:'Symbol'
1892+
- Generator.prototype[Symbol.toStringTag]:'Generator'
1893+
- GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
1894+
18741895
* **11.Symbol.unscopables**
18751896
该对象指定了使用with关键字时,哪些属性会被with环境排除。
18761897
```js
@@ -2015,8 +2036,102 @@ let a = new Set(['a','b','c']);
20152036
a.forEach((v,k) => console.log(k + ' : ' + v));
20162037
```
20172038

2039+
### 10.3 WeakSet
2040+
与 Set 类型,都是表示不重复的值的集合,但是区别如下:
2041+
1. WeakSet 成员只能为对象,不能为其他类型的值:
2042+
```js
2043+
const leo = new WeakSet();
2044+
leo.add(1); // TypeError: Invalid value used in weak set
2045+
leo.add(Symbol()); // TypeError: Invalid value used in weak set
2046+
```
2047+
2048+
2. WeakSet 成员对象为弱引用
2049+
垃圾回收机制不考虑 WeakSet 对该对象对引用,即如果其他对象不再引用该对象,那么就会被垃圾回收机制自动回收。
2050+
2051+
3. WeakSet 对象没有 `size` 属性,是不可枚举的,无法获取集合的大小。
2052+
2053+
2054+
WeakSet 是一个构造函数,可以使用 `new` 命令,创建 WeakSet 数据结构。
2055+
```js
2056+
const leo = new WeakSet();
2057+
```
2058+
2059+
WeakSet 可以接受一个**数组****类数组对象**作为参数。该数组的所有成员,都会自动成为 WeakSet 实例对象的成员。
2060+
2061+
实际上,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数。
2062+
2063+
```js
2064+
const arr = [[1, 2], [3, 4]];
2065+
const leo = new WeakSet(arr);
2066+
// WeakSet {[1, 2], [3, 4]}
2067+
```
2068+
2069+
上面代码中,`arr` 是一个**数组**,它有两个成员,也都是数组。`arr` 作为 WeakSet 构造函数的参数,`arr` 的成员会自动成为 WeakSet 的成员。
2070+
2071+
注意,是 `arr` 数组的成员成为 WeakSet 的成员,而不是 `arr` 数组本身。这意味着,**数组的成员只能是对象**
2072+
2073+
```js
2074+
const arr = [3, 4];
2075+
const leo = new WeakSet(arr);
2076+
// Uncaught TypeError: Invalid value used in weak set(…)
2077+
```
2078+
上面代码中,数组 `arr` 的成员不是对象,加入 WeakSet 就会**报错**
2079+
2080+
WeakSet 结构有以下三个方法。
2081+
2082+
- `WeakSet.prototype.add(value)`:向 WeakSet 实例添加一个新成员。
2083+
- `WeakSet.prototype.delete(value)`:清除 WeakSet 实例的指定成员。
2084+
- `WeakSet.prototype.has(value)`:返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
2085+
2086+
下面是一个例子。
2087+
```js
2088+
const ws = new WeakSet();
2089+
const obj = {};
2090+
const foo = {};
2091+
2092+
ws.add(window);
2093+
ws.add(obj);
2094+
2095+
ws.has(window); // true
2096+
ws.has(foo); // false
2097+
2098+
ws.delete(window);
2099+
ws.has(window); // false
2100+
```
2101+
2102+
**注意**:WeakSet 没有 `size` 属性,没有办法遍历它的成员。
20182103

2019-
### 10.3 Map
2104+
```js
2105+
ws.size // undefined
2106+
ws.forEach // undefined
2107+
2108+
ws.forEach(function(item){ console.log('WeakSet has ' + item)})
2109+
// TypeError: undefined is not a function
2110+
```
2111+
2112+
上面代码试图获取 `size``forEach` 属性,结果都不能成功。
2113+
2114+
WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
2115+
2116+
下面是 WeakSet 的另一个例子。
2117+
2118+
```js
2119+
const foos = new WeakSet()
2120+
class Foo {
2121+
constructor() {
2122+
foos.add(this)
2123+
}
2124+
method () {
2125+
if (!foos.has(this)) {
2126+
throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
2127+
}
2128+
}
2129+
}
2130+
```
2131+
2132+
上面代码保证了 `Foo` 的实例方法,只能在 `Foo` 的实例上调用。这里使用 WeakSet 的好处是,`foos` 对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑 `foos`,也不会出现内存泄漏。
2133+
2134+
### 10.4 Map
20202135
由于传统的`JavaScript`对象只能用字符串当做键,给开发带来很大限制,ES6增加`Map`数据结构,使得**各种类型的值**(包括对象)都可以作为键。
20212136
`Map`结构提供了“**值—值**”的对应,是一种更完善的 Hash 结构实现。
20222137
**基础使用**
@@ -2088,7 +2203,7 @@ let a2 = [...a.values()]; // a2 =>  ["leo", 18]
20882203
let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]]
20892204
```
20902205

2091-
### 10.4 Map与其他数据结构互相转换
2206+
### 10.5 Map与其他数据结构互相转换
20922207
* Map 转 数组
20932208
```js
20942209
let a = new Map().set(true,1).set({f:2},['abc']);
@@ -2176,6 +2291,163 @@ fun2('[[true,7],[{"foo":3},["abc"]]]')
21762291
// Map {true => 7, Object {foo: 3} => ['abc']}
21772292
```
21782293

2294+
### 10.6 WeakMap
2295+
WeakMap 结构与 Map 结构类似,也是用于**生成键值对的集合**
2296+
2297+
```js
2298+
// WeakMap 可以使用 set 方法添加成员
2299+
const wm1 = new WeakMap();
2300+
const key = {foo: 1};
2301+
wm1.set(key, 2);
2302+
wm1.get(key) // 2
2303+
2304+
// WeakMap 也可以接受一个数组,
2305+
// 作为构造函数的参数
2306+
const k1 = [1, 2, 3];
2307+
const k2 = [4, 5, 6];
2308+
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
2309+
wm2.get(k2) // "bar"
2310+
```
2311+
2312+
WeakMap 与 Map 的区别有两点:
2313+
2314+
1. WeakMap 只接受对象作为键名(null除外),不接受其他类型的值作为键名。
2315+
2316+
```js
2317+
const map = new WeakMap();
2318+
map.set(1, 2)
2319+
// TypeError: 1 is not an object!
2320+
map.set(Symbol(), 2)
2321+
// TypeError: Invalid value used as weak map key
2322+
map.set(null, 2)
2323+
// TypeError: Invalid value used as weak map key
2324+
```
2325+
2326+
2. WeakMap 的键名所指向的对象,不计入垃圾回收机制。
2327+
2328+
WeakMap 的设计目的在于,有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。请看下面的例子。
2329+
2330+
```js
2331+
const e1 = document.getElementById('foo');
2332+
const e2 = document.getElementById('bar');
2333+
const arr = [
2334+
[e1, 'foo 元素'],
2335+
[e2, 'bar 元素'],
2336+
];
2337+
```
2338+
2339+
上面代码中,e1和e2是两个对象,我们通过arr数组对这两个对象添加一些文字说明。这就形成了arr对e1和e2的引用。
2340+
2341+
一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放e1和e2占用的内存。
2342+
2343+
```js
2344+
// 不需要 e1 和 e2 的时候
2345+
// 必须手动删除引用
2346+
arr [0] = null;
2347+
arr [1] = null;
2348+
```
2349+
上面这样的写法显然很不方便。一旦忘了写,就会造成内存泄露。
2350+
2351+
WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
2352+
2353+
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。
2354+
2355+
```js
2356+
const wm = new WeakMap();
2357+
2358+
const element = document.getElementById('example');
2359+
2360+
wm.set(element, 'some information');
2361+
wm.get(element) // "some information"
2362+
```
2363+
2364+
上面代码中,先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
2365+
2366+
也就是说,上面的 DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
2367+
2368+
总之,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
2369+
2370+
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
2371+
2372+
```js
2373+
const wm = new WeakMap();
2374+
let key = {};
2375+
let obj = {foo: 1};
2376+
2377+
wm.set(key, obj);
2378+
obj = null;
2379+
wm.get(key)
2380+
// Object {foo: 1}
2381+
```
2382+
上面代码中,键值obj是正常引用。所以,即使在 WeakMap 外部消除了obj的引用,WeakMap 内部的引用依然存在。
2383+
2384+
3. WeakMap 没有遍历操作对 API。
2385+
WeakMap 是没有遍历操作(即没有`keys()``values()`和e`ntries()`方法),也没有`size`属性。
2386+
因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名。
2387+
```js
2388+
const wm = new WeakMap();
2389+
2390+
wm.size // undefined
2391+
wm.forEach // undefined
2392+
```
2393+
2394+
2395+
4. WeakMap 无法清空。
2396+
2397+
即不支持 `clear`方法。
2398+
因此,WeakMap 只有四个方法可用:`get()``set()``has()``delete()`
2399+
2400+
```js
2401+
const wm = new WeakMap();
2402+
2403+
wm.clear // undefined
2404+
```
2405+
2406+
**WeakMap 应用**
2407+
2408+
**场景1:当我们想要为DOM添加数据时,可使用 `WeakMap`**
2409+
2410+
好处在于,当DOM元素移除时,对应 WeakMap 记录也会自动移除:
2411+
2412+
```html
2413+
<div id="WeakMap"></div>
2414+
```
2415+
2416+
```javascript
2417+
const wm = new WeakMap();
2418+
const weakMap = document.getElementById('WeakMap');
2419+
wm.set(weakMap, 'some information');
2420+
wm.get(weakMap) //"some information"
2421+
```
2422+
**场景2:当我们想要为DOM元素添加事件监听时,可使用 `WeakMap`**
2423+
```html
2424+
<button id="button1">按钮1</button>
2425+
<button id="button2">按钮2</button>
2426+
```
2427+
```javascript
2428+
const button1 = document.getElementById('button1');
2429+
const button2 = document.getElementById('button2');
2430+
const handler1 = () => { console.log("button1 被点击") };
2431+
const handler2 = () => { console.log("button2 被点击") };
2432+
2433+
// 代码1
2434+
button1.addEventListener('click', handler1, false);
2435+
button2.addEventListener('click', handler2, false);
2436+
2437+
// 代码2
2438+
const listener = new WeakMap();
2439+
2440+
listener.set(button1, handler1);
2441+
listener.set(button2, handler2);
2442+
2443+
button1.addEventListener('click', listener.get(button1), false);
2444+
button2.addEventListener('click', listener.get(button2), false);
2445+
```
2446+
代码2比起代码1的好处是:由于监听函数是放在 WeakMap 里面,
2447+
2448+
则一旦 DOM 对象button1 / button2消失,与它绑定的监听函数handler1和handler2 也会自动消失。
2449+
2450+
21792451
[⬆ 返回目录](#二目录)
21802452

21812453

0 commit comments

Comments
 (0)