├── configs └── .jshintrc └── README.md /configs/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, // Require {} for every new block or scope 3 | "eqeqeq": true, // Require triple equals (===) for comparison 4 | "immed": true, // Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 5 | "latedef": true, // Require variables/functions to be defined before being used 6 | "newcap": true, // Require capitalization of all constructor functions e.g. `new F()` 7 | "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee` 8 | "undef": true, // Require all non-global variables to be declared (prevents global leaks) 9 | "unused": true, // Warns when you define and never use your variables 10 | "node": true , // Node.js 11 | "bitwise": true, // Prohibits the use of bitwise operators such as ^ (XOR), | (OR) 12 | "indent": 2, // Enforces specific tab width for your code 13 | "noempty": true, // Warns when you have an empty block in your code 14 | "quotmark": "single", // Enforces the consistency of quotation marks 15 | "strict": false, // Requires all functions to run in ECMAScript 5's strict mode 16 | "trailing": true, // Makes it an error to leave a trailing whitespace in your code 17 | "forin": true, // Requires all for in loops to filter object's items 18 | "camelcase": "true" // Force all variable names to use either camelCase style or UPPER_CASE with underscores 19 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js 风格指南 2 | 3 | 4 | 5 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 6 | 7 | - [参考资料](#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99) 8 | - [类型](#%E7%B1%BB%E5%9E%8B) 9 | - [对象](#%E5%AF%B9%E8%B1%A1) 10 | - [数组](#%E6%95%B0%E7%BB%84) 11 | - [字符串](#%E5%AD%97%E7%AC%A6%E4%B8%B2) 12 | - [函数](#%E5%87%BD%E6%95%B0) 13 | - [属性](#%E5%B1%9E%E6%80%A7) 14 | - [变量](#%E5%8F%98%E9%87%8F) 15 | - [Requires](#requires) 16 | - [回调函数](#%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0) 17 | - [Try catch](#try-catch) 18 | - [提升](#%E6%8F%90%E5%8D%87) 19 | - [条件表达式 & 相等性](#%E6%9D%A1%E4%BB%B6%E8%A1%A8%E8%BE%BE%E5%BC%8F-&-%E7%9B%B8%E7%AD%89%E6%80%A7) 20 | - [代码块](#%E4%BB%A3%E7%A0%81%E5%9D%97) 21 | - [注释](#%E6%B3%A8%E9%87%8A) 22 | - [空格](#%E7%A9%BA%E6%A0%BC) 23 | - [逗号](#%E9%80%97%E5%8F%B7) 24 | - [分号作为语句块的结束](#%E5%88%86%E5%8F%B7%E4%BD%9C%E4%B8%BA%E8%AF%AD%E5%8F%A5%E5%9D%97%E7%9A%84%E7%BB%93%E6%9D%9F) 25 | - [类型转换 & 强制类型转换](#%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2-&-%E5%BC%BA%E5%88%B6%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2) 26 | - [命名约定](#%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A) 27 | - [访问器](#%E8%AE%BF%E9%97%AE%E5%99%A8) 28 | - [构造函数](#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0) 29 | - [ES6箭头函数](#es6%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0) 30 | - [ES6增强的对象字面量](#es6%E5%A2%9E%E5%BC%BA%E7%9A%84%E5%AF%B9%E8%B1%A1%E5%AD%97%E9%9D%A2%E9%87%8F) 31 | - [ES6模板字符串](#es6%E6%A8%A1%E6%9D%BF%E5%AD%97%E7%AC%A6%E4%B8%B2) 32 | - [ES6函数参数增强](#es6%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0%E5%A2%9E%E5%BC%BA) 33 | - [ES6新增关键字let和const](#es6%E6%96%B0%E5%A2%9E%E5%85%B3%E9%94%AE%E5%AD%97let%E5%92%8Cconst) 34 | - [ES6迭代器和`for..of`](#es6%E8%BF%AD%E4%BB%A3%E5%99%A8%E5%92%8Cforof) 35 | - [ES6生成器](#es6%E7%94%9F%E6%88%90%E5%99%A8) 36 | - [ES6模块](#es6%E6%A8%A1%E5%9D%97) 37 | - [ES6新增集合Map和Set](#es6%E6%96%B0%E5%A2%9E%E9%9B%86%E5%90%88map%E5%92%8Cset) 38 | - [ES6 Promise](#es6-promise) 39 | - [推荐的书](#%E6%8E%A8%E8%8D%90%E7%9A%84%E4%B9%A6) 40 | - [推荐的博客](#%E6%8E%A8%E8%8D%90%E7%9A%84%E5%8D%9A%E5%AE%A2) 41 | - [};](#) 42 | 43 | 44 | 45 | ### 参考资料 46 | 47 | - [Airbnb styleguide](https://github.com/airbnb/javascript) 48 | - RisingStack's [Node.js styleguide](https://github.com/RisingStack/node-style-guide) 49 | - Caolan's [Node.js styleguide](http://caolanmcmahon.com/posts/nodejs_style_and_structure) 50 | - Felixge's [Node.js styleguide](https://github.com/felixge/node-style-guide) 51 | - Baidu EFE: [ES6 develop overview](http://efe.baidu.com/blog/es6-develop-overview/) 52 | 53 | ## 类型 54 | 55 | - **原始类型**: 当你访问一个原始数据类型时,你直接操作的是它的值 56 | 57 | + `string` 58 | + `number` 59 | + `boolean` 60 | + `null` 61 | + `undefined` 62 | 63 | ```javascript 64 | var foo = 1; 65 | var bar = foo; 66 | 67 | bar = 9; 68 | 69 | console.log(foo, bar); // => 1, 9 70 | ``` 71 | - **复杂情况**: 当你访问的是一个复杂类型时,你操作的是它的引用 72 | 73 | + `object` 74 | + `array` 75 | + `function` 76 | 77 | ```javascript 78 | var foo = [1, 2]; 79 | var bar = foo; 80 | 81 | bar[0] = 9; 82 | 83 | console.log(foo[0], bar[0]); // => 9, 9 84 | ``` 85 | 86 | **[⬆ back to top](#)** 87 | 88 | ## 对象 89 | 90 | - 在对象创建时使用字面量语法 91 | 92 | ```javascript 93 | // bad 94 | var item = new Object(); 95 | 96 | // good 97 | var item = {}; 98 | ``` 99 | 100 | - 使用可读的别名,避免使用保留字 101 | 102 | ```javascript 103 | // bad 104 | var superman = { 105 | class: 'alien' 106 | }; 107 | 108 | // bad 109 | var superman = { 110 | klass: 'alien' 111 | }; 112 | 113 | // good 114 | var superman = { 115 | type: 'alien' 116 | }; 117 | ``` 118 | 119 | **[⬆ back to top](#)** 120 | 121 | ## 数组 122 | 123 | - 使用字面量语法创建数组 124 | 125 | ```javascript 126 | // bad 127 | var items = new Array(); 128 | 129 | // good 130 | var items = []; 131 | ``` 132 | 133 | - 当你不知道数组的长度时使用Array#push. 134 | 135 | ```javascript 136 | var someStack = []; 137 | 138 | 139 | // bad 140 | someStack[someStack.length] = 'abracadabra'; 141 | 142 | // good 143 | someStack.push('abracadabra'); 144 | ``` 145 | 146 | - 当你需要复制数据时使用Array#slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 147 | 148 | ```javascript 149 | var len = items.length; 150 | var itemsCopy = []; 151 | var i; 152 | 153 | // bad 154 | for (i = 0; i < len; i++) { 155 | itemsCopy[i] = items[i]; 156 | } 157 | 158 | // good 159 | itemsCopy = items.slice(); 160 | ``` 161 | 162 | - 将一个类数组对象转为数组,使用Array#slice. 163 | 164 | ```javascript 165 | function trigger() { 166 | var args = Array.prototype.slice.call(arguments); 167 | ... 168 | } 169 | ``` 170 | 171 | **[⬆ back to top](#)** 172 | 173 | 174 | ## 字符串 175 | 176 | - 使用单引号 `''` 表示字符串 177 | 178 | ```javascript 179 | // bad 180 | var name = "Bob Parr"; 181 | 182 | // good 183 | var name = 'Bob Parr'; 184 | 185 | // bad 186 | var fullName = "Bob " + this.lastName; 187 | 188 | // good 189 | var fullName = 'Bob ' + this.lastName; 190 | ``` 191 | 192 | - 长于80个字符的字符串应该被写成多行(使用字符串拼接或ES6的模板字符串) 193 | - 注意:如果过度使用,长字符串拼接会影响性能. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) 194 | 195 | ```javascript 196 | // bad 197 | var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; 198 | 199 | // bad 200 | var errorMessage = 'This is a super long error that was thrown because \ 201 | of Batman. When you stop to think about how Batman had anything to do \ 202 | with this, you would get nowhere \ 203 | fast.'; 204 | 205 | // good 206 | var errorMessage = 'This is a super long error that was thrown because ' + 207 | 'of Batman. When you stop to think about how Batman had anything to do ' + 208 | 'with this, you would get nowhere fast.'; 209 | 210 | // good 211 | var errorMessage = ` 212 | hello 213 | world 214 | this 215 | is 216 | `; 217 | 218 | ``` 219 | 220 | - 当使用程序来生成字符串时,使用Array#join,而不是字符串拼接. 221 | 222 | ```javascript 223 | var items; 224 | var messages; 225 | var length; 226 | var i; 227 | 228 | messages = [{ 229 | state: 'success', 230 | message: 'This one worked.' 231 | }, { 232 | state: 'success', 233 | message: 'This one worked as well.' 234 | }, { 235 | state: 'error', 236 | message: 'This one did not work.' 237 | }]; 238 | 239 | length = messages.length; 240 | 241 | // bad 242 | function inbox(messages) { 243 | items = ''; 250 | } 251 | 252 | // good 253 | function inbox(messages) { 254 | items = []; 255 | 256 | for (i = 0; i < length; i++) { 257 | items[i] = messages[i].message; 258 | } 259 | 260 | return ''; 261 | } 262 | ``` 263 | 264 | **[⬆ back to top](#)** 265 | 266 | 267 | ## 函数 268 | 269 | - 函数表达式: 270 | 271 | ```javascript 272 | // anonymous function expression 273 | var anonymous = function() { 274 | return true; 275 | }; 276 | 277 | // named function expression 278 | var named = function named() { 279 | return true; 280 | }; 281 | 282 | // immediately-invoked function expression (IIFE) 283 | (function() { 284 | console.log('Welcome to the Internet. Please follow me.'); 285 | })(); 286 | ``` 287 | 288 | - 永远不要再一个非函数语句块内使用函数声明(if, while, 等). 将函数赋值给一个变量. 289 | 290 | ```javascript 291 | // bad 292 | if (currentUser) { 293 | function test() { 294 | console.log('Nope.'); 295 | } 296 | } 297 | 298 | // good 299 | var test; 300 | if (currentUser) { 301 | test = function test() { 302 | console.log('Yup.'); 303 | }; 304 | } 305 | ``` 306 | 307 | - 永远不要将你的参数命名为 `arguments`, 这会与函数范围内的 `arguments` 对象冲突. 308 | 309 | ```javascript 310 | // bad 311 | function nope(name, options, arguments) { 312 | // ...stuff... 313 | } 314 | 315 | // good 316 | function yup(name, options, args) { 317 | // ...stuff... 318 | } 319 | ``` 320 | 321 | **[⬆ back to top](#)** 322 | 323 | 324 | 325 | ## 属性 326 | 327 | - 当访问属性时请使用`.`操作符. 328 | 329 | ```javascript 330 | var luke = { 331 | jedi: true, 332 | age: 28 333 | }; 334 | 335 | // bad 336 | var isJedi = luke['jedi']; 337 | 338 | // good 339 | var isJedi = luke.jedi; 340 | ``` 341 | 342 | - 当你要用变量访问属性时,请使用 `[]` . 343 | 344 | ```javascript 345 | var luke = { 346 | jedi: true, 347 | age: 28 348 | }; 349 | 350 | function getProp(prop) { 351 | return luke[prop]; 352 | } 353 | 354 | var isJedi = getProp('jedi'); 355 | ``` 356 | 357 | **[⬆ back to top](#)** 358 | 359 | 360 | ## 变量 361 | 362 | - 始终使用 `var` 来声明变量. 否则会创建全局变量,污染全局命名空间. 363 | 364 | ```javascript 365 | // bad 366 | superPower = new SuperPower(); 367 | 368 | // good 369 | var superPower = new SuperPower(); 370 | ``` 371 | 372 | - 声明变量时使用一个新行, 并且每一行都使用 `var` 来声明. 373 | 374 | ```javascript 375 | // bad 376 | var items = getItems(), 377 | goSportsTeam = true, 378 | dragonball = 'z'; 379 | 380 | // good 381 | var items = getItems(); 382 | var goSportsTeam = true; 383 | var dragonball = 'z'; 384 | ``` 385 | 386 | - 最后声明未赋值的便利. 后面如果你要使用前面的变量进行赋值时会显得很便利. 387 | 388 | ```javascript 389 | // bad 390 | var i; 391 | var items = getItems(); 392 | var dragonball; 393 | var goSportsTeam = true; 394 | var len; 395 | 396 | // good 397 | var items = getItems(); 398 | var goSportsTeam = true; 399 | var dragonball; 400 | var length; 401 | var i; 402 | ``` 403 | 404 | - 避免冗余的变量声明, 可已使用 `Object` 对象来构建命名空间. 405 | 406 | ```javascript 407 | 408 | // bad 409 | var kaleidoscopeName = '..'; 410 | var kaleidoscopeLens = []; 411 | var kaleidoscopeColors = []; 412 | 413 | // good 414 | var kaleidoscope = { 415 | name: '..', 416 | lens: [], 417 | colors: [] 418 | }; 419 | ``` 420 | 421 | - 在变量作用范围的最顶端声明变量. 这可以帮你避免赋值提升的问题. 422 | 423 | ```javascript 424 | // bad 425 | function() { 426 | test(); 427 | console.log('doing stuff..'); 428 | 429 | //..other stuff.. 430 | 431 | var name = getName(); 432 | 433 | if (name === 'test') { 434 | return false; 435 | } 436 | 437 | return name; 438 | } 439 | 440 | // good 441 | function() { 442 | var name = getName(); 443 | 444 | test(); 445 | console.log('doing stuff..'); 446 | 447 | //..other stuff.. 448 | 449 | if (name === 'test') { 450 | return false; 451 | } 452 | 453 | return name; 454 | } 455 | 456 | // bad 457 | function() { 458 | var name = getName(); 459 | 460 | if (!arguments.length) { 461 | return false; 462 | } 463 | 464 | return true; 465 | } 466 | 467 | // good 468 | function() { 469 | if (!arguments.length) { 470 | return false; 471 | } 472 | 473 | var name = getName(); 474 | 475 | return true; 476 | } 477 | ``` 478 | 479 | **[⬆ back to top](#)** 480 | 481 | ## Requires 482 | 483 | - 使用如下顺序来组织node代码中的require语句: 484 | - core modules 485 | - npm modules 486 | - others 487 | 488 | ```javascript 489 | // bad 490 | var Car = require('./models/Car'); 491 | var async = require('async'); 492 | var http = require('http'); 493 | 494 | // good 495 | var http = require('http'); 496 | var fs = require('fs'); 497 | 498 | var async = require('async'); 499 | var mongoose = require('mongoose'); 500 | 501 | var Car = require('./models/Car'); 502 | ``` 503 | 504 | - 当你引入模块时,不要加 `.js` 后缀 505 | 506 | ```javascript 507 | // bad 508 | var Batmobil = require('./models/Car.js'); 509 | 510 | // good 511 | var Batmobil = require('./models/Car'); 512 | 513 | ``` 514 | 515 | **[⬆ back to top](#)** 516 | 517 | ## 回调函数 518 | 519 | - 在回调函数中要始终检测错误 520 | 521 | ```javascript 522 | //bad 523 | database.get('pokemons', function(err, pokemons) { 524 | console.log(pokemons); 525 | }); 526 | 527 | //good 528 | database.get('drabonballs', function(err, drabonballs) { 529 | if (err) { 530 | // handle the error somehow, maybe return with a callback 531 | return console.log(err); 532 | } 533 | console.log(drabonballs); 534 | }); 535 | ``` 536 | 537 | - 遇到错误时从回调中返回 538 | 539 | ```javascript 540 | //bad 541 | database.get('drabonballs', function(err, drabonballs) { 542 | if (err) { 543 | // if not return here 544 | console.log(err); 545 | } 546 | // this line will be executed as well 547 | console.log(drabonballs); 548 | }); 549 | 550 | //good 551 | database.get('drabonballs', function(err, drabonballs) { 552 | if (err) { 553 | // handle the error somehow, maybe return with a callback 554 | return console.log(err); 555 | } 556 | console.log(drabonballs); 557 | }); 558 | ``` 559 | 560 | - 当你要开发接口给外部时,在你的回调函数中使用描述性的参数。它能够让你的代码更可读。 561 | 562 | ```javascript 563 | // bad 564 | function getAnimals(done) { 565 | Animal.get(done); 566 | } 567 | 568 | // good 569 | function getAnimals(done) { 570 | Animal.get(function(err, animals) { 571 | if(err) { 572 | return done(err); 573 | } 574 | 575 | return done(null, { 576 | dogs: animals.dogs, 577 | cats: animals.cats 578 | }) 579 | }); 580 | } 581 | ``` 582 | 583 | **[⬆ back to top](#)** 584 | 585 | 586 | ## Try catch 587 | 588 | - 只能在同步函数中使用`throw` 589 | 590 | Try-catch 语句块不能被用在异步代码块中。 591 | 592 | ```javascript 593 | //bad 594 | function readPackageJson (callback) { 595 | fs.readFile('package.json', function(err, file) { 596 | if (err) { 597 | throw err; 598 | } 599 | ... 600 | }); 601 | } 602 | //good 603 | function readPackageJson (callback) { 604 | fs.readFile('package.json', function(err, file) { 605 | if (err) { 606 | return callback(err); 607 | } 608 | ... 609 | }); 610 | } 611 | ``` 612 | 613 | - 在同步调用中捕获错误,`JSON.parse()`应该使用`try-catch`语句块 614 | 615 | ```javascript 616 | //bad 617 | var data = JSON.parse(jsonAsAString); 618 | 619 | //good 620 | var data; 621 | try { 622 | data = JSON.parse(jsonAsAString); 623 | } catch (e) { 624 | //handle error - hopefully not with a console.log ;) 625 | console.log(e); 626 | } 627 | ``` 628 | 629 | **[⬆ back to top](#)** 630 | 631 | ## 提升 632 | 633 | - 变量声明会被提升到作用域的顶端,而赋值操作则不会。 634 | 635 | ```javascript 636 | // 先看个简单的例子,显然它会抛出错误 637 | function example() { 638 | console.log(notDefined); // => throws a ReferenceError 639 | } 640 | 641 | // 我们先使用了一个变量,而后再声明并初始化这个变量 642 | // 输出结果没有报错,而是 `undefined`,意思是未被初始化 643 | function example() { 644 | console.log(declaredButNotAssigned); // => undefined 645 | var declaredButNotAssigned = true; 646 | } 647 | 648 | // 变量声明部分会被提升,赋值部分仍保持不变 649 | // 上面的代码等同于 650 | function example() { 651 | var declaredButNotAssigned; 652 | console.log(declaredButNotAssigned); // => undefined 653 | declaredButNotAssigned = true; 654 | } 655 | ``` 656 | 657 | - 匿名函数表达式会提升它们的变量名,但是函数赋值部门不会被提升 658 | 659 | ```javascript 660 | function example() { 661 | console.log(anonymous); // => undefined 662 | 663 | anonymous(); // => TypeError anonymous is not a function 664 | 665 | var anonymous = function() { 666 | console.log('anonymous function expression'); 667 | }; 668 | } 669 | ``` 670 | 671 | - 命名函数表达式会提升它们的变量名,但函数名或函数体不会被提升。 672 | 673 | ```javascript 674 | function example() { 675 | console.log(named); // => undefined 676 | 677 | named(); // => TypeError named is not a function 678 | 679 | superPower(); // => ReferenceError superPower is not defined 680 | 681 | var named = function superPower() { 682 | console.log('Flying'); 683 | }; 684 | } 685 | 686 | // the same is true when the function name 687 | // is the same as the variable name. 688 | function example() { 689 | console.log(named); // => undefined 690 | 691 | named(); // => TypeError named is not a function 692 | 693 | var named = function named() { 694 | console.log('named'); 695 | } 696 | } 697 | ``` 698 | 699 | - 函数声明会被整体提升到作用域顶端 700 | 701 | ```javascript 702 | function example() { 703 | superPower(); // => Flying 704 | 705 | function superPower() { 706 | console.log('Flying'); 707 | } 708 | } 709 | ``` 710 | 711 | - 更多信息请参考 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/) 712 | 713 | **[⬆ back to top](#)** 714 | 715 | ## 条件表达式 & 相等性 716 | 717 | - 使用 `===` 和 `!==` 来代替 `==` 和 `!=`. 718 | - 条件表达式会被使用`ToBoolean`方法进行强制类型转换。并且服从如下规则: 719 | 720 | + **Objects** 被转换为 **true** 721 | + **Undefined** 被转换为 **false** 722 | + **Null** 被转换为 **false** 723 | + **Booleans** 被转换为 **实际的boolean值** 724 | + **Numbers** 被转换为 **false** 如果是 **+0, -0, or NaN**, 其他都为 **true** 725 | + **Strings** 被转换为 **false** 如果是空字符串 `''`, 其他都为 **true** 726 | 727 | ```javascript 728 | if ([0]) { 729 | // true 730 | // 数组是对象,对象始终被转换为 `true` 731 | } 732 | ``` 733 | 734 | - 使用缩减版. 735 | 736 | ```javascript 737 | // bad 738 | if (name !== '') { 739 | // ...stuff... 740 | } 741 | 742 | // good 743 | if (name) { 744 | // ...stuff... 745 | } 746 | 747 | // bad 748 | if (collection.length > 0) { 749 | // ...stuff... 750 | } 751 | 752 | // good 753 | if (collection.length) { 754 | // ...stuff... 755 | } 756 | ``` 757 | 758 | - 更多信息请参考 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll 759 | 760 | **[⬆ back to top](#)** 761 | 762 | 763 | ## 代码块 764 | 765 | - 所有的多行代码块都要使用大括号,并且不要写在一行 766 | 767 | ```javascript 768 | // bad 769 | if (test) 770 | return false; 771 | 772 | // bad 773 | if (test) return false; 774 | 775 | // good 776 | if (test) { 777 | return false; 778 | } 779 | 780 | // bad 781 | function() { return false; } 782 | 783 | // good 784 | function() { 785 | return false; 786 | } 787 | ``` 788 | 789 | **[⬆ back to top](#)** 790 | 791 | 792 | ## 注释 793 | 794 | - 使用 `/** ... */` 进行多行注释. 请在你们加入注释说明,指明参数和返回值的类型 795 | 796 | ```javascript 797 | // bad 798 | // make() returns a new element 799 | // based on the passed in tag name 800 | // 801 | // @param tag 802 | // @return element 803 | function make(tag) { 804 | 805 | // ...stuff... 806 | 807 | return element; 808 | } 809 | 810 | // good 811 | /** 812 | * make() returns a new element 813 | * based on the passed in tag name 814 | * 815 | * @param tag 816 | * @return element 817 | */ 818 | function make(tag) { 819 | 820 | // ...stuff... 821 | 822 | return element; 823 | } 824 | ``` 825 | 826 | - 使用 `//` 进行单行注释. 请用一个新行来添加注释。并在注释行前增加一个空行。 827 | 828 | ```javascript 829 | // bad 830 | var active = true; // is current tab 831 | 832 | // good 833 | // is current tab 834 | var active = true; 835 | 836 | // bad 837 | function getType() { 838 | console.log('fetching type...'); 839 | // set the default type to 'no type' 840 | var type = this._type || 'no type'; 841 | 842 | return type; 843 | } 844 | 845 | // good 846 | function getType() { 847 | console.log('fetching type...'); 848 | 849 | // set the default type to 'no type' 850 | var type = this._type || 'no type'; 851 | 852 | return type; 853 | } 854 | ``` 855 | 856 | - 如果是为了指明一个错误,请在你的注释前加上`FIXME`或`TODO`前缀来帮助其他开发者快速的了解你的注释意图。 857 | 其中`FIXME`可以表示这个问题需要解决,或者`TODO`来表示需要被实现的功能块。 858 | 859 | - 使用 `// FIXME:` 来注解一个问题。 860 | 861 | ```javascript 862 | function Calculator() { 863 | 864 | // FIXME: shouldn't use a global here 865 | total = 0; 866 | 867 | return this; 868 | } 869 | ``` 870 | 871 | - 使用 `// TODO:` 来注解一个需要被实现(完成)的任务。 872 | 873 | ```javascript 874 | function Calculator() { 875 | 876 | // TODO: total should be configurable by an options param 877 | this.total = 0; 878 | 879 | return this; 880 | } 881 | ``` 882 | 883 | 884 | **[⬆ back to top](#)** 885 | 886 | ## 空格 887 | 888 | - 推荐使用2个空格作为缩进 889 | 890 | ```javascript 891 | // bad 892 | function() { 893 | ∙∙∙∙var name; 894 | } 895 | 896 | // bad 897 | function() { 898 | ∙var name; 899 | } 900 | 901 | // good 902 | function() { 903 | ∙∙var name; 904 | } 905 | ``` 906 | 907 | - 在所有起始的大括号前加一个空格 908 | 909 | ```javascript 910 | // bad 911 | function test(){ 912 | console.log('test'); 913 | } 914 | 915 | // good 916 | function test() { 917 | console.log('test'); 918 | } 919 | 920 | // bad 921 | dog.set('attr',{ 922 | age: '1 year', 923 | breed: 'Bernese Mountain Dog' 924 | }); 925 | 926 | // good 927 | dog.set('attr', { 928 | age: '1 year', 929 | breed: 'Bernese Mountain Dog' 930 | }); 931 | ``` 932 | 933 | - 在操作符见使用一个空格 934 | 935 | ```javascript 936 | // bad 937 | var x=y+5; 938 | 939 | // good 940 | var x = y + 5; 941 | ``` 942 | 943 | - 文件结束后增加一个空行 944 | 945 | ```javascript 946 | // bad 947 | (function(global) { 948 | // ...stuff... 949 | })(this); 950 | ``` 951 | 952 | ```javascript 953 | // bad 954 | (function(global) { 955 | // ...stuff... 956 | })(this);↵ 957 | ↵ 958 | ``` 959 | 960 | ```javascript 961 | // good 962 | (function(global) { 963 | // ...stuff... 964 | })(this);↵ 965 | ``` 966 | 967 | - 对链接起来的方法使用缩进成多行的形式 968 | 969 | ```javascript 970 | // bad 971 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 972 | 973 | // good 974 | $('#items') 975 | .find('.selected') 976 | .highlight() 977 | .end() 978 | .find('.open') 979 | .updateCount(); 980 | 981 | // bad 982 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 983 | .attr('width', (radius + margin) * 2).append('svg:g') 984 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 985 | .call(tron.led); 986 | 987 | // good 988 | var leds = stage.selectAll('.led') 989 | .data(data) 990 | .enter().append('svg:svg') 991 | .class('led', true) 992 | .attr('width', (radius + margin) * 2) 993 | .append('svg:g') 994 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 995 | .call(tron.led); 996 | ``` 997 | 998 | **[⬆ back to top](#)** 999 | 1000 | ## 逗号 1001 | 1002 | - 推荐的做法是逗号在每一行的末尾 1003 | 1004 | ```javascript 1005 | // bad 1006 | var hero = { 1007 | firstName: 'Bob' 1008 | , lastName: 'Parr' 1009 | , heroName: 'Mr. Incredible' 1010 | , superPower: 'strength' 1011 | }; 1012 | 1013 | // good 1014 | var hero = { 1015 | firstName: 'Bob', 1016 | lastName: 'Parr', 1017 | heroName: 'Mr. Incredible', 1018 | superPower: 'strength' 1019 | }; 1020 | ``` 1021 | 1022 | **[⬆ back to top](#)** 1023 | 1024 | 1025 | ## 分号作为语句块的结束 1026 | 1027 | - **Yup.** 1028 | 1029 | ```javascript 1030 | // bad 1031 | (function() { 1032 | var name = 'Skywalker' 1033 | return name 1034 | })() 1035 | 1036 | // good 1037 | (function() { 1038 | var name = 'Skywalker'; 1039 | return name; 1040 | })(); 1041 | 1042 | // good 1043 | ;(function() { 1044 | var name = 'Skywalker'; 1045 | return name; 1046 | })(); 1047 | ``` 1048 | 1049 | **[⬆ back to top](#)** 1050 | 1051 | 1052 | ## 类型转换 & 强制类型转换 1053 | 1054 | - 在声明语句的最前端执行强制类型转换. 1055 | - Strings: 1056 | 1057 | ```javascript 1058 | // => this.reviewScore = 9; 1059 | 1060 | // bad 1061 | var totalScore = this.reviewScore + ''; 1062 | 1063 | // good 1064 | var totalScore = '' + this.reviewScore; 1065 | 1066 | // bad 1067 | var totalScore = '' + this.reviewScore + ' total score'; 1068 | 1069 | // good 1070 | var totalScore = this.reviewScore + ' total score'; 1071 | ``` 1072 | 1073 | - 使用 `parseInt` 来进行整数的类型转换,并且始终提供一个基数. 1074 | 1075 | ```javascript 1076 | var inputValue = '4'; 1077 | 1078 | // bad 1079 | var val = new Number(inputValue); 1080 | 1081 | // bad 1082 | var val = +inputValue; 1083 | 1084 | // bad 1085 | var val = inputValue >> 0; 1086 | 1087 | // bad 1088 | var val = parseInt(inputValue); 1089 | 1090 | // good 1091 | var val = Number(inputValue); 1092 | 1093 | // good 1094 | var val = parseInt(inputValue, 10); 1095 | ``` 1096 | 1097 | - 如果某种情况下你为了性能原因需要避免使用`parseInt`,而是使用移位操作符,请添加注释说明 1098 | 1099 | ```javascript 1100 | // good 1101 | /** 1102 | * parseInt was the reason my code was slow. 1103 | * Bitshifting the String to coerce it to a 1104 | * Number made it a lot faster. 1105 | */ 1106 | var val = inputValue >> 0; 1107 | ``` 1108 | 1109 | - **Note:** 在使用移位操作符时需要特别谨慎. 数字使用 [64-bit values](http://es5.github.io/#x4.3.19)表示的, 但是移位操作符总是返回32-bit的整数 ([source](http://es5.github.io/#x11.7)). 这对大于32-bit的整数而言,这会导致意向不到的行为. [Discussion](https://github.com/airbnb/javascript/issues/109). 最大的有符号32位整数是 2,147,483,647: 1110 | 1111 | ```javascript 1112 | 2147483647 >> 0 //=> 2147483647 1113 | 2147483648 >> 0 //=> -2147483648 1114 | 2147483649 >> 0 //=> -2147483647 1115 | ``` 1116 | 1117 | - Booleans: 1118 | 1119 | ```javascript 1120 | var age = 0; 1121 | 1122 | // bad 1123 | var hasAge = new Boolean(age); 1124 | 1125 | // good 1126 | var hasAge = Boolean(age); 1127 | 1128 | // good 1129 | var hasAge = !!age; 1130 | ``` 1131 | 1132 | **[⬆ back to top](#)** 1133 | 1134 | 1135 | ## 命名约定 1136 | 1137 | - 避免使用当个字符命名,使用描述性的名字: 1138 | 1139 | ```javascript 1140 | // bad 1141 | function q() { 1142 | // ...stuff... 1143 | } 1144 | 1145 | // good 1146 | function query() { 1147 | // ..stuff.. 1148 | } 1149 | ``` 1150 | 1151 | - 对于对象、函数、和实例采用小驼峰(camelCase)命名法 1152 | 1153 | ```javascript 1154 | // bad 1155 | var OBJEcttsssss = {}; 1156 | var this_is_my_object = {}; 1157 | function c() {} 1158 | var u = new user({ 1159 | name: 'Bob Parr' 1160 | }); 1161 | 1162 | // good 1163 | var thisIsMyObject = {}; 1164 | function thisIsMyFunction() {} 1165 | var user = new User({ 1166 | name: 'Bob Parr' 1167 | }); 1168 | ``` 1169 | 1170 | - 当命名类或构造函数时使用大驼峰或Pascal命名法(PascalCase) 1171 | 1172 | ```javascript 1173 | // bad 1174 | function user(options) { 1175 | this.name = options.name; 1176 | } 1177 | 1178 | var bad = new user({ 1179 | name: 'nope' 1180 | }); 1181 | 1182 | // good 1183 | function User(options) { 1184 | this.name = options.name; 1185 | } 1186 | 1187 | var good = new User({ 1188 | name: 'yup' 1189 | }); 1190 | ``` 1191 | 1192 | - 在私有属性前加上一个 `_` 前缀 1193 | 1194 | ```javascript 1195 | // bad 1196 | this.__firstName__ = 'Panda'; 1197 | this.firstName_ = 'Panda'; 1198 | 1199 | // good 1200 | this._firstName = 'Panda'; 1201 | ``` 1202 | 1203 | - 当你要保存 `this` 值时,可以将其命名为 `_this`. 1204 | 1205 | ```javascript 1206 | // bad 1207 | function() { 1208 | var self = this; 1209 | return function() { 1210 | console.log(self); 1211 | }; 1212 | } 1213 | 1214 | // bad 1215 | function() { 1216 | var that = this; 1217 | return function() { 1218 | console.log(that); 1219 | }; 1220 | } 1221 | 1222 | // good 1223 | function() { 1224 | var _this = this; 1225 | return function() { 1226 | console.log(_this); 1227 | }; 1228 | } 1229 | ``` 1230 | 1231 | - 命名你的函数。这将有助于堆栈跟踪。 1232 | 1233 | ```javascript 1234 | // bad 1235 | var log = function(msg) { 1236 | console.log(msg); 1237 | }; 1238 | 1239 | // good 1240 | var log = function log(msg) { 1241 | console.log(msg); 1242 | }; 1243 | ``` 1244 | 1245 | **[⬆ back to top](#)** 1246 | 1247 | 1248 | ## 访问器 1249 | 1250 | - 属性访问器并不是必须的。 1251 | - 如果你确实需要,请命名为 getVal() 和 setVal('hello') 的形式 1252 | 1253 | ```javascript 1254 | // bad 1255 | dragon.age(); 1256 | 1257 | // good 1258 | dragon.getAge(); 1259 | 1260 | // bad 1261 | dragon.age(25); 1262 | 1263 | // good 1264 | dragon.setAge(25); 1265 | ``` 1266 | 1267 | - 如果属性是一个布尔值,请使用 isVal() 或 hasVal() 1268 | 1269 | ```javascript 1270 | // bad 1271 | if (!dragon.age()) { 1272 | return false; 1273 | } 1274 | 1275 | // good 1276 | if (!dragon.hasAge()) { 1277 | return false; 1278 | } 1279 | ``` 1280 | 1281 | - 你也可以创建 get() 和 set() 函数, 但一定要保持一致. 1282 | 1283 | ```javascript 1284 | function Jedi(options) { 1285 | options || (options = {}); 1286 | var lightsaber = options.lightsaber || 'blue'; 1287 | this.set('lightsaber', lightsaber); 1288 | } 1289 | 1290 | Jedi.prototype.set = function(key, val) { 1291 | this[key] = val; 1292 | }; 1293 | 1294 | Jedi.prototype.get = function(key) { 1295 | return this[key]; 1296 | }; 1297 | ``` 1298 | 1299 | **[⬆ back to top](#)** 1300 | 1301 | ## 构造函数 1302 | 1303 | - 在原型链上增加属性,而不是覆写原型链。 1304 | 1305 | ```javascript 1306 | function Jedi() { 1307 | console.log('new jedi'); 1308 | } 1309 | 1310 | // bad 1311 | Jedi.prototype = { 1312 | fight: function fight() { 1313 | console.log('fighting'); 1314 | }, 1315 | 1316 | block: function block() { 1317 | console.log('blocking'); 1318 | } 1319 | }; 1320 | 1321 | // good 1322 | Jedi.prototype.fight = function fight() { 1323 | console.log('fighting'); 1324 | }; 1325 | 1326 | Jedi.prototype.block = function block() { 1327 | console.log('blocking'); 1328 | }; 1329 | ``` 1330 | 1331 | - 你可以在方法中返回 `this` 从而来构建可链接的方法。 1332 | 1333 | ```javascript 1334 | // bad 1335 | Jedi.prototype.jump = function() { 1336 | this.jumping = true; 1337 | return true; 1338 | }; 1339 | 1340 | Jedi.prototype.setHeight = function(height) { 1341 | this.height = height; 1342 | }; 1343 | 1344 | var luke = new Jedi(); 1345 | luke.jump(); // => true 1346 | luke.setHeight(20) // => undefined 1347 | 1348 | // good 1349 | Jedi.prototype.jump = function() { 1350 | this.jumping = true; 1351 | return this; 1352 | }; 1353 | 1354 | Jedi.prototype.setHeight = function(height) { 1355 | this.height = height; 1356 | return this; 1357 | }; 1358 | 1359 | var luke = new Jedi(); 1360 | 1361 | luke.jump() 1362 | .setHeight(20); 1363 | ``` 1364 | 1365 | 1366 | - 你可以创建一个自定义的`toString()`方法,但是你要确保它能正常工作并且没有其他副作用。 1367 | 1368 | ```javascript 1369 | function Jedi(options) { 1370 | options || (options = {}); 1371 | this.name = options.name || 'no name'; 1372 | } 1373 | 1374 | Jedi.prototype.getName = function getName() { 1375 | return this.name; 1376 | }; 1377 | 1378 | Jedi.prototype.toString = function toString() { 1379 | return 'Jedi - ' + this.getName(); 1380 | }; 1381 | ``` 1382 | 1383 | **[⬆ back to top](#)** 1384 | 1385 | ## ES6箭头函数 1386 | 1387 | - 当你必须使用函数表达式(或传递一个匿名函数时),使用箭头函数符号(能够自动绑定`this`到父对象) 1388 | 1389 | ```javascript 1390 | // bad 1391 | [1, 2, 3].map(function (x) { 1392 | return x * x; 1393 | }); 1394 | 1395 | // good 1396 | [1, 2, 3].map((x) => x * x); 1397 | ``` 1398 | 1399 | - 建议所有的Arrow Function的参数均使用 `()`包裹,即便只有一个参数: 1400 | 1401 | ```javascript 1402 | // good 1403 | let foo = (x) => x + 1; 1404 | 1405 | // bad 1406 | let foo = x => x + 1; 1407 | `` 1408 | 1409 | - 对于对象、类中的方法,使用增强的对象字面量 1410 | 1411 | ```javascript 1412 | // good 1413 | let foo = { 1414 | bar () { 1415 | // code 1416 | } 1417 | } 1418 | 1419 | // bad 1420 | let foo = { 1421 | bar: () => { 1422 | // code 1423 | } 1424 | } 1425 | 1426 | // bad 1427 | let foo = { 1428 | bar: function () { 1429 | // code 1430 | } 1431 | } 1432 | ``` 1433 | 1434 | **[⬆ back to top](#)** 1435 | 1436 | 1437 | ## ES6增强的对象字面量 1438 | 1439 | - 可以在对象总直接定义方法 1440 | 1441 | ```javascript 1442 | // good 1443 | let foo = { 1444 | bar() { 1445 | // code 1446 | } 1447 | } 1448 | ``` 1449 | 1450 | - 可使用通过计算得出的键值 1451 | 1452 | ```javascript 1453 | // 当你需要的时候使用 1454 | let MY_KEY = 'bar'; 1455 | let foo = { 1456 | [MY_KEY + 'Hash']: 123 1457 | } 1458 | ``` 1459 | 1460 | - 与当前scope中同名变量的简写 1461 | 1462 | ```javascript 1463 | // bad 1464 | let bar = 'bar'; 1465 | let foo = { 1466 | bar // 相当于bar: bar 1467 | }; 1468 | ``` 1469 | 1470 | **[⬆ back to top](#)** 1471 | 1472 | 1473 | ## ES6模板字符串 1474 | 1475 | - 不推荐使用多行字符串,因为不方便代码缩进 1476 | 1477 | ```javascript 1478 | // bad 1479 | let html = 1480 | `
1481 |

