├── .gitignore ├── README.md ├── images └── github-search-results.png └── src └── classes.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | _book 3 | book.pdf 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ES6: QuickStart and Tips 2 | ------------------------ 3 | 4 | ES6: 快速体验,及实用小贴士 5 | 6 | > 2016 年是 ES6 大力推广和普及的黄金时期,也是今年的流行趋势, 7 | > 就一个 [ES6](https://github.com/search?o=desc&q=ES6&s=stars&type=Repositories&utf8=✓) 关键词, 8 | > 在 GitHub 上就有这么多搜索结果。(赶紧跟上大部队!) 9 | 10 | ![It's very hot!](images/github-search-results.png) 11 | 12 | 13 | ## 简介 14 | 15 | ES6 是 **ECMAScript 6** 的简称,是 [ECMA-262 的第 6 版本](http://www.ecma-international.org/ecma-262/6.0/index.html)。 16 | 17 | 18 | * ES5、ES2015 / ES6、ES2016 / ES7、ES2017 / ES8 这些关键词具体代表什么? 19 | 20 | [ES5] 发布于 2009 年。 21 | 22 | [ES6],比起 ES5,是第一个大版本更新,在 2015-06 发布,所以又称 ES2015。 23 | 24 | [ES7 和 ES8] 统称为 ECMAScript Next。 25 | 26 | [ES7] 在 2016-06 发布,又称 ES2016。 27 | 28 | ES8 在 2017-06 发布,又称 ES2017。 29 | 30 | * ECMAScript 与 JavaScript 有什么关系? 31 | 32 | 前者是后者的语言标准,后者是前者的一个实现。 33 | 34 | * ES6 在浏览器,Node.js 支持如何,适不适合开发,生产? 35 | 36 | - 浏览器: [ECMAScript 兼容列表](http://kangax.github.io/compat-table/es6/) 37 | 38 | - Node.js:[ES2015 Support](http://node.green) 39 | 40 | - 性能:[Performance of ES6 features relative to the ES5 baseline operations per second](https://kpdecker.github.io/six-speed/) 41 | 42 | - 工具:使用一些转换工具,可以把 ES6 => ES5 43 | 44 | 45 | * 为什么要学习新语法? 46 | 47 | 当前很多库、框架、工具都在使用 **ES6+** 进行开发,典型的就是 React 和 Vue,使用新语法特性的优势进行快速开发,然后使用转换构建工具部署生产代码。 48 | 49 | 50 | ## ES6 新特性 51 | 52 | 53 | ### Arrows and Lexical This 54 | 55 | 「箭头」函数(`=>`)和 `this`: 56 | 57 | > 使用「箭头」函数我们可以体验函数式编程的”美”,高效、简洁,当然也要注意上下文 `this`。 58 | 59 | * e.g. 60 | 61 | ```js 62 | // old 63 | var sum = function (a, b) { return a + b } 64 | ``` 65 | 66 | ```js 67 | // new 68 | var sum = (a, b) => a + b 69 | ``` 70 | 71 | * 猜猜猜 72 | 73 | 0. *a.js* 74 | 75 | ```js 76 | var PI = 3.14 77 | 78 | var c = r => 2 * PI * r 79 | 80 | // c(2) = ? 81 | ``` 82 | 83 | 0. *b.js* 84 | 85 | ```js 86 | var PI = 3.14 87 | 88 | var circle = { 89 | PI: 3.14159, 90 | c: r => 2 * this.PI * r 91 | } 92 | 93 | // circle.c(2) = ? 94 | ``` 95 | 96 | 0. *c.js* 97 | 98 | ```js 99 | var PI = 3.14 100 | 101 | var circle = { 102 | PI: 3.14159, 103 | c (r) { 104 | return 2 * this.PI * r 105 | } 106 | } 107 | 108 | // circle.c(2) = ? 109 | ``` 110 | 111 | 112 | ### Classes 113 | 114 | 类: 115 | 116 | > 基于原型链的语法糖,简单、清晰;面向对象编程更加轻松。 117 | > 再也不会被其他语言吐槽了! 118 | 119 | * e.g. 120 | 121 | ```js 122 | // old 123 | function Cat () { 124 | this.voice = 'miao' 125 | } 126 | Cat.prototype.speak = function () { 127 | console.log(this.voice) 128 | } 129 | 130 | function Lion () { 131 | this.voice = 'roar' 132 | } 133 | 134 | Lion.prototype = Cat.prototype 135 | 136 | var c = new Cat() 137 | var l = new Lion() 138 | c.speak() 139 | l.speak() 140 | ``` 141 | 142 | ```js 143 | // new 144 | class Cat { 145 | constructor () { 146 | this.voice = 'miao' 147 | } 148 | 149 | speak () { 150 | console.log(this.voice) 151 | } 152 | } 153 | 154 | class Lion extends Cat { 155 | constructor () { 156 | super() 157 | 158 | this.voice = 'roar' 159 | } 160 | } 161 | var c = new Cat() 162 | var l = new Lion() 163 | c.speak() 164 | l.speak() 165 | ``` 166 | 167 | * 猜猜猜 168 | 169 | 0. *cat.js* 170 | 171 | ```js 172 | class Cat { 173 | constructor () { 174 | this.voice = 'miao' 175 | } 176 | 177 | speak () { 178 | console.log(this.voice) 179 | } 180 | 181 | static type () { 182 | return Cat.name.toLowercase() 183 | } 184 | } 185 | 186 | // Cat.prototype= ? 187 | ``` 188 | 189 | 0. *getters-setters.js* 190 | 191 | ```js 192 | class Cat { 193 | constructor (options) { 194 | this.voice = 'miao' 195 | this.options = options || {} 196 | } 197 | 198 | speak () { 199 | console.log(this.voice) 200 | } 201 | 202 | get name () { 203 | return this.options.name 204 | } 205 | 206 | set name (name) { 207 | this.options.name = name 208 | } 209 | } 210 | 211 | var a = new Cat({ name: 'Garfield' }) 212 | // a.name ? 213 | // a.name = 'Tom' 214 | // a.name ? 215 | ``` 216 | 217 | 0. *[mixins.js](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)* 218 | 219 | ```js 220 | var CalculatorMixin = Base => class extends Base { 221 | calc() { } 222 | } 223 | 224 | var RandomizerMixin = Base => class extends Base { 225 | randomize() { } 226 | } 227 | 228 | class Foo { } 229 | class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { } 230 | 231 | // Bar.prototype ? 232 | ``` 233 | 234 | 235 | ### Enhanced Object Literals 236 | 237 | 改进对象声明: 238 | 239 | > 大大减少了代码量,创建对象更加简洁。 240 | 241 | - 属性缩写 242 | 243 | - 函数缩写 244 | 245 | - 属性名计算 246 | 247 | * e.g. 248 | 249 | ```js 250 | // old 251 | var a = 1 252 | var b = 2 253 | var c = 3 254 | 255 | var o = { 256 | a: a, 257 | b: b 258 | c: c, 259 | d: function () { 260 | return this.a + this.b + this.c 261 | } 262 | } 263 | ``` 264 | 265 | ```js 266 | // new 267 | var o = { 268 | a, 269 | b, 270 | c, 271 | d () { 272 | return this.a + this.b + this.c 273 | } 274 | } 275 | ``` 276 | 277 | * 猜猜猜 278 | 279 | 0. *returns.js* 280 | 281 | ```js 282 | var generate = (name, age) => ({ name, age }) 283 | 284 | // generate('github', 5) ? 285 | ``` 286 | 287 | 0. *cumputed-properties.js* 288 | 289 | ```js 290 | var create = (path, verb) => { 291 | return { 292 | path, 293 | verb, 294 | ['is' + verb[0].toUpperCase() + verb.substring(1)]: true 295 | } 296 | } 297 | 298 | // create('/', 'get') ? 299 | ``` 300 | 301 | 0. *complicated.js* 302 | 303 | ```js 304 | var path = '/' 305 | var verb = 'get' 306 | var root = { 307 | path, 308 | verb 309 | } 310 | 311 | var route = { 312 | root 313 | } 314 | ``` 315 | 316 | 317 | ### Template Strings 318 | 319 | 模板字符串: 320 | 321 | > 终于可以舒服的写多行字符串了,这功能等到花儿都谢了! 322 | 323 | - 支持多行 324 | 325 | - 支持变量绑定 326 | 327 | - 也支持对字符串不转义,不解析 328 | 329 | * e.g. 330 | 331 | ```js 332 | // old 333 | var first = 1 334 | var second = 2 335 | var third = 3 336 | 337 | var str = 'No.' + first + '\n' + 338 | 'No.' + second +'\n' + 339 | 'No.' + third 340 | ``` 341 | 342 | ```js 343 | // new 344 | var first = 1 345 | var second = 2 346 | var third = 3 347 | 348 | var str = `No. ${first} 349 | No. ${second} 350 | No. ${third} 351 | ` 352 | ``` 353 | 354 | * 猜猜猜 355 | 356 | 0. *raw-tag.js* 357 | 358 | ```js 359 | var t0 = `In ES5 "\n" is a line-feed.` 360 | var t1 = String.raw`In ES5 "\n" is a line-feed.` 361 | 362 | // console.log(t0) 363 | // console.log(t1) 364 | ``` 365 | 366 | 0. *expression.js* 367 | 368 | ```js 369 | var a = 5 370 | var b = 10 371 | 372 | // console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + ".") 373 | // console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`) 374 | ``` 375 | 376 | 0. *custom-tag.js* 377 | 378 | ```js 379 | var generatePath = (strings, ...values) => { 380 | return strings[0] + values.reduce((prev, curr) => `${prev}/${curr}`, '') 381 | } 382 | 383 | var user = 'user' 384 | var id = '233' 385 | var profile = 'profile' 386 | // generatePath`GET: ${user}${id}${profile}` 387 | ``` 388 | 389 | 390 | ### Destructuring 391 | 392 | 解析赋值 393 | 394 | > 可以轻松获取对象、数组等的元素,并赋值到指定变量 395 | 396 | - Array ArrayLike Object 等,具有迭代器接口的对象 397 | 398 | * e.g. 399 | 400 | ```js 401 | // old 402 | var arr = [1, 2, 3, 4] 403 | 404 | var a0 = arr[0] 405 | var a1 = arr[1] 406 | var a2 = arr[2] 407 | 408 | var obj = { 409 | name: 'github', 410 | age: 5 411 | } 412 | 413 | var name = obj.name 414 | var age = obj.age 415 | ``` 416 | 417 | ```js 418 | // new 419 | var arr = [1, 2, 3, 4] 420 | 421 | var [a0, a1, a2] = arr 422 | 423 | var obj = { 424 | name: 'github', 425 | age: 5 426 | } 427 | 428 | var { name, age } = obj 429 | ``` 430 | 431 | * 猜猜猜 432 | 433 | 0. *print.js* 434 | 435 | ```js 436 | var print = ({ name, age }) => console.log(name, age) 437 | 438 | // print({ name: 'ES6', age: 2015 }) ? 439 | ``` 440 | 441 | 0. *alias.js* 442 | 443 | ```js 444 | var js = { name: 'ES6', age: 2015 } 445 | 446 | var { name: es, age } = js 447 | // name, es, age? 448 | ``` 449 | 450 | 0. *defaults.js* 451 | 452 | ```js 453 | var js = { name: 'ES6', age: 2015 } 454 | var date = [2015, 9, 14] 455 | 456 | var { version = '6' } = js 457 | // version ? 458 | 459 | var { fullname: f = 'ECMAScript 6' } = js 460 | // fullname, f ? 461 | 462 | var [y, m, d, h = 9] = date 463 | // y, m, d, h ? 464 | ``` 465 | 466 | 467 | ### Default + Rest + Spread 468 | 469 | 默认值、余下参数(Rest),数组展开(Spread) 470 | 471 | - 默认值: 减少了对输入参数的检查的代码量,即可读又简洁 472 | 473 | - Rest:对参数数组操作更加灵活 474 | 475 | - Spread:可以看作是 Rest 的反操作,更加方便对数组的操作 476 | 477 | * e.g. 478 | 479 | ```js 480 | // old 481 | function bar (a) { 482 | a = a || 5 483 | } 484 | 485 | function sum (a) { 486 | a = a || 5 487 | var l = arguments.length 488 | var i = 1 489 | for (; i < l; ++i) { 490 | a += arguments[i] 491 | } 492 | return a 493 | } 494 | 495 | function apply () { 496 | function fn () {} 497 | var l = arguments.length 498 | var args = new Array(l) 499 | for (var i = 0; i < l; ++i) { 500 | args[i] = arguments[i] 501 | } 502 | fn.apply(null, args) 503 | } 504 | ``` 505 | 506 | ```js 507 | // new 508 | function bar(a = 5) { 509 | } 510 | 511 | function sum (a = 5, ...args) { 512 | var l = args.length 513 | var i = 0 514 | for (; i < l; ++i) { 515 | a += args[i] 516 | } 517 | return a 518 | } 519 | 520 | function apply (...args) { 521 | function fn () {} 522 | fn.apply(null, args) 523 | } 524 | ``` 525 | 526 | * 猜猜猜 527 | 528 | 0. *string.js* 529 | 530 | ```js 531 | var str = '1234567890' 532 | 533 | // [...str] ? 534 | ``` 535 | 536 | 0. *concat.js* 537 | 538 | ```js 539 | var a = [1, 2, 3] 540 | var b = [6, 5, 4] 541 | 542 | var c = [...a, ...b] 543 | // c ? 544 | ``` 545 | 546 | 0. *parse-args.js* 547 | 548 | ```js 549 | /** 550 | * 解析参数,返回特定格式 551 | * 552 | * @return {Array} [arr, options, cb] 553 | */ 554 | 555 | function parseArgs (...args) { 556 | const last = args[args.length - 1] 557 | const type = typeof last 558 | let opts 559 | let cb 560 | 561 | if ('function' === type) { 562 | cb = args.pop() 563 | if ('object' === typeof args[args.length - 1]) { 564 | opts = args.pop() 565 | } 566 | } else if ('object' === type && !Array.isArray(last)) { 567 | opts = args.pop() 568 | } else if ('undefined' === type) { 569 | args.pop() 570 | return parseArgs(...args) 571 | } 572 | 573 | if (Array.isArray(args[0])) { 574 | args = args[0] 575 | } 576 | return [args, opts || {}, cb] 577 | } 578 | 579 | // parseArgs('users') ? 580 | // parseArgs('users', {}) ? 581 | // parseArgs('users', () => {}) ? 582 | // parseArgs('users', {}, () => {}) ? 583 | // parseArgs('users', 'books') ? 584 | // parseArgs(['users', 'books']) ? 585 | ``` 586 | 587 | 588 | ### Let + Const 589 | 590 | 变量、常量定义声明: 591 | 592 | > 当满世界都是 `var` 的时候,变量管理是个神坑! 593 | 594 | - 块级作用域 595 | 596 | - const: 一次性声明 597 | 598 | * e.g. 599 | 600 | ```js 601 | // old 602 | // 函数作用域下覆盖全局作用域 603 | var bar = 1 604 | var bar = 3 605 | function method () { 606 | console.log(bar) // undefined 607 | var bar = 2 608 | } 609 | 610 | // 变量泄漏 611 | var s = 'hello'; 612 | for (var i = 0; i < s.length; i++) { 613 | console.log(s[i]); 614 | } 615 | console.log(i); // 5 616 | ``` 617 | 618 | ```js 619 | // new 620 | let bar0 = 1 621 | let bar1 = 3 622 | 623 | function method () { 624 | console.log(bar0) 625 | let bar3 = 2 626 | } 627 | 628 | var s = 'hello'; 629 | for (let i = 0; i < s.length; i++) { 630 | console.log(s[i]); 631 | } 632 | ``` 633 | 634 | * 猜猜猜 635 | 636 | 0. *global.js* 637 | 638 | ```js 639 | var a = 1 640 | let b = 2 641 | const c = 3 642 | 643 | // this.a ? 644 | // this.b ? 645 | // this.c ? 646 | ``` 647 | 648 | 0. *for.js* 649 | 650 | ```js 651 | var s = 'hello'; 652 | for (let i = 0; i < s.length; i++) { 653 | console.log(s[i]); 654 | } 655 | console.log(i); // ? 656 | ``` 657 | 658 | 659 | ### Iterators + For..Of 660 | 661 | 迭代器和 `for..of` 662 | 663 | > 像 `[...arr]` 就是迭代器一个很好的例子。 664 | 665 | - 可迭代协议:ES6 定义了一套统一的标准,允许对 JavaScript 对象自定义它们的迭代行为。 666 | 667 | - 内置可迭代类型有 String,Array,TypedArray,Map,Set,因为在它们的原型对象上已经有了 `[Symbol.iterator]` 方法。 668 | 669 | * e.g. 670 | 671 | ```js 672 | // old 673 | var arr = [1, 2, 3] 674 | 675 | for (let i in arr) { 676 | console.log(i) 677 | } 678 | ``` 679 | 680 | ```js 681 | // new 682 | var arr = [1, 2, 3] 683 | 684 | for (let i of arr) { 685 | console.log(i) 686 | } 687 | ``` 688 | 689 | * 猜猜猜 690 | 691 | 0. *for-loops.js* 692 | 693 | ```js 694 | Array.prototype.arrCustom = function () {} 695 | var arr = [1, 2, 3] 696 | arr.isArray = true 697 | 698 | for (let i in arr) { 699 | console.log(i) // ? 700 | } 701 | 702 | for (let i of arr) { 703 | console.log(i) // ? 704 | } 705 | ``` 706 | 707 | 0. *iterable.js* 708 | 709 | ```js 710 | var iterable = { 711 | [Symbol.iterator]() { 712 | return { 713 | i: 0, 714 | next () { 715 | return { 716 | done: this.i === 10, 717 | value: this.i++ 718 | } 719 | } 720 | } 721 | } 722 | } 723 | 724 | for (const i of iterable) { 725 | console.log(i) // ? 726 | } 727 | 728 | // [...iterable] ? 729 | ``` 730 | 731 | 0. *iterator.js* 732 | 733 | ```js 734 | var iterable = { 735 | [Symbol.iterator]() { 736 | return { 737 | i: 0, 738 | next () { 739 | const done = this.i === 10 740 | const value = done ? undefined : this.i++ 741 | return { done, value } 742 | } 743 | } 744 | } 745 | } 746 | 747 | const iterator = iterable[Symbol.iterator]() 748 | 749 | iterator.next() // ? 750 | iterator.next() // ? 751 | iterator.next() // ? 752 | // ... 753 | 754 | const iterator2 = iterable[Symbol.iterator]() 755 | 756 | iterator2.next() // ? 757 | iterator2.next() // ? 758 | iterator2.next() // ? 759 | // ... 760 | ``` 761 | 762 | 763 | ### Generators 764 | 765 | 生成器: 766 | 767 | > 生成器大杀器! 768 | 769 | - [可迭代](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable) 770 | 771 | - [遵循迭代器协议](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator) 772 | 773 | - 可在单个函数内 `GeneratorFunction` 自定义迭代逻辑,可以替代迭代器,功能更强大 774 | 775 | - 可中断 776 | 777 | - 可赋值 778 | 779 | * e.g. 780 | 781 | ```js 782 | // old 783 | var iterable = { 784 | [Symbol.iterator]() { 785 | return { 786 | i: 0, 787 | next () { 788 | const done = this.i === 10 789 | const value = done ? undefined : this.i++ 790 | return { done, value } 791 | } 792 | } 793 | } 794 | } 795 | const iterator = iterable[Symbol.iterator]() 796 | ``` 797 | 798 | ```js 799 | // new 800 | function* generatable () { 801 | for (let i = 0, l = 10; i < l; ++i) { 802 | yield i 803 | } 804 | } 805 | const iterator = generatable() 806 | ``` 807 | 808 | * 猜猜猜 809 | 810 | 0. *generatable.js* 811 | 812 | ```js 813 | function* generatable () { 814 | for (let i = 0, l = 10; i < l; ++i) { 815 | yield i 816 | } 817 | } 818 | const iterator = generatable() 819 | 820 | for (const i of generatable()) { 821 | console.log(i) // ? 822 | } 823 | // [...generatable()] ? 824 | ``` 825 | 826 | 0. *next.js* 827 | 828 | ```js 829 | function* range(min = 0, max = 10, step = 1) { 830 | for (; min < max; min += step) { 831 | let rest = yield min 832 | if (rest) min = step * -1 833 | } 834 | } 835 | 836 | const iterator = range() 837 | 838 | iterator.next() // ? 839 | iterator.next() // ? 840 | iterator.next() // ? 841 | iterator.next(true) // ? 842 | iterator.next() // ? 843 | iterator.next() // ? 844 | iterator.next() // ? 845 | 846 | const iterator2 = range(0, 100, 2) 847 | 848 | [...iterator2] // ? 849 | iterator.next() // ? 850 | ``` 851 | 852 | 0. *return.js* 853 | 854 | ```js 855 | function* range(min = 0, max = 10, step = 1) { 856 | for (; min < max; min += step) { 857 | let rest = yield min 858 | if (rest) min = step * -1 859 | } 860 | } 861 | 862 | const iterator = range() 863 | iterator.next() 864 | iterator.next(true) 865 | iterator.next() 866 | iterator.return() // ? 867 | iterator.next() // ? 868 | iterator.return(1) // ? 869 | iterator.next() // ? 870 | ``` 871 | 872 | 0. *yield.js* 873 | 874 | ```js 875 | function* g() { 876 | yield 1 877 | yield 2 878 | yield 3 879 | yield* [4, 5, 6] 880 | yield* 'Hello World!' 881 | yield 7 882 | } 883 | 884 | [...g()] // ? 885 | ``` 886 | 887 | 888 | ### Unicode 889 | 890 | Unicode 891 | 892 | - 加强对 Unicode 的支持,并且扩展了字符串对象 893 | 894 | * e.g. 895 | 896 | ```js 897 | // same as ES5.1 898 | "𠮷".length == 2 899 | 900 | // new RegExp behaviour, opt-in ‘u’ 901 | "𠮷".match(/./u)[0].length == 2 902 | 903 | // new form 904 | "\u{20BB7}" == "𠮷" == "\uD842\uDFB7" 905 | 906 | // new String ops 907 | "𠮷".codePointAt(0) == 0x20BB7 908 | 909 | // for-of iterates code points 910 | for(var c of "𠮷") { 911 | console.log(c); 912 | } 913 | ``` 914 | 915 | 916 | ### Modules ? 917 | 918 | 模块化系统目前还未实现! 919 | 920 | 921 | ### Subclassable Built-ins 922 | 923 | 子类可继承自内置数据类型 924 | 925 | > 真的太方便了,比如想对 Array 进行扩展,现在无需修改 `Array.prototype`,`extends Array` 就可以了。 926 | 927 | - Array Boolean String Number Map Set Error RegExp Function Promise 928 | 929 | * e.g. 930 | 931 | ```js 932 | // old 933 | // This is danger. 934 | Array.prototype.sum = function () { 935 | return this.reduce((t, curr) => t + curr, 0) 936 | } 937 | 938 | var a = [1, 2, 3] 939 | a.sum() 940 | ``` 941 | 942 | ```js 943 | // new 944 | class CustomArray extends Array { 945 | constructor (...args) { 946 | super(...args) 947 | } 948 | 949 | sum () { 950 | return this.reduce((t, curr) => t + curr, 0) 951 | } 952 | } 953 | 954 | var a = CustomArray.from([1, 2, 3]) 955 | a.sum() 956 | ``` 957 | 958 | * 猜猜猜 959 | 960 | 0. *[middleware.js](https://github.com/trekjs/middleware)* 961 | 962 | ```js 963 | const SYMBOL_ITERATOR = Symbol.iterator 964 | 965 | class Middleware extends Array { 966 | 967 | [SYMBOL_ITERATOR] () { 968 | return this 969 | } 970 | 971 | next (i = 0, context, nextFunc) { 972 | const fn = this[i] || nextFunc 973 | 974 | return { 975 | done: i === this.length, 976 | value: fn && fn(context, () => { 977 | return this.next(i + 1, context, nextFunc).value 978 | }) 979 | } 980 | } 981 | 982 | compose (context, nextFunc) { 983 | return this[SYMBOL_ITERATOR]().next(0, context, nextFunc).value 984 | } 985 | 986 | } 987 | 988 | const middleware = new Middleware() 989 | 990 | middleware.push((ctx, next) => { 991 | ctx.arr.push(1) 992 | next() 993 | ctx.arr.push(6) 994 | }) 995 | 996 | middleware.push((ctx, next) => { 997 | ctx.arr.push(2) 998 | next() 999 | ctx.arr.push(5) 1000 | }) 1001 | 1002 | middleware.push((ctx, next) => { 1003 | ctx.arr.push(3) 1004 | next() 1005 | ctx.arr.push(4) 1006 | }) 1007 | 1008 | const ctx = { arr: [] } 1009 | middleware.compose(ctx) 1010 | console.log(ctx.arr) // ? 1011 | ``` 1012 | 1013 | 1014 | ### Map + Set + WeakMap + WeakSet 1015 | 1016 | 新增 `Map` `Set` `WeakMap` `WeakSet` 几种高效的数据类型 1017 | 1018 | * e.g. 1019 | 1020 | ```js 1021 | // Sets 1022 | var s = new Set(); 1023 | s.add("hello").add("goodbye").add("hello"); 1024 | s.size === 2; 1025 | s.has("hello") === true; 1026 | 1027 | // Maps 1028 | var m = new Map(); 1029 | m.set("hello", 42); 1030 | m.set(s, 34); 1031 | m.get(s) == 34; 1032 | 1033 | // Weak Maps 1034 | var wm = new WeakMap(); 1035 | wm.set(s, { extra: 42 }); 1036 | wm.size === undefined 1037 | 1038 | // Weak Sets 1039 | var ws = new WeakSet(); 1040 | ws.add({ data: 42 }); 1041 | // Because the added object has no other references, it will not be held in the set 1042 | ``` 1043 | 1044 | 1045 | ### Proxies 1046 | 1047 | > 当我们不想把对象暴露出来,不想直接操作它们,想增加一层校验时,`Proxies` 是一个最佳方案。 1048 | > 但当增加了 `Proxies` 这一层,对性能还是会有影响的。 1049 | 1050 | * e.g. 1051 | 1052 | ```js 1053 | // old 1054 | const inner = { 1055 | name: 'ES6' 1056 | } 1057 | 1058 | var outer = { 1059 | inner, 1060 | get name () { 1061 | return this.inner.name 1062 | }, 1063 | set name (name) { 1064 | this.inner.name = name 1065 | } 1066 | } 1067 | 1068 | // outer.name 1069 | ``` 1070 | 1071 | ```js 1072 | // new 1073 | const inner = { 1074 | name: 'ES6' 1075 | } 1076 | 1077 | var p = new Proxy(inner, { 1078 | get (target, name) { 1079 | return target[name] 1080 | }, 1081 | 1082 | set (target, name, value) { 1083 | if ('string' !== typeof value) throw new TypeError('value must be String!') 1084 | target[name] = value 1085 | } 1086 | }) 1087 | 1088 | p.name 1089 | p.name = 2 1090 | p.name = 'ES2015' 1091 | ``` 1092 | 1093 | * 猜猜猜 1094 | 1095 | 0. *[delegate-proxy.js](https://github.com/fundon/delegate-proxy)* 1096 | 1097 | ```js 1098 | function delegateProxy (target, origin) { 1099 | return new Proxy(target, { 1100 | get (target, key, receiver) { 1101 | if (key in target) return Reflect.get(target, key, receiver) 1102 | const value = origin[key] 1103 | return 'function' === typeof value ? function method () { 1104 | return value.apply(origin, arguments) 1105 | } : value 1106 | }, 1107 | set (target, key, value, receiver) { 1108 | if (key in target) return Reflect.set(target, key, value, receiver) 1109 | origin[key] = value 1110 | return true 1111 | } 1112 | }) 1113 | } 1114 | 1115 | const bar = { 1116 | n: 1, 1117 | 1118 | add (i) { 1119 | this.n += i 1120 | } 1121 | } 1122 | 1123 | const foo = { 1124 | 1125 | set (n) { 1126 | this.n = n | 0 1127 | }, 1128 | 1129 | sub (i) { 1130 | this.n -= i 1131 | } 1132 | 1133 | } 1134 | 1135 | const p = delegateProxy(foo, bar) 1136 | 1137 | bar 1138 | foo 1139 | p 1140 | 1141 | p.n // ? 1142 | p.add(1) 1143 | p.n // ? 1144 | 1145 | p.sub(2) 1146 | p.n // ? 1147 | 1148 | p.set(1) 1149 | p.n // ? 1150 | 1151 | p.n = 233 1152 | p.n // ? 1153 | ``` 1154 | 1155 | 1156 | ### [Symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) 1157 | 1158 | 符号: 1159 | 1160 | - 唯一性 1161 | 1162 | - 不可变 1163 | 1164 | - 不列入对象的 `Object.getOwnPropertyNames(obj)` 和 `Object.keys(obj)` 1165 | 1166 | - 安全(在一些场景下可以作为私有属性的 `key`) 1167 | 1168 | > 想给对象打一个暗号,再也不难了! 1169 | 1170 | * e.g. 1171 | 1172 | ```js 1173 | // old 1174 | let obj = { 1175 | id: 1 1176 | } 1177 | 1178 | obj.id // 1 1179 | obj.id = 2 1180 | obj.id // 2 1181 | ``` 1182 | 1183 | ```js 1184 | // new 1185 | let obj = { 1186 | [Symbol('id')]: 1 1187 | } 1188 | 1189 | obj[Symbol('id')] // undefined 1190 | obj[Symbol('id')] = 2 1191 | obj[Symbol('id')] // undefined 1192 | 1193 | for (const k of Object.getOwnPropertySymbols(obj)) { 1194 | console.log(obj[k]) 1195 | } 1196 | ``` 1197 | 1198 | 1199 | ### Math + Number + String + Array + Object APIs 1200 | 1201 | 新增 APIs,数据操作更加方便。 1202 | 1203 | * e.g. 1204 | 1205 | ```js 1206 | Number.EPSILON 1207 | Number.isInteger(Infinity) // false 1208 | Number.isNaN("NaN") // false 1209 | 1210 | Math.acosh(3) // 1.762747174039086 1211 | Math.hypot(3, 4) // 5 1212 | Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2 1213 | 1214 | "abcde".includes("cd") // true 1215 | "abc".repeat(3) // "abcabcabc" 1216 | 1217 | Array.from(document.querySelectorAll("*")) // Returns a real Array 1218 | Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior 1219 | [0, 0, 0].fill(7, 1) // [0,7,7] 1220 | [1,2,3].findIndex(x => x == 2) // 1 1221 | ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"] 1222 | ["a", "b", "c"].keys() // iterator 0, 1, 2 1223 | ["a", "b", "c"].values() // iterator "a", "b", "c" 1224 | 1225 | Object.assign(Point, { origin: new Point(0,0) }) 1226 | ``` 1227 | 1228 | 1229 | ### Binary and Octal Literals 1230 | 1231 | 二进制 `b`,八进制 `o` 字面量 1232 | 1233 | * e.g. 1234 | 1235 | ```js 1236 | 0b111110111 === 503 // true 1237 | 0o767 === 503 // true 1238 | 0x1f7 === 503 // true 1239 | ``` 1240 | 1241 | 1242 | ### Promises 1243 | 1244 | Promises:更加优雅的异步编程方式。想更加清晰了解 `Promise` 的执行过程,可以看这个可视化工具 [promisees](https://github.com/bevacqua/promisees)。 1245 | 1246 | > 面对异步编程,`callback-hell` 就是 JavaScript 给人的最大诟病! 1247 | 1248 | * e.g. 1249 | 1250 | ```js 1251 | // old 1252 | getProfileById(233, (err, res) => { 1253 | if (err) throw err 1254 | getFollowing(233, (err, following) => { 1255 | if (err) throw err 1256 | getFollowers(233, (err, followers) => { 1257 | if (err) throw err 1258 | getStarred(233, (err, starred) => { 1259 | if (err) throw err 1260 | // .... 1261 | }) 1262 | }) 1263 | }) 1264 | }) 1265 | ``` 1266 | 1267 | ```js 1268 | // new 1269 | getProfileById(233) 1270 | .then(res => getFollowing(233)) 1271 | .then(res => getFollowers(233)) 1272 | .then(res => getStarred(233)) 1273 | .catch(err => console.log(err)) 1274 | // ... 1275 | ``` 1276 | 1277 | * 猜猜猜 1278 | 1279 | 0. *simple-promise.js* 1280 | 1281 | ```js 1282 | function loadImage (url) { 1283 | return new Promise((resolve, reject) => { 1284 | const img = new Image() 1285 | 1286 | img.onload = function () { 1287 | resolve(img) 1288 | } 1289 | 1290 | img.onerror = function () { 1291 | reject(new Error('Could not load image at ' + url)) 1292 | } 1293 | 1294 | img.url = url 1295 | }) 1296 | } 1297 | 1298 | loadImage('https://nodejs.org/static/images/logo-header.png') 1299 | .then(img => document.body.appendChild(img)) 1300 | .catch(err => console.log(err)) 1301 | ``` 1302 | 1303 | 0. *all.js* 1304 | 1305 | ```js 1306 | function delay(value, duration = 0) { 1307 | return new Promise((resolve, reject) => { 1308 | setTimeout(() => resolve(value), duration) 1309 | }) 1310 | } 1311 | 1312 | let res = Promise.all([ 1313 | delay(10, 1), 1314 | delay(8, 2), 1315 | delay(6, 3), 1316 | delay(4, 4), 1317 | delay(2, 5), 1318 | delay(0, 6), 1319 | ]) 1320 | 1321 | res.then(arr => { 1322 | console.log(arr) // ? 1323 | }) 1324 | ``` 1325 | 1326 | 0. *race.js* 1327 | 1328 | ```js 1329 | function delay(value, duration = 0) { 1330 | return new Promise((resolve, reject) => { 1331 | setTimeout(() => resolve(value), duration) 1332 | }) 1333 | } 1334 | 1335 | let res = Promise.race([ 1336 | delay(10, 1), 1337 | delay(8, 2), 1338 | delay(6, 3), 1339 | delay(4, 4), 1340 | delay(2, 5), 1341 | delay(0, 6), 1342 | ]) 1343 | 1344 | res.then(arr => { 1345 | console.log(arr) // ? 1346 | }) 1347 | ``` 1348 | 1349 | 0. *reduce.js* 1350 | 1351 | ```js 1352 | const reduce = (arr, cb, initialValue = 0) => { 1353 | return arr.reduce(cb, Promise.resolve(initialValue)) 1354 | } 1355 | 1356 | const cb = (prev, curr) => prev.then(v => v + curr) 1357 | 1358 | reduce([1, 2, 3, 4, 5, 6, 7, 8, 9], cb) 1359 | .then(res => { 1360 | console.log(res) // ? 1361 | }) 1362 | ``` 1363 | 1364 | 1365 | ### [Reflect API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 1366 | 1367 | 反射 API:公开了对象的元操作,效果跟 Proxy API 相反 1368 | 1369 | * e.g. 1370 | 1371 | ```js 1372 | var O = {a: 1}; 1373 | Object.defineProperty(O, 'b', {value: 2}); 1374 | O[Symbol('c')] = 3; 1375 | 1376 | Reflect.ownKeys(O); // ['a', 'b', Symbol(c)] 1377 | 1378 | function C(a, b){ 1379 | this.c = a + b; 1380 | } 1381 | var instance = Reflect.construct(C, [20, 22]); 1382 | instance.c; // 42 1383 | ``` 1384 | 1385 | 1386 | ### Tail Calls 1387 | 1388 | 优化了尾递归算法,保证栈不会无限增长,使得尾递归算法安全。 1389 | 1390 | 1391 | ## 快速体验 1392 | 1393 | > 对以上新特性,快速体验一番,环境包括 浏览器 和 Node.js 1394 | 1395 | 1396 | ## 高级应用 1397 | 1398 | > 深入学习特性,应用生产 1399 | 1400 | * http://ramdajs.com/ 1401 | 1402 | * https://github.com/cujojs/most 1403 | 1404 | * https://lodash.com/ 1405 | 1406 | * `fs.readdir` 问题 1407 | 1408 | - https://github.com/nodejs/node/issues/583 1409 | 1410 | - https://github.com/w3c/filesystem-api 1411 | 1412 | * https://github.com/kriskowal/gtor/#asynchronous-generator-functions 1413 | 1414 | 1415 | ## 兼容,代码转换 1416 | 1417 | > 使用转换工具,对 ES6+ 的代码进行转换,适配浏览器或者 Node < v6 1418 | 1419 | 1420 | ## 其他 1421 | 1422 | * http://es6.ruanyifeng.com 1423 | 1424 | * https://github.com/lukehoban/es6features 1425 | 1426 | * https://babeljs.io/docs/learn-es2015/ 1427 | 1428 | * https://ponyfoo.com/articles/tagged/es6-in-depth 1429 | 1430 | * https://github.com/bevacqua/es6 1431 | 1432 | * https://github.com/DrkSephy/es6-cheatsheet 1433 | 1434 | * https://github.com/ericdouglas/ES6-Learning 1435 | 1436 | * https://github.com/addyosmani/es6-tools 1437 | 1438 | * https://github.com/bevacqua/promisees 1439 | 1440 | 1441 | ## License 1442 | 1443 | 授权:[署名-非商业性使用](https://creativecommons.org/licenses/by-nc/4.0/) 1444 | 1445 | --- 1446 | 1447 | > [fundon.me](https://fundon.me)  ·  1448 | > GitHub [@fundon](https://github.com/fundon)  ·  1449 | > Twitter [@_fundon](https://twitter.com/_fundon) 1450 | 1451 | [ES5]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_5_support_in_Mozilla 1452 | 1453 | [ES6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla 1454 | 1455 | [ES7 和 ES8]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_Next_support_in_Mozilla 1456 | 1457 | [ES7]: http://www.ecma-international.org/ecma-262/7.0/index.html 1458 | -------------------------------------------------------------------------------- /images/github-search-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fundon/ES6-QuickStart-and-Tips/a13ad0c995eec34e8ba3222a1641d4c25a2060ca/images/github-search-results.png -------------------------------------------------------------------------------- /src/classes.js: -------------------------------------------------------------------------------- 1 | var CalculatorMixin = Base => class extends Base { 2 | calc() { } 3 | }; 4 | 5 | var RandomizerMixin = Base => class extends Base { 6 | randomize() { } 7 | }; 8 | 9 | class Foo { } 10 | class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { } 11 | 12 | console.log(Bar.prototype.calc) 13 | console.log(Bar.prototype.randomize) 14 | --------------------------------------------------------------------------------