├── README.md
├── chapter-01
└── README.md
├── chapter-02
└── README.md
├── chapter-03
└── README.md
├── chapter-04
└── README.md
├── chapter-05
└── README.md
├── chapter-06
└── README.md
├── chapter-07
└── README.md
├── chapter-09
└── README.md
├── chapter-13
└── README.md
├── chapter-14
└── README.md
├── chapter-15
└── README.md
├── chapter-17
└── README.md
└── introducton
└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # 歡迎加入 ES6+ 讀書會
2 | 本讀書會採用自助式共同協作
3 | > es6+ 是泛指 es6 以上本版的 ECMAscript
4 |
5 | ## 導讀內容:
6 | * [第一章 ECMAScript 6 簡介 - polo](chapter-01)
7 | * [第二章 const 和 let - jawayang](chapter-02) / [投影片](https://hackmd.io/p/rJvZCMUM#/) / [線上導讀影片](https://youtu.be/mzQTOIaEqMs)
8 | * [第三章 解構賦值 Destructuring assignment](chapter-03) / [投影片](https://hackmd.io/p/HJcb_nKf#/)
9 | * [第四章 String - Peng Jie](chapter-04) / [投影片](https://hackmd.io/p/S10A0p5G#/) / [線上導讀影片](https://www.youtube.com/watch?v=7LOpj19J4nw)
10 | * [第五章 RegExp - cashbook](chapter-05) / [投影片](https://hackmd.io/p/rkHAATpM#/) / [線上導讀影片](https://www.youtube.com/watch?v=eDvxODarM3U)
11 | * [第六章 數值的拓展 - RayWay](chapter-06) / [投影片](https://hackmd.io/p/SkIRuHSB#/) / [線上導讀影片](https://www.youtube.com/watch?v=vJxlYXu1Dvc)
12 | * [第七章 陣列的拓展 - AllenHsieh](chapter-07) / [投影片](https://hackmd.io/p/S1Jll4LH#/) / [線上導讀影片](https://youtu.be/1eugOttKFfA)
13 | * [第十五章 Generator 函數 - shiningjason](chapter-15) / [投影片](https://hackmd.io/p/r1_8z2Z_#/) / [線上導讀影片](https://youtu.be/5_mUqevT3cM)
14 | * [第十七章 異步操作和 Async 函數 - shiningjason](chapter-17) / [線上導讀影片](https://youtu.be/RvMeDS2yjzU)
15 |
16 | ## 讀書會閱讀工具:
17 |
18 | * [hackmd.io](https://hackmd.io/) [教學影片-Jacky](https://www.youtube.com/watch?v=8maKJ6CJ9no)
19 | * [新同文堂](https://chrome.google.com/webstore/detail/new-tong-wen-tang/ldmgbgaoglmaiblpnphffibpbfchjaeg?hl=zh-TW) 簡體繁體轉換工具
20 | * [hangout 直播](https://plus.google.com/hangouts/onair) 導讀錄影
21 | * markdown 編輯工具
22 | 1. [mac](http://superuser.com/questions/616899/github-flavored-markdown-editor-for-osx)
23 | 2. [atom](https://atom.io/)
24 |
25 | ## 導讀的製作流程
26 | 1. 挑選書中的某個單元
27 | 2. 研讀後將重點透過 github 記下來。
28 | 1. 建立單元
29 | 2. 撰寫內容
30 | 3. 錄製影片 & 線上導讀
31 | 4. 將相關檔案根據單元分類上傳
32 |
33 | ## 書籍內容
34 | * [ECMAScript 6 入门 - 阮一峰](http://es6.ruanyifeng.com/)
35 |
36 | ## 入會方式
37 | * [說明連結](https://softnshare.wordpress.com/portfolio/ecmascript-6%E5%85%A5%E9%96%80%E8%AE%80%E6%9B%B8%E6%9C%83/)
38 |
39 | ## 相關書籍
40 |
41 | * [Understanding ECMAScript 6](https://leanpub.com/understandinges6/read)
42 |
43 | ## ES6 相關資源
44 |
45 | ### 線上課程
46 |
47 | * [ES6 Katas](http://es6katas.org/) - Learn ES6 by doing it. Fix failing tests. Keep all learnings.
48 | * [Let's Learn ES6](https://www.youtube.com/playlist?list=PL57atfCFqj2h5fpdZD-doGEIs0NZxeJTX) Youtube 影片
49 |
50 |
51 | ### 文章整理及投影片
52 |
53 | * [ES6-In-Depth](http://www.infoq.com/cn/es6-in-depth/) - 本迷你书是 InfoQ 特别推出的深入浅出 ES6 专栏合集迷你书,欢迎各位 ES6 爱好者收集下载。
54 | * [ES6-Learning](https://github.com/ericdouglas/ES6-Learning) - List of resources to learn ECMAScript 6!
55 | * [Strikingly ES6 JavaScript Style Guide](https://github.com/strikingly/javascript) - A mostly reasonable approach to JavaScript.
56 | * [ECMAScript 6 簡介](https://hackmd.io/p/4JlFcJKOe#/) - by [jackymaxj](https://twitter.com/jackymaxj)
57 | * [ES6-Cheatsheet](http://slides.com/drksephy/ecmascript-2015)
58 | * [ES6-Feature](http://es6-features.org/#Constants) - New Features: Overview & Comparison.
59 | * [ECMAScript 6 compatibility table](http://kangax.github.io/compat-table/es6/)
60 | * [Computer Science in JavaScript (ES5 and ES6)](https://github.com/benoitvallon/computer-science-in-javascript)
61 |
62 | ### ES6 VS ES5
63 |
64 | * [ES6 equivalents in ES5](https://github.com/addyosmani/es6-equivalents-in-es5)
65 | * [React/React Native 的 ES5 ES6 寫法對照表](http://bbs.reactnative.cn/topic/15/react-react-native-%E7%9A%84es5-es6%E5%86%99%E6%B3%95%E5%AF%B9%E7%85%A7%E8%A1%A8)
66 |
67 | ### 開發工具
68 |
69 | * [ES6 tools](https://github.com/addyosmani/es6-tools)
70 |
--------------------------------------------------------------------------------
/chapter-01/README.md:
--------------------------------------------------------------------------------
1 |
2 | 讀書會第一章內容 請參考 http://es6.ruanyifeng.com/#docs/intro 阮一峰大大
3 |
4 | # 筆記整理
5 | * 主講 polo
6 | * 文字資料補充 james yang
7 |
8 | ## 第1節 ECMAScript和JavaScript的關係
9 | ECMAScript和JavaScript的關係是,前者是後者的規格,後者是前者的一種實現
10 |
11 | * 導讀影片 : https://youtu.be/sqh41P1FMbs
12 |
13 | ## 第2節 ECMAScript的歷史
14 |
15 | 2000開始 -> 2013凍結 -> 20015正式通過
16 |
17 | 
18 |
19 | * 導讀影片 : https://youtu.be/aPVJ-8dHSns
20 |
21 | ## 第3節 部署進度
22 |
23 | 這個章節主要是講各平台目前對es6的支援度
24 |
25 | * Node.js
26 |
27 | * npm install 跟 使用
28 | ``` bash
29 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
30 | nvm ls-remote
31 | nvm ls
32 | nvm use
33 | ```
34 |
35 | * 導讀影片 : https://youtu.be/RRN7yiGX9Ec
36 |
37 | ## 第4節 Babel 轉碼器
38 |
39 | * 配置文件 `.babelrc`
40 |
41 | * 各種轉換方式:
42 |
43 | * 命令行轉碼 `babel-cli`
44 | - babel-node (babel-cli安裝時就會裝)
45 | - babel-register (程式直接轉不用另外產出)
46 | - babel-core (使用Babel的API轉碼)
47 | - babel-polyfill (讓 es6 api 與 es5 相容)
48 | > 由於 babel 預設不轉換 es6 相關的物件, 只轉語法 , 透過 import babel-polyfill 可以讓 es5 執行環境支援相關的物件,像是 generator , promise...)
49 |
50 |
51 | * 瀏覽器環境
52 | - 在線轉換 https://babeljs.io/repl/
53 |
54 |
55 | * 導讀影片 : https://youtu.be/eOw7cp_JXTQ
56 |
57 | ## 第5節 Traceur 轉碼器
58 |
59 | * 安裝 `npm install -g traceur`
60 | * 使用 `traceur calc.js`
61 | * `node` 中使用 `traceur`
62 |
63 | * 導讀影片 : https://youtu.be/Cjb4-3r4DLk
64 |
65 | ## 第6節 ECMAScript 7
66 | 任何人都可以向TC39提案,從提案到變成正式標準,需要經歷五個階段。每個階段的變動都需要由TC39委員會批准。
67 |
68 | * Stage 0 - Strawman(展示階段)
69 | * Stage 1 - Proposal(徵求意見階段)
70 | * Stage 2 - Draft(草案階段)
71 | * Stage 3 - Candidate(候選人階段)
72 | * Stage 4 - Finished(定案階段)
73 |
74 | > 一個提案只要能進入Stage 2,就差不多等於肯定會包括在ES7裡面。
75 |
--------------------------------------------------------------------------------
/chapter-02/README.md:
--------------------------------------------------------------------------------
1 |
2 | # ES6 讀書會
3 |
4 | let 與 const
5 |
6 | ###### tags: `let` `const`
7 |
8 | ----
9 |
10 | ## 關於我
11 |
12 | * James Yang (jawayang)
13 | * ES6 讀書會召集人
14 | * blog : [jamestw.logdown.com](jamestw.logdown.com)
15 |
16 |
17 | ----
18 |
19 |
20 | ## let 與 const
21 |
22 | ES6 入門 第二章
23 | 作者:阮一峰
24 | http://es6.ruanyifeng.com/#docs/let
25 |
26 |
27 | ----
28 |
29 | let 指令
30 | =====
31 |
32 | 基本用法
33 |
34 | ```
35 | 使用 `let` 陳述式來宣告變數,其範圍限於宣告所在的`區塊`
36 | 在 `javascript` 中 我們使用 {} 來代表`區塊`
37 | ```
38 |
39 |
40 |
41 | ----
42 |
43 | ## var 與 let 的差別
44 |
45 | ``` js
46 | {
47 | let a = 10;
48 | var b = 1;
49 | }
50 |
51 | console.log(a) // ReferenceError: a is not defined.
52 | console.log(b) // 1
53 |
54 | ```
55 | ``` text
56 | 區塊中,分別用 var 與 let 宣告了兩個不同的變數
57 | 在區塊外部,很明確地可以看出差異,使用`let`的變數,只能用在區塊內部。
58 | ```
59 |
60 | ----
61 |
62 | ## 使用時機
63 |
64 | let 適合用在 for 迴圈
65 | ``` text
66 | 與 var 比較
67 | ```
68 | ``` js
69 | var a = [];
70 | for (var i = 0; i < 10; i++) {
71 | a[i] = function () {
72 | console.log(i);
73 | };
74 | }
75 | a[1](); //10
76 | a[6](); //10
77 | ```
78 |
79 | ``` text
80 | 使用 var 的時候,i 會不斷地被覆蓋,結果永遠等於 10
81 | ```
82 |
83 | ----
84 |
85 | ## 使用時機
86 |
87 | 在迴圈中使用 let
88 |
89 | ``` js
90 | var a = [];
91 | for (let i = 0; i < 10; i++) {
92 | a[i] = function () {
93 | console.log(i);
94 | };
95 | }
96 | a[1](); // 1
97 | a[6](); // 6
98 |
99 | ```
100 | ``` text
101 | 使用 let 的時候,i 不會被覆蓋,每次循環都會是一個新的宣告
102 | ```
103 |
104 | ----
105 |
106 | ## Hoisting 提升
107 |
108 | 宣告提升到其所在區域內頂端的行為
109 | ``` text
110 | 與 var 不同,不會有變數提升的現象。
111 | ```
112 |
113 | ``` js
114 | console.log(foo); //顯示 undefined;
115 | console.log(bar); //報錯 ReferenceError
116 | var foo = 2;
117 | let bar = 2;
118 | ```
119 |
120 | ----
121 |
122 | ## 暫時性死區
123 |
124 | ``` text
125 | ES6 明確規定,區塊中如果存在 `let` 或 `const` 宣告、這個區塊對這些宣告
126 | 會產生封閉的作用區。
127 | 簡單說:在宣告 let 跟 const 變數之前,該變數都是不可用的。
128 | 這在語法上,稱為「暫時性死區」(temporal dead zone,簡稱TDZ)。
129 | ```
130 |
131 |
132 |
133 | ``` js
134 | if (true) {
135 | // TDZ開始
136 | tmp = 'abc'; // ReferenceError
137 | console.log(tmp); // ReferenceError
138 |
139 | let tmp; // TDZ結束
140 | console.log(tmp); // undefined
141 |
142 | tmp = 123;
143 | console.log(tmp); // 123
144 | }
145 | ```
146 |
147 |
148 | ``` text
149 | 這樣的設計是為了讓大家養成良好的編程習慣,變量一定要在宣告之後使用
150 | ```
151 |
152 |
153 | ---
154 |
155 | ``` text
156 | 實際測試的時候,測試環境結果有所不同
157 | chrome 50 版本會根據本書上的敘述,表示錯誤。
158 | 若用 `Babel` 轉譯則不會顯示錯誤
159 | 支援程度可以參考:http://kangax.github.io/compat-table/es6/
160 | ```
161 |
162 | ----
163 |
164 | ## 暫時性死區
165 |
166 | ``` text
167 | 這也表示使用`typeof`來檢查使用`let`宣告的變數,可能會出現錯誤
168 | ```
169 | ``` text
170 | 範例:
171 | ```
172 |
173 | ``` js
174 | typeof x;// 出現錯誤
175 | let x;
176 | ```
177 | ```
178 | 因為在 let 宣告 x 之前,無法使用 x 這個變數
179 | ```
180 |
181 |
182 |
183 | ----
184 |
185 | ## 暫時性死區
186 |
187 | 較為不容易察覺的`死區`
188 |
189 | ``` js
190 | function bar (x=y,y=2){
191 | return [x,y];
192 | }
193 | // 參數 x 預設為 y
194 | // 參數 y 預設為 2
195 | ```
196 | ``` text
197 | 因為此時 y 尚未被宣告,所以會報錯
198 | ```
199 |
200 | 修正的方式
201 | ``` js
202 | function bar (x=2,y=x){
203 | return [x,y];
204 | }
205 | // 先宣告 x 再宣告 y
206 | ```
207 |
208 |
209 | ----
210 |
211 | ## let 不允許重複
212 | ``` text
213 | `let` 不允許在同一個區塊內宣告同一個變數
214 | ```
215 |
216 | ``` js
217 | function(){
218 | let a = 10;
219 | let a = 9 // 發生錯誤
220 | }
221 | ```
222 | ``` text
223 | 所以不能再函式內部重新宣告參數
224 | ```
225 |
226 | ``` js
227 | function func(arg){
228 | let art; // 發生錯誤
229 | }
230 | function func(arg){
231 | {
232 | let arg; // 不發生錯誤
233 | }
234 | }
235 | ```
236 |
237 | ----
238 |
239 | ## 為什麼需要區塊範圍變數 ?
240 |
241 | ``` text
242 | 在`ES5`中只有全域變數與函式變數,沒有區塊範圍變數。
243 | 因此造成了很多不合理的狀況。
244 | ```
245 |
246 | ### 狀況一
247 | ``` js
248 | var tmp = new Data();
249 |
250 | function f(){
251 | console.log(tmp); // undefined
252 | if(false){
253 | var tmp = "hello world";
254 | }
255 | }
256 | f();
257 | ```
258 | ``` text
259 | 在 `es5` 執行之後,tmp 在函數內輸出的結果會是 `undefined`,
260 | 由於 `tmp` 變數提升,導致函式內部的變數,覆蓋了外部的 `tmp`
261 | ```
262 |
263 |
264 |
265 | ----
266 |
267 | ## 狀況二
268 |
269 |
270 | ``` js
271 | var s = 'hello';
272 | for(var i=0 ; i< s.length; i++){
273 | console.log(s[i]);
274 | }
275 | console.log(i) //5
276 | ```
277 | ``` text
278 | 變數 i 只用來提供迴圈執行,執行結束後,並沒有消失,變成了全域變數。
279 | ```
280 |
281 | ----
282 |
283 | ## ES6 區塊作用域
284 |
285 | let 提供了新的變數作用域
286 |
287 | ``` js
288 | function f1() {
289 | let n = 5; //5
290 | if (true) {
291 | let n = 10; //10
292 | }
293 | console.log(n); // 5
294 | }
295 | ```
296 | ```
297 | 若使用 var 最後輸出結果會變成 10
298 | ```
299 |
300 | ----
301 |
302 | ## let 限制變數作用區域
303 |
304 |
305 | ``` js
306 | {{{
307 | {
308 | let a = 1;
309 | {let b = 1};
310 | console.log(b); //報錯
311 | }
312 | console.log(a); //報錯
313 | }}}
314 | ```
315 |
316 | ----
317 |
318 | 過去我們希望隔離不同的變數區域時,
319 | 會需要用到立即執行匿名函式
320 | 像這樣:
321 |
322 | ``` js
323 | (function(){
324 | var a = 0;
325 | })();
326 | //確保 a 不會污染全域
327 |
328 | console.log(a); // undefined
329 |
330 | //在ES6中可以改成用區塊撰寫
331 | {
332 | let b = 0;
333 | }
334 | console.log(b);
335 |
336 | ```
337 |
338 | ----
339 |
340 | ## 區塊中的函式
341 |
342 | ``` text
343 | ES6 中規定 函數本身的作用域,在其所在的塊級作用域之內。
344 | ```
345 | ``` js
346 | function f(){ console.log('我在外面');}
347 | (function(){
348 | if(true){
349 | function f(){ console.log('我在裡面');}
350 | }
351 | f();
352 | })();
353 | ```
354 | ```
355 | 若由 es5 的執行環境執行時,結果會是'我在裡面'
356 | 若由 es6 執行環境執行,結果會是'我在外面'
357 | ```
358 | ``` test
359 | 在ES6中內部宣告的函數皆不會影響到作用域的外部
360 | ```
361 |
362 | ----
363 |
364 | ## 嚴格模式 use strict
365 | ``` text
366 | 在ES5嚴格模式規定,函式只能在頂層作用域或是函式內宣告,
367 | 其他狀況(例如`if`指令、循環指令區)的宣告都會報錯。
368 | ```
369 | ```js
370 | //ES5
371 | `use strict`
372 | if(true){
373 | function f(){ } //報錯
374 | }
375 |
376 |
377 | ```
378 |
379 |
380 | ----
381 |
382 | ## ES6 嚴格模式
383 | ``` text
384 | ES6由於引入了塊級作用域,這種情況可以理解成函數在塊級作用域內聲明
385 | ```
386 |
387 | ```js
388 | // 不報錯
389 | 'use strict';
390 | if (true) {
391 | function f() {}
392 | }
393 |
394 | // 報錯 不能缺少 { }
395 | 'use strict';
396 | if (true)
397 | function f() {}
398 |
399 | f(); // 無法執行因為在區塊外部
400 | ```
401 |
402 |
403 | ----
404 |
405 |
406 |
407 | # Const 命令
408 |
409 | 基本用法
410 |
411 | ----
412 |
413 | ## 宣告常數
414 |
415 | const 是一個唯讀的常數。
416 | 一旦被宣告,常數的值就不能變。
417 |
418 | ``` text
419 | 嚴格模式
420 | ```
421 | ``` js
422 | 'use strict';
423 | const PI = 3.1415; // 3.1415
424 | PI = 3; // TypeError: "PI" is read-only
425 |
426 | ```
427 | ``` text
428 | 非嚴格模式,不報錯,且PI的值不變。
429 | ```
430 | ```
431 | const PI = 3.1415; // 3.1415
432 | PI = 3; // 3.1415
433 | ```
434 |
435 | ----
436 |
437 | ## 宣告常數
438 | ``` text
439 | 嚴格模式'use strict';
440 | ```
441 | ``` js
442 | 'use strict';
443 | const foo;
444 | // SyntaxError: missing = in const declaration
445 |
446 | ```
447 | ``` text
448 | 非嚴格模式
449 | ```
450 | ``` js
451 | const foo;
452 | foo = 1; // 重新賦值無效
453 | foo // undefined
454 | ```
455 |
456 | ----
457 |
458 | ## const 作用域
459 | ``` text
460 | 作用域與 let 相同,同樣存在暫時性死區,只能在宣告後面的位置使用。
461 | ```
462 | ``` js
463 | if(true){
464 | console.log(MAX); //錯誤參照
465 | const MAX = 5;
466 | }
467 | MAX // 未定義
468 | ```
469 |
470 | ----
471 |
472 | ## 宣告不可重複
473 | ``` text
474 | 與 let 相同,同一個變數名稱在同一區塊不得重複宣告。
475 | ```
476 | ```
477 | var message = "Hello!";
478 | let age = 25;
479 |
480 | // 以下兩行都會報錯
481 | const message = "Goodbye!";
482 | const age = 30;
483 | ```
484 |
485 | ----
486 |
487 | ## const 宣告物件及陣列
488 | ``` text
489 | 對於複合資料類型之數據,像是 array 或 obj,
490 | 宣告時指向的是數據所在的參考,無法保證變數內的數據不變。
491 | 所以當宣告一個物件為常數的時候要很小心。
492 | ```
493 | ``` js
494 | const foo = {}; //foo 指向物件位置
495 | foo.prop = 123; //物件內容可以被修改
496 |
497 | foo.prop; //123
498 | ```
499 |
500 | ----
501 |
502 | ## 避免常數物件或陣列異動
503 | ``` text
504 | 為了避免宣告的物件或陣列被修改,
505 | 可以使用 Object.freeze();
506 | 方法凍結該物件或陣列。
507 |
508 | ```
509 | ``` js
510 | const obj = Object.freeze({}); //宣告一個不能異動的常數物件
511 | const arr = Object.freeze([]); //宣告一個不能異動的陣列物件
512 | // 下面的指令沒有作用
513 | // 嚴格模式下會顯示錯誤
514 | obj.prop = 123;
515 | arr[0] = 123;
516 | console.log(obj.prop); //無法執行
517 | console.log(arr); //無法執行
518 | ```
519 | ``` text
520 | 在 http://www.es6fiddle.net/ 中執行會出錯,必須拿掉修改才會正常執行
521 | ```
522 |
523 | ----
524 |
525 | ## 凍結屬性
526 | ``` text
527 | 除了物件本身應該要凍結之外,物件的屬性若指向物件時應該也要凍結,凍結的方法
528 | ```
529 | ``` js
530 | var constantize = (obj) => {
531 | Object.freeze(obj);
532 | Object.keys(obj).forEach( (key, value) => {
533 | if ( typeof obj[key] === 'object' ) {
534 | constantize( obj[key] );
535 | }
536 | });
537 | };
538 | ```
539 |
540 | ----
541 |
542 | ## 變數宣告
543 |
544 | ES5只有兩種聲明變量的方法:
545 | `var` 和 `function`
546 |
547 | ``` text
548 | 除了原本的 var 跟 function 之外
549 | ```
550 | ES6有 `let` 跟 `const`
551 | 還有 `import` 與 `class`
552 | ``` text
553 | 共六種變數宣告方式
554 | ```
555 |
556 | ----
557 |
558 | ## 全域物件
559 | ``` text
560 | 全域物件,是指最上層的物件,
561 | 在瀏覽器中指的是 `window` 物件
562 | 在`Node.js`指的是`global` 物件
563 | ```
564 | ``` js
565 | //在 ES5 中 全域物件的屬性,就等於全域變數
566 | window.a = 1;
567 | console.log(a); // 1
568 |
569 | a = 2;
570 | console.log(window.a); //2;
571 |
572 | b = 3; //沒有宣告
573 | console.log(window.b); //3;
574 | ```
575 |
576 | ``` text
577 | 在 ES5 中沒有宣告的全域變數,自動變成為全域物件window的屬性,
578 | 不容易除錯,很容易就產生問題。
579 | ```
580 |
581 | ----
582 |
583 | ## 全域物件
584 |
585 | 從ES6開始,全域變數將逐步與全域物件的屬性脫鉤。
586 |
587 | ``` text
588 | ES6為了改變這一點,但仍然保持兼容性,
589 | var 和 function 宣告的全域變數,依舊是全域物件的屬性;
590 | 另一方面規定,let命令、const命令、class命令聲明的全域變數,
591 | 不屬於全域物件的屬性。
592 | ```
593 | ``` js
594 | //如果是在最外層宣告
595 | var a = 1;
596 | window.a; //1
597 |
598 | let b = 1;
599 | window.b // undefined
600 | ```
601 |
602 | ----
603 |
604 | 結論
605 | ====
606 |
607 |
608 | let 與 const 是用來宣告區塊中的變數
609 | 這些變數在區塊外部是無效的
610 | 並且要注意宣告與使用的順序
611 |
612 |
613 |
614 | 謝謝指教
615 | 期待下次的分享
616 |
617 |
618 |
--------------------------------------------------------------------------------
/chapter-03/README.md:
--------------------------------------------------------------------------------
1 | # ES6 讀書會
2 |
3 | 解構賦值
4 |
5 |
6 | ###### tags: `解構賦值`
7 |
8 | ----
9 |
10 | ## 關於我
11 |
12 | * James Yang (jawayang)
13 | * ES6 讀書會召集人
14 | * blog : [jamestw.logdown.com](jamestw.logdown.com)
15 |
16 |
17 | ###### tags: `解構`,`Destructuring`
18 |
19 | ----
20 |
21 | ## 解構賦值 / 分割代入
22 |
23 | Destructuring assignment
24 |
25 | 基本用法
26 |
27 | 透過 `解構賦值` 來映射 `物件` 或 `陣列` 中的資料
28 |
29 |
30 | ---
31 |
32 | ## 陣列的解構賦值
33 | Array destructuring
34 |
35 | ``` text
36 | es5 寫法:
37 | ```
38 |
39 | ``` js
40 | var a = 1;
41 | var b = 2;
42 | var c = 3;
43 | ```
44 |
45 |
46 |
47 |
48 | ``` text
49 | es6 寫法:
50 | ```
51 |
52 |
53 |
54 | ``` js
55 | var [a, b, c] = [1, 2, 3];
56 | ```
57 |
58 |
59 |
60 | ``` text
61 | 上面代碼表示,可以從陣列中提取值,按照對應位置,對變數賦值。
62 | ```
63 |
64 |
65 |
66 | ----
67 |
68 | ``` text
69 | 簡單說就是一對一的關係
70 | ```
71 |
72 | ----
73 |
74 | ``` text
75 | 在陣列前可以使用 var 或 let 或 const 等指令..
76 | ```
77 |
78 | ``` js
79 | var [a1,b1,c1] =[1,2,3];
80 | ```
81 |
82 | ``` js
83 | let [a2,b2,c2] = [1,2,3];
84 | ```
85 |
86 |
87 | ``` js
88 | const [d3,e3,f3] = [1,2,3];
89 | ```
90 |
91 |
92 | ----
93 |
94 | ## 格式匹配
95 |
96 | ``` text
97 | 只要等號兩邊的格式相同,左邊的變數就會被賦予對應的值
98 | ```
99 |
100 |
101 | ``` js
102 | var [foo, [[bar], baz]] = [1, [[2], 3]];
103 | foo //1
104 | bar //2
105 | baz //3
106 | ```
107 |
108 |
109 | ``` text
110 | 也可保留空位
111 | ```
112 |
113 |
114 | ``` js
115 | var [, , third] = ["foo","bar","baz"];
116 | third // "baz"
117 | ```
118 |
119 |
120 | ----
121 |
122 | ## 部分匹配
123 |
124 | ``` text
125 | 如果沒有對應的結構,變數的值就會是 undefined
126 | ```
127 |
128 |
129 | ``` js
130 | var [foo] = [];
131 | var [bar,foo] = [1];
132 | //foo 都等於 undefined
133 | ```
134 |
135 |
136 | ``` text
137 | 不完全對應
138 | ```
139 |
140 |
141 | ``` js
142 | let [x,y] = [1,2,3];
143 | x //1
144 | y //2
145 |
146 | let [a,[b],c] = [1,[2,3],4];
147 | a //1
148 | b //2
149 | c //4
150 | ```
151 |
152 |
153 | ``` text
154 | 雖然沒有完全解構,但是還是可以匹配部分的變數
155 | ```
156 |
157 |
158 |
159 | ----
160 |
161 | ## 不定量參數 "..."
162 |
163 | 不定參數 / 其餘參數 / 剩餘參數 / 殘餘引數 /
164 | Rest parameters
165 |
166 | ``` text
167 | 用法:不定量參數會回傳陣列
168 | ```
169 |
170 |
171 | ``` js
172 | var [head, ...tail] = [1,2,3,4];
173 | tail // [2,3,4]
174 |
175 | let [x, y, ...z] = ['a'];
176 | x // 'a'
177 | y // undefined
178 | z // []
179 | ```
180 |
181 |
182 | ----
183 |
184 | ## 非陣列指派
185 |
186 | ```
187 | 若等號的右邊不是同樣的陣列格式,就會出現錯誤
188 | ```
189 |
190 |
191 | ``` js
192 | // 報錯
193 | let [foo] = 1;
194 | let [foo] = false;
195 | let [foo] = NaN;
196 | let [foo] = undefined;
197 | let [foo] = null;
198 | let [foo] = {};
199 | ```
200 |
201 |
202 | ```
203 | 主要原因是因為不具備 Iterator 迭代介面 / 疊代
204 | ```
205 |
206 |
207 | ----
208 |
209 | ## 具備迭代介面的物件
210 |
211 | ``` text
212 | 若是使用 Set 物件,也可以用在陣列解構賦值
213 | ```
214 |
215 | ``` js
216 | let [x,y,z] = new Set(["a","b","c"]);
217 | x //"a"
218 | y //"b"
219 | z //"c"
220 | ```
221 |
222 |
223 | ``` text
224 | 可遍歷的物件,(指針物件 / 指標物件)
225 | 像陣列、物件都具備這樣的結構
226 | 而 ES6 又新增了 Map 與 Set
227 | ```
228 |
229 |
230 | ----
231 |
232 | ## 自建具備迭代介面的物件
233 |
234 | ``` js
235 | function* fib(){
236 | var a = 0;
237 | var b = 1;
238 | while (true){
239 | yield a;
240 | [a,b] = [b, a + b];
241 | }
242 | }
243 |
244 | var [first,second,third,fourth] = fib();
245 | first // 0
246 | second // 1
247 | third // 2
248 | ```
249 |
250 |
251 | ```
252 | fibs 是一個 Generator 函式,具備 Iterator 介面
253 | 解構賦值會依次從這個介面取值
254 | ```
255 |
256 |
257 |
258 | ----
259 |
260 | ## 陣列解構賦值的運用
261 |
262 | ``` text
263 | 用來交換變數
264 | ```
265 | ``` js
266 | var a = 1; b = 2;
267 | [b,a] = [a,b];
268 | a //2
269 | b //1
270 | ```
271 |
272 | ----
273 |
274 | ## 陣列解構賦值的運用
275 |
276 | ``` text
277 | 與函式並用,可以取得函式回傳的陣列值
278 | ```
279 | ```
280 | function f(){
281 | return [1,2,3];
282 | }
283 | var [a,,b] = f();
284 | a //1
285 | b //3
286 | ```
287 |
288 | ----
289 |
290 | ## 陣列解構賦值的運用
291 |
292 | ``` text
293 | 用正則表達式匹配提取值
294 | ```
295 | ``` js
296 | var url = "http://www.w3schools.com/svg/";
297 |
298 | var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
299 | var [, protocol, fullhost, fullpath] = parsedURL;
300 |
301 | console.log(protocol); // 輸出"https:"
302 | ```
303 |
304 | ----
305 |
306 | ## 陣列解構賦值的運用
307 |
308 | ``` text
309 | 函式的參數也可以使用解構賦值
310 | ```
311 | ``` js
312 | function add([x, y]){
313 | return x + y;
314 | }
315 |
316 | add([1, 2]); // 3
317 | ```
318 | ``` text
319 | 函式 add 的參數表面上是一個陣列,但在傳入參數的那一刻,
320 | 陣列參數就被解構成變量x和y
321 | ```
322 | ``` text
323 | 下面是另一個例子
324 | ```
325 | ``` js
326 | [[1, 2], [3, 4]].map(([a, b]) => a + b);
327 | // [ 3, 7 ]
328 |
329 | [[1,2],[3,4]].map(function([a,b]){
330 | return a+b;
331 | })
332 |
333 | ```
334 |
335 | ----
336 |
337 | ## 指定預設值
338 |
339 | ```text
340 | 解構賦值允許指定預設值
341 | 當對應值為 undefined 的時候,就會使用預設值
342 | ```
343 |
344 |
345 | ```javascript
346 | var [a=0,b=0] = [1,];
347 | a //1
348 | b //0
349 | ```
350 |
351 |
352 | ``` text
353 | 預設值可以引用解構賦值的其他變數,但該變數必須已經宣告。
354 | ```
355 |
356 |
357 | ``` js
358 | var [x = 1, y = x] = []; // x=1; y=1
359 | var [x = 1, y = x] = [2]; // x=2; y=2
360 | var [x = 1, y = x] = [1, 2]; // x=1; y=2
361 | var [x = y, y = 1] = []; // ReferenceError
362 | ```
363 |
364 |
365 |
366 | ---
367 |
368 | ## 物件的解構賦值
369 | Object destructuring
370 |
371 |
372 |
373 | ``` text
374 | 解構不僅僅可以用在陣列,也可以用在物件
375 | ```
376 |
377 |
378 | ``` js
379 | var { foo, bar } = { foo: "aaa", bar: "bbb" };
380 | foo // "aaa"
381 | bar // "bbb"
382 | ```
383 |
384 |
385 | ``` text
386 | 物件與陣列的差別,在於陣列是根據順序排列,
387 | 變數的內容根據位置決定,而物件並沒有順序,
388 | 變數必須與物件屬性同名,才能取得到內容
389 | ```
390 |
391 |
392 | ``` js
393 | var { baz } = { foo: "aaa", bar: "bbb" };
394 | baz // undefined
395 | ```
396 |
397 |
398 | ----
399 |
400 | ## 名稱不一致
401 | ``` text
402 | 如果變量名與屬性名不一致,必須寫成下面這樣。
403 | ```
404 |
405 |
406 | ``` js
407 | var { foo: baz } = { foo: "aaa", bar: "bbb" };
408 | baz // "aaa"
409 | ```
410 |
411 |
412 | ```text
413 | 再複雜一點
414 | ```
415 |
416 |
417 | ``` js
418 | let obj = { first: 'hello', last: 'world' };
419 | let { first: f, last: l } = obj;
420 | f // 'hello'
421 | l // 'world'
422 | ```
423 |
424 |
425 | ----
426 |
427 |
428 | ## 實際的內部機制
429 | ``` js
430 | var { foo, bar } = { foo: "aaa", bar: "bbb" };
431 | ```
432 |
433 | ```text
434 | 上方的程式碼同等於下方的程式碼
435 | ```
436 |
437 | ``` js
438 | var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
439 | ```
440 |
441 | ``` text
442 | 物件的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變數。
443 | 真正被賦值的是後者,而不是前者
444 | ```
445 |
446 | ``` js
447 | var { foo: baz } = { foo: "aaa", bar: "bbb" };
448 | baz // "aaa"
449 | foo // error: foo is not defined
450 | ```
451 |
452 | ``` text
453 | 上面代碼中,真正被賦值的是變數 baz,而不是模式foo。
454 | ```
455 |
456 |
457 | ----
458 |
459 | ## 注意事項
460 |
461 | ``` text
462 | 使用這種寫法,變數的宣告與賦值是一起的。但是對於 let 與 const 指令來說,
463 | 變數不能重新被宣告,所以一旦變數之前已經被宣告過了,
464 | 在結構賦值的時候,就會發生錯誤。
465 | ```
466 |
467 | ```js
468 | let foo;
469 | let {foo} = {foo: 1};
470 | // SyntaxError: Duplicate declaration "foo"
471 |
472 | let baz;
473 | let {bar: baz} = {bar: 1};
474 | // SyntaxError: Duplicate declaration "baz"
475 | ```
476 |
477 | ----
478 |
479 | ## 解決方案
480 | ``` text
481 | 在解構賦值時不要使用 let 指令
482 | ```
483 |
484 | ```js
485 | let foo;
486 | ({foo} = {foo: 1}); // 成功
487 |
488 | let baz;
489 | ({bar: baz} = {bar: 1}); // 成功
490 | ```
491 |
492 | ``` text
493 | const只能賦值時並用,沒辦法分成兩段
494 | ```
495 |
496 | ```js
497 | const a; //報錯
498 | const {bar: a} = {bar: 1}; // 成功
499 | ```
500 |
501 |
502 | ----
503 |
504 | ## 解構嵌套/巢狀解構/Nested
505 | ``` text
506 | 和陣列一樣,解構也可以用於嵌套結構的物件
507 | ```
508 | ``` js
509 | var obj = {
510 | p: [
511 | "Hello",
512 | { y: "World" }
513 | ]
514 | };
515 |
516 | var { p: [x, { y }] } = obj;
517 | x // "Hello"
518 | y // "World"
519 | ```
520 |
521 | ``` text
522 | 注意,這時p是樣式 patten ,不是變數,因此不會被賦值。
523 | ```
524 |
525 | ----
526 |
527 | ## 解構嵌套/巢狀解構/Nested
528 |
529 | ```js
530 | var node = {
531 | loc: {
532 | start: {
533 | line: 1,
534 | column: 5
535 | }
536 | }
537 | };
538 |
539 | var { loc: { start: { line }} } = node;
540 | line // 1
541 | loc // error: loc is undefined
542 | start // error: start is undefined
543 | ```
544 | ``` text
545 | 上面代碼中,只有line是變量,loc和start都是模式,不會被賦值。
546 | ```
547 |
548 | ----
549 |
550 | ## 解構嵌套/巢狀解構/Nested
551 |
552 | ```js
553 | let obj = {};
554 | let arr = [];
555 |
556 | ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
557 |
558 | obj // {prop:123}
559 | arr // [true]
560 | ```
561 |
562 | ----
563 |
564 | ## 設定預設值
565 | ```
566 | var {x = 3} = {};
567 | x // 3
568 |
569 | var {x, y = 5} = {x: 1};
570 | x // 1
571 | y // 5
572 |
573 | var { message: msg = "Something went wrong" } = {};
574 | msg // "Something went wrong"
575 | ```
576 | ``` text
577 | 預設值生效的條件是,物件的屬性值嚴格等於 undefined
578 | ```
579 | ``` js
580 | var {x = 3} = {x: undefined};
581 | x // 3
582 |
583 | var {x = 3} = {x: null};
584 | x // null
585 | ```
586 |
587 | ----
588 |
589 | ## 解構失敗
590 |
591 | ``` text
592 | 如果解構失敗,變量的值等於 undefined。
593 | ```
594 | ``` js
595 | var {foo} = {bar: 'baz'};
596 | foo // undefined
597 | ```
598 |
599 | ``` text
600 | 如果解構模式是嵌套的物件,而且子物件所在的父屬性不存在,那麼將會報錯。
601 | ```
602 | ``` js
603 | var {foo: {bar}} = {baz: 'baz'};
604 | ```
605 | ``` text
606 | 因為 foo 這時等於 undefined , 再取子屬性就會報錯
607 | ```
608 |
609 | ----
610 |
611 | ## 奇怪的寫法
612 |
613 | ``` text
614 | 如果要將一個已經聲明的變數用於解構賦值,必須非常小心。
615 | ```
616 | ```
617 | var x;
618 | {x} = {x: 1};
619 | ```
620 | ``` text
621 | 把大括號放在聚首,會被誤以為是程式區塊,而發生錯誤,
622 | 避免這個問題要在外面加上圓括號
623 | ```
624 | ```js
625 | var x;
626 | ({x} = {x: 1});
627 | ```
628 |
629 | ``` text
630 | 解構賦值允許,等號左邊的模式之中,不放置任何變數名
631 | ```
632 | ``` js
633 | ({} = [true, false]);
634 | ({} = 'abc');
635 | ({} = []);
636 | ```
637 | ``` js
638 | 上面的表達式雖然毫無意義,但是語法是合法的,可以執行。
639 | ```
640 |
641 | ----
642 |
643 | ## 物件解構賦值的應用
644 |
645 | ``` text
646 | 物件的解構賦值,可以很方便地將現有物件的方法,賦值到某個變數。
647 | ```
648 | ``` js
649 | let { log, sin, cos } = Math;
650 | ```
651 | ``` text
652 | 上面代碼將Math對象的對數、正弦、餘弦三個方法,
653 | 賦值到對應的變量上,使用起來就會方便很多。
654 | ```
655 |
656 | ----
657 |
658 | ## 布林值與數值的解構
659 |
660 | ```
661 | let {toString: s} = 123;
662 | s === Number.prototype.toString // true
663 |
664 | let {toString: s} = true;
665 | s === Boolean.prototype.toString // true
666 | ```
667 |
668 | ``` text
669 | 解構賦值的規則是,只要等號右邊的值不是對象,
670 | 就先將其轉為對象。由於undefined和null無法轉為對象,
671 | 所以對它們進行解構賦值,都會報錯。
672 | ```
673 |
674 | ----
675 |
676 | ## 字串的解構賦值的應用
677 |
678 | ``` text
679 | 字串與陣列都有一個length屬性,因此還可以對這個屬性解構賦值。
680 | ```
681 | ``` js
682 | let {length : len} = 'hello';
683 | len // 5
684 | ```
685 |
686 | ----
687 |
688 | ## 函式參數解構賦值的運用
689 |
690 | ``` text
691 | 函式參數的解構可以使用預設值。
692 | ```
693 | ``` js
694 | function move({x = 0, y = 0} = {}) {
695 | return [x, y];
696 | }
697 |
698 | move({x: 3, y: 8}); // [3, 8]
699 | move({x: 3}); // [3, 0]
700 | move({}); // [0, 0]
701 | move(); // [0, 0]
702 | ```
703 | ``` text
704 | 上面程式中,函式 move 的參數是一個物件,通過對這個物件進行解構,
705 | 得到變數x和y的值。如果解構失敗,x和y等於預設值。
706 | ```
707 |
James 解讀:
708 | 若 move() 沒有傳參數進去,參數預設就會是一個空物件,
709 | 然後 x 預設就是 0 , y 預設為 0
710 |
711 |
712 | ----
713 |
714 | ## 函式參數解構賦值的運用
715 |
716 | ``` text
717 | 寫法不同,結果不同
718 | ```
719 |
720 | ``` js
721 | function move({x, y} = { x: 0, y: 0 }) {
722 | return [x, y];
723 | }
724 |
725 | move({x: 3, y: 8}); // [3, 8]
726 | move({x: 3}); // [3, undefined]
727 | move({}); // [undefined, undefined]
728 | move(); // [0, 0]
729 | ```
730 |
731 | ```
732 | undefined就會觸發函式參數的預設值。
733 | ```
734 |
735 | ----
736 |
737 | ## 圓括號的使用
738 |
739 | ``` text
740 | 解構賦值雖然很方便,但是解析起來並不容易。
741 | 對於編譯器來說,一個`程式碼`到底是`樣式`,
742 | 還是`表達式`,沒有辦法從一開始就知道。
743 | ```
744 | ``` text
745 | ES6 的處理規則,只要是有可能導致解構的爭議,就不得使用。
746 | 建議盡量不要在樣式中放置圓括號
747 | ```
748 |
749 | ----
750 | ## 不能使用圓括號的狀況
751 |
752 | 1. 變數宣告
753 | ``` js
754 | var [(a)] = [1];
755 |
756 | var {x: (c)} = {};
757 | var ({x: c}) = {};
758 | var {(x: c)} = {};
759 | var {(x): c} = {};}
760 |
761 | var { o: ({ p: p }) } = { o: { p: 2 } };
762 |
763 | ```
764 |
765 | 2. 函式參數中不得使用
766 |
767 | ``` js
768 | function f([(z)] reture z );
769 | ```
770 |
771 | 3. 解構賦值,不能將整個或某一層,放在圓括符中。
772 | ``` js
773 | ({ p: a }) = { p: 42 };
774 | ([a]) = [5];
775 | [({ p: a }), { x: c }] = [{}, {}];
776 | ```
777 |
778 | ## 可以使用圓括號的狀況
779 |
780 | 1. 賦值程式的非樣式部分,可以使用圓括號。
781 | ``` js
782 | [(b)] = [3]; // 正確
783 | ({ p: (d) } = {}); // 正確
784 | [(parseInt.prop)] = [3]; // 正確
785 | ```
786 | ```
787 | 全是賦值,而非宣告,圓括號全都不是模式的部分。
788 | ```
789 |
790 | ----
791 |
792 | ## 解構的用途
793 |
794 | 1.交換變數
795 |
796 |
797 | ``` js
798 | [x,y] = [y,x];
799 | ```
800 |
801 | ----
802 |
803 | 2.從函式返回多個值
804 |
805 | ``` text
806 | 函式只能返回一個值,如果要返回多個值,
807 | 只能將它們放在陣列或物件裡返回。
808 | 有了解構賦值,取出這些值就非常方便。
809 | ```
810 | ``` js
811 | // 返回一個陣列
812 | function example() {
813 | return [1, 2, 3];
814 | }
815 | var [a, b, c] = example();
816 | ```
817 | ``` js
818 | // 返回一個物件
819 | function example() {
820 | return {
821 | foo: 1,
822 | bar: 2
823 | };
824 | }
825 | var { foo, bar } = example();
826 |
827 | ```
828 |
829 | ----
830 |
831 | 3.函式參數定義
832 |
833 | ``` text
834 | 解構賦值可以方便地將一組參數與變數名稱對應
835 | ```
836 |
837 | ``` js
838 | // 參數是一組有次序的值
839 | function f([x, y, z]) { ... }
840 | f([1, 2, 3]);
841 |
842 | // 參數是一組無次序的值
843 | function f({x, y, z}) { ... }
844 | f({z: 3, y: 2, x: 1});
845 | ```
846 |
847 | ----
848 |
849 | 4.取出 JSON 數據
850 |
851 | ``` text
852 | 解構賦值對提取JSON對象中的數據,尤其有用。
853 | ```
854 | ``` js
855 | var jsonData = {
856 | id: 42,
857 | status: "OK",
858 | data: [867, 5309]
859 | };
860 |
861 | let { id, status, data: number } = jsonData;
862 |
863 | console.log(id, status, number);
864 | // 42, "OK", [867, 5309]
865 | ```
866 |
867 | ----
868 |
869 | 5.設定參數的預設值
870 | ``` js
871 | jQuery.ajax = function (url, {
872 | async = true,
873 | beforeSend = function () {},
874 | cache = true,
875 | complete = function () {},
876 | crossDomain = false,
877 | global = true,
878 | // ... more config
879 | }) {
880 | // ... do stuff
881 | };
882 | ```
883 | ```
884 | 指定參數的預設值,就避免了在函式內部寫
885 | var foo = config.foo || 'default foo';
886 | 這樣的程式碼。
887 | ```
888 |
889 | ----
890 |
891 | 6.遍歷map結構
892 | ``` text
893 | 任何部署了Iterator介面的物件,
894 | 都可以用for...of循環遍歷。
895 | Map結構原生支持Iterator介面,
896 | 配合變數的解構賦值,獲取鍵名和鍵值就非常方便。
897 | ```
898 |
899 | ```js
900 | var map = new Map();
901 | map.set('first', 'hello');
902 | map.set('second', 'world');
903 |
904 | for (let [key, value] of map) {
905 | console.log(key + " is " + value);
906 | }
907 |
908 | // first is hello
909 | // second is world
910 |
911 | ```
912 |
913 | ----
914 |
915 | ``` text
916 | 如果只想獲取鍵名,或者只想獲取鍵值,可以寫成下面這樣。
917 | ```
918 |
919 | ```js
920 | // 獲取鍵名
921 | for (let [key] of map) {
922 | // ...
923 | }
924 |
925 | // 獲取鍵值
926 | for (let [,value] of map) {
927 | // ...
928 | }
929 | ```
930 |
931 | ----
932 |
933 | 7.輸入模組的指定方法
934 | ``` text
935 | 加載模組時,往往需要指定輸入那些方法。解構賦值使得輸入語句非常清晰。
936 | ```
937 | ```js
938 | const { SourceMapConsumer, SourceNode } = require("source-map");
939 | ```
940 |
941 |
942 | ---
943 |
--------------------------------------------------------------------------------
/chapter-04/README.md:
--------------------------------------------------------------------------------
1 | # ES6 讀書會 - ECMAScript 6 入門
2 |
3 | ### Chapter 4 - String
4 |
5 | _Prepared By [Peng Jie](https://github.com/neighborhood999)_
6 |
7 | ## Table Of Content
8 |
9 | - [使用 unicode 表示字串](#使用-unicode-表示字串)
10 | - [在 JavaScript 中的 String](#在-javascript-中的-string)
11 | - [codePointAt 方法](#codepointat-方法)
12 | - [String.fromCodePoint 方法](#stringfromcodepoint-方法)
13 | - [String 的 iterator](#string-的-iterator)
14 | - [at 方法](#at-方法)
15 | - [normalize 方法](#normalize-方法)
16 | - [字串確認](#字串確認)
17 | - [includes 方法](#includes-方法)
18 | - [startsWith 方法](#startswith-方法)
19 | - [endsWith 方法](#endswith-方法)
20 | - [repeat 方法](#repeat-方法)
21 | - [字串補全](#字串補全)
22 | - [padStart 方法](#padstart-方法)
23 | - [padEnd 方法](#padend-方法)
24 | - [Template Strings](#template-strings)
25 | - [raw 方法](#raw-方法)
26 |
27 | ## 使用 unicode 表示字串
28 |
29 | **TL;DR**
30 |
31 | - Unicode 解決了不同文字編碼的問題。
32 | - 每個文字和符號都對應到一組 Code Point。
33 | - 十六進制格式:`\u0000` - `\uFFFF`。
34 |
35 | _延伸閱讀:_ [Wiki - Unicode](https://zh.wikipedia.org/wiki/Unicode)、[Unicode字元平面對映](http://bit.ly/1TIEHvG)
36 |
37 | ### 在 JavaScript 中的 String
38 |
39 | - UCS-2 encode。
40 | - Support **UTF-16** encode。
41 | - Every string is **2 Byte**。
42 |
43 | 在 JavaScript 中,我們可以透過這樣的方式來表示字串:
44 |
45 | ```js
46 | const a = '\x61'; //a
47 | const b = '\u0061'; // a
48 |
49 | '\x61' === '\u0061'; // true
50 | ```
51 |
52 | 由**十六進制格式的兩個位元**組成,代表一個編碼位置。
53 | 超出 `\u0000` - `\uFFFF` 範圍必須要用**兩個** Code Ponit 來表示。
54 |
55 | 在 ES6 中,改善了這個問題,將超出範圍的 Code Point 放入 **{ }** 就可以了。
56 |
57 | ```js
58 | // ES5
59 |
60 | const a = '\uD842\uDFB7'; // '𠮷' (4 Bytes)
61 | const b = '\u20BB7'; // ₻7 (\u20BB+7)
62 |
63 | // ES6
64 |
65 | const c = '\u{20BB7}'; // '𠮷'
66 | ```
67 |
68 | ### codePointAt 方法
69 |
70 | 簡單來說,透過 `codePointAt()` 可以正確處理 **4 Byte** 的字元。
71 |
72 | ```js
73 | const emoji = '\uD83D\uDE02'; // 😂
74 |
75 | const tmp = emoji.codePointAt(0); // 128514 (十進制)
76 | tmp.toString(16); //1f602 (十六進制)
77 |
78 | emoji.codePointAt(1); // 56834 (十進制)
79 |
80 | ```
81 |
82 | `codePointAt()` 方法回傳一個非負整數,它是 Unicode Code Point 的值。
83 |
84 | ### String.fromCodePoint 方法
85 |
86 | _ES5 和 ES6 方法比較:_
87 |
88 | > ES5:`String.fromCharCode()` < _(0xFFFF)_
89 |
90 | > ES6:`String.fromCodePoint()` > _(0xFFFF)_
91 |
92 | `String.fromCharCode()` 會省略超過 `0xFFFF` 最高位元:
93 |
94 | ```js
95 | String.fromCharCode(0x1f602); // => Actual:0xf602
96 | String.fromCodePoint(0x1f602); // 😂 => success
97 | ```
98 |
99 | ## String 的 iterator
100 |
101 | ES6 的 `for..of` 在可以 **Iterable** 的物件上建立一個 loop。
102 |
103 | _(Iterator 的詳細介紹請期待下次的讀書會)_ :wink:
104 |
105 | ```js
106 | const text = 'ES6 線上讀書會';
107 |
108 | for (t of text) {
109 | console.log(t);
110 | }
111 |
112 | //
113 |
114 | const emoji = String.fromCodePoint(0x1f602);
115 |
116 | for (e of emoji) {
117 | console.log(e); // 😂
118 | }
119 | ```
120 |
121 | ## at 方法
122 |
123 | _at 方法目前還在 [Proposal stage 0](https://github.com/tc39/proposals/blob/master/stage-0-proposals.md) 階段_
124 |
125 | 在前面 [codePointAt()](#codepointat-方法) 提到了這個方法可以正確的處理 **4 Byte** 的字元,因為實際上它是被拆成兩組。
126 | 在這裡我們比較一下 `charAt` 和 `at` 方法差異:
127 |
128 | ```js
129 | const text = 'abc';
130 | const emoji = '😂';
131 |
132 | console.log(text.charAt(0)); // a
133 | console.log(emoji.charAt(0)); // �
134 | console.log(emoji.at(0)); // 😂
135 | ```
136 |
137 | **at()** 可以正確的回傳大於 `0XFFFF` 的字元。
138 |
139 | ## normalize 方法
140 |
141 | 帶有**語調和重音符號**的字元,Unicode 有**兩種**方式可以表示:
142 |
143 | 例如 `Ǒ` 符號可以表達成:
144 |
145 | 1. `Ǒ(\u01D1)` // 直接表達
146 | 2. `O(\u004F)` + `ˇ(\u030C)` // 組合字元
147 |
148 | ---
149 |
150 | _所以**直接表達**和**組合字元**是等價的嗎?_
151 |
152 | ```js
153 | console.log('\u01D1' === '\u004f\u030C'); // false
154 | ```
155 | ---
156 |
157 | `normalize()` 會根據你所指定的 Unicode 正規形式,將字符串正規化。
158 |
159 | > - NFC:預設參數,標準形式規範組成。
160 | > - NFD:標準形式規範分解。
161 | > - NFKC:標準形式兼容組成。
162 | > - NFKD:標準形式兼容分解。
163 |
164 | _更多參考 MDN:[String.prototype.normalize()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)_。
165 |
166 | 加入 `normalize()` 後的比較結果:
167 |
168 | ```js
169 | console.log(
170 | '\u01D1'.normalize() === '\u004f\u030C'.normalize()
171 | ); // true
172 | ```
173 |
174 | ## 字串確認
175 |
176 | 我們會使用 `indexOf` 來尋找字串首次出現的位置,`lastIndex` 來尋找字串最後出現的位置。
177 |
178 | ```js
179 | const text = 'ES6 Study';
180 |
181 | console.log(text.indexOf('E')); // 0
182 | console.log(text.indexOf('I')); // 不存在回傳 -1
183 |
184 | //
185 |
186 | const text2 = 'Happy Birthday';
187 |
188 | console.log(text2.lastIndexOf('y')); // 13
189 | ```
190 |
191 | ### includes 方法
192 |
193 | 確認字串是否包含如你參數中所指定的,回傳 `boolean` 值:
194 |
195 | ```js
196 | // Syntax:str.includes(searchString[, position])
197 |
198 | const text = 'ES6 Study';
199 |
200 | console.log(text.includes('ES6')); // true
201 | ```
202 |
203 | ### startsWith 方法
204 |
205 | 確認字串的起始是否如你參數中所指定的,回傳 `boolean` 值:
206 |
207 | ```js
208 | // Syntax:str.startsWith(searchString[, position])
209 |
210 | const text = 'ES6 Study';
211 |
212 | console.log(text.startsWith('ES6')); // true
213 | ```
214 |
215 | ### endsWith 方法
216 |
217 | 確認字串的尾部是否如你參數中所指定的,回傳 `boolean` 值:
218 |
219 | ```js
220 | // Syntax:str.endsWith(searchString[, position])
221 |
222 | const text = 'ES6 Study';
223 |
224 | console.log(text.endsWith('Study')); // true
225 | ```
226 |
227 | ## repeat 方法
228 |
229 | `repeat()` 方法回傳一個新字串。
230 |
231 | ```js
232 | const A = 'A';
233 |
234 | console.log(A.repeat(2)); // AA
235 | console.log(A.repeat(3.6)); // AAA
236 | console.log(A.repeat(-1.2)); // RangeError
237 | ```
238 |
239 | ## 字串補全
240 |
241 | 目前 `padStart()` 和 `padEnd()` 還在 **Proposal** [stage-3](https://github.com/tc39/proposal-string-pad-start-end) 階段。
242 |
243 | 這兩個方法可以自動幫我們補全字串的長度,可以在 **start** 或是 **end** 的地方做補全。
244 |
245 | ### padStart 方法
246 |
247 | ```js
248 | // Syntax:str.padStart(targetLength [, padString])
249 |
250 | 'o'.padStart(5, 'Hell'); // Hello
251 | ```
252 |
253 | ### padEnd 方法
254 |
255 | ```js
256 | // Syntax:str.padEnd(targetLength [, padString])
257 |
258 | '123'.padEnd(6, 'x'); // 123xxx
259 | ```
260 |
261 | ## Template Strings
262 |
263 | 終於來到我們最常使用的 **Template String** 啦!
264 |
265 | 
266 |
267 | 我們以前可能都是這樣輸出 HTML template 的:
268 |
269 | ```js
270 | const t = 'My name is ' + name + ', Nice to meet you'; // 不易閱讀!
271 | ```
272 |
273 | _Template Strings - Example 1:_
274 |
275 | ```js
276 | const name = 'PJ';
277 | const t = `My name is ${name}, Nice to meet you`; // 變數的部份寫在 `${}` 內來顯示
278 | ```
279 |
280 | _Template Strings - Example 2:_
281 |
282 | ```js
283 | const t1 = `Hello\nWorld`;
284 | const t2 = `Hello, World
285 | Hello ECMAScript 6
286 | `;
287 |
288 | console.log(t1); // 透過 \n 做換行
289 | console.log(t2); // 換行
290 | ````
291 |
292 | 
293 |
294 | 也可以用在 HTML 的 style 設定:
295 |
296 | ```js
297 | const style = {
298 | color: 'red',
299 | size: 4
300 | };
301 | let temp = `
302 |
303 | Hi!
304 |
305 | `;
306 |
307 | console.log(temp);
308 | ```
309 |
310 | ## Tagged template literals
311 |
312 | function 可以處理跟在後面的 template strings:
313 |
314 | ```js
315 | function tag(str, ...values) {
316 | console.log(str);
317 | console.log(values[0]);
318 | console.log(values[1]);
319 | }
320 |
321 | const a = 5;
322 | const b = 10;
323 |
324 | tag`I have ${a} and ${b} number.`
325 |
326 | // result =>
327 | // [I have, and, number.]
328 | // 5
329 | // 10
330 | ```
331 |
332 | ## raw 方法
333 |
334 | `raw` 是 ES6 String 原生的方法,可以用來處理 template stirngs:
335 |
336 | ```js
337 | const text = String.raw`ECMAScript 6: \n`;
338 | console.log(text === 'ECMAScript 6: \\n') // true
339 | ```
340 |
341 | ## Reference
342 | - [Wiki - Unicode](https://zh.wikipedia.org/wiki/Unicode)
343 | - [Unicode 字元平面對映](http://bit.ly/1TIEHvG)
344 | - [MDN - String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)
345 | - [Unicode 与 JavaScript详解](http://www.ruanyifeng.com/blog/2014/12/unicode.html)
346 | - [Javascript Unicode](http://andyyou.github.io/javascript/2015/05/21/js-unicode-issue.html)
347 |
--------------------------------------------------------------------------------
/chapter-05/README.md:
--------------------------------------------------------------------------------
1 | # Expansion of RegExp in ES6
2 |
3 |
4 |
5 | #### 資料來源:[ECMAScript 6 入門](http://es6.ruanyifeng.com/#docs/regex)
6 | #### 資料整理:[cashbook](https://github.com/cashbooktw/)
7 |
8 | ---
9 |
10 | ## Outline
11 | - RegExp Constructor
12 | - RegExp Methods
13 | - The Flag of "u" (Unicode)
14 | - The Flag of "y" (Sticky)
15 | - The "sticky" Property
16 | - The "flags" Property
17 | - RegExp.escape()
18 | - Lookbehind
19 |
20 | ---
21 |
22 | ## RegExp Constructor
23 |
24 | ES5,在 RegExp 建構子內使用正則表達式,無法使用第二個參數
25 | ```js
26 | let regex = /xyz/i;
27 | let regex = new RegExp('xyz', 'i');
28 | let regex = new RegExp(/xyz/i);
29 |
30 | let regex = new RegExp(/xyz/, 'i');
31 | //error
32 | ```
33 | ES6,可使用第二個參數,且 flag 會覆蓋
34 | ```js
35 | let regex = new RegExp(/xyz/ig, 'i');
36 | let foo = regex.flags;
37 | //i
38 | ```
39 |
40 | ---
41 |
42 | ## RegExp Methods
43 |
44 | `RegExp.prototype[Symbol.match]`於內部被 `String.prototype.match()`呼叫
45 | 其餘方法如`replace`、`search`、`split`同上
46 | ```js
47 | 'abc'.match(/a/);
48 | /a/[Symbol.match]('abc');
49 | //return same result
50 | ```
51 |
52 | ---
53 |
54 | ## The Flag of "u" (Unicode)
55 |
56 | 加上 `u` flag 正確處理 UTF-16 編碼
57 | ```js
58 | /^\uD83D/.test('\uD83D\uDC2A');
59 | // true
60 | /^\uD83D/u.test('\uD83D\uDC2A');
61 | // false
62 | ```
63 | 加上 `u` flag,讓以下正則表達式的內容可以正確處理
64 | - The Character "."
65 | - Unicode Representation
66 | - Occurrences
67 | - Predefined Character Classes
68 | - The Flag of "i" (ignore case)
69 |
70 | ----
71 |
72 | ### The Character "."
73 |
74 | 加上 `u` flag,讓 "." 可以正確處理 UTF-16 編碼
75 | ```js
76 | let s = '𠮷';
77 |
78 | /^.$/.test(s); // false
79 | /^.$/u.test(s); // true
80 | ```
81 |
82 | ----
83 |
84 | ### Unicode Representation
85 |
86 | `\u{n}`用來表示Unicode
87 | 加上 `u` flag,處理大括號內表示的 Unicode 字元
88 | ```js
89 | /\u{61}/.test('a'); // false
90 | /\u{61}/u.test('a'); // true
91 | ```
92 |
93 | ----
94 |
95 | ### Occurrences
96 |
97 | 加上 `u` flag,讓 `{n}` (出現次數)正確處理
98 | ```js
99 | /a{2}/.test('aa'); // true
100 | /a{2}/u.test('aa'); // true
101 | /𠮷{2}/.test('𠮷𠮷'); // false
102 | /𠮷{2}/u.test('𠮷𠮷'); // true
103 | ```
104 | 第三個式子等於
105 | ```js
106 | /\uD842\uDFB7{2}/.test('\uD842\uDFB7\uD842\uDFB7');
107 | ```
108 |
109 | ----
110 |
111 | ### Predefined Character Classes
112 |
113 | 加上 `u` flag,讓預定義模式正確處理
114 | ```js
115 | /^\S$/.test('𠮷') // false
116 | /^\S$/u.test('𠮷') // true
117 | ```
118 |
119 | ----
120 |
121 | ### The Flag of "i" (ignore case)
122 |
123 | 有些Unicode字元編碼不同,但長得幾乎一樣,例如
124 | >`\u004B` = K
125 | >`\u212A` = K
126 |
127 | 加上 `u` flag,可識別非規範內的字元
128 | ```js
129 | /[a-z]/i.test('\u212A') // false
130 | /[a-z]/iu.test('\u212A') // true
131 | ```
132 |
133 | ---
134 |
135 | ## The Flag of "y" (Sticky)
136 |
137 | 針對目標字串,只會從`lastIndex`屬性指定的索引位置開始尋找
138 | ```js
139 | let s = 'aaa_aa_a';
140 | let r1 = /a+/g;
141 | let r2 = /a+/y;
142 |
143 | r1.exec(s) // ["aaa"]
144 | r2.exec(s) // ["aaa"]
145 |
146 | r1.exec(s) // ["aa"]
147 | r2.exec(s) // null
148 | ```
149 |
150 | ----
151 |
152 | 在`split`方法中使用 flag `y`,若匹配成功,則回傳矩陣第一個值一定為空字串
153 | 目標值必須相鄰才會被匹配成功
154 | ```js
155 | '#x#'.split(/#/y);// [ '', 'x#' ]
156 | '##'.split(/#/y);// [ '', '', '' ]
157 | ```
158 |
159 | ----
160 |
161 | 使用replace方法的例子
162 | ```js
163 | const REGEX = /a/gy;
164 | 'aaxa'.replace(REGEX, '-'); // '--xa'
165 | ```
166 |
167 | ----
168 |
169 | 使用`match`方法只能返回第一個匹配,必須與flag `g`並用才能回傳所有匹配
170 | ```js
171 | 'a1a2a3'.match(/a\d/y); // ["a1"]
172 | 'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"]
173 | ```
174 |
175 | ----
176 |
177 | flag y的一個應用,是從字串中提取 token,確保每一個匹配。若有非pattern的字出現則停止匹配。
178 | ```js
179 | const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
180 | const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;
181 |
182 | tokenize(TOKEN_Y, '3x + 4');
183 | // [ '3' ]
184 | tokenize(TOKEN_G, '3x + 4');
185 | // [ '3', '+', '4' ]
186 |
187 | function tokenize(TOKEN_REGEX, str) {
188 | let result = [];
189 | let match;
190 | while (match = TOKEN_REGEX.exec(str)) {
191 | result.push(match[1]);
192 | }
193 | return result;
194 | }
195 | ```
196 |
197 | ---
198 |
199 | ## The "sticky" Property
200 |
201 | `sticky`屬性為布林,若正則表達式物件使用flag `y`則為`true`
202 | ```js
203 | let r = (/hello\d/y).sticky;
204 | // true
205 | ```
206 |
207 | ---
208 |
209 | ## The "flags" Property
210 |
211 | `flags`屬性為字串,回傳正則表達式使用的 flags
212 | ```js
213 | // ES5的source property
214 | // return pattern of RegExp
215 | let foo = /abc/ig.source;
216 | // "abc"
217 |
218 | // ES6的flags property
219 | // return flags of RegExp
220 | let bar = /abc/ig.flags;
221 | // 'gi'
222 | ```
223 |
224 | ---
225 |
226 | ## RegExp.escape()
227 |
228 | > 未成為ES6標準
229 |
230 | 處理需要被跳脫的特殊字元
231 | ```js
232 | RegExp.escape("The Quick Brown Fox"); // "The Quick Brown Fox"
233 | RegExp.escape("Buy it. use it. break it. fix it.");
234 | // "Buy it\. use it\. break it\. fix it\."
235 | RegExp.escape("(*.*)"); // "\(\*\.\*\)"
236 | ```
237 |
238 | ---
239 |
240 | ## Lookbehind
241 |
242 | > 未成為ES6標準
243 |
244 | lookahead
245 | ```js
246 | /\d+(?=%)/.exec('100% of US presidents have been male');
247 | // ["100"]
248 | ```
249 | negative lookahead
250 | ```js
251 | /\d+(?!%)/.exec('that’s all 44 of them'); // ["44"]
252 | ```
253 |
254 | ----
255 |
256 | lookbehind
257 | ```js
258 | /(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill');
259 | // ["100"]
260 | ```
261 | negative lookbehind
262 | ```js
263 | /\d+(?!%)/.exec('that’s all 44 of them'); // ["44"]
264 | ```
265 |
266 | ----
267 |
268 | 不符合預期的行為
269 | >capturing group 內容不同
270 | ```js
271 | /^(\d+)(\d+)$/.exec('1053');
272 | // ["1053", "105", "3"]
273 | /(?<=(\d+)(\d+))$/.exec('1053');
274 | // ["", "1", "053"]
275 | ```
276 | capturing group 順序不同
277 | ```js
278 | /(?<=(o)d\1)r/.exec('hodor') // null
279 | /(?<=\1d(o))r/.exec('hodor') // ["r", "o"]
280 | ```
281 |
282 | ---
283 |
284 | ## Reference
285 |
286 | [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
287 | [ECMA-262 Ed.6](http://www.ecma-international.org/ecma-262/6.0/)
288 |
289 | 使用工具
290 | >[es6fiddle](http://www.es6fiddle.net/)
291 | [jsbin](http://jsbin.com/?js,console)
292 | [Scratch JS](https://chrome.google.com/webstore/detail/scratch-js/alploljligeomonipppgaahpkenfnfkn)
293 | [regex101](https://regex101.com/#javascript)
294 |
--------------------------------------------------------------------------------
/chapter-06/README.md:
--------------------------------------------------------------------------------
1 | # ES6讀書會
2 | - [數值的擴展-作者:阮一峰](http://es6.ruanyifeng.com/#docs/number)
3 | - 6/21(二) RayWay
4 |
5 | ---
6 |
7 | ## 二進制和八進制表示法
8 |
9 | ----
10 |
11 | ### 二進制和八進制表示法 (ES6)
12 | - 二進制(Binary)
13 | - `OB` or `ob`
14 |
15 | ```js
16 | 0b111110111 === 503 // true
17 | ```
18 |
19 | - 八進制(Octal)
20 | - `0O` or `0o`
21 |
22 | ```js
23 | 0o767 === 503 // true
24 | ```
25 |
26 | - 轉成十進制
27 |
28 | ```js
29 | Number('0b111') // 7
30 | Number('0o10') // 8
31 | ```
32 |
33 | ----
34 |
35 | ### 二進制和八進制表示法 (ES5)
36 | - 0開始表示八進位
37 | ```js
38 | (function(){
39 | console.log(0o11 === 011);
40 | })() // true
41 | ```
42 | - strict mode禁用
43 | ```js
44 | (function(){
45 | 'use strict';
46 | console.log(0o11 === 011);
47 | })()
48 | // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
49 | ```
50 |
51 | ---
52 |
53 | ## Number.isFinite(), Number.isNaN()
54 |
55 | ----
56 |
57 | ### ES6
58 |
59 | ----
60 |
61 | #### Number.isFinite()
62 | - Infinity, -Infinity
63 |
64 | ```js
65 | Number.isFinite(15); // true
66 | Number.isFinite(0.8); // true
67 | Number.isFinite(NaN); // false
68 | Number.isFinite(Infinity); // false
69 | Number.isFinite(-Infinity); // false
70 | Number.isFinite('foo'); // false
71 | Number.isFinite('15'); // false
72 | Number.isFinite(true); // false
73 | Number.isFinite(1/0); // false
74 | Number.isFinite(-1/0); // false
75 | ```
76 |
77 | ----
78 |
79 | #### Number.isNaN()
80 |
81 | ```js
82 | Number.isNaN(NaN) // true
83 | Number.isNaN(15) // false
84 | Number.isNaN('15') // false
85 | Number.isNaN(true) // false
86 | Number.isNaN(9/NaN) // true
87 | Number.isNaN('true'/0) // true
88 | Number.isNaN('true'/'true') // true
89 | NaN === NaN // false
90 | ```
91 |
92 | ----
93 |
94 | ### ES5
95 |
96 | ----
97 |
98 | #### Number.isFinite()
99 |
100 | ```js
101 | (function (global) {
102 | var global_isFinite = global.isFinite;
103 |
104 | Object.defineProperty(Number, 'isFinite', {
105 | value: function isFinite(value) {
106 | return typeof value === 'number' && global_isFinite(value);
107 | },
108 | configurable: true,
109 | enumerable: false,
110 | writable: true
111 | });
112 | })(this);
113 | ```
114 |
115 | ----
116 |
117 | #### Number.isNaN()
118 |
119 | ```js
120 | (function (global) {
121 | var global_isNaN = global.isNaN;
122 |
123 | Object.defineProperty(Number, 'isNaN', {
124 | value: function isNaN(value) {
125 | return typeof value === 'number' && global_isNaN(value);
126 | },
127 | configurable: true,
128 | enumerable: false,
129 | writable: true
130 | });
131 | })(this);
132 | ```
133 |
134 | ----
135 |
136 | #### 比較
137 |
138 | ```js
139 | isFinite(25) // true
140 | isFinite("25") // true
141 | Number.isFinite(25) // true
142 | Number.isFinite("25") // false
143 |
144 | isNaN(NaN) // true
145 | isNaN("NaN") // true
146 | Number.isNaN(NaN) // true
147 | Number.isNaN("NaN") // false
148 | ```
149 |
150 | ---
151 |
152 | ## Number.parseInt(), Number.parseFloat()
153 |
154 | ----
155 |
156 | - Global -> Number
157 |
158 | ```js
159 | // ES5
160 | parseInt('12.34') // 12
161 | parseFloat('123.45#') // 123.45
162 |
163 | // ES6
164 | Number.parseInt('12.34') // 12
165 | Number.parseFloat('123.45#') // 123.45
166 | ```
167 |
168 | - 行為不變
169 |
170 | ```js
171 | Number.parseInt === parseInt // true
172 | Number.parseFloat === parseFloat // true
173 | ```
174 |
175 | ---
176 |
177 | ## Number.isInteger()
178 |
179 | ----
180 |
181 | - integer和float在js中是一樣的貯存方式
182 |
183 | ```js
184 | Number.isInteger(25) // true
185 | Number.isInteger(25.0) // true
186 | Number.isInteger(25.1) // false
187 | Number.isInteger("15") // false
188 | Number.isInteger(true) // false
189 | ```
190 |
191 | ----
192 |
193 | ### ES5
194 |
195 | ```js
196 |
197 | (function (global) {
198 | var floor = Math.floor,
199 | isFinite = global.isFinite;
200 |
201 | Object.defineProperty(Number, 'isInteger', {
202 | value: function isInteger(value) {
203 | return typeof value === 'number' && isFinite(value) &&
204 | value > -9007199254740992 && value < 9007199254740992 &&
205 | floor(value) === value;
206 | },
207 | configurable: true,
208 | enumerable: false,
209 | writable: true
210 | });
211 | })(this);
212 | ```
213 |
214 | ---
215 |
216 | ## Number.EPSILON
217 |
218 | ----
219 |
220 | ### IEEE 754 造成的誤差
221 | - JS沒有所謂的Integer
222 |
223 | ```js
224 | 0.1 + 0.2
225 | // 0.30000000000000004
226 |
227 | 0.1 + 0.2 - 0.3
228 | // 5.551115123125783e-17
229 |
230 | 5.551115123125783e-17.toFixed(20)
231 | // '0.00000000000000005551'
232 | ```
233 |
234 | ----
235 |
236 | ### Number.EPSILON
237 | - ES6引進之極小常量
238 |
239 | ```js
240 | Number.EPSILON
241 | // 2.220446049250313e-16
242 | Number.EPSILON.toFixed(20)
243 | // '0.00000000000000022204'
244 | ```
245 |
246 | ----
247 |
248 | ### 可接受之誤差
249 |
250 | ```js
251 | function withinErrorMargin (left, right) {
252 | return Math.abs(left - right) < Number.EPSILON;
253 | }
254 | withinErrorMargin(0.1 + 0.2, 0.3)
255 | // true
256 | withinErrorMargin(0.2 + 0.2, 0.3)
257 | // false
258 | ```
259 |
260 | ---
261 |
262 | ## 安全整數和Number.isSafeInteger()
263 |
264 | ----
265 |
266 | ### JS的數值範圍
267 | - -2^53^~2^53^ (不包含)
268 |
269 | ```js
270 | Math.pow(2, 53) // 9007199254740992
271 |
272 | 9007199254740992 // 9007199254740992
273 | 9007199254740993 // 9007199254740992
274 |
275 | Math.pow(2, 53) === Math.pow(2, 53) + 1
276 | // true
277 | ```
278 |
279 | ----
280 |
281 | ### 安全整數
282 | - Number.MAX_SAFE_INTEGER
283 | - Number.MIN_SAFE_INTEGER
284 |
285 | ```js
286 | Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
287 | // true
288 | Number.MAX_SAFE_INTEGER === 9007199254740991
289 | // true
290 |
291 | Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
292 | // true
293 | Number.MIN_SAFE_INTEGER === -9007199254740991
294 | // true
295 | ```
296 |
297 | ----
298 |
299 | - Number.isSafeInteger()
300 | - 若拿來計算時,參與運算的每個數都要檢查!
301 |
302 | ```js
303 | Number.isSafeInteger('a') // false
304 | Number.isSafeInteger(null) // false
305 | Number.isSafeInteger(NaN) // false
306 | Number.isSafeInteger(Infinity) // false
307 | Number.isSafeInteger(-Infinity) // false
308 |
309 | Number.isSafeInteger(3) // true
310 | Number.isSafeInteger(1.2) // false
311 | Number.isSafeInteger(9007199254740990) // true
312 | Number.isSafeInteger(9007199254740992) // false
313 |
314 | Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
315 | Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
316 | Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
317 | Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false
318 | ```
319 |
320 |
321 |
322 | ---
323 |
324 | ## Math物件的擴展
325 |
326 | ----
327 |
328 | ### 一般
329 |
330 | ----
331 |
332 | #### Math.trunc()
333 | - 去除小數
334 |
335 | ```js
336 | Math.trunc(4.1) // 4
337 | Math.trunc(4.9) // 4
338 | Math.trunc(-4.1) // -4
339 | Math.trunc(-4.9) // -4
340 | Math.trunc(-0.1234) // -0
341 | ```
342 |
343 | - 非數值先型態轉換
344 |
345 | ```js
346 | Math.trunc('123.456') // 123
347 | ```
348 |
349 | - 無法處理就回傳NaN
350 |
351 | ```js
352 | Math.trunc(NaN); // NaN
353 | Math.trunc('foo'); // NaN
354 | Math.trunc(); // NaN
355 |
356 | ```
357 |
358 | ----
359 |
360 | #### Math.sign()
361 | - 正數, +1
362 | - 負數, -1
363 | - 零
364 | - +0
365 | - -0
366 | - NaN
367 |
368 | ```js
369 |
370 | Math.sign(-5) // -1
371 | Math.sign(5) // +1
372 | Math.sign(0) // +0
373 | Math.sign(-0) // -0
374 | Math.sign(NaN) // NaN
375 | Math.sign('foo'); // NaN
376 | Math.sign(); // NaN
377 |
378 | ```
379 |
380 | ----
381 |
382 | #### Math.cbrt()
383 |
384 | - 立方根
385 |
386 | ```js
387 | Math.cbrt(-1) // -1
388 | Math.cbrt(0) // 0
389 | Math.cbrt(1) // 1
390 | Math.cbrt(2) // 1.2599210498948734
391 | ```
392 |
393 | ----
394 |
395 | #### Math.clz32()
396 | - 回傳整數以32bit二進位顯示之前導`0`個數
397 | - 浮點數只取整數部分,非數值會先轉為數值。
398 |
399 | ```js
400 | Math.clz32(0) // 32
401 | Math.clz32(1) // 31
402 | Math.clz32(1000) // 22
403 | Math.clz32(0b01000000000000000000000000000000) // 1
404 | Math.clz32(0b00100000000000000000000000000000) // 2
405 | Math.clz32(1 << 1) // 30
406 | Math.clz32(1 << 2) // 29
407 | Math.clz32(1 << 29) // 2
408 | Math.clz32(3.2) // 30
409 | Math.clz32() // 32
410 | Math.clz32(NaN) // 32
411 | Math.clz32(Infinity) // 32
412 | Math.clz32(null) // 32
413 | Math.clz32('foo') // 32
414 | Math.clz32([]) // 32
415 | Math.clz32({}) // 32
416 | Math.clz32(true) // 31
417 | ```
418 |
419 | ----
420 |
421 | #### Math.imul()
422 | - 32-bit 整數相乘
423 |
424 | ```js
425 | Math.imul(2, 4) // 8
426 | Math.imul(-1, 8) // -8
427 | Math.imul(-2, -2) // 4
428 | ```
429 |
430 | - 可以返回正確低位數值
431 |
432 | ```js
433 | (0x7fffffff * 0x7fffffff) | 0 // 0
434 | Math.imul(0x7fffffff, 0x7fffffff) // 1
435 | ```
436 |
437 | ----
438 |
439 | #### Math.fround()
440 | - 返回單精度浮點數
441 |
442 | ```js
443 | Math.fround(0) // 0
444 | Math.fround(1) // 1
445 | Math.fround(1.337) // 1.3370000123977661
446 | Math.fround(1.5) // 1.5
447 | Math.fround(NaN) // NaN
448 | ```
449 |
450 | ----
451 |
452 | #### Math.hypot()
453 | - 返回所有參數的平方和之平方根
454 |
455 | ```js
456 | Math.hypot(3, 4); // 5
457 | Math.hypot(3, 4, 5); // 7.0710678118654755
458 | Math.hypot(); // 0
459 | Math.hypot(NaN); // NaN
460 | Math.hypot(3, 4, 'foo'); // NaN
461 | Math.hypot(3, 4, '5'); // 7.0710678118654755
462 | Math.hypot(-3); // 3
463 | ```
464 |
465 | ----
466 |
467 | ### 對數
468 |
469 | ----
470 |
471 | #### Math.expm1()
472 | - Math.expm1(x)返回e^x^ - 1,即Math.exp(x) - 1。
473 |
474 | ```js
475 | Math.expm1(-1) // -0.6321205588285577
476 | Math.expm1(0) // 0
477 | Math.expm1(1) // 1.718281828459045
478 | ```
479 |
480 | ----
481 |
482 | #### Math.log1p()
483 | - Math.log1p(x)方法返回1 + x的自然對數
484 | - Math.log(1 + x)
485 | - x < -1
486 | - NaN
487 |
488 | ```js
489 | Math.log1p(1) // 0.6931471805599453
490 | Math.log1p(0) // 0
491 | Math.log1p(-1) // -Infinity
492 | Math.log1p(-2) // NaN
493 | ```
494 |
495 | ----
496 |
497 | #### Math.log10()
498 | - 以10為底
499 |
500 | #### Math.log2()
501 | - 以2為底
502 |
503 | ```js
504 | Math.log10(2) // 0.3010299956639812
505 | Math.log10(1) // 0
506 | Math.log10(0) // -Infinity
507 | Math.log10(-2) // NaN
508 | Math.log10(100000) // 5
509 | //---
510 | Math.log2(3) // 1.584962500721156
511 | Math.log2(2) // 1
512 | Math.log2(1) // 0
513 | Math.log2(0) // -Infinity
514 | Math.log2(-2) // NaN
515 | Math.log2(1024) // 10
516 | Math.log2(1 << 29) // 29
517 | ```
518 |
519 | ----
520 |
521 | ### 三角函数
522 |
523 | ```js
524 | Math.sinh(x)
525 | Math.cosh(x)
526 | Math.tanh(x)
527 | Math.asinh(x)
528 | Math.acosh(x)
529 | Math.atanh(x)
530 | ```
531 |
532 | ---
533 |
534 | ## 指數運算符
535 | - ES7 (Babel已支援)
536 | - ** 運算子
537 | - 可以與等號連用
538 |
539 | ```js
540 | 2 ** 2 // 4
541 | 2 ** 3 // 8
542 | //---
543 | let a = 2;
544 | a **= 2; // a = a * a;
545 |
546 | let b = 3;
547 | b **= 3; // b = b * b * b;
548 | ```
549 |
550 |
--------------------------------------------------------------------------------
/chapter-07/README.md:
--------------------------------------------------------------------------------
1 | ## ES6 讀書會 - 關於「陣列」的擴充
2 |
3 |
4 |
5 |
6 | ### 資料來源
7 | * [ECMAScript 6 入门](http://es6.ruanyifeng.com/#docs/array) (阮一峰)
8 | * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
9 |
10 |
11 |
12 |
13 | ### 關於我
14 | ```
15 | {
16 | name: "Allen Hsieh",
17 | roles: [
18 | "Front-end Engineer",
19 | "Java Programmer"
20 | ],
21 | worksAt: "達暉資訊",
22 | email: "allen@lustertech.net"
23 | }
24 | ```
25 |
26 |
27 |
28 |
29 | ## 大綱
30 | * Array.from()
31 | * Array.of()
32 | * copyWithin()
33 | * find() 和 findIndex()
34 | * fill()
35 | * entries(),keys() 和 values()
36 | * includes()
37 | * 空位
38 |
39 |
40 |
41 |
42 |
43 |
44 | ## Array.from()
45 | ### 將物件轉換成陣列
46 |
47 |
48 |
49 |
50 | ### 語法
51 | ```javascript
52 | Array.from(arrayLike[, mapFn[, thisArg]])
53 |
54 | 用途:
55 | 將「物件」轉換為「陣列」
56 |
57 | 參數:
58 | arrayLike - 物件,ArrayLike 或 Iterable
59 | mapFn - map function
60 | thisArg - map function 執行時的 this 物件
61 | ```
62 |
63 |
64 |
65 |
66 | ### Array-like 轉換範例
67 |
68 | ```javascript
69 | let arrayLike = {
70 | '0': 'a',
71 | '1': 'b',
72 | '2': 'c',
73 | length: 3
74 | };
75 |
76 | // ES5的写法
77 | var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
78 |
79 | // ES6的写法
80 | let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
81 | ```
82 |
83 |
84 |
85 |
86 | ### Array-like 轉換範例(續)
87 | ```javascript
88 | // NodeList对象
89 | let ps = document.querySelectorAll('p');
90 | Array.from(ps).forEach(function (p) {
91 | console.log(p);
92 | });
93 |
94 | // arguments对象
95 | function foo() {
96 | var args = Array.from(arguments);
97 | // ...
98 | }
99 | ```
100 |
101 |
102 |
103 |
104 | ### Array-like 必要條件
105 | ```javascript
106 | // Array-like 必要條件,必須有 length 屬性
107 |
108 | Array.from({ length: 3 });
109 | // [ undefined, undefined, undefinded ]
110 |
111 | Array.from({ 0: 'a', 1: 'b' })
112 | // []
113 | ```
114 |
115 |
116 |
117 |
118 | ### Iterable 轉換範例
119 | ```javascript
120 | Array.from('hello')
121 | // ['h', 'e', 'l', 'l', 'o']
122 |
123 | let namesSet = new Set(['a', 'b'])
124 | Array.from(namesSet) // ['a', 'b']
125 | ```
126 |
127 |
128 |
129 |
130 | ### 擴展運算符號
131 | ```javascript
132 | // 擴展運算符號,也可以將物件轉換成數據
133 |
134 | // arguments对象
135 | function foo() {
136 | var args = [...arguments];
137 | }
138 |
139 | // NodeList对象
140 | [...document.querySelectorAll('div')]
141 |
142 | // 要注意「擴展運算符號」是透過 Symbol.iterator 來進行轉換
143 | // 所以無法轉換 { length: 3 } 這樣的 array-like 物件
144 | // 這時必須透過 Array.from() 來處理
145 | ```
146 |
147 |
148 |
149 |
150 | ### 加上 map funciton 的範例
151 | ```javascript
152 | Array.from(arrayLike).map(x => x * x);
153 |
154 | // 等同於
155 | Array.from(arrayLike, x => x * x);
156 |
157 | Array.from([1, 2, 3], x => x * x);
158 | // [1, 4, 9]
159 |
160 |
161 | let spans = document.querySelectorAll('span.name');
162 | let names1 = Array.prototype.map.call(spans, s => s.textContent);
163 |
164 | // 等同於
165 | let names2 = Array.from(spans, s => s.textContent)
166 | ```
167 |
168 |
169 |
170 |
171 | ### 更多範例
172 | ```javascript
173 | Array.from([1, , 2, , 3], (n) => n || 0)
174 | // [1, 0, 2, 0, 3]
175 |
176 |
177 | function typesOf () {
178 | return Array.from(arguments, value => typeof value)
179 | }
180 | typesOf(null, [], NaN)
181 | // ['object', 'object', 'number']
182 | ```
183 |
184 |
185 |
186 |
187 | ### 另一個用途,計算字串長度
188 | ```javascript
189 | // 因為 from 函數可以正確處理各種 Unicode 字,
190 | // 因此可避免 JavaScript 將大於 \uFFFF 的 Unicode 字,
191 | // 算成兩個字的 bug.
192 |
193 | function countSymbols(string) {
194 | return Array.from(string).length;
195 | }
196 | ```
197 |
198 |
199 |
200 |
201 |
202 |
203 | ## Array.of()
204 | ### 建立陣列,彌補建構子的不足
205 |
206 |
207 |
208 |
209 | ### 傳統建構子的問題
210 | ```javascript
211 | // Array() 遞入沒有、一個、三個參數,所代表意思為皆不同。
212 |
213 | Array() // []
214 |
215 | Array(3) // [, , ,]
216 |
217 | Array(3, 11, 8) // [3, 11, 8]
218 | ```
219 |
220 |
221 |
222 |
223 | ### of 函式的好處
224 | ```javascript=
225 | // Array.of() 統一參數所代表的意思
226 |
227 | Array.of() // []
228 |
229 | Array.of(undefined) // [undefined]
230 |
231 | Array.of(1) // [1]
232 |
233 | Array.of(1, 2) // [1, 2]
234 | ```
235 |
236 |
237 |
238 |
239 |
240 |
241 | ## copyWithin()
242 | ### 複製陣列元素
243 |
244 |
245 |
246 |
247 | ### copyWithin 語法
248 | ```javascript
249 | arr.copyWithin(target[, start[, end]])
250 |
251 | 參數:
252 | target - 替換的起始點
253 | start - 來源的起始點
254 | end - 來源的結束點
255 | 3 個參數都是數字,array index
256 | 從 0 開始,負數代表從後面開始算
257 |
258 | ```
259 |
260 |
261 |
262 |
263 | ### 範例
264 | ```javascript
265 | [1, 2, 3, 4, 5].copyWithin(0, 3)
266 | // [4, 5, 3, 4, 5]
267 |
268 | // 将3号位复制到0号位
269 | [1, 2, 3, 4, 5].copyWithin(0, 3, 4)
270 | // [4, 2, 3, 4, 5]
271 |
272 | // -2相当于3号位,-1相当于4号位
273 | [1, 2, 3, 4, 5].copyWithin(0, -2, -1)
274 | // [4, 2, 3, 4, 5]
275 | ```
276 |
277 |
278 |
279 |
280 | ### 範例(續.)
281 | ```javascript
282 | // 将3号位复制到0号位
283 | [].copyWithin.call({length: 5, 3: 1}, 0, 3)
284 | // {0: 1, 3: 1, length: 5}
285 |
286 | // 将2号位到数组结束,复制到0号位
287 | var i32a = new Int32Array([1, 2, 3, 4, 5]);
288 | i32a.copyWithin(0, 2);
289 | // Int32Array [3, 4, 5, 4, 5]
290 |
291 | // 对于没有部署TypedArray的copyWithin方法的平台
292 | // 需要采用下面的写法
293 | [].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
294 | // Int32Array [4, 2, 3, 4, 5]
295 | ```
296 |
297 |
298 |
299 |
300 |
301 |
302 | ## find() 和 findIndex()
303 | ### 尋找陣列元素
304 |
305 |
306 |
307 |
308 | ### find 語法
309 | ```javascript
310 | arr.find(callback[, thisArg])
311 |
312 | 參數:
313 | callback - 判斷函式,可以接到三個參數
314 | * element - 目前元素
315 | * index - 目前 array index
316 | * array - 目前 array
317 |
318 | thisArg - 給 callback 函式用的 this
319 |
320 | 回傳:
321 | 回傳第一個符合條件的元素,反之則回傳 undefined
322 | ```
323 |
324 |
325 |
326 |
327 | ### find 範例
328 | ```javascript=
329 | [1, 4, -5, 10].find((n) => n < 0)
330 | // -5
331 |
332 | [1, 5, 10, 15].find(function(value, index, arr) {
333 | return value > 9;
334 | })
335 | // 10
336 |
337 | // 上面的例子也可以寫成
338 | [1, 5, 10, 15].find(value => value > 9);
339 | ```
340 |
341 |
342 |
343 |
344 | ### findIndex 語法
345 | ```javascript
346 | arr.findIndex(callback[, thisArg])
347 |
348 | 參數:
349 | callback - 判斷函式,可以接到三個參數
350 | * element - 目前元素
351 | * index - 目前 array index
352 | * array - 目前 array
353 |
354 | thisArg - 給 callback 函式用的 this
355 |
356 | 回傳:
357 | 回傳第一個符合條件的位置,反之則回傳 -1
358 | ```
359 |
360 |
361 |
362 |
363 | ### findIndex 範例
364 | ```javascript
365 | [1, 5, 10, 15].findIndex(function(value, index, arr) {
366 | return value > 9;
367 | }) // 2
368 |
369 | [NaN].indexOf(NaN)
370 | // -1
371 |
372 | [NaN].findIndex(y => Object.is(NaN, y))
373 | // 0
374 | ```
375 |
376 |
377 |
378 |
379 |
380 |
381 | ## fill()
382 | ### 填入陣列元素
383 |
384 |
385 |
386 |
387 |
388 | ### 語法
389 | ```javascript
390 | arr.fill(value[, start = 0[, end = this.length]])
391 |
392 | 參數:
393 | value - 填入的值
394 | start - 起始點,array index,起始為 0
395 | end - 結束點,array index,不包含該點
396 | ```
397 |
398 |
399 |
400 |
401 | ### 範例
402 | ```javascript
403 | ['a', 'b', 'c'].fill(7)
404 | // [7, 7, 7]
405 |
406 | new Array(3).fill(7)
407 | // [7, 7, 7]
408 |
409 | ['a', 'b', 'c'].fill(7, 1, 2)
410 | // ['a', 7, 'c']
411 | ```
412 |
413 |
414 |
415 |
416 |
417 |
418 | ## entries()、Keys() 和 values()
419 | ### 取得陣列 key, value or both
420 |
421 |
422 |
423 |
424 | ### 範例
425 | ```javascript
426 | for (let index of ['a', 'b'].keys()) {
427 | console.log(index);
428 | }
429 | // 0
430 | // 1
431 |
432 | for (let elem of ['a', 'b'].values()) {
433 | console.log(elem);
434 | }
435 | // "a"
436 | // "b"
437 |
438 | for (let [index, elem] of ['a', 'b'].entries()) {
439 | console.log(index, elem);
440 | }
441 | // 0 "a"
442 | // 1 "b"
443 | ```
444 |
445 |
446 |
447 |
448 | ### 手動用 next 取資料
449 | ```javascript
450 | // 如不想使用 for...of 迴圈,也可改用 next 方法
451 |
452 | let letter = ['a', 'b', 'c'];
453 | let entries = letter.entries();
454 |
455 | console.log(entries.next().value); // [0, "a"]
456 | console.log(entries.next().value); // [1, "b"]
457 | console.log(entries.next().value); // [2, "c"]
458 | ```
459 |
460 |
461 |
462 |
463 |
464 |
465 | ## includes()
466 | ### 判斷陣列是否包含該元素
467 |
468 |
469 |
470 |
471 | ### 語法
472 | ```javascript
473 | var boolean = array.includes(searchElement[, fromIndex])
474 |
475 | 參數:
476 | searchElement - 搜尋物
477 | fromIndex - 搜尋起點,array index,起始為 0
478 |
479 | 回傳:
480 | boolean,找到回傳 true,反之 false
481 | ```
482 |
483 |
484 |
485 |
486 | ### 範例
487 | ```javascript
488 | [1, 2, 3].includes(2); // true
489 |
490 | [1, 2, 3].includes(4); // false
491 |
492 | [1, 2, 3].includes(3, 3); // false
493 |
494 | [1, 2, 3].includes(3, -1); // true
495 |
496 | [1, 2, NaN].includes(NaN); // true
497 | ```
498 |
499 |
500 |
501 |
502 | ### 以前判斷的方式
503 | ```javascript
504 | if (arr.indexOf(el) !== -1) {
505 | // ...
506 | }
507 |
508 | // 缺點:
509 | // 1. 不直覺
510 | // 2. NaN 會出現誤判
511 | [NaN].indexOf(NaN) // -1
512 |
513 | // includes 沒有 NaN 的問題
514 | [NaN].includes(NaN) // true
515 | ```
516 |
517 |
518 |
519 |
520 | ### 支援
521 | * [屬於 ES7 規範](https://tc39.github.io/ecma262/#sec-array.prototype.includes)
522 | * [Babel 已經支援](http://kangax.github.io/compat-table/esnext/)
523 |
524 |
525 |
526 |
527 | ### 補充
528 | ```javascript
529 | Map 和 Set 有一個 has 方法,需要注意與 includes 區分。
530 |
531 | Map 的 has 方法,是用来找 key 的:
532 | * Map.prototype.has(key)
533 | * WeakMap.prototype.has(key)
534 | * Reflect.has(target, propertyKey)
535 |
536 | Set 的 has 方法,是用来找 value 的:
537 | * Set.prototype.has(value)
538 | * WeakSet.prototype.has(value)
539 | ```
540 |
541 |
542 |
543 |
544 |
545 |
546 | ## 空位
547 |
548 |
549 |
550 |
551 | ### 先說結論
552 | > 由於處理規則不統一,陣列 Array 應盡量「避免出現空位」。
553 |
554 |
555 |
556 |
557 |
558 | ### 空位代表該位置沒有任何值
559 | ```javascript
560 | // 例如宣告一個長度 3 的陣列
561 | Array(3)
562 |
563 | // 因為沒有初始值,所以裡面都是「空位」
564 | // [, , ,]
565 | ```
566 |
567 |
568 |
569 |
570 | ### 注意! undefined 是有值的
571 |
572 | ```javascript
573 | // 透過 in 運算子來判斷陣列0號位子有沒有值
574 |
575 | // 回傳 true
576 | 0 in [undefined, undefined, undefined]
577 |
578 | // 回傳 false
579 | 0 in [, , ,]
580 | ```
581 |
582 |
583 |
584 |
585 |
586 | ### ES5 對空位處理的方式不一
587 | ```javascript
588 | // 大多數都是跳過空位
589 |
590 | // forEach - 跳過空位
591 | [,'a'].forEach((x,i) => console.log(i)); // 1
592 |
593 | // filter - 跳過空位
594 | ['a',,'b'].filter(x => true) // ['a', 'b']
595 |
596 | // every - 跳過空位
597 | [,'a'].every(x => x==='a') // true
598 |
599 | // some - 跳過空位
600 | [,'a'].some(x => x !== 'a') // false
601 | ```
602 |
603 |
604 |
605 |
606 | ### ES5 對空位處理的方式不一(續.)
607 | ```javascript
608 | // map - 跳過空位,回傳值 undefined
609 | [,'a'].map(x => 1) // [undefined, 1]
610 |
611 | // join - 將空位視為 undefined,並被處理成空字串
612 | [,'a',undefined,null].join('#') // "#a##"
613 |
614 | // toString - 將空位視為 undefined,並被處理成空字串
615 | [,'a',undefined,null].toString() // ",a,,"
616 | ```
617 |
618 |
619 |
620 |
621 | ### ES6 把空位當 undefined 處理
622 | ```javascript
623 | Array.from(['a',,'b']) // [ "a", undefined, "b" ]
624 |
625 | [...['a',,'b']] // [ "a", undefined, "b" ]
626 |
627 | [,'a','b',,].copyWithin(2,0) // [undefined, "a", undefined, "a"]
628 |
629 | new Array(3).fill('a') // ["a","a","a"]
630 |
631 | let arr = [, ,];
632 | for (let i of arr) {
633 | console.log(1);
634 | }
635 | // 輸出兩次 1
636 | ```
637 |
638 |
639 |
640 |
641 | ### 以下函式也將空位處理成 undefined
642 | ```javascript
643 | // entries()
644 | [...[,'a'].entries()] // [[0, undefined], [1, "a"]]
645 |
646 | // keys()
647 | [...[,'a'].keys()] // [0, 1]
648 |
649 | // values()
650 | [...[,'a'].values()] // [undefined, "a"]
651 |
652 | // find()
653 | [,'a'].find(x => true) // undefined
654 |
655 | // findIndex()
656 | [,'a'].findIndex(x => true) // 0
657 | ```
658 |
--------------------------------------------------------------------------------
/chapter-09/README.md:
--------------------------------------------------------------------------------
1 | ES6 讀書會
2 | ==
3 | 
4 |
5 | ## 對象的擴展
6 |
7 | Ellen
8 |
9 | ----
10 |
11 | 參考資料
12 | - [ECMAScript 6 入門](http://es6.ruanyifeng.com/#docs/object)
13 |
14 | - [MDN document](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
15 |
16 | ---
17 |
18 | ## 1. 屬性的簡潔表示法
19 |
20 | ----
21 |
22 | 物件屬性使用變數簡潔表示
23 |
24 | ``` javascript
25 | var name = 'Anna',
26 | age = 25,
27 | gender = 'female';
28 |
29 |
30 | var es5Obj = { name: name, age: age, gender: gender };
31 | var es6Obj = { name, age, gender };
32 |
33 | console.log(es5Obj);
34 | console.log(es6Obj);
35 | // { name: 'Anna', age: 25, gender: 'female' }
36 | ```
37 |
38 | ----
39 |
40 | 物件方法使用函數簡潔表示
41 |
42 | ```javascript
43 | var es5Obj = {
44 | foo: function() {},
45 | bar: function() {}
46 | };
47 |
48 | var es6Obj = {
49 | foo() {},
50 | bar() {}
51 | };
52 | ```
53 |
54 | ----
55 |
56 | 範例 1
57 | ``` javascript
58 | const obj = {
59 | get phone() {
60 | return this._phone;
61 | },
62 | set phone(phone) {
63 | if (!this.validatePhone(phone)) {
64 | this._phone = '+886' + phone.slice(2, 10);
65 | }
66 | },
67 | validatePhone(phone) {
68 | return (phone.indexOf("+886") > 0) ? true : false;
69 | },
70 | }
71 | obj.phone = '0931123456';
72 | console.log(obj.phone); // +88631123456
73 | ```
74 |
75 | ----
76 |
77 | 範例 2
78 | ``` javascript
79 | function cube(value) {
80 | return Math.pow(value, 3);
81 | }
82 |
83 | function cubeRoot(value) {
84 | return Math.pow(value, 1/3);
85 | }
86 |
87 | module.exports = { cube, cubeRoot };
88 | // 同下
89 | module.exports = {
90 | cube: cube,
91 | cubeRoot: cubeRoot
92 | };
93 | ```
94 |
95 | ---
96 |
97 | ## 2. 屬性名表達式
98 |
99 | ----
100 |
101 | ``` javascript
102 | // ES6
103 | function getCar(make, model, value) {
104 | return {
105 | ['make' + make]: true
106 | };
107 | }
108 |
109 | // before ES6
110 | function getCar(make, model, value) {
111 | var car = {};
112 |
113 | // 只能在個別指定值時使用表達式
114 | car['make' + make] = true;
115 |
116 | return car;
117 | }
118 | ```
119 |
120 | ----
121 |
122 | 注意:屬性名表達式與簡潔表示法不能同時使用
123 |
124 | ``` javascript
125 | let key = 'value',
126 |
127 | value = 'hello',
128 |
129 | // 語法錯誤
130 | // Uncaught SyntaxError: Unexpected token }
131 | errObj = { [key] };
132 |
133 | // 可寫為
134 | obj = { [key] : value }
135 | ```
136 |
137 | ---
138 |
139 | ## 3. 方法的 name 屬性
140 |
141 | ----
142 |
143 | name 屬性回傳函數的名稱
144 |
145 | ``` javascriptvar
146 | var object = {
147 | someMethod: function test() {}
148 | };
149 |
150 | console.log(object.someMethod.name); // 印出 "test"
151 |
152 | object.someMethod.name = 'hello' // 僅供讀取
153 |
154 | console.log(object.someMethod.name); // 依舊印出 "test"
155 |
156 | ```
157 |
158 | ----
159 |
160 | 匿名函數 name 屬性
161 |
162 | ``` javascript
163 | var object = {
164 | someMethod: function() {}
165 | };
166 |
167 | console.log(object.someMethod.name); // ES6: someMethod
168 | ```
169 |
170 | ---
171 |
172 | ## 4. Object . is ( )
173 |
174 | ----
175 |
176 | 同值相等
177 |
178 | ``` javascript
179 | Object.is('foo', 'bar'); // false
180 | Object.is([], []); // false
181 |
182 | var test = { a: 1 };
183 | Object.is(test, test); // true
184 |
185 | Object.is(null, null); // true
186 |
187 | // 特殊例子
188 | Object.is(0, -0); // false
189 | Object.is(-0, -0); // true
190 | Object.is(NaN, 0/0); // true
191 | ```
192 |
193 | ----
194 |
195 | 改善 `==` & `===` 比較運算符
196 |
197 | - 相等運算符 (`==`)
198 |
199 | :::danger
200 | `"" == false // true`
201 | :::
202 |
203 | - 嚴格運算符 (`===`)
204 |
205 | :::danger
206 | `-0 === +0 // true`
207 | :::
208 |
209 | ---
210 |
211 | ## 5. Object . assign ()
212 |
213 | ----
214 |
215 | 將來源物件的所有可枚舉屬性,複製到目標物件
216 |
217 | ``` javascript
218 | Object.assign(target, ...sources)
219 | ```
220 |
221 | ----
222 |
223 | - 物件的每個屬性都有一個描述對象(Descriptor),控制該屬性的行為
224 |
225 | ```javascript
226 | let obj = { foo: 123 };
227 | Object.getOwnPropertyDescriptor(obj, 'foo')
228 | // {
229 | // value: 123,
230 | // writable: true,
231 | // enumerable: true,
232 | // configurable: true
233 | // }
234 | ```
235 |
236 | ----
237 |
238 | 常見用法
239 |
240 | ----
241 |
242 | 拷貝物件
243 |
244 | ```javascript
245 | var obj = { a: 1 };
246 |
247 | var copy = Object.assign({}, obj);
248 |
249 | console.log(copy); // { a: 1 }
250 | ```
251 |
252 | ----
253 |
254 | 合併多個物件
255 |
256 | ```javascript
257 | var o1 = { a: 1 };
258 | var o2 = { b: 2 };
259 | var o3 = { c: 3 };
260 |
261 | var obj = Object.assign(o1, o2, o3);
262 | console.log(obj); // { a: 1, b: 2, c: 3 }
263 | console.log(o1); // { a: 1, b: 2, c: 3 }, 目標物件已被改變
264 | ```
265 |
266 | ----
267 |
268 | 複製 Symbol 型別屬性
269 |
270 | ```javascript
271 | var o1 = { a: 1 };
272 | var o2 = { [Symbol('foo')]: 2 };
273 |
274 | var obj = Object.assign({}, o1, o2);
275 | console.log(obj); // { a: 1, [Symbol("foo")]: 2 }
276 | ```
277 |
278 | ----
279 |
280 | - 限制:
281 | 只拷貝物件自身屬性 ( 不拷貝繼承屬性 )
282 | 不拷貝不可枚舉 ( `enumerable: false` ) 的屬性
283 |
284 | ```javascript
285 | var obj = Object.assign({b: 'c'},
286 | Object.defineProperty({}, 'invisible', {
287 | enumerable: false,
288 | value: 'hello'
289 | })
290 | )
291 | // { b: 'c' }
292 | ```
293 |
294 | ----
295 |
296 | - 注意:
297 | Primitives 型別會被包裹成物件
298 | (string, number, boolean, null, undefined, symbol)
299 | ```javascript
300 | var v1 = 'abc';
301 | var v2 = true;
302 | var v3 = 10;
303 | var v4 = Symbol('foo');
304 |
305 | var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
306 | // null & undefined 會被忽略
307 | // 字串會以數組形式,複製進目標物件
308 | console.log(obj); // { "0": "a", "1": "b", "2": "c" }
309 | ```
310 |
311 | ```javascript
312 | Object(true) // {[[PrimitiveValue]]: true}
313 | Object(10) // {[[PrimitiveValue]]: 10}
314 | Object('abc')
315 | // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
316 | ```
317 |
318 | ---
319 |
320 | ## 6. 屬性的遍例
321 |
322 | ----
323 |
324 | ES6 有五種方式可遍歷物件屬性:
325 |
326 | 1. for...in
327 |
328 | 2. Object.keys(obj)
329 |
330 | 3. Object.getOwnPropertyNames(obj)
331 |
332 | 4. Object.getOwnPropertySymbols(obj)
333 |
334 | 5. Reflect.ownKeys(obj)
335 |
336 | ----
337 |
338 | 1. `for...in`
339 | 循環遍歷物件本身的屬性和繼承的可枚舉屬性
340 | (不含Symbol屬性)
341 |
342 | ```
343 | 注意:操作中引入繼承的屬性會讓問題複雜化,通常我們只關心物件本身的屬性
344 | 儘量少用for...in循環,而用 Object.keys() 代替
345 | ```
346 |
347 | ----
348 |
349 | 2. `Object.keys(obj)`
350 | 返回一個陣列
351 | 包括物件本身的(不含繼承的)所有可枚舉屬性
352 | (不含Symbol屬性)
353 |
354 | ```javascript
355 | var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
356 |
357 | console.log(Object.keys(an_obj)); // console: ['2', '7', '100']
358 | ```
359 |
360 | ----
361 |
362 | 將非物件傳入函式
363 |
364 | ```javascript
365 | Object.keys("foo");
366 | // TypeError: "foo" is not an object (ES5 code)
367 |
368 | Object.keys("foo");
369 | // ["0", "1", "2"] (ES6 code)
370 | ```
371 |
372 | ----
373 |
374 | 3. `Object.getOwnPropertyNames(obj)`
375 | 返回一陣列,包含對象自身的所有屬性
376 | (不含Symbol屬性,但包括不可枚舉屬性)
377 |
378 | ```javascript
379 | var my_obj = Object.create({}, {
380 | getFoo: {
381 | value: function() { return this.foo; },
382 | enumerable: false
383 | }
384 | });
385 | my_obj.foo = 1;
386 |
387 | console.log(Object.getOwnPropertyNames(my_obj).sort());
388 | // logs ["foo", "getFoo"]
389 | ```
390 |
391 | ----
392 |
393 | 將非物件傳入函式
394 |
395 | ```javascript
396 | Object.getOwnPropertyNames('foo');
397 | // TypeError: "foo" is not an object (ES5 code)
398 |
399 | Object.getOwnPropertyNames('foo');
400 | // ["0", "1", "2", "length"] (ES6 code)
401 | ```
402 |
403 | ----
404 |
405 | 4. `Object.getOwnPropertySymbols(obj)`
406 | 返回一陣列,包含物件本身的所有Symbol屬性
407 |
408 | ```javascript
409 | var obj = {};
410 | var a = Symbol('a');
411 | var b = Symbol.for('b');
412 |
413 | obj[a] = 'localSymbol';
414 | obj[b] = 'globalSymbol';
415 |
416 | var objectSymbols = Object.getOwnPropertySymbols(obj);
417 |
418 | console.log(objectSymbols.length); // 2
419 | console.log(objectSymbols); // [Symbol(a), Symbol(b)]
420 | ```
421 |
422 | ----
423 |
424 | 5. `Reflect.ownKeys(obj)`
425 | 返回一陣列,包含物件本身的所有屬性,無論屬性名是Symbol或字符串,以及是否可枚舉
426 |
427 | ```javascript
428 | Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
429 | // ['2', '10', 'b', 'a', Symbol()]
430 | ```
431 |
432 | ---
433 |
434 | ## 7. Object Prototype
435 |
436 | ----
437 |
438 | 1. __proto__屬性
439 |
440 | 2. Object.setPrototypeOf()
441 |
442 | 3. Object.getPrototypeOf()
443 |
444 | ----
445 |
446 | 1. `__proto__` 屬性
447 | 用來讀取或設置當前物件的 `prototype` 物件
448 |
449 | ```javascript
450 | var shape = {};
451 | var circle = new Circle();
452 |
453 | // 設定物件原型
454 | // 不建議正式使用
455 | shape.__proto__ = circle;
456 |
457 | // 拿取物件原型
458 | console.log(shape.__proto__ === circle); // true
459 | ```
460 |
461 | ----
462 |
463 | - 為一內部屬性,不正式對外
464 | 由於瀏覽器廣泛支持,才被加入了ES6
465 |
466 | ----
467 |
468 | 2. Object.setPrototypeOf()
469 | 同`__proto__`,用來設置一物件的 prototype 物件
470 |
471 | ```javascript
472 | Object.setPrototypeOf(obj, prototype);
473 | ```
474 |
475 | ----
476 |
477 | 範例
478 | ```javascript
479 | var o = Object.setPrototypeOf({}, null);
480 |
481 | // 同下
482 | function (obj, proto) {
483 | obj.__proto__ = proto;
484 | return obj;
485 | }
486 | ```
487 |
488 | ----
489 |
490 | :exclamation:
491 |
492 | 改變一物件的 [[Prototype]] 需較長作業時間
493 | 如有性能考量建議使用 `Object.create()` 建一新物件設定相關 [[Prototype]]
494 |
495 | ----
496 |
497 | 3. Object.getPrototypeOf()
498 | 用於讀取物件的原型
499 |
500 | ```javascript
501 | var proto = {};
502 | var obj = Object.create(proto);
503 | Object.getPrototypeOf(obj) === proto; // true
504 | ```
505 |
506 | ----
507 |
508 | 傳入非物件參數
509 | ```javascript
510 | Object.getPrototypeOf("foo");
511 | // TypeError: "foo" is not an object (ES5 code)
512 | Object.getPrototypeOf("foo");
513 | // String.prototype (ES6 code)
514 | ```
515 |
516 | ---
517 |
518 | :microscope: ES7
519 |
520 | ---
521 |
522 | ## 8. Object.value() & Object.entries()
523 |
524 | ----
525 |
526 | ES7提議引入 Object.keys() 的配套
527 |
528 | Object.value()
529 | ```javascript
530 | var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
531 |
532 | console.log(Object.values(an_obj));
533 | // ['b', 'c', 'a']
534 | ```
535 |
536 | Object.entries()
537 | ```javascript
538 | var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
539 |
540 | console.log(Object.entries(an_obj));
541 | // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
542 | ```
543 |
544 | ----
545 |
546 | - Object.entries()
547 | 可用於將物件轉換為 Map 結構
548 |
549 | ```javascript
550 | var obj = { foo: "bar", baz: 42 };
551 |
552 | var map = new Map(Object.entries(obj));
553 | console.log(map); // Map { foo: "bar", baz: 42 }
554 | ```
555 |
556 | ---
557 |
558 | ## 9. 物件的擴展運算符
559 |
560 | ----
561 |
562 | ES7 提案
563 | 將 Rest 解構賦值 / 擴展運算符 (...) 引入物件
564 |
565 | ----
566 |
567 | 1. Rest 解構賦值
568 | 從一個物件取值,將所有可遍歷的、但尚未被讀取的屬性,分配到指定的物件上面。所有的鍵和它們的值,都會拷貝到新物件上面
569 |
570 | ```javascript
571 | let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
572 | x; // 1
573 | y; // 2
574 | z; // { a: 3, b: 4 }
575 | ```
576 |
577 | ----
578 |
579 | - Rest 解構賦值的拷貝為淺拷貝
580 |
581 | ```javascript
582 | let obj = { a: { b: 1 } };
583 | let { ...x } = obj;
584 | obj.a.b = 2;
585 | x.a.b // 2
586 | ```
587 |
588 | 如果一個鍵的值是復合類型的值(陣列、物件、函數)
589 | 那麼Rest解構賦值拷貝的是這個值的引用,而不是這個值的副本
590 |
591 | ----
592 |
593 | - Rest 解構賦值不會拷貝繼承自原型物件的屬性
594 |
595 | ```javascript
596 | let o1 = { a: 1 };
597 |
598 | let o2 = { b: 2 };
599 | o2.__proto__ = o1;
600 |
601 | let o3 = { ...o2 };
602 | o3 // { b: 2 }
603 | ```
604 |
605 | ----
606 |
607 | 2. 擴展運算符 (...)
608 | 用於取出參數物件的所有可遍歷屬性,拷貝到當前物件中
609 |
610 | ```javascript
611 | let n = { x, y, ...z };
612 | n; // { x: 1, y: 2, a: 3, b: 4 }
613 | ```
614 |
615 | :arrow_right: 相當於使用 Object.assign()
616 |
617 | ----
618 |
619 | 可用於合併兩物件
620 |
621 | ```javascript
622 | let ab = { ...a, ...b };
623 |
624 | // 同下
625 | let ab = Object.assign({}, a, b);
626 | ```
627 |
628 | ---
629 |
630 | ### 10. Object.getOwnPropertyDescriptors()
631 |
632 | ----
633 |
634 | ES7 提案新增一方法:
635 | 返回指定物件所有自身屬性(非繼承屬性)的描述物件
636 |
637 | ```javascript
638 | const obj = {
639 | foo: 123,
640 | get bar() { return 'abc' }
641 | };
642 |
643 | Object.getOwnPropertyDescriptors(obj)
644 | // { foo:
645 | // { value: 123,
646 | // writable: true,
647 | // enumerable: true,
648 | // configurable: true },
649 | // bar:
650 | // { get: [Function: bar],
651 | // set: undefined,
652 | // enumerable: true,
653 | // configurable: true } }
654 | ```
655 |
656 | ----
657 |
658 | - 目的:為解決 Object.assign()
659 | 無法正確拷貝get屬性和set屬性的問題
660 |
661 | - 配合Object.defineProperties方法實現正確拷貝
662 | ```javascript
663 | const source = {
664 | set foo(value) {
665 | console.log(value);
666 | }
667 | };
668 |
669 | const target = {};
670 | Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
671 | Object.getOwnPropertyDescriptor(target, 'foo')
672 | // { get: undefined,
673 | // set: [Function: foo],
674 | // enumerable: true,
675 | // configurable: true }
676 | ```
677 |
678 | ----
679 |
680 | - 可配合 Object.create(),將物件屬性複製到一個新物件(屬於淺拷貝)
681 |
682 |
683 | ```javascript
684 | Object.create(
685 | Object.getPrototypeOf(obj),
686 | Object.getOwnPropertyDescriptors(obj)
687 | );
688 | ```
689 |
690 | ---
691 |
692 | Thanks :bow:
693 |
--------------------------------------------------------------------------------
/chapter-13/README.md:
--------------------------------------------------------------------------------
1 | ES6 讀書會
2 | ====
3 | Set 和 Map 數據結構
4 |
5 | Doris
6 |
7 | ---
8 |
9 | ## 導讀內容
10 | - Set
11 | - Map
12 | - WeakSet
13 | - WeakMap
14 |
15 | ---
16 |
17 | ## Set
18 | - 有序列表集合(a list of values)
19 | - 類似 Array ,但成員值都是唯一的。
20 | - 是構造函數,可以用 new 來創建 Set 資料結構。
21 | - 可接受的參數型別:Array (或類似 Array 的物件)。
22 | - 加入值時,不會轉換型別;5 和 “5” 視為不同。
23 | - 內部判斷重複值可視為使用“===”,但有些不同:
24 | - NaN 只能加入一次
25 | - “-0”和“0 或 +0”可同時存在
26 | - 兩個物件總是不相等。
27 |
28 | ----
29 |
30 | ## Set 程式範例
31 | ```javascript=
32 | //構造函數
33 | var s = new Set([1,2,3,4,4,5,"5",NaN,NaN,0,-0]);
34 | [...s]; //[1,2,3,4,5,"5",NaN,0,-0] 將重複值剔除
35 |
36 | //類似 Array 的物件
37 | function divs () {
38 | return [...document.querySelectorAll('div')];
39 | }
40 |
41 | var set = new Set(divs());
42 | set.size;
43 | ```
44 |
45 | ----
46 |
47 | ## Set 實做的屬性與方法
48 | - 屬性
49 | - Set.prototype.constructor
50 | - Set.prototype.size
51 | - 方法
52 | - add(value):新增某個值,返回 Set 結構本身
53 | - delete(value):刪除某個值,返回 Boolean
54 | - has(value):返回 Boolean,表示是否為 Set 的成員
55 | - clear():清除所有值
56 |
57 | ----
58 |
59 | ## Set 實做的屬性與方法程式範例
60 | ```javascript=
61 | //add(value) 因為回傳 Set 結構本身,可用 Cascading
62 | var s = new Set();
63 | s.add('a')
64 | .add('b')
65 | .add('c');
66 |
67 | //delete(value)
68 | s.delete('b');
69 |
70 | //has(value)
71 | s.has('a');
72 |
73 | //clear()
74 | s.clear();
75 | ```
76 |
77 | ----
78 |
79 | ## Set 結構轉為 Array
80 | - Array.from()
81 | - Set 與 Array.from() 提供 Array 除去重複值的另一種方法
82 | ```javascript=
83 | var array2 = Array.from(new Set([1,1,2,2,3]));
84 | [...array2]
85 | ```
86 |
87 | ----
88 |
89 | ## Set 的遍歴操作
90 | - Set 遍歴順序就是插入順序,這個特性保證使用 Set 保存一個回調函數列表,調用時可以保證按照新增順序調用。
91 | - keys():返回鍵名
92 | - values():返回值
93 | - entries():返回鍵值對
94 | - forEach():利用回調函數,遍歴所有成員
95 |
96 | ----
97 |
98 | ## keys(),values(),entries()
99 | - Set 結構只有鍵值,或說鍵名與鍵值是同一個值,所以 keys() 與 values() 行為相同。
100 | - Set 結構實作預設遍歷器生成函數就是 values(),表示可省略 values(),直接使用 for...of 歷遍 Set
101 |
102 | ----
103 |
104 | ## 程式範例
105 | ```javascript=
106 | let set = new Set(['red', 'green', 'blue']);
107 | for (let item of set.keys()) {
108 | console.log(item);
109 | }
110 | for (let item of set.values()) {
111 | console.log(item);
112 | }
113 | for (let item of set.entries()) {
114 | console.log(item);
115 | }
116 | for (let item of set) {
117 | console.log(item);
118 | }
119 | ```
120 |
121 | ----
122 |
123 | ## forEach()
124 | ```javascript=
125 | let set = new Set([1, 2, 3]);
126 | set.forEach((v, k) => console.log(v * 2));
127 | ```
128 |
129 | ----
130 |
131 | ## 遍歷的應用
132 | - 擴展運算符(...)
133 | - 去除 Array 重複的成員
134 | - Array 的 filter 與 map 可用於 Set
135 | - 實現聯集(Union),交集(Intersect),差集(Defference)
136 | - 遍歷操作中,同步改變 Set 結構
137 | - 利用 Set 映射出新的 Set 結構,再賦值給原本的 Set
138 | - 利用 Array.from()
139 |
140 | ----
141 |
142 | ## 程式範例 - 擴展運算符(...)
143 | ```javascript=
144 | let set = new Set([1, 2, 3]);
145 | set = new Set([...set].map(x => x * 2));
146 |
147 | let set = new Set([1, 2, 3, 4, 5]);
148 | set = new Set([...set].filter(x => (x % 2) == 0));
149 | ```
150 |
151 | ----
152 |
153 | ## 程式範例 - 遍歷操作中,同步改變 Set 結構
154 | ```javascript=
155 | // 映射新的 Set 再賦值給原本的 Set
156 | let set = new Set([1, 2, 3]);
157 | set = new Set([...set].map(val => val * 2));
158 |
159 | // Array.from()
160 | let set = new Set([1, 2, 3]);
161 | set = new Set(Array.from(set, val => val * 2));
162 | ```
163 |
164 | ---
165 |
166 | ## Map
167 | - 有序鍵值對集合(a collection of keys that correspond to specific values)
168 | - 類似物件,也是鍵值對的集合。
169 | - 鍵的範圍不限於字串。
170 | - 是構造函數,可以用 new 來創建 Map 資料結構。
171 | - 可接受成員為鍵值對的 Array 作為參數。
172 | - 對同一個鍵多次賦值,後面的值會覆蓋前面的。
173 | - key 是物件時,對同一物件的引用才會視為同一個。
174 | - 若兩個鍵嚴格相等視為同一個鍵,但有例外
175 | - NaN 視為同一個鍵
176 | - “-0”和“0 或 +0”視為同一個鍵
177 |
178 | ----
179 |
180 | ## Map 程式範例
181 | ```javascript=
182 | //Array 中有兩個鍵值對的 Array
183 | var map = new Map([['name', 'Jhon'], ['title', 'test']]);
184 | console.log(map); //Map {"name" => "Jhon", "title" => "test"}
185 |
186 | //對同一物件的引用
187 | var map1 = new Map();
188 | var obj = ['a'];
189 | map1.set(obj, 555);
190 | map1.get(obj);
191 | ```
192 |
193 | ----
194 |
195 | ## Map 實做的屬性與方法
196 | - 屬性
197 | - size
198 | - 方法
199 | - set(key, value):設置 key 對應的 value,返回整個 Map 資料結構。
200 | - get(key):讀取 key 對應的 value。
201 | - has(key):返回 Boolean,表示某個 key 是否在 Map 結構中。
202 | - delete(key):返回 Boolean,刪除某個 key。
203 | - clear():清除所有成員。
204 |
205 | ----
206 |
207 | ## Map 實做的屬性與方法程式範例
208 | ```javascript=
209 | let m = new Map();
210 | m.set("edition", 6) // 鍵是字符串
211 | m.set(262, "standard") // 鍵是數值
212 | m.set(undefined, "nah") // 鍵是undefined
213 |
214 | var hello = function() {console.log("hello");}
215 | m.set(hello, "Hello ES6!") // 鍵是函數
216 |
217 | m.size // 4
218 | m.get(hello); // Hello ES6!
219 | m.has(262); // true
220 | m.has(123); // false
221 | m.delete(262); // true
222 | m.clear();
223 | ```
224 |
225 | ----
226 |
227 | ## Map 的遍歴操作
228 | - Map 遍歴順序就是插入順序。
229 | - keys():返回鍵名
230 | - values():返回值
231 | - entries():返回鍵值對
232 | - forEach():利用回調函數,遍歴所有成員
233 |
234 | ----
235 |
236 | ## entries()
237 | - Map 結構實作預設遍歷器生成函數就是 entries(),表示可省略 entries(),直接使用 for...of 歷遍 Map
238 | ```javascript=
239 | let map = new Map([
240 | ['F', 'no'],
241 | ['T', 'yes'],
242 | ]);
243 |
244 | for (let item of map.entries()) {
245 | console.log(item[0], item[1]);
246 | }
247 |
248 | // 或者
249 | for (let [key, value] of map.entries()) {
250 | console.log(key, value);
251 | }
252 |
253 | // 等同于使用map.entries()
254 | for (let [key, value] of map) {
255 | console.log(key, value);
256 | }
257 | ```
258 |
259 | ----
260 |
261 | ## forEach
262 | ```javascript=
263 | let map = new Map([
264 | ['F', 'no'],
265 | ['T', 'yes'],
266 | ]);
267 |
268 | map.forEach(function(value, key, map){
269 | console.log("Key: %s, Value: %s", key, value);
270 | });
271 |
272 | //可以接受第二個參數,用來綁定 this
273 | var map2 = new Map().set(1, 'a').set(2, 'b').set(3, 'c');
274 | var reporter = {
275 | report: function(x, y){
276 | return x + y;
277 | }
278 | }
279 | map2.forEach(function(v, k){
280 | console.log(this.report(1, 2)); //this 指向 reporter
281 | }, reporter);
282 | ```
283 |
284 | ----
285 |
286 | ## 與其他資料結構的互相轉換
287 | - Map 與 Array
288 | - Map 與 Object
289 | - Map 與 JSON
290 |
291 | ----
292 |
293 | ## Map 與 Array
294 | - Map 轉為 Array >> 使用擴展運算符(...)
295 | - 結合 Array 的 map() 與 filter(),可實現 Map 的遍歷(map)與過濾(filter)
296 |
297 | ----
298 |
299 | ## Map 轉為 Array
300 | ```javascript=
301 | let map = new Map([
302 | [1, 'a'],
303 | [2, 'b'],
304 | [3, 'c'],
305 | ]);
306 |
307 | // 產生 Map 結構 {1 => 'a', 2 => 'b'}
308 | let map1 = new Map(
309 | [...map].filter(([k, v]) => k < 3)
310 | );
311 |
312 | // 產生 Map 結構 {2 => '_a', 4 => '_b', 6 => '_c'}
313 | let map2 = new Map(
314 | [...map].map(([k, v]) => [k * 2, '_' + v])
315 | );
316 | ```
317 |
318 | ----
319 |
320 | ## Array 轉為 Map
321 | - 將 Array 轉入 Map 構造函數即可
322 | ```javascript=
323 | var map3 = new Map([[true, 7], [{foo: 3}, ['abc']]]);
324 | ```
325 |
326 | ----
327 |
328 | ## Map 與 Object
329 | - 若 Map 的 key 都是字串,可以轉為 Object;key 不為 字串,轉換後的物件 key 會不正確。
330 | ```javascript=
331 | function strMapToObj(strMap) {
332 | let obj = Object.create(null);
333 | for (let [k,v] of strMap) {
334 | obj[k] = v;
335 | }
336 | return obj;
337 | }
338 |
339 | let myMap = new Map().set('yes', true).set('no', false);
340 | strMapToObj(myMap)
341 | ```
342 |
343 | ----
344 |
345 | ## Map 與 JSON
346 | - Map 轉 JSON (JSON.stringify)
347 | - Map 的 key 都是字串符,可轉為物件 JSON。
348 | - Map 的 key 有非字串符,可轉為陣列 JSON。
349 | ```javascript=
350 | //key 都是字串
351 | function strMapToJson(strMap) {
352 | return JSON.stringify(strMapToObj(strMap));
353 | }
354 |
355 | let myMap = new Map().set('yes', true).set('no', false);
356 | strMapToJson(myMap)
357 |
358 | //key 有非字串符
359 | function mapToArrayJson(map) {
360 | return JSON.stringify([...map]);
361 | }
362 |
363 | let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
364 | mapToArrayJson(myMap)
365 | ```
366 |
367 | ---
368 |
369 | ## WeakSet
370 | - 與 Set 結構相似,也是不重複的值的集合。
371 | - 區別:
372 | - 成員只能是物件類型
373 | - 成員都是弱引用,這個特性意味 WeakSet 不可遍歷
374 | - 可用在 DOM 節點的儲存,不需擔心節點被刪除,引發記憶體洩漏(Memory Leak)
375 | ```javascript=
376 | var ws = new WeakSet();
377 | ws.add(1) // TypeError: Invalid value used in weak set
378 |
379 | //以下範例,a 是陣列,有兩個成員,都是陣列
380 | //a 的成員會成為 WeakSet 的成員,不是 a 陣列本身
381 | //這意味陣列的成員只能是物件類型
382 | var a = [[1,2],[3,4]];
383 | ws.add(a);
384 | ```
385 |
386 | ----
387 |
388 | ## WeakSet 方法
389 | - WeakSet.prototype.add(value)
390 | - WeakSet.prototype.delete(value)
391 | - WeakSet.prototype.has(value)
392 |
393 |
394 | ---
395 |
396 | ## WeakMap
397 | - 與 Map 結構相似。
398 | - 區別:
399 | - 只接受非空物件 (non-null object) 為 key
400 | - key 指向的物件不計入垃圾回收機制。
401 | - 設計目的:
402 | - key 是物件的弱引用,物件回收後 WeakMap 自動移除對應的鍵值對
403 | - 典型應用於一個 DOM 元素的 WeakMap 結構。
404 | - 有助於防止記憶體洩漏(Memory Leak)。
405 |
406 | ----
407 |
408 | ## WeakMap 與 Map 在 API 上的區別
409 | - 沒有遍歷操作(沒有 keys(), values(), entries()),也沒有 size 屬性
410 | - 無法清空,不支持 clear()
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
--------------------------------------------------------------------------------
/chapter-14/README.md:
--------------------------------------------------------------------------------
1 | ES6 讀書會
2 | ==
3 | 
4 |
5 | ## Iterator & for...of 循環
6 |
7 | ----
8 |
9 | 參考資料
10 | - [ECMAScript 6 入門](http://es6.ruanyifeng.com/#docs/iterator)
11 |
12 | - [MDN document](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)
13 |
14 | - [Exploring ES6](http://exploringjs.com/es6/ch_iteration.html)
15 |
16 | ---
17 |
18 | # Iterator Concepts
19 |
20 | ----
21 |
22 | ## Usage
23 |
24 | * 為各種數據結構,提供一個統一的、簡便的訪問接口
25 |
26 | * 任何數據結構只要部署 Iterator 接口,就可依次處理該數據結構的所有成員
27 |
28 | * ES6 創造了一種新的遍歷命令 for...of 循環
29 |
30 |
31 | ----
32 |
33 | ## Iterator 遍歷過程
34 |
35 | ``` javascript
36 | var someString = "hi";
37 |
38 | var iterator = someString[Symbol.iterator]();
39 | iterator + ""; // "[object String Iterator]"
40 |
41 | iterator.next(); // { value: "h", done: false }
42 | iterator.next(); // { value: "i", done: false }
43 | iterator.next(); // { value: undefined, done: true }
44 | ```
45 | * `next` 方法返回一個對象,表示當前數據成員的信息
46 | * `value` 屬性返回當前位置的成員
47 | * `done` 屬性是一個布林值,表示遍歷是否結束
48 |
49 | ----
50 |
51 | ## Builtin Iterables
52 |
53 | * 陣列
54 | * 字串
55 | * Sets
56 | * Maps
57 |
58 | :exclamation: 物件原生不具備遍歷器接口
59 |
60 | ----
61 |
62 | - 不用任何處理,就可以被 `for...of` 循環遍歷
63 |
64 | - 這些數據結構原生部署了 `Symbol.iterator` 屬性
65 |
66 | ---
67 |
68 | # Iterator Protocal
69 |
70 | ----
71 |
72 | ## Iterable Protocol
73 |
74 | * 部署在數據結構的 `Symbol.iterator` 屬性
75 |
76 | :arrow_right: 具有 `Symbol.iterator` 屬性,就可以認為是 `iterable`
77 |
78 | ----
79 |
80 | ```javascript
81 | let arr = ['a', 'b', 'c'];
82 | let iter = arr[Symbol.iterator]();
83 |
84 | iter.next() // { value: 'a', done: false }
85 | iter.next() // { value: 'b', done: false }
86 | iter.next() // { value: 'c', done: false }
87 | iter.next() // { value: undefined, done: true }
88 | ```
89 |
90 | ----
91 |
92 | ```javascript
93 | var someArray = [1, 5, 7];
94 | var someArrayEntries = someArray.entries();
95 |
96 | someArrayEntries.toString(); // "[object Array Iterator]"
97 | someArrayEntries === someArrayEntries[Symbol.iterator](); // true
98 | typeof someArrayEntries[Symbol.iterator]; // "function"
99 | ```
100 |
101 | ---
102 |
103 | # Constucts Supporting Iteration
104 |
105 | ----
106 |
107 | 1. for-of loop
108 |
109 | ```javascript
110 | let iterable = [10, 20, 30];
111 |
112 | for (let value of iterable) {
113 | console.log(value);
114 | }
115 |
116 | // 10
117 | // 20
118 | // 30
119 | ```
120 |
121 | ----
122 |
123 | 2. Spread operator (...)
124 |
125 | ```javascript
126 | var someString = "hi";
127 |
128 | [...someString] // ["h", "i"]
129 | ```
130 |
131 | :+1: 可以將任何部署了Iterator接口的數據結構,轉為陣列
132 |
133 | ----
134 |
135 | 3. yield*
136 |
137 | ```javascript
138 | let generator = function* () {
139 | yield 1;
140 | yield* [2,3,4];
141 | yield 5;
142 | };
143 |
144 | var iterator = generator();
145 |
146 | iterator.next() // { value: 1, done: false }
147 | iterator.next() // { value: 2, done: false }
148 | iterator.next() // { value: 3, done: false }
149 | iterator.next() // { value: 4, done: false }
150 | iterator.next() // { value: 5, done: false }
151 | iterator.next() // { value: undefined, done: true }
152 | ```
153 |
154 | ----
155 |
156 | ## Others
157 |
158 | * Array.from():
159 | `const arr = Array.from(new Set(['a', 'b', 'c']));`
160 |
161 | * Constructors of Maps and Sets:
162 | `const map = new Map([[false, 'no'], [true, 'yes']]);`
163 | `const set = new Set(['a', 'b', 'c']);`
164 |
165 | * Promise.all(), Promise.race():
166 | `Promise.all(iterableOverPromises).then(···);`
167 | `Promise.race(iterableOverPromises).then(···);`
168 |
169 | ---
170 |
171 | # Iterators and Generators
172 |
173 | ----
174 |
175 | ```javascript
176 | var myIterable = {}
177 | myIterable[Symbol.iterator] = function* () {
178 | yield 1;
179 | yield 2;
180 | yield 3;
181 | };
182 | [...myIterable] // [1, 2, 3]
183 | ```
184 |
185 | ---
186 |
187 | # return() and throw() Methods
188 |
189 | ----
190 |
191 | ## return()
192 |
193 | * 使用場合: 如果for...of循環提前退出(通常是因為出錯,或者有break語句或continue語句)
194 | * 如果一個對象在完成遍歷前,需要清理或釋放資源,就可以部署return方法
195 |
196 | ----
197 |
198 | ```javascript
199 | function* gen() {
200 | yield 1;
201 | yield 2;
202 | yield 3;
203 | }
204 |
205 | var g = gen();
206 |
207 | g.next() // { value: 1, done: false }
208 | g.return('foo') // { value: "foo", done: true }
209 | g.next() // { value: undefined, done: true }
210 | ```
211 |
212 | ----
213 |
214 | ## throw()
215 |
216 | * 主要是配合Generator函數使用,一般的遍歷器對象用不到這個方法
217 |
218 | ---
219 |
220 | # for...of 循環
221 |
222 | ----
223 |
224 | ## 使用的範圍包括:
225 |
226 | * Array
227 | * Set
228 | * Map
229 | * DOM NodeList ......
230 |
231 | ----
232 |
233 | ```javascript
234 | // DOM NodeList obj
235 | let paras = document.querySelectorAll("p");
236 |
237 | for (let p of paras) {
238 | p.classList.add("test");
239 | }
240 | ```
241 |
242 | ----
243 |
244 | ## 與 for...in 的差異 - 1
245 |
246 | ```javascript
247 | var arr = ['a', 'b', 'c', 'd'];
248 |
249 | for (let a in arr) {
250 | console.log(a); // 0 1 2 3
251 | }
252 |
253 | // 只能獲得對象的鍵名,不能直接獲取鍵值
254 |
255 | for (let a of arr) {
256 | console.log(a); // a b c d
257 | }
258 |
259 | // 允許遍歷獲得鍵值
260 | ```
261 |
262 | ----
263 |
264 | ## 與 for...in 的差異 - 2
265 |
266 | ```javascript
267 | let arr = [3, 5, 7];
268 | arr.foo = 'hello';
269 |
270 | for (let i in arr) {
271 | console.log(i); // "0", "1", "2", "foo"
272 | }
273 |
274 | for (let i of arr) {
275 | console.log(i); // "3", "5", "7"
276 | }
277 |
278 | // 數組的遍歷器接口只返回具有數字索引的屬性
279 | ```
280 |
281 | ----
282 |
283 | ## for...in 循環缺點:
284 |
285 | * 陣列的鍵名是數字,但是for...in循環是以字符串作為鍵名“0”、“1”、“2”等等
286 | * for...in循環不僅遍歷數字鍵名,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵
287 | * 某些情況下,for...in循環會以任意順序遍歷鍵名
288 |
289 | :exclamation: 主要是為遍歷物件而設計的,不適用於遍歷陣列
290 |
291 | ----
292 |
293 | ## for...of 與其他遍歷語法的比較之優點
294 |
295 | * 有著同for...in一樣的簡潔語法,但是沒有for...in的缺點
296 | * 不同用於forEach方法,它可以與break、continue和return配合使用
297 | * 提供了遍歷所有數據結構的統一操作接口
298 |
299 | ----
300 |
301 | ## forEach()
302 |
303 | ```javascript
304 | myArray.forEach(function (value) {
305 | console.log(value);
306 | });
307 | ```
308 |
309 | * 無法中途跳出forEach循環
310 |
311 | ---
312 |
313 | Thanks :bow:
314 |
--------------------------------------------------------------------------------
/chapter-15/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 15. Generator function
2 |
3 | 以下章節 **由淺入深** 帶大家探索 Generator function!
4 | :seedling: :herb: :deciduous_tree:
5 |
6 | ### 大綱
7 | - [一. 淺入淺出 Generator function](#ㄧ-淺入淺出-generator-function)
8 | - [1. 它是一個狀態機](#1-它是一個狀態機)
9 | - [2. 它會回傳一個遍歷器](#2-它會回傳一個遍歷器)
10 | - [3. 使用 `next()` 遍歷狀態](#3-使用-next-遍歷狀態)
11 | - [4. 在 `next()` 中傳入參數,與 generator 互動](#4-在-next-中傳入參數與-generator-互動)
12 | - [二. 中入淺出 Generator function](#二-中入淺出-generator-function)
13 | - [1. for…of](#1-forof)
14 | - [2. Generator.prototype.throw](#2-generatorprototypethrow)
15 | - [3. Generator.prototype.return](#3-generatorprototypereturn)
16 | - [4. yield*](#4-yield)
17 | - [5. 當物件屬性是 generator](#5-當物件屬性是-generator)
18 | - [三. 深入淺出 Generator function](#三-深入淺出-generator-function)
19 | - [1. generator 的 this](#1-generator-的-this)
20 | - [2. generator 的應用](#2-generator-的應用)
21 | - [四. 參考連結](#四-參考連結)
22 |
23 | :seedling:
24 | ## ㄧ. 淺入淺出 Generator function
25 |
26 | ### 1. 它是一個狀態機
27 |
28 | 它封裝多個內部狀態,而 ***yield 是一個暫停標誌***。
29 |
30 | ```js
31 | function* generator() {
32 | yield 'state1';
33 | yield 'state2';
34 | }
35 | ```
36 |
37 | ### 2. 它會回傳一個遍歷器
38 |
39 | ```js
40 | function* speak() {
41 | yield 'hello';
42 | yield 'world';
43 | }
44 |
45 | const iterator = speak();
46 | ```
47 |
48 | ### 3. 使用 `next()` 遍歷狀態
49 |
50 | ```js
51 | function* speak() {
52 | yield 'hello';
53 | yield 'world';
54 | }
55 |
56 | const i = speak();
57 |
58 | i.next(); // { value: 'hello', done: false }
59 | i.next(); // { value: 'world', done: false }
60 | i.next(); // { value: undefined, done: true }
61 | ```
62 |
63 | #### 如果 generator 有 `return` 值
64 |
65 | ```js
66 | function* speak() {
67 | yield 'hello';
68 | yield 'world';
69 | return '!';
70 | }
71 |
72 | const i = speak();
73 |
74 | i.next(); // { value: 'hello', done: false }
75 | i.next(); // { value: 'world', done: false }
76 | i.next(); // { value: '!', done: true } -> done 已經是 true,而 value 取得的是 return 的值
77 | i.next(); // { value: undefined, done: true }
78 | ```
79 |
80 | #### `yield` 後面的表達式,只有被呼叫 `next()` 才會執行
81 |
82 | ```js
83 | function getName() {
84 | console.log('My name is world.');
85 | return 'world';
86 | }
87 |
88 | function* speak() {
89 | yield 'hello ' + getName();
90 | }
91 |
92 | const i = speak();
93 |
94 | i.next(); // My name is world.
95 | // { value: 'hello world', done: false }
96 | ```
97 |
98 | #### 更多語法特性
99 |
100 | ```js
101 | // 不一定要使用 yield
102 | function* speak() {}
103 |
104 | // 但是 yield 一定要在 generator function 中使用
105 | function speak() {
106 | yield 'hello world'; // Error!
107 | }
108 |
109 | // 在表達式中必須加括弧
110 | console.log('hello' + (yield 'world'));
111 |
112 | // 在參數中不用
113 | getName(yield 'Who are you?');
114 | ```
115 |
116 | ### 4. 在 `next()` 中傳入參數,與 generator 互動
117 |
118 | ```js
119 | function* conversation() {
120 | const name = yield 'What\'s your name?'; // name 會取得第二個 next 回傳的參數
121 | yield `Ok, your name is ${name}, right?`;
122 | }
123 |
124 | const i = conversation();
125 | i.next().value; // What's your name?
126 | i.next('Jason').value; // Ok, your name is Jason, right?
127 | ```
128 |
129 | #### 第一個 `next()` 是不能給參數的
130 |
131 | ```js
132 | const i = conversation();
133 | i.next().value;
134 | i.next('Jason').value;
135 | ```
136 |
137 | #### 小試題:下方例子會分別印出什麼呢?
138 |
139 | ```js
140 | function* foo(x) {
141 | const y = 2 * (yield (x + 1));
142 | const z = yield (y / 3);
143 | return (x + y + z);
144 | }
145 |
146 | const i = foo(5);
147 | console.log(i.next().value);
148 | console.log(i.next(12).value);
149 | console.log(i.next(13).value);
150 | ```
151 |
152 | ```js
153 | function* foo(x) {
154 | const y = 2 * (yield (x + 1));
155 | const z = yield (y / 3);
156 | return (x + y + z);
157 | }
158 |
159 | const i = foo(5); // x=5
160 | console.log(i.next().value); // yield x+1 -> 6
161 | console.log(i.next(12).value); // y=2*12 -> yield 24/3 -> 8
162 | console.log(i.next(13).value); // z=13 -> 5+24+13 -> 42
163 | ```
164 |
165 | ### 5. 淺入淺出小結
166 |
167 | 1. 它是一個狀態機
168 | 2. 它會回傳一個遍歷器
169 | 3. 使用 `next()` 遍歷狀態
170 | 4. 在 `next()` 中傳入參數,與 generator 互動
171 |
172 | :herb:
173 | ## 二. 中入淺出 Generator function
174 |
175 | ### 1. for...of
176 |
177 | ```js
178 | function *foo() {
179 | yield 1;
180 | yield 2;
181 | yield 3;
182 | yield 4;
183 | yield 5;
184 | return 6;
185 | }
186 |
187 | for (let v of foo()) {
188 | console.log(v);
189 | }
190 | // 1 2 3 4 5
191 | // 注意:沒有 `return` 的值
192 | ```
193 |
194 | #### 一個 fibonacci 例子
195 |
196 | ```js
197 | function* fibonacci() {
198 | let [prev, curr] = [0, 1];
199 | while (true) {
200 | [prev, curr] = [curr, prev + curr];
201 | yield curr;
202 | }
203 | }
204 |
205 | for (let n of fibonacci()) {
206 | if (n > 1000) break;
207 | console.log(n);
208 | }
209 | ```
210 |
211 | #### 使用 `Array.form()`、擴展和解構運算式
212 |
213 | ```js
214 | function* numbers () {
215 | yield 1
216 | yield 2
217 | return 3
218 | yield 4
219 | }
220 |
221 | Array.from(numbers()) // [1, 2]
222 |
223 | [...numbers()] // [1, 2]
224 |
225 | let [x, y] = numbers(); // x=1, y=2
226 |
227 | for (let n of numbers()) {
228 | console.log(n);
229 | }
230 | // 1 2
231 | ```
232 |
233 | ### 2. Generator.prototype.throw
234 |
235 | ```js
236 | function* g() {
237 | try {
238 | yield;
239 | } catch (e) {
240 | console.log('内部捕獲', e);
241 | }
242 | }
243 |
244 | var i = g();
245 | i.next();
246 |
247 | try {
248 | i.throw('a');
249 | i.throw('b');
250 | } catch (e) {
251 | console.log('外部捕獲', e);
252 | }
253 |
254 | // 內部捕獲, a
255 | // 外部補獲, b
256 | ```
257 |
258 | #### `throw` 被捕獲以後,會附帶執行下一條 `yield` 語句
259 |
260 | ```js
261 | function* g() {
262 | try {
263 | yield console.log('a');
264 | } catch (e) {
265 | // ...
266 | }
267 | yield console.log('b');
268 | yield console.log('c');
269 | }
270 |
271 | var i = g();
272 | i.next(); // a
273 | i.throw(); // b
274 | i.next(); // c
275 | ```
276 |
277 | ### 3. Generator.prototype.return
278 |
279 | ```js
280 | function* g() {
281 | yield 1;
282 | yield 2;
283 | yield 3;
284 | }
285 |
286 | var i = g();
287 |
288 | i.next(); // { value: 1, done: false }
289 | i.return('foo'); // { value: 'foo', done: true }
290 | i.next(); // { value: undefined, done: true }
291 | ```
292 |
293 | #### 如果使用 `finally`
294 |
295 | ```js
296 | function* numbers() {
297 | yield 1;
298 | try {
299 | yield 2;
300 | yield 3;
301 | } finally {
302 | yield 4;
303 | yield 5;
304 | }
305 | yield 6;
306 | }
307 | var i = numbers()
308 | i.next(); // { done: false, value: 1 }
309 | i.next(); // { done: false, value: 2 }
310 | i.return(7); // { done: false, value: 4 }
311 | i.next(); // { done: false, value: 5 }
312 | i.next(); // { done: true, value: 7 }
313 | ```
314 |
315 | ### 4. yield*
316 |
317 | ```js
318 | function* foo() {
319 | yield 'a';
320 | yield 'b';
321 | }
322 |
323 | function* bar() {
324 | yield 'x';
325 | yield* foo();
326 | yield 'y';
327 | }
328 |
329 | for (let v of bar()) {
330 | console.log(v);
331 | }
332 | // x a b y
333 | ```
334 |
335 | #### 陣列是 iterator
336 |
337 | ```js
338 | function* g() {
339 | yield* ['a', 'b', 'c'];
340 | }
341 |
342 | g().next() // { value: 'a', done: false }
343 | ```
344 |
345 | #### 字串也是 iterator
346 |
347 | ```js
348 | let read = (function* () {
349 | yield 'hello';
350 | yield* 'hello';
351 | })();
352 |
353 | read.next().value // 'hello'
354 | read.next().value // 'h'
355 | ```
356 |
357 | #### 接收 `yield*` 的 `return`
358 |
359 | ```
360 | function* foo() {
361 | yield 2;
362 | yield 3;
363 | return 'foo';
364 | }
365 |
366 | function* bar() {
367 | yield 1;
368 | var v = yield* foo();
369 | console.log('v: ' + v);
370 | yield 4;
371 | }
372 |
373 | var i = bar();
374 |
375 | i.next(); // { value: 1, done: false }
376 | i.next(); // { value: 2, done: false }
377 | i.next(); // { value: 3, done: false }
378 | i.next(); // 'v: foo'
379 | // { value: 4, done: false }
380 | i.next(); // { value: undefined, done: true }
381 | ```
382 |
383 | ### 5. 當物件屬性是 generator
384 |
385 | ```js
386 | const obj = {
387 | * myGeneratorMethod() {
388 | ···
389 | }
390 | };
391 | ```
392 |
393 | ### 6. 中入淺出小結
394 |
395 | 我們介紹了以下語法:
396 |
397 | 1. for…of
398 | 2. Generator.prototype.throw
399 | 3. Generator.prototype.return
400 | 4. yield*
401 | 5. 當物件屬性是 generator
402 |
403 | :deciduous_tree:
404 | ## 三. 深入淺出 Generator function
405 |
406 | ### 1. generator 的 this
407 |
408 | #### gernerator 回傳的是 gernerator 實例
409 |
410 | ```js
411 | function* g() {}
412 |
413 | g.prototype.hello = function () {
414 | return 'hi!';
415 | };
416 |
417 | const obj = g();
418 |
419 | obj instanceof g; // true
420 | obj.hello(); // 'hi!'
421 | ```
422 |
423 | #### generator 返回的是遍歷器,不是 this
424 |
425 | ```js
426 | function* g() {
427 | this.a = 11;
428 | }
429 |
430 | let obj = g();
431 | obj.a; // undefined
432 | ```
433 |
434 | #### 不能使用 new
435 |
436 | ```js
437 | function* F() {
438 | yield this.x = 2;
439 | yield this.y = 3;
440 | }
441 |
442 | new F()
443 | // TypeError: F is not a constructor
444 | ```
445 |
446 | ### 2. generator 的應用
447 |
448 | 1. 以同步化的方式撰寫異步操作
449 | 2. 控制流管理
450 | 3. 部署 iterator 接口
451 | 4. 作為數據結構
452 |
453 | :mortar_board:
454 |
455 | ## 四. 參考連結
456 |
457 | 1. [ECMAScript 6 入门 | 阮一峰](http://es6.ruanyifeng.com/#docs/generator)
458 | 2. [Egghead](https://egghead.io/lessons/ecmascript-6-generators?course=learn-es6-ecmascript-2015)
459 | 3. [淺入淺出 Generator Function](http://abalone0204.github.io/2016/05/08/es6-generator-func/)
460 | 4. [Promise, generator, async 與 ES6](http://huli.logdown.com/posts/292655-javascript-promise-generator-async-es6)
461 | 5. [Airbnb style guide](https://github.com/airbnb/javascript#iterators-and-generators)
462 |
--------------------------------------------------------------------------------
/chapter-17/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 17. 異步操作和 Async 函数
2 |
3 | ### 大綱
4 |
5 | - [一. Blocking and waiting](#ㄧ-blocking-and-waiting)
6 | - [二. Callback function](#二-callback-function)
7 | - [三. ES2015 Promise](#三-es2015-promise)
8 | - [四. ES2015 Generator function](#四-es2015-generator-function)
9 | - [五. ES2016 Async function](#五-es2016-async-function)
10 | - [六. More Async programming](#六-more-async-programming)
11 | - [七. 參考資源](#七-參考資源)
12 | - [八. Curators](#八-curators)
13 |
14 | ## ㄧ. Blocking and waiting
15 |
16 | #### Blocking (pulling):
17 |
18 | ```js
19 | function login() {
20 | const fbUser = fbLogin();
21 | const user = getCurrentUser(fbUser);
22 | return user;
23 | }
24 | ```
25 |
26 | 在 Jafar Husain 的影片([連結](https://www.youtube.com/watch?v=lil4YCCXRYc))中,他提到的 Pulling 概念可以用下面這一句來想像:
27 | `fbUser <- fbLogin()`
28 |
29 | 換成白話文的意思就是 - 我們調用 fbLogin 這個函數,並將 fbUser 的值從中拉 (pull) 出來。
30 |
31 | #### Waiting (pushing):
32 |
33 | ```js
34 | function login() {
35 | fbLogin((fbUser) => {
36 | getCurrentUser(fbUser, (user) => {
37 | });
38 | });
39 | }
40 | ```
41 |
42 | 如果 fbLogin 是非同步的請求(例如 AJAX),我們沒辦法馬上得到資料,也不能讓程式 blocking 在這一行(如果使用者點了登入按鈕,卻看到頁面卡在那裡是會抓狂的)。
43 |
44 | 所以我們可以使用 Pushing 的概念,用下面這一行想像:
45 | `fbLogin( -> fbUser)`
46 |
47 | 白話文 - 我們調用 fbLogin,並將結果值推 (push) 給 fbUser。
48 |
49 | ## 二. Callback function
50 |
51 | 如果瞭解了 blocking 和 waiting 的概念,在 ES6 以前,如果我們要寫 Async 的邏輯,勢必一定要用 callback。
52 |
53 | ```js
54 | function login(callback) {
55 | fbLogin((err, fbUser) => {
56 | if (err) {
57 | callback(err);
58 | return;
59 | }
60 | getCurrentUser(fbUser, (err, user) => {
61 | callback(err, user);
62 | });
63 | });
64 | }
65 | ```
66 |
67 | 為了不讓我們的程式停在非同步請求,我們將邏輯拆分成兩段:
68 | 一段是調用請求的主程式;
69 | 一段是處理請求結果的 callback 函數。
70 |
71 | 不過使用 Callback 處理 Async 的隱憂就是:
72 |
73 | 
74 |
75 | ## 三. ES2015 Promise
76 |
77 | 到了 ES6,想必你也用過 Promise 來避免 Callback hell:
78 |
79 | ```js
80 | fbLogin()
81 | .then(getCurrentUser)
82 | .then((user) => console.log(user))
83 | .catch((err) => console.log(err));
84 | ```
85 |
86 | ## 四. ES2015 Generator function
87 |
88 | ES6 提供了我們另外一個語法糖 - [Generator](../chapter-15):
89 |
90 | ```js
91 | function* login() {
92 | const fbUser = yield fbLogin();
93 | const user = yield getCurrentUser(fbUser);
94 | return user;
95 | }
96 | ```
97 |
98 | 這語法糖讓我們可以使用 [Pulling](#blocking-pulling) 的方式撰寫非同步操作,讓編寫同步和非同步程式的思維一致化。
99 |
100 | > PS. 如果你把上面那一段程式的 `*` 與 `yield` 拿掉,是不是就跟同步邏輯一樣了呢!
101 |
102 | #### 使用 `spawn`
103 |
104 | 不過 Generator 是一個狀態機,我們必須調用 login 函數後拿到最後 user 的值,而不是停在每一個 `yield` 的中斷點上;因此我們需要一個執行器。
105 |
106 | ```js
107 | const promise = spawn(login());
108 |
109 | promise
110 | .then((user) => console.log(user))
111 | .catch((err) => console.log(err));
112 | ```
113 |
114 | `spawn.js` 幫我們解決了這個問題,我們將 Generator 回傳的 iterator 遞給 spawn,它會幫我們執行每一個非同步操作,直到那到最後的值,並 push 到你的程式中。
115 |
116 | #### `spawn.js` 是如何執行這些非同步的?
117 |
118 | ```js
119 | function spawn(iterator) {
120 | return new Promise((resolve, reject) => {
121 | const onResult = (data) => {
122 | const { value, done } = iterator.next(data);
123 | if (!done) value.then(onResult);
124 | else resolve(data);
125 | };
126 | onResult();
127 | });
128 | }
129 | ```
130 |
131 | 不過使用 Generator 來撰寫 push 風格的非同步操作,卻需要額外的執行器(spawn.js);想像以後我們需要專們處理 Async 的語法糖,不再需要使用者些執行器...
132 |
133 | ## 五. ES2016 Async function
134 |
135 | ES7 的 Async/Await 就是專門解決非同步操作的語法糖,我們不再需要引用額外的程式。
136 |
137 | ```js
138 | async function login() {
139 | const fbUser = await fbLogin();
140 | const user = await getCurrentUser(fbUser);
141 | return user;
142 | }
143 |
144 | login()
145 | .then((user) => console.log(user))
146 | .catch((err) => console.log(err));
147 | ```
148 |
149 | ## 六. More Async programming
150 |
151 | 在 Jafar Husain 的影片末也有提到未來 ES Async programming 的走向,例如 Observable interface:
152 |
153 | ```js
154 | async function getReplies() {
155 | for (let reply on new WebSocket('/posts/1/replies')) {
156 | console.log(reply);
157 | }
158 | }
159 | ```
160 |
161 | 這部影片值得去看一看,可以了解整個 Async programming 的來龍去脈([連結](https://www.youtube.com/watch?v=lil4YCCXRYc))!
162 |
163 | ## 七. 參考資源
164 |
165 | 1. [Jafar Husain: Async Programming in ES7 | JSConf US 2015](https://www.youtube.com/watch?v=lil4YCCXRYc)
166 | 2. [异步操作和 Async 函数](http://es6.ruanyifeng.com/#docs/async)
167 | 3. [Callback Hell - A guide to writing asynchronous JavaScript programs](http://callbackhell.com/)
168 |
169 | ## 八. Curators
170 |
171 | - [shiningjason](http://shiningjason1989.github.io/): Hope this article will inspire you 🍾🍾🍾
172 |
173 |
--------------------------------------------------------------------------------
/introducton/README.md:
--------------------------------------------------------------------------------
1 | # 前言
2 |
3 | # 成立讀書會的目的
4 | 讀書會的成員,有老手,也有初學者。
5 | 由於程式語言的進步非常的快速,不管是從事開發十年以上的老手,
6 | 或是剛入門的初學者,都必須重新適應新的開發語言,新的特性。
7 | 所以我們希望透過一個平台
8 | 打造一個學習與實作的環境,
9 | 共同成長和學習。
10 |
11 | # 練習環境
12 | 1. 需要一個可以編譯 es6 成 es5 的測試環境。
13 | 2. 驗證語法是否正確。
14 |
15 | # 工具
16 |
17 | * 簡體轉繁體
18 |
19 | - 特色 : 網頁內容簡體中文轉繁體中文
20 | - 安裝 : [Chrome 線上應用程式商店](https://chrome.google.com/webstore/detail/new-tong-wen-tang/ldmgbgaoglmaiblpnphffibpbfchjaeg?hl=zh-TW)
21 |
22 |
23 | * 編輯器
24 | - 線上編輯
25 | - [jsbin](http://jsbin.com/)
26 | - [codepen](http://codepen.io/)
27 | - [jsfiddle](https://jsfiddle.net/)
28 |
29 | - 線上即時轉譯
30 | - [babel](https://babeljs.io/repl/)
31 | - [traceur](https://google.github.io/traceur-compiler/demo/repl.html#)
32 |
33 | - ATOM
34 | - 特色 : 免費跨平台使用,可以提供語法檢查、高亮語法顯示、語法提示...
35 | - 安裝 : [官網](https://atom.io/)
36 | - 外掛 :
37 | - [es6-javascript](https://atom.io/packages/es6-javascript)
38 | - [linter-eslint](https://atom.io/packages/linter-eslint)
39 |
40 | * 錄影工具
41 |
42 | - 線上
43 | - [youtube 螢幕錄影](http://www.playpcesor.com/2016/01/youtube-desktop-screen-record.html)
44 | - windows
45 | - [ActivePresenter](http://www.playpcesor.com/2016/01/activepresenter.html)
46 | - mac
47 | - [QuickTime Player](https://support.apple.com/zh-tw/HT201066)
48 |
49 |
50 |
51 | 歡迎其他成員協助撰寫內容補充資料
52 |
--------------------------------------------------------------------------------