Hello world

1482 |
` 1483 | ``` 1484 | 1485 | - 推荐使用ES6的字符串变量替换功能,这样可以取代字符串拼接 1486 | 1487 | ```javascript 1488 | //good 1489 | let name = 'weiwei'; 1490 | let time = '22:00'; 1491 | let message = `Hello ${name}, it's ${time} now`; 1492 | ``` 1493 | 1494 | **[⬆ back to top](#)** 1495 | 1496 | ## ES6函数参数增强 1497 | 1498 | - 推荐使用默认值、剩余参数等功能,这能让你的函数声明和调用变得更为简洁 1499 | 1500 | ```javascript 1501 | var foo = (x = 1) => x + 1; 1502 | foo(); // 2 1503 | 1504 | var extend = (source, ...args) => { 1505 | for (let target in args) { 1506 | for (let name in Object.keys(target) { 1507 | if (!source.hasOwnProperty(name) { 1508 | source[name] = target[name]; 1509 | } 1510 | } 1511 | } 1512 | }; 1513 | 1514 | var extensions = [ 1515 | {name: 'Zhang'}, 1516 | {age: 17}, 1517 | {work: 'hard'} 1518 | ]; 1519 | extend({}, ...extensions); 1520 | ``` 1521 | 1522 | **[⬆ back to top](#)** 1523 | 1524 | 1525 | ## ES6新增关键字let和const 1526 | 1527 | - 推荐使用`let`全面代替`var`,因为它创建了块级作用域变量(变量只在代码块内生效),尤其是`for`循环 1528 | 1529 | ```javascript 1530 | for(let i = 0; i < 10; i++) { 1531 | foo[i].onclick = function() { 1532 | console.log(i); 1533 | }; 1534 | } 1535 | ``` 1536 | 1537 | - 建议自由在逻辑上是常量的情况才使用 `const`,它代表常量,定的同时必须赋值 1538 | 1539 | ```javascript 1540 | // good 1541 | const MAX_CAT_SIZE_KG = 3000; 1542 | 1543 | // bad 1544 | MAX_CAT_SIZE_KG = 5000; // SyntaxError 1545 | MAX_CAT_SIZE_KG++; // nice try, but still a SyntaxError 1546 | ``` 1547 | 1548 | **[⬆ back to top](#)** 1549 | 1550 | 1551 | ## ES6迭代器和`for..of` 1552 | 1553 | - 推荐使用`for..of`来迭代集合对象(Array, Map, Set, arguments对象)的**值** 1554 | 1555 | ```javascript 1556 | // good 1557 | for (let item of array) { 1558 | // do somehting 1559 | } 1560 | ``` 1561 | 1562 | - 避免使用`for...in`来迭代结合对象,它通常用于迭代对象的**属性名** 1563 | 1564 | 1565 | **[⬆ back to top](#)** 1566 | 1567 | 1568 | ## ES6生成器 1569 | 1570 | - 谨慎使用生成器,异步控制器的未来是`async`和`await`这两个关键字 1571 | 1572 | ```javascript 1573 | // good 1574 | async function save(Something) { 1575 | try { 1576 | await Something.save(); // 等待await后面的代码执行完,类似于yield 1577 | } catch (ex) { 1578 | //error handling 1579 | } 1580 | console.log('success'); 1581 | } 1582 | ``` 1583 | 1584 | **[⬆ back to top](#)** 1585 | 1586 | 1587 | ## ES6模块 1588 | 1589 | - 谨慎使用ES6的模块系统,Node项目建议使用CommonJS方案,因为ES6并没有包括模块加载器规范,[参考文章](http://www.csdn.net/article/2015-04-30/2824595-Modules-in-ES6) 1590 | - 或者使用ES6的模块定义,但使用ADM作为运行时模块解决方案 1591 | - 保持使用`import`和`export`进行模块的引入和定义,可以安全地使用命名`export`和默认`export` 1592 | - 在使用Babel转换时,配置`modules: 'amd'`转换为AMD的模块定义 1593 | - 不要依赖`SystemJS`这样的ES6模块加载器 1594 | 1595 | 1596 | **[⬆ back to top](#)** 1597 | 1598 | 1599 | ## ES6新增集合Map和Set 1600 | 1601 | - 当你的元素或者键值有可能不是字符串时,无条件地使用`Map`和`Set` 1602 | - 有移除操作的需求时,使用`Map`和`Set` 1603 | - 当仅需要一个不可重复的集合时,使用`Set`优先于普通对象,而不要使用`{foo: true}`这样的对象 1604 | - 当需要遍历功能时,使用`Map`和`Set`,因为其可以简单地使用`for..of`进行遍历 1605 | - `WeakMap`和`WeakSet`是没有办法模拟实现的,因此不要使用 1606 | 1607 | **[⬆ back to top](#)** 1608 | 1609 | ## ES6 Promise 1610 | 1611 | - 建议所有异步均使用Promise实现 1612 | 1613 | ```javascript 1614 | // 构造一个Promise实例 1615 | var promise = new Promise(function(resolve, reject) { 1616 | // ... some code 1617 | 1618 | if (/* 异步操作成功 */){ 1619 | resolve(value); 1620 | } else { 1621 | reject(error); 1622 | } 1623 | }); 1624 | ``` 1625 | 1626 | - Promise实例生成后,可以用`then`方法分别制定Resolved状态和Reject状态的回调函数 1627 | 1628 | ```javascript 1629 | promise.then(function(value) { 1630 | // success 1631 | }, function(value) { 1632 | // failure 1633 | }); 1634 | ``` 1635 | 1636 | **[⬆ back to top](#)** 1637 | 1638 | 1639 | ## 推荐的书 1640 | 1641 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford 1642 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov 1643 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz 1644 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders 1645 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas 1646 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw 1647 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig 1648 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch 1649 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault 1650 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg 1651 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy 1652 | - [JSBooks](http://jsbooks.revolunet.com/) 1653 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov 1654 | 1655 | ## 推荐的博客 1656 | 1657 | - [DailyJS](http://dailyjs.com/) 1658 | - [JavaScript Weekly](http://javascriptweekly.com/) 1659 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) 1660 | - [Bocoup Weblog](http://weblog.bocoup.com/) 1661 | - [Adequately Good](http://www.adequatelygood.com/) 1662 | - [NCZOnline](http://www.nczonline.net/) 1663 | - [Perfection Kills](http://perfectionkills.com/) 1664 | - [Ben Alman](http://benalman.com/) 1665 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) 1666 | - [Dustin Diaz](http://dustindiaz.com/) 1667 | - [nettuts](http://net.tutsplus.com/?s=javascript) 1668 | 1669 | **[⬆ back to top](#)** 1670 | 1671 | **The JavaScript Style Guide Guide** 1672 | 1673 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 1674 | 1675 | 1676 | **[⬆ back to top](#)** 1677 | 1678 | # }; --------------------------------------------------------------------------------