@@ -1078,7 +1078,7 @@ let f = function (){return 1};
10781078
10791079** 箭头函数与变量结构结合使用** :
10801080``` js
1081- // 正常函数写法
1081+ // 普通函数写法
10821082function 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 ); // 报错
14671467Object .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 介绍
14881488ES6引入` 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
14921492let a = Symbol ();
@@ -1762,14 +1762,15 @@ class P {
17621762```
17631763
17641764* ** 5.Symbol.replace**
1765+
17651766当该对象被` String.prototype.replace ` 方法调用时,会返回该方法的返回值。
17661767``` js
17671768let a = {};
17681769a[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
17751776class P {
@@ -1871,6 +1872,26 @@ let x = new Collection();
18711872Object .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']);
20152036a .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]
20882203let a3 = [... a .entries ()];// a3 => [['name','leo'], ['age',18]]
20892204```
20902205
2091- ### 10.4 Map与其他数据结构互相转换
2206+ ### 10.5 Map与其他数据结构互相转换
20922207* Map 转 数组
20932208``` js
20942209let 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