├── .editorconfig ├── .eslintrc ├── .gitignore ├── JavaScript.md ├── LICENSE ├── README.md └── React.js.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.py] 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | "react" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "ecmaFeatures": { 11 | "arrowFunctions": true, 12 | "blockBindings": true, 13 | "classes": true, 14 | "defaultParams": true, 15 | "destructuring": true, 16 | "forOf": true, 17 | "generators": false, 18 | "modules": true, 19 | "objectLiteralComputedProperties": true, 20 | "objectLiteralDuplicateProperties": false, 21 | "objectLiteralShorthandMethods": true, 22 | "objectLiteralShorthandProperties": true, 23 | "spread": true, 24 | "superInFunctions": true, 25 | "templateStrings": true, 26 | "jsx": true 27 | }, 28 | "rules": { 29 | /** 30 | * Strict mode 31 | */ 32 | // babel inserts "use strict"; for us 33 | // http://eslint.org/docs/rules/strict 34 | "strict": [2, "never"], 35 | 36 | /** 37 | * ES6 38 | */ 39 | "no-var": 2, // http://eslint.org/docs/rules/no-var 40 | 41 | /** 42 | * Variables 43 | */ 44 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 45 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 46 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars 47 | "vars": "local", 48 | "args": "after-used" 49 | }], 50 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 51 | 52 | /** 53 | * Possible errors 54 | */ 55 | "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle 56 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 57 | "no-console": 1, // http://eslint.org/docs/rules/no-console 58 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 59 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 60 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 61 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 62 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 63 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 64 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 65 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 66 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 67 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 68 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 69 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 70 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 71 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 72 | "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys 73 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 74 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 75 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 76 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 77 | 78 | /** 79 | * Best practices 80 | */ 81 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 82 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 83 | "default-case": 2, // http://eslint.org/docs/rules/default-case 84 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 85 | "allowKeywords": true 86 | }], 87 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 88 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 89 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 90 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 91 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 92 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 93 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 94 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 95 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 96 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 97 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 98 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 99 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 100 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 101 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 102 | "no-new": 2, // http://eslint.org/docs/rules/no-new 103 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 104 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 105 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 106 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 107 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 108 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 109 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 110 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 111 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 112 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 113 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 114 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 115 | "no-with": 2, // http://eslint.org/docs/rules/no-with 116 | "radix": 2, // http://eslint.org/docs/rules/radix 117 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 118 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 119 | "yoda": 2, // http://eslint.org/docs/rules/yoda 120 | 121 | /** 122 | * Style 123 | */ 124 | "indent": [2, 2], // http://eslint.org/docs/rules/ 125 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 126 | "1tbs", { 127 | "allowSingleLine": true 128 | }], 129 | "quotes": [ 130 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 131 | ], 132 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 133 | "properties": "never" 134 | }], 135 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 136 | "before": false, 137 | "after": true 138 | }], 139 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 140 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 141 | "func-names": 1, // http://eslint.org/docs/rules/func-names 142 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 143 | "beforeColon": false, 144 | "afterColon": true 145 | }], 146 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 147 | "newIsCap": true 148 | }], 149 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 150 | "max": 2 151 | }], 152 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 153 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 154 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 155 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 156 | "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func 157 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 158 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 159 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks 160 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 161 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 162 | "before": false, 163 | "after": true 164 | }], 165 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 166 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 167 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 168 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 169 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 170 | "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment 171 | 172 | /** 173 | * JSX style 174 | */ 175 | "react/display-name": 0, 176 | "react/jsx-boolean-value": 2, 177 | "react/jsx-quotes": [2, "double"], 178 | "react/jsx-no-undef": 2, 179 | "react/jsx-sort-props": 0, 180 | "react/jsx-sort-prop-types": 0, 181 | "react/jsx-uses-react": 2, 182 | "react/jsx-uses-vars": 2, 183 | "react/no-did-mount-set-state": [2, "allow-in-func"], 184 | "react/no-did-update-set-state": 2, 185 | "react/no-multi-comp": 2, 186 | "react/no-unknown-property": 2, 187 | "react/prop-types": 2, 188 | "react/react-in-jsx-scope": 2, 189 | "react/self-closing-comp": 2, 190 | "react/wrap-multilines": 2, 191 | "react/sort-comp": [2, { 192 | "order": [ 193 | "displayName", 194 | "mixins", 195 | "statics", 196 | "propTypes", 197 | "getDefaultProps", 198 | "getInitialState", 199 | "componentWillMount", 200 | "componentDidMount", 201 | "componentWillReceiveProps", 202 | "shouldComponentUpdate", 203 | "componentWillUpdate", 204 | "componentWillUnmount", 205 | "/^on.+$/", 206 | "/^get.+$/", 207 | "/^render.+$/", 208 | "render" 209 | ] 210 | }] 211 | } 212 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /JavaScript.md: -------------------------------------------------------------------------------- 1 | # JavaScript Style Guide() { 2 | 3 | *更合理的 JavaScript 编写方式。 via [Airbnb](https://github.com/airbnb/javascript)* 4 | 5 | [For the ES5-only guide click here](es5/). 6 | 7 | ## 目录 8 | 9 | 1. [类型](#types) 10 | 1. [引用](#references) 11 | 1. [Objects](#objects) 12 | 1. [Arrays](#arrays) 13 | 1. [Destructuring](#destructuring) 14 | 1. [Strings](#strings) 15 | 1. [Functions](#functions) 16 | 1. [Arrow Functions](#arrow-functions) 17 | 1. [Constructors](#constructors) 18 | 1. [Modules](#modules) 19 | 1. [Iterators and Generators](#iterators-and-generators) 20 | 1. [Properties](#properties) 21 | 1. [Variables](#variables) 22 | 1. [Hoisting](#hoisting) 23 | 1. [Comparison Operators & Equality](#comparison-operators--equality) 24 | 1. [Blocks](#blocks) 25 | 1. [Comments](#comments) 26 | 1. [Whitespace](#whitespace) 27 | 1. [Commas](#commas) 28 | 1. [Semicolons](#semicolons) 29 | 1. [Type Casting & Coercion](#type-casting--coercion) 30 | 1. [Naming Conventions](#naming-conventions) 31 | 1. [Accessors](#accessors) 32 | 1. [Events](#events) 33 | 1. [jQuery](#jquery) 34 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 35 | 1. [ECMAScript 6 Styles](#ecmascript-6-styles) 36 | 1. [Testing](#testing) 37 | 1. [Performance](#performance) 38 | 1. [Resources](#resources) 39 | 1. [In the Wild](#in-the-wild) 40 | 1. [Translation](#translation) 41 | 1. [The JavaScript Style Guide Guide](#the-javascript-style-guide-guide) 42 | 1. [Chat With Us About Javascript](#chat-with-us-about-javascript) 43 | 1. [Contributors](#contributors) 44 | 1. [License](#license) 45 | 46 | ## 类型 47 | 48 | - [1.1](#1.1) **基本类型**:基本类型数据直接存取。 49 | 50 | + `string` 51 | + `number` 52 | + `boolean` 53 | + `null` 54 | + `undefined` 55 | 56 | ```javascript 57 | const foo = 1; 58 | let bar = foo; 59 | 60 | bar = 9; 61 | 62 | console.log(foo, bar); // => 1, 9 63 | ``` 64 | - [1.2](#1.2) **复杂类型**:复杂数据类型通过**引用**方式存取。 65 | 66 | + `object` 67 | + `array` 68 | + `function` 69 | 70 | ```javascript 71 | const foo = [1, 2]; 72 | const bar = foo; 73 | 74 | bar[0] = 9; 75 | 76 | console.log(foo[0], bar[0]); // => 9, 9 77 | ``` 78 | 79 | **[⬆ 返回目录](#table-of-contents)** 80 | 81 | 82 | ## 引用 83 | 84 | - [2.1](#2.1) 对所用引用使用 `const`;避免使用 `var`。 85 | 86 | > 为什么?这能确保无法对引用重新赋值,以避免可能导致的 bug 和难以理解的代码。 87 | 88 | ```javascript 89 | // bad 90 | var a = 1; 91 | var b = 2; 92 | 93 | // good 94 | const a = 1; 95 | const b = 2; 96 | ``` 97 | 98 | - [2.2](#2.2) 必须使用可变引用时,使用 `let` 而不是 `var`。 99 | 100 | > 为什么?使用块级作用域(`let`),而不是函数作用域(`var`)。 101 | 102 | ```javascript 103 | // bad 104 | var count = 1; 105 | if (true) { 106 | count += 1; 107 | } 108 | 109 | // good, use the let. 110 | let count = 1; 111 | if (true) { 112 | count += 1; 113 | } 114 | ``` 115 | 116 | - [2.3](#2.3) 注意:`let` 和 `const` 都是块级作用域。 117 | 118 | ```javascript 119 | // const 和 let 只存在于它们被定义的区块中 120 | { 121 | let a = 1; 122 | const b = 1; 123 | } 124 | 125 | console.log(a); // ReferenceError 126 | console.log(b); // ReferenceError 127 | ``` 128 | 129 | **[⬆ 返回目录](#table-of-contents)** 130 | 131 | 132 | ## Objects 133 | 134 | - [3.1](#3.1) 使用字面量语法创建对象。 135 | 136 | ```javascript 137 | // bad 138 | const item = new Object(); 139 | 140 | // good 141 | const item = {}; 142 | ``` 143 | 144 | - [3.2](#3.2) 如果代码在浏览器中执行,不要使用[保留字](http://es5.github.io/#x7.6.1)作为键值,不然的话在 IE8 中不会执行[更多信息](https://github.com/airbnb/javascript/issues/61)(but who care IE8?)。在 ES6 模块和服务器端代码中使用则没有问题。 145 | 146 | ```javascript 147 | // bad 148 | const superman = { 149 | default: { 150 | clark: 'kent' 151 | }, 152 | private: true, 153 | }; 154 | 155 | // good 156 | const superman = { 157 | defaults: { 158 | clark: 'kent' 159 | }, 160 | hidden: true, 161 | }; 162 | ``` 163 | 164 | - [3.3](#3.3) 使用可读的同义词替换需要使用的保留字。 165 | 166 | ```javascript 167 | // bad 168 | const superman = { 169 | class: 'alien', 170 | }; 171 | 172 | // bad 173 | const superman = { 174 | klass: 'alien', 175 | }; 176 | 177 | // good 178 | const superman = { 179 | type: 'alien', 180 | }; 181 | ``` 182 | 183 | 184 | - [3.4](#3.4) 创建包含动态属性名的对象时,使用经过计算的属性名称。 185 | 186 | > 为什么?这样可以一次定义所有属性。 187 | 188 | ```javascript 189 | 190 | function getKey(k) { 191 | return `a key named ${k}`; 192 | } 193 | 194 | // bad 195 | const obj = { 196 | id: 5, 197 | name: 'San Francisco', 198 | }; 199 | obj[getKey('enabled')] = true; 200 | 201 | // good 202 | const obj = { 203 | id: 5, 204 | name: 'San Francisco', 205 | [getKey('enabled')]: true, 206 | }; 207 | ``` 208 | 209 | 210 | - [3.5](#3.5) 使用对象方法简写语法(省略 `function` 关键字)。 211 | 212 | ```javascript 213 | // bad 214 | const atom = { 215 | value: 1, 216 | 217 | addValue: function (value) { 218 | return atom.value + value; 219 | }, 220 | }; 221 | 222 | // good 223 | const atom = { 224 | value: 1, 225 | 226 | addValue(value) { 227 | return atom.value + value; 228 | }, 229 | }; 230 | ``` 231 | 232 | 233 | - [3.6](#3.6) 使用属性值简写语法。 234 | 235 | > 为什么?这样更简洁、更具描述性。 236 | 237 | ```javascript 238 | const lukeSkywalker = 'Luke Skywalker'; 239 | 240 | // bad 241 | const obj = { 242 | lukeSkywalker: lukeSkywalker, 243 | }; 244 | 245 | // good 246 | const obj = { 247 | lukeSkywalker, 248 | }; 249 | ``` 250 | 251 | - [3.7](#3.7) 把简写的属性分组排列在对象声明顶部。 252 | > 为什么?这样更容易区分哪些属性采用了简写。 253 | 254 | ```javascript 255 | const anakinSkywalker = 'Anakin Skywalker'; 256 | const lukeSkywalker = 'Luke Skywalker'; 257 | 258 | // bad 259 | const obj = { 260 | episodeOne: 1, 261 | twoJediWalkIntoACantina: 2, 262 | lukeSkywalker, 263 | episodeThree: 3, 264 | mayTheFourth: 4, 265 | anakinSkywalker, 266 | }; 267 | 268 | // good 269 | const obj = { 270 | lukeSkywalker, 271 | anakinSkywalker, 272 | episodeOne: 1, 273 | twoJediWalkIntoACantina: 2, 274 | episodeThree: 3, 275 | mayTheFourth: 4, 276 | }; 277 | ``` 278 | 279 | **[⬆ 返回目录](#table-of-contents)** 280 | 281 | 282 | 283 | ## 数组 284 | 285 | - [4.1](#4.1) 使用字面量语法创建数组。 286 | 287 | ```javascript 288 | // bad 289 | const items = new Array(); 290 | 291 | // good 292 | const items = []; 293 | ``` 294 | 295 | - [4.2](#4.2) 使用 `push` 添加元素,而不是直接赋值。 296 | 297 | ```javascript 298 | const someStack = []; 299 | 300 | 301 | // bad 302 | someStack[someStack.length] = 'abracadabra'; 303 | 304 | // good 305 | someStack.push('abracadabra'); 306 | ``` 307 | 308 | 309 | - [4.3](#4.3) 使用数组扩展运算符 `...` 拷贝数组。 310 | 311 | ```javascript 312 | // bad 313 | const len = items.length; 314 | const itemsCopy = []; 315 | let i; 316 | 317 | for (i = 0; i < len; i++) { 318 | itemsCopy[i] = items[i]; 319 | } 320 | 321 | // good 322 | const itemsCopy = [...items]; 323 | ``` 324 | - [4.4](#4.4) 使用 `Array.from` 转换类数组对象。 325 | 326 | ```javascript 327 | const foo = document.querySelectorAll('.foo'); 328 | const nodes = Array.from(foo); 329 | ``` 330 | 331 | **[⬆ 返回目录](#table-of-contents)** 332 | 333 | 334 | 335 | ## 解构 336 | 337 | - [5.1](#5.1) 调用对象的多个属性时,使用解构语法。 338 | 339 | > 为什么?解构可以减少临时引用属性。 340 | 341 | ```javascript 342 | // bad 343 | function getFullName(user) { 344 | const firstName = user.firstName; 345 | const lastName = user.lastName; 346 | 347 | return `${firstName} ${lastName}`; 348 | } 349 | 350 | // good 351 | function getFullName(obj) { 352 | const { firstName, lastName } = obj; 353 | return `${firstName} ${lastName}`; 354 | } 355 | 356 | // best 357 | function getFullName({ firstName, lastName }) { 358 | return `${firstName} ${lastName}`; 359 | } 360 | ``` 361 | 362 | - [5.2](#5.2) 使用数组解构。 363 | 364 | 365 | ```javascript 366 | const arr = [1, 2, 3, 4]; 367 | 368 | // bad 369 | const first = arr[0]; 370 | const second = arr[1]; 371 | 372 | // good 373 | const [first, second] = arr; 374 | ``` 375 | 376 | - [5.3](#5.3) 多个返回值时使用对象解构,而不是数组。 377 | 378 | > 为什么?对象顺序不敏感,方便添加新属性。 379 | 380 | ```javascript 381 | // bad 382 | function processInput(input) { 383 | // then a miracle occurs 384 | return [left, right, top, bottom]; 385 | } 386 | 387 | // the caller needs to think about the order of return data 388 | const [left, __, top] = processInput(input); 389 | 390 | // good 391 | function processInput(input) { 392 | // then a miracle occurs 393 | return { left, right, top, bottom }; 394 | } 395 | 396 | // the caller selects only the data they need 397 | const { left, right } = processInput(input); 398 | ``` 399 | 400 | 401 | **[⬆ 返回目录](#table-of-contents)** 402 | 403 | 404 | ## 字符串 405 | 406 | - [6.1](#6.1) 使用单引号 `''`。 407 | 408 | ```javascript 409 | // bad 410 | const name = "Capt. Janeway"; 411 | 412 | // good 413 | const name = 'Capt. Janeway'; 414 | ``` 415 | 416 | - [6.2](#6.2) 长度超过 80 个字符的字符串应采用多行拼接。 417 | - [6.3](#6.3) 注意:过度使用长字符串拼接会影响性能。[jsPerf 测试](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40)。 418 | 419 | ```javascript 420 | // bad 421 | const 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.'; 422 | 423 | // bad 424 | const errorMessage = 'This is a super long error that was thrown because \ 425 | of Batman. When you stop to think about how Batman had anything to do \ 426 | with this, you would get nowhere \ 427 | fast.'; 428 | 429 | // good 430 | const errorMessage = 'This is a super long error that was thrown because ' + 431 | 'of Batman. When you stop to think about how Batman had anything to do ' + 432 | 'with this, you would get nowhere fast.'; 433 | ``` 434 | 435 | 436 | - [6.4](#6.4) 编程创建字符串时使用模板语法替代拼接。 437 | > 为什么?模板字符串提供换行、变量插入功能,让代码更简洁易读。 438 | 439 | ```javascript 440 | // bad 441 | function sayHi(name) { 442 | return 'How are you, ' + name + '?'; 443 | } 444 | 445 | // bad 446 | function sayHi(name) { 447 | return ['How are you, ', name, '?'].join(); 448 | } 449 | 450 | // good 451 | function sayHi(name) { 452 | return `How are you, ${name}?`; 453 | } 454 | ``` 455 | 456 | **[⬆ 返回目录](#table-of-contents)** 457 | 458 | 459 | 460 | ## Functions 461 | 462 | - [7.1](#7.1) 使用函数声明而不是函数表达式。 463 | 464 | > 为什么?函数声明是命名的,易于跟踪调试;函数声明把真个函数提升,而函数表达式只提升引用的变量名。此规则使得 [箭头函数](#arrow-functions) 可以替代函数表达式。 465 | 466 | ```javascript 467 | // bad 468 | const foo = function () { 469 | }; 470 | 471 | // good 472 | function foo() { 473 | } 474 | ``` 475 | 476 | - [7.2](#7.2) 函数表达式: 477 | 478 | ```javascript 479 | // immediately-invoked function expression (IIFE) 480 | (() => { 481 | console.log('Welcome to the Internet. Please follow me.'); 482 | })(); 483 | ``` 484 | 485 | - [7.3](#7.3) 绝不要在非函数代码块中声明函数(`if`、`while` 等等),可以把函数赋值给一个变量。浏览器允许这么做,但解析结果会不一致。 486 | 487 | - [7.4](#7.4) **注意:** ECMA-262 为一系列语句定义了「块」(`block`),函数什么并不包含在其中。 [阅读 ECMA-262 关于这个问题问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). 488 | 489 | ```javascript 490 | // bad 491 | if (currentUser) { 492 | function test() { 493 | console.log('Nope.'); 494 | } 495 | } 496 | 497 | // good 498 | let test; 499 | if (currentUser) { 500 | test = () => { 501 | console.log('Yup.'); 502 | }; 503 | } 504 | ``` 505 | 506 | - [7.5](#7.5) 绝不要把参数命名为 `arguments`,这会覆盖每个函数作用域里的 `arguments` 对象。 507 | 508 | ```javascript 509 | // bad 510 | function nope(name, options, arguments) { 511 | // ...stuff... 512 | } 513 | 514 | // good 515 | function yup(name, options, args) { 516 | // ...stuff... 517 | } 518 | ``` 519 | 520 | 521 | - [7.6](#7.6) 绝不要使用 `arguments`,使用 `...` 语法代替。 522 | 523 | > 为什么? `...` 明确地反应所处理的参数;`...` 语法返回一个真正的数组,而不是像 `arguments` 一样的类数组。 524 | 525 | ```javascript 526 | // bad 527 | function concatenateAll() { 528 | const args = Array.prototype.slice.call(arguments); 529 | return args.join(''); 530 | } 531 | 532 | // good 533 | function concatenateAll(...args) { 534 | return args.join(''); 535 | } 536 | ``` 537 | 538 | 539 | - [7.7](#7.7) 使用默认参数语法,而不是去改变函数参数。 540 | 541 | ```javascript 542 | // really bad 543 | function handleThings(opts) { 544 | // No! We shouldn't mutate function arguments. 545 | // Double bad: if opts is falsy it'll be set to an object which may 546 | // be what you want but it can introduce subtle bugs. 547 | opts = opts || {}; 548 | // ... 549 | } 550 | 551 | // still bad 552 | function handleThings(opts) { 553 | if (opts === void 0) { 554 | opts = {}; 555 | } 556 | // ... 557 | } 558 | 559 | // good 560 | function handleThings(opts = {}) { 561 | // ... 562 | } 563 | ``` 564 | 565 | - [7.8](#7.8) 设置默认参数时应避免产生其他影响。 566 | 567 | > 为什么?这会产生令人迷惑的结果。 568 | 569 | ```javascript 570 | var b = 1; 571 | // bad 572 | function count(a = b++) { 573 | console.log(a); 574 | } 575 | count(); // 1 576 | count(); // 2 577 | count(3); // 3 578 | count(); // 3 579 | ``` 580 | 581 | 582 | **[⬆ 返回目录](#table-of-contents)** 583 | 584 | 585 | ## 箭头函数 586 | 587 | - [8.1](#8.1) 必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数语法。 588 | > 为什么?箭头函数自动绑定执行上下文文为创建时的 `this`;更简洁。 589 | 590 | > 为什么不?当遇到复杂的函数时,或许应该把逻辑放到独立的函数声明中去。 591 | 592 | ```javascript 593 | // bad 594 | [1, 2, 3].map(function (x) { 595 | return x * x; 596 | }); 597 | 598 | // good 599 | [1, 2, 3].map((x) => { 600 | return x * x; 601 | }); 602 | ``` 603 | 604 | - [8.2](#8.2) 函数体只有一行、只有一个参数而且不需要返回值时,可以省略花括号和圆括号。否则,保留括号,并使用 `return` 语句。 605 | 606 | > 为什么?语法糖。多个函数连缀时可读性很好。 607 | > 为什么不?需要返回值的时候。 608 | 609 | ```javascript 610 | // good 611 | [1, 2, 3].map(x => x * x); 612 | 613 | // good 614 | [1, 2, 3].reduce((total, n) => { 615 | return total + n; 616 | }, 0); 617 | ``` 618 | 619 | **[⬆ 返回目录](#table-of-contents)** 620 | 621 | 622 | ## Constructors 623 | 624 | - [9.1](#9.1) Always use `class`. Avoid manipulating `prototype` directly. 625 | 626 | > Why? `class` syntax is more concise and easier to reason about. 627 | 628 | ```javascript 629 | // bad 630 | function Queue(contents = []) { 631 | this._queue = [...contents]; 632 | } 633 | Queue.prototype.pop = function() { 634 | const value = this._queue[0]; 635 | this._queue.splice(0, 1); 636 | return value; 637 | } 638 | 639 | 640 | // good 641 | class Queue { 642 | constructor(contents = []) { 643 | this._queue = [...contents]; 644 | } 645 | pop() { 646 | const value = this._queue[0]; 647 | this._queue.splice(0, 1); 648 | return value; 649 | } 650 | } 651 | ``` 652 | 653 | - [9.2](#9.2) Use `extends` for inheritance. 654 | 655 | > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`. 656 | 657 | ```javascript 658 | // bad 659 | const inherits = require('inherits'); 660 | function PeekableQueue(contents) { 661 | Queue.apply(this, contents); 662 | } 663 | inherits(PeekableQueue, Queue); 664 | PeekableQueue.prototype.peek = function() { 665 | return this._queue[0]; 666 | } 667 | 668 | // good 669 | class PeekableQueue extends Queue { 670 | peek() { 671 | return this._queue[0]; 672 | } 673 | } 674 | ``` 675 | 676 | - [9.3](#9.3) Methods can return `this` to help with method chaining. 677 | 678 | ```javascript 679 | // bad 680 | Jedi.prototype.jump = function() { 681 | this.jumping = true; 682 | return true; 683 | }; 684 | 685 | Jedi.prototype.setHeight = function(height) { 686 | this.height = height; 687 | }; 688 | 689 | const luke = new Jedi(); 690 | luke.jump(); // => true 691 | luke.setHeight(20); // => undefined 692 | 693 | // good 694 | class Jedi { 695 | jump() { 696 | this.jumping = true; 697 | return this; 698 | } 699 | 700 | setHeight(height) { 701 | this.height = height; 702 | return this; 703 | } 704 | } 705 | 706 | const luke = new Jedi(); 707 | 708 | luke.jump() 709 | .setHeight(20); 710 | ``` 711 | 712 | 713 | - [9.4](#9.4) It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. 714 | 715 | ```javascript 716 | class Jedi { 717 | constructor(options = {}) { 718 | this.name = options.name || 'no name'; 719 | } 720 | 721 | getName() { 722 | return this.name; 723 | } 724 | 725 | toString() { 726 | return `Jedi - ${this.getName()}`; 727 | } 728 | } 729 | ``` 730 | 731 | **[⬆ 返回目录](#table-of-contents)** 732 | 733 | 734 | ## Modules 735 | 736 | - [10.1](#10.1) Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system. 737 | 738 | > Why? Modules are the future, let's start using the future now. 739 | 740 | ```javascript 741 | // bad 742 | const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 743 | module.exports = AirbnbStyleGuide.es6; 744 | 745 | // ok 746 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 747 | export default AirbnbStyleGuide.es6; 748 | 749 | // best 750 | import { es6 } from './AirbnbStyleGuide'; 751 | export default es6; 752 | ``` 753 | 754 | - [10.2](#10.2) Do not use wildcard imports. 755 | 756 | > Why? This makes sure you have a single default export. 757 | 758 | ```javascript 759 | // bad 760 | import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 761 | 762 | // good 763 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 764 | ``` 765 | 766 | - [10.3](#10.3) And do not export directly from an import. 767 | 768 | > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent. 769 | 770 | ```javascript 771 | // bad 772 | // filename es6.js 773 | export { es6 as default } from './airbnbStyleGuide'; 774 | 775 | // good 776 | // filename es6.js 777 | import { es6 } from './AirbnbStyleGuide'; 778 | export default es6; 779 | ``` 780 | 781 | **[⬆ 返回目录](#table-of-contents)** 782 | 783 | ## Iterators and Generators 784 | 785 | - [11.1](#11.1) Don't use iterators. Prefer JavaScript's higher-order functions like `map()` and `reduce()` instead of loops like `for-of`. 786 | 787 | > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects. 788 | 789 | ```javascript 790 | const numbers = [1, 2, 3, 4, 5]; 791 | 792 | // bad 793 | let sum = 0; 794 | for (let num of numbers) { 795 | sum += num; 796 | } 797 | 798 | sum === 15; 799 | 800 | // good 801 | let sum = 0; 802 | numbers.forEach((num) => sum += num); 803 | sum === 15; 804 | 805 | // best (use the functional force) 806 | const sum = numbers.reduce((total, num) => total + num, 0); 807 | sum === 15; 808 | ``` 809 | 810 | - [11.2](#11.2) Don't use generators for now. 811 | 812 | > Why? They don't transpile well to ES5. 813 | 814 | **[⬆ 返回目录](#table-of-contents)** 815 | 816 | 817 | ## Properties 818 | 819 | - [12.1](#12.1) Use dot notation when accessing properties. 820 | 821 | ```javascript 822 | const luke = { 823 | jedi: true, 824 | age: 28, 825 | }; 826 | 827 | // bad 828 | const isJedi = luke['jedi']; 829 | 830 | // good 831 | const isJedi = luke.jedi; 832 | ``` 833 | 834 | - [12.2](#12.2) Use subscript notation `[]` when accessing properties with a variable. 835 | 836 | ```javascript 837 | const luke = { 838 | jedi: true, 839 | age: 28, 840 | }; 841 | 842 | function getProp(prop) { 843 | return luke[prop]; 844 | } 845 | 846 | const isJedi = getProp('jedi'); 847 | ``` 848 | 849 | **[⬆ 返回目录](#table-of-contents)** 850 | 851 | 852 | ## Variables 853 | 854 | - [13.1](#13.1) Always use `const` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 855 | 856 | ```javascript 857 | // bad 858 | superPower = new SuperPower(); 859 | 860 | // good 861 | const superPower = new SuperPower(); 862 | ``` 863 | 864 | - [13.2](#13.2) Use one `const` declaration per variable. 865 | 866 | > Why? It's easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs. 867 | 868 | ```javascript 869 | // bad 870 | const items = getItems(), 871 | goSportsTeam = true, 872 | dragonball = 'z'; 873 | 874 | // bad 875 | // (compare to above, and try to spot the mistake) 876 | const items = getItems(), 877 | goSportsTeam = true; 878 | dragonball = 'z'; 879 | 880 | // good 881 | const items = getItems(); 882 | const goSportsTeam = true; 883 | const dragonball = 'z'; 884 | ``` 885 | 886 | - [13.3](#13.3) Group all your `const`s and then group all your `let`s. 887 | 888 | > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 889 | 890 | ```javascript 891 | // bad 892 | let i, len, dragonball, 893 | items = getItems(), 894 | goSportsTeam = true; 895 | 896 | // bad 897 | let i; 898 | const items = getItems(); 899 | let dragonball; 900 | const goSportsTeam = true; 901 | let len; 902 | 903 | // good 904 | const goSportsTeam = true; 905 | const items = getItems(); 906 | let dragonball; 907 | let i; 908 | let length; 909 | ``` 910 | 911 | - [13.4](#13.4) Assign variables where you need them, but place them in a reasonable place. 912 | 913 | > Why? `let` and `const` are block scoped and not function scoped. 914 | 915 | ```javascript 916 | // good 917 | function() { 918 | test(); 919 | console.log('doing stuff..'); 920 | 921 | //..other stuff.. 922 | 923 | const name = getName(); 924 | 925 | if (name === 'test') { 926 | return false; 927 | } 928 | 929 | return name; 930 | } 931 | 932 | // bad - unnecessary function call 933 | function(hasName) { 934 | const name = getName(); 935 | 936 | if (!hasName) { 937 | return false; 938 | } 939 | 940 | this.setFirstName(name); 941 | 942 | return true; 943 | } 944 | 945 | // good 946 | function(hasName) { 947 | if (!hasName) { 948 | return false; 949 | } 950 | 951 | const name = getName(); 952 | this.setFirstName(name); 953 | 954 | return true; 955 | } 956 | ``` 957 | 958 | **[⬆ 返回目录](#table-of-contents)** 959 | 960 | 961 | ## Hoisting 962 | 963 | - [14.1](#14.1) `var` declarations get hoisted to the top of their scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let). It's important to know why [typeof is no longer safe](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15). 964 | 965 | ```javascript 966 | // we know this wouldn't work (assuming there 967 | // is no notDefined global variable) 968 | function example() { 969 | console.log(notDefined); // => throws a ReferenceError 970 | } 971 | 972 | // creating a variable declaration after you 973 | // reference the variable will work due to 974 | // variable hoisting. Note: the assignment 975 | // value of `true` is not hoisted. 976 | function example() { 977 | console.log(declaredButNotAssigned); // => undefined 978 | var declaredButNotAssigned = true; 979 | } 980 | 981 | // The interpreter is hoisting the variable 982 | // declaration to the top of the scope, 983 | // which means our example could be rewritten as: 984 | function example() { 985 | let declaredButNotAssigned; 986 | console.log(declaredButNotAssigned); // => undefined 987 | declaredButNotAssigned = true; 988 | } 989 | 990 | // using const and let 991 | function example() { 992 | console.log(declaredButNotAssigned); // => throws a ReferenceError 993 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError 994 | const declaredButNotAssigned = true; 995 | } 996 | ``` 997 | 998 | - [14.2](#14.2) Anonymous function expressions hoist their variable name, but not the function assignment. 999 | 1000 | ```javascript 1001 | function example() { 1002 | console.log(anonymous); // => undefined 1003 | 1004 | anonymous(); // => TypeError anonymous is not a function 1005 | 1006 | var anonymous = function() { 1007 | console.log('anonymous function expression'); 1008 | }; 1009 | } 1010 | ``` 1011 | 1012 | - [14.3](#14.3) Named function expressions hoist the variable name, not the function name or the function body. 1013 | 1014 | ```javascript 1015 | function example() { 1016 | console.log(named); // => undefined 1017 | 1018 | named(); // => TypeError named is not a function 1019 | 1020 | superPower(); // => ReferenceError superPower is not defined 1021 | 1022 | var named = function superPower() { 1023 | console.log('Flying'); 1024 | }; 1025 | } 1026 | 1027 | // the same is true when the function name 1028 | // is the same as the variable name. 1029 | function example() { 1030 | console.log(named); // => undefined 1031 | 1032 | named(); // => TypeError named is not a function 1033 | 1034 | var named = function named() { 1035 | console.log('named'); 1036 | } 1037 | } 1038 | ``` 1039 | 1040 | - [14.4](#14.4) Function declarations hoist their name and the function body. 1041 | 1042 | ```javascript 1043 | function example() { 1044 | superPower(); // => Flying 1045 | 1046 | function superPower() { 1047 | console.log('Flying'); 1048 | } 1049 | } 1050 | ``` 1051 | 1052 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). 1053 | 1054 | **[⬆ 返回目录](#table-of-contents)** 1055 | 1056 | 1057 | ## Comparison Operators & Equality 1058 | 1059 | - [15.1](#15.1) Use `===` and `!==` over `==` and `!=`. 1060 | - [15.2](#15.2) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules: 1061 | 1062 | + **Objects** evaluate to **true** 1063 | + **Undefined** evaluates to **false** 1064 | + **Null** evaluates to **false** 1065 | + **Booleans** evaluate to **the value of the boolean** 1066 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 1067 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** 1068 | 1069 | ```javascript 1070 | if ([0]) { 1071 | // true 1072 | // An array is an object, objects evaluate to true 1073 | } 1074 | ``` 1075 | 1076 | - [15.3](#15.3) Use shortcuts. 1077 | 1078 | ```javascript 1079 | // bad 1080 | if (name !== '') { 1081 | // ...stuff... 1082 | } 1083 | 1084 | // good 1085 | if (name) { 1086 | // ...stuff... 1087 | } 1088 | 1089 | // bad 1090 | if (collection.length > 0) { 1091 | // ...stuff... 1092 | } 1093 | 1094 | // good 1095 | if (collection.length) { 1096 | // ...stuff... 1097 | } 1098 | ``` 1099 | 1100 | - [15.4](#15.4) For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. 1101 | 1102 | **[⬆ 返回目录](#table-of-contents)** 1103 | 1104 | 1105 | ## Blocks 1106 | 1107 | - [16.1](#16.1) Use braces with all multi-line blocks. 1108 | 1109 | ```javascript 1110 | // bad 1111 | if (test) 1112 | return false; 1113 | 1114 | // good 1115 | if (test) return false; 1116 | 1117 | // good 1118 | if (test) { 1119 | return false; 1120 | } 1121 | 1122 | // bad 1123 | function() { return false; } 1124 | 1125 | // good 1126 | function() { 1127 | return false; 1128 | } 1129 | ``` 1130 | 1131 | - [16.2](#16.2) If you're using multi-line blocks with `if` and `else`, put `else` on the same line as your 1132 | `if` block's closing brace. 1133 | 1134 | ```javascript 1135 | // bad 1136 | if (test) { 1137 | thing1(); 1138 | thing2(); 1139 | } 1140 | else { 1141 | thing3(); 1142 | } 1143 | 1144 | // good 1145 | if (test) { 1146 | thing1(); 1147 | thing2(); 1148 | } else { 1149 | thing3(); 1150 | } 1151 | ``` 1152 | 1153 | 1154 | **[⬆ 返回目录](#table-of-contents)** 1155 | 1156 | 1157 | ## Comments 1158 | 1159 | - [17.1](#17.1) Use `/** ... */` for multi-line comments. Include a description, specify types and values for all parameters and return values. 1160 | 1161 | ```javascript 1162 | // bad 1163 | // make() returns a new element 1164 | // based on the passed in tag name 1165 | // 1166 | // @param {String} tag 1167 | // @return {Element} element 1168 | function make(tag) { 1169 | 1170 | // ...stuff... 1171 | 1172 | return element; 1173 | } 1174 | 1175 | // good 1176 | /** 1177 | * make() returns a new element 1178 | * based on the passed in tag name 1179 | * 1180 | * @param {String} tag 1181 | * @return {Element} element 1182 | */ 1183 | function make(tag) { 1184 | 1185 | // ...stuff... 1186 | 1187 | return element; 1188 | } 1189 | ``` 1190 | 1191 | - [17.2](#17.2) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. 1192 | 1193 | ```javascript 1194 | // bad 1195 | const active = true; // is current tab 1196 | 1197 | // good 1198 | // is current tab 1199 | const active = true; 1200 | 1201 | // bad 1202 | function getType() { 1203 | console.log('fetching type...'); 1204 | // set the default type to 'no type' 1205 | const type = this._type || 'no type'; 1206 | 1207 | return type; 1208 | } 1209 | 1210 | // good 1211 | function getType() { 1212 | console.log('fetching type...'); 1213 | 1214 | // set the default type to 'no type' 1215 | const type = this._type || 'no type'; 1216 | 1217 | return type; 1218 | } 1219 | ``` 1220 | 1221 | - [17.3](#17.3) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 1222 | 1223 | - [17.4](#17.4) Use `// FIXME:` to annotate problems. 1224 | 1225 | ```javascript 1226 | class Calculator { 1227 | constructor() { 1228 | // FIXME: shouldn't use a global here 1229 | total = 0; 1230 | } 1231 | } 1232 | ``` 1233 | 1234 | - [17.5](#17.5) Use `// TODO:` to annotate solutions to problems. 1235 | 1236 | ```javascript 1237 | class Calculator { 1238 | constructor() { 1239 | // TODO: total should be configurable by an options param 1240 | this.total = 0; 1241 | } 1242 | } 1243 | ``` 1244 | 1245 | **[⬆ 返回目录](#table-of-contents)** 1246 | 1247 | 1248 | ## Whitespace 1249 | 1250 | - [18.1](#18.1) Use soft tabs set to 2 spaces. 1251 | 1252 | ```javascript 1253 | // bad 1254 | function() { 1255 | ∙∙∙∙const name; 1256 | } 1257 | 1258 | // bad 1259 | function() { 1260 | ∙const name; 1261 | } 1262 | 1263 | // good 1264 | function() { 1265 | ∙∙const name; 1266 | } 1267 | ``` 1268 | 1269 | - [18.2](#18.2) Place 1 space before the leading brace. 1270 | 1271 | ```javascript 1272 | // bad 1273 | function test(){ 1274 | console.log('test'); 1275 | } 1276 | 1277 | // good 1278 | function test() { 1279 | console.log('test'); 1280 | } 1281 | 1282 | // bad 1283 | dog.set('attr',{ 1284 | age: '1 year', 1285 | breed: 'Bernese Mountain Dog', 1286 | }); 1287 | 1288 | // good 1289 | dog.set('attr', { 1290 | age: '1 year', 1291 | breed: 'Bernese Mountain Dog', 1292 | }); 1293 | ``` 1294 | 1295 | - [18.3](#18.3) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space before the argument list in function calls and declarations. 1296 | 1297 | ```javascript 1298 | // bad 1299 | if(isJedi) { 1300 | fight (); 1301 | } 1302 | 1303 | // good 1304 | if (isJedi) { 1305 | fight(); 1306 | } 1307 | 1308 | // bad 1309 | function fight () { 1310 | console.log ('Swooosh!'); 1311 | } 1312 | 1313 | // good 1314 | function fight() { 1315 | console.log('Swooosh!'); 1316 | } 1317 | ``` 1318 | 1319 | - [18.4](#18.4) Set off operators with spaces. 1320 | 1321 | ```javascript 1322 | // bad 1323 | const x=y+5; 1324 | 1325 | // good 1326 | const x = y + 5; 1327 | ``` 1328 | 1329 | - [18.5](#18.5) End files with a single newline character. 1330 | 1331 | ```javascript 1332 | // bad 1333 | (function(global) { 1334 | // ...stuff... 1335 | })(this); 1336 | ``` 1337 | 1338 | ```javascript 1339 | // bad 1340 | (function(global) { 1341 | // ...stuff... 1342 | })(this);↵ 1343 | ↵ 1344 | ``` 1345 | 1346 | ```javascript 1347 | // good 1348 | (function(global) { 1349 | // ...stuff... 1350 | })(this);↵ 1351 | ``` 1352 | 1353 | - [18.5](#18.5) Use indentation when making long method chains. Use a leading dot, which 1354 | emphasizes that the line is a method call, not a new statement. 1355 | 1356 | ```javascript 1357 | // bad 1358 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 1359 | 1360 | // bad 1361 | $('#items'). 1362 | find('.selected'). 1363 | highlight(). 1364 | end(). 1365 | find('.open'). 1366 | updateCount(); 1367 | 1368 | // good 1369 | $('#items') 1370 | .find('.selected') 1371 | .highlight() 1372 | .end() 1373 | .find('.open') 1374 | .updateCount(); 1375 | 1376 | // bad 1377 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 1378 | .attr('width', (radius + margin) * 2).append('svg:g') 1379 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1380 | .call(tron.led); 1381 | 1382 | // good 1383 | const leds = stage.selectAll('.led') 1384 | .data(data) 1385 | .enter().append('svg:svg') 1386 | .classed('led', true) 1387 | .attr('width', (radius + margin) * 2) 1388 | .append('svg:g') 1389 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1390 | .call(tron.led); 1391 | ``` 1392 | 1393 | - [18.6](#18.6) Leave a blank line after blocks and before the next statement. 1394 | 1395 | ```javascript 1396 | // bad 1397 | if (foo) { 1398 | return bar; 1399 | } 1400 | return baz; 1401 | 1402 | // good 1403 | if (foo) { 1404 | return bar; 1405 | } 1406 | 1407 | return baz; 1408 | 1409 | // bad 1410 | const obj = { 1411 | foo() { 1412 | }, 1413 | bar() { 1414 | }, 1415 | }; 1416 | return obj; 1417 | 1418 | // good 1419 | const obj = { 1420 | foo() { 1421 | }, 1422 | 1423 | bar() { 1424 | }, 1425 | }; 1426 | 1427 | return obj; 1428 | ``` 1429 | 1430 | 1431 | **[⬆ 返回目录](#table-of-contents)** 1432 | 1433 | ## Commas 1434 | 1435 | - [19.1](#19.1) Leading commas: **Nope.** 1436 | 1437 | ```javascript 1438 | // bad 1439 | const story = [ 1440 | once 1441 | , upon 1442 | , aTime 1443 | ]; 1444 | 1445 | // good 1446 | const story = [ 1447 | once, 1448 | upon, 1449 | aTime, 1450 | ]; 1451 | 1452 | // bad 1453 | const hero = { 1454 | firstName: 'Ada' 1455 | , lastName: 'Lovelace' 1456 | , birthYear: 1815 1457 | , superPower: 'computers' 1458 | }; 1459 | 1460 | // good 1461 | const hero = { 1462 | firstName: 'Ada', 1463 | lastName: 'Lovelace', 1464 | birthYear: 1815, 1465 | superPower: 'computers', 1466 | }; 1467 | ``` 1468 | 1469 | - [19.2](#19.2) Additional trailing comma: **Yup.** 1470 | 1471 | > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don't have to worry about the [trailing comma problem](es5/README.md#commas) in legacy browsers. 1472 | 1473 | ```javascript 1474 | // bad - git diff without trailing comma 1475 | const hero = { 1476 | firstName: 'Florence', 1477 | - lastName: 'Nightingale' 1478 | + lastName: 'Nightingale', 1479 | + inventorOf: ['coxcomb graph', 'modern nursing'] 1480 | } 1481 | 1482 | // good - git diff with trailing comma 1483 | const hero = { 1484 | firstName: 'Florence', 1485 | lastName: 'Nightingale', 1486 | + inventorOf: ['coxcomb chart', 'modern nursing'], 1487 | } 1488 | 1489 | // bad 1490 | const hero = { 1491 | firstName: 'Dana', 1492 | lastName: 'Scully' 1493 | }; 1494 | 1495 | const heroes = [ 1496 | 'Batman', 1497 | 'Superman' 1498 | ]; 1499 | 1500 | // good 1501 | const hero = { 1502 | firstName: 'Dana', 1503 | lastName: 'Scully', 1504 | }; 1505 | 1506 | const heroes = [ 1507 | 'Batman', 1508 | 'Superman', 1509 | ]; 1510 | ``` 1511 | 1512 | **[⬆ 返回目录](#table-of-contents)** 1513 | 1514 | 1515 | ## Semicolons 1516 | 1517 | - [20.1](#20.1) **Yup.** 1518 | 1519 | ```javascript 1520 | // bad 1521 | (function() { 1522 | const name = 'Skywalker' 1523 | return name 1524 | })() 1525 | 1526 | // good 1527 | (() => { 1528 | const name = 'Skywalker'; 1529 | return name; 1530 | })(); 1531 | 1532 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated) 1533 | ;(() => { 1534 | const name = 'Skywalker'; 1535 | return name; 1536 | })(); 1537 | ``` 1538 | 1539 | [Read more](http://stackoverflow.com/a/7365214/1712802). 1540 | 1541 | **[⬆ 返回目录](#table-of-contents)** 1542 | 1543 | 1544 | ## Type Casting & Coercion 1545 | 1546 | - [21.1](#21.1) Perform type coercion at the beginning of the statement. 1547 | - [21.2](#21.2) Strings: 1548 | 1549 | ```javascript 1550 | // => this.reviewScore = 9; 1551 | 1552 | // bad 1553 | const totalScore = this.reviewScore + ''; 1554 | 1555 | // good 1556 | const totalScore = String(this.reviewScore); 1557 | ``` 1558 | 1559 | - [21.3](#21.3) Use `parseInt` for Numbers and always with a radix for type casting. 1560 | 1561 | ```javascript 1562 | const inputValue = '4'; 1563 | 1564 | // bad 1565 | const val = new Number(inputValue); 1566 | 1567 | // bad 1568 | const val = +inputValue; 1569 | 1570 | // bad 1571 | const val = inputValue >> 0; 1572 | 1573 | // bad 1574 | const val = parseInt(inputValue); 1575 | 1576 | // good 1577 | const val = Number(inputValue); 1578 | 1579 | // good 1580 | const val = parseInt(inputValue, 10); 1581 | ``` 1582 | 1583 | - [21.4](#21.4) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. 1584 | 1585 | ```javascript 1586 | // good 1587 | /** 1588 | * parseInt was the reason my code was slow. 1589 | * Bitshifting the String to coerce it to a 1590 | * Number made it a lot faster. 1591 | */ 1592 | const val = inputValue >> 0; 1593 | ``` 1594 | 1595 | - [21.5](#21.5) **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647: 1596 | 1597 | ```javascript 1598 | 2147483647 >> 0 //=> 2147483647 1599 | 2147483648 >> 0 //=> -2147483648 1600 | 2147483649 >> 0 //=> -2147483647 1601 | ``` 1602 | 1603 | - [21.6](#21.6) Booleans: 1604 | 1605 | ```javascript 1606 | const age = 0; 1607 | 1608 | // bad 1609 | const hasAge = new Boolean(age); 1610 | 1611 | // good 1612 | const hasAge = Boolean(age); 1613 | 1614 | // good 1615 | const hasAge = !!age; 1616 | ``` 1617 | 1618 | **[⬆ 返回目录](#table-of-contents)** 1619 | 1620 | 1621 | ## Naming Conventions 1622 | 1623 | - [22.1](#22.1) Avoid single letter names. Be descriptive with your naming. 1624 | 1625 | ```javascript 1626 | // bad 1627 | function q() { 1628 | // ...stuff... 1629 | } 1630 | 1631 | // good 1632 | function query() { 1633 | // ..stuff.. 1634 | } 1635 | ``` 1636 | 1637 | - [22.2](#22.2) Use camelCase when naming objects, functions, and instances. 1638 | 1639 | ```javascript 1640 | // bad 1641 | const OBJEcttsssss = {}; 1642 | const this_is_my_object = {}; 1643 | function c() {} 1644 | 1645 | // good 1646 | const thisIsMyObject = {}; 1647 | function thisIsMyFunction() {} 1648 | ``` 1649 | 1650 | - [22.3](#22.3) Use PascalCase when naming constructors or classes. 1651 | 1652 | ```javascript 1653 | // bad 1654 | function user(options) { 1655 | this.name = options.name; 1656 | } 1657 | 1658 | const bad = new user({ 1659 | name: 'nope', 1660 | }); 1661 | 1662 | // good 1663 | class User { 1664 | constructor(options) { 1665 | this.name = options.name; 1666 | } 1667 | } 1668 | 1669 | const good = new User({ 1670 | name: 'yup', 1671 | }); 1672 | ``` 1673 | 1674 | - [22.4](#22.4) Use a leading underscore `_` when naming private properties. 1675 | 1676 | ```javascript 1677 | // bad 1678 | this.__firstName__ = 'Panda'; 1679 | this.firstName_ = 'Panda'; 1680 | 1681 | // good 1682 | this._firstName = 'Panda'; 1683 | ``` 1684 | 1685 | - [22.5](#22.5) Don't save references to `this`. Use arrow functions or Function#bind. 1686 | 1687 | ```javascript 1688 | // bad 1689 | function foo() { 1690 | const self = this; 1691 | return function() { 1692 | console.log(self); 1693 | }; 1694 | } 1695 | 1696 | // bad 1697 | function foo() { 1698 | const that = this; 1699 | return function() { 1700 | console.log(that); 1701 | }; 1702 | } 1703 | 1704 | // good 1705 | function foo() { 1706 | return () => { 1707 | console.log(this); 1708 | }; 1709 | } 1710 | ``` 1711 | 1712 | - [22.6](#22.6) If your file exports a single class, your filename should be exactly the name of the class. 1713 | ```javascript 1714 | // file contents 1715 | class CheckBox { 1716 | // ... 1717 | } 1718 | export default CheckBox; 1719 | 1720 | // in some other file 1721 | // bad 1722 | import CheckBox from './checkBox'; 1723 | 1724 | // bad 1725 | import CheckBox from './check_box'; 1726 | 1727 | // good 1728 | import CheckBox from './CheckBox'; 1729 | ``` 1730 | 1731 | - [22.7](#22.7) Use camelCase when you export-default a function. Your filename should be identical to your function's name. 1732 | 1733 | ```javascript 1734 | function makeStyleGuide() { 1735 | } 1736 | 1737 | export default makeStyleGuide; 1738 | ``` 1739 | 1740 | - [22.8](#22.8) Use PascalCase when you export a singleton / function library / bare object. 1741 | 1742 | ```javascript 1743 | const AirbnbStyleGuide = { 1744 | es6: { 1745 | } 1746 | }; 1747 | 1748 | export default AirbnbStyleGuide; 1749 | ``` 1750 | 1751 | 1752 | **[⬆ 返回目录](#table-of-contents)** 1753 | 1754 | 1755 | ## Accessors 1756 | 1757 | - [23.1](#23.1) Accessor functions for properties are not required. 1758 | - [23.2](#23.2) If you do make accessor functions use getVal() and setVal('hello'). 1759 | 1760 | ```javascript 1761 | // bad 1762 | dragon.age(); 1763 | 1764 | // good 1765 | dragon.getAge(); 1766 | 1767 | // bad 1768 | dragon.age(25); 1769 | 1770 | // good 1771 | dragon.setAge(25); 1772 | ``` 1773 | 1774 | - [23.3](#23.3) If the property is a `boolean`, use `isVal()` or `hasVal()`. 1775 | 1776 | ```javascript 1777 | // bad 1778 | if (!dragon.age()) { 1779 | return false; 1780 | } 1781 | 1782 | // good 1783 | if (!dragon.hasAge()) { 1784 | return false; 1785 | } 1786 | ``` 1787 | 1788 | - [23.4](#23.4) It's okay to create get() and set() functions, but be consistent. 1789 | 1790 | ```javascript 1791 | class Jedi { 1792 | constructor(options = {}) { 1793 | const lightsaber = options.lightsaber || 'blue'; 1794 | this.set('lightsaber', lightsaber); 1795 | } 1796 | 1797 | set(key, val) { 1798 | this[key] = val; 1799 | } 1800 | 1801 | get(key) { 1802 | return this[key]; 1803 | } 1804 | } 1805 | ``` 1806 | 1807 | **[⬆ 返回目录](#table-of-contents)** 1808 | 1809 | 1810 | ## Events 1811 | 1812 | - [24.1](#24.1) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1813 | 1814 | ```javascript 1815 | // bad 1816 | $(this).trigger('listingUpdated', listing.id); 1817 | 1818 | ... 1819 | 1820 | $(this).on('listingUpdated', function(e, listingId) { 1821 | // do something with listingId 1822 | }); 1823 | ``` 1824 | 1825 | prefer: 1826 | 1827 | ```javascript 1828 | // good 1829 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1830 | 1831 | ... 1832 | 1833 | $(this).on('listingUpdated', function(e, data) { 1834 | // do something with data.listingId 1835 | }); 1836 | ``` 1837 | 1838 | **[⬆ 返回目录 1839 | ](#table-of-contents)** 1840 | 1841 | 1842 | ## jQuery 1843 | 1844 | - [25.1](#25.1) Prefix jQuery object variables with a `$`. 1845 | 1846 | ```javascript 1847 | // bad 1848 | const sidebar = $('.sidebar'); 1849 | 1850 | // good 1851 | const $sidebar = $('.sidebar'); 1852 | ``` 1853 | 1854 | - [25.2](#25.2) Cache jQuery lookups. 1855 | 1856 | ```javascript 1857 | // bad 1858 | function setSidebar() { 1859 | $('.sidebar').hide(); 1860 | 1861 | // ...stuff... 1862 | 1863 | $('.sidebar').css({ 1864 | 'background-color': 'pink' 1865 | }); 1866 | } 1867 | 1868 | // good 1869 | function setSidebar() { 1870 | const $sidebar = $('.sidebar'); 1871 | $sidebar.hide(); 1872 | 1873 | // ...stuff... 1874 | 1875 | $sidebar.css({ 1876 | 'background-color': 'pink' 1877 | }); 1878 | } 1879 | ``` 1880 | 1881 | - [25.3](#25.3) For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1882 | - [25.4](#25.4) Use `find` with scoped jQuery object queries. 1883 | 1884 | ```javascript 1885 | // bad 1886 | $('ul', '.sidebar').hide(); 1887 | 1888 | // bad 1889 | $('.sidebar').find('ul').hide(); 1890 | 1891 | // good 1892 | $('.sidebar ul').hide(); 1893 | 1894 | // good 1895 | $('.sidebar > ul').hide(); 1896 | 1897 | // good 1898 | $sidebar.find('ul').hide(); 1899 | ``` 1900 | 1901 | **[⬆ 返回目录](#table-of-contents)** 1902 | 1903 | 1904 | ## ECMAScript 5 Compatibility 1905 | 1906 | - [26.1](#26.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/). 1907 | 1908 | **[⬆ 返回目录](#table-of-contents)** 1909 | 1910 | ## ECMAScript 6 Styles 1911 | 1912 | - [27.1](#27.1) This is a collection of links to the various es6 features. 1913 | 1914 | 1. [Arrow Functions](#arrow-functions) 1915 | 1. [Classes](#constructors) 1916 | 1. [Object Shorthand](#es6-object-shorthand) 1917 | 1. [Object Concise](#es6-object-concise) 1918 | 1. [Object Computed Properties](#es6-computed-properties) 1919 | 1. [Template Strings](#es6-template-literals) 1920 | 1. [Destructuring](#destructuring) 1921 | 1. [Default Parameters](#es6-default-parameters) 1922 | 1. [Rest](#es6-rest) 1923 | 1. [Array Spreads](#es6-array-spreads) 1924 | 1. [Let and Const](#references) 1925 | 1. [Iterators and Generators](#iterators-and-generators) 1926 | 1. [Modules](#modules) 1927 | 1928 | **[⬆ 返回目录](#table-of-contents)** 1929 | 1930 | ## Testing 1931 | 1932 | - [28.1](#28.1) **Yup.** 1933 | 1934 | ```javascript 1935 | function() { 1936 | return true; 1937 | } 1938 | ``` 1939 | 1940 | **[⬆ 返回目录](#table-of-contents)** 1941 | 1942 | 1943 | ## Performance 1944 | 1945 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 1946 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 1947 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 1948 | - [Bang Function](http://jsperf.com/bang-function) 1949 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 1950 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 1951 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 1952 | - Loading... 1953 | 1954 | **[⬆ 返回目录](#table-of-contents)** 1955 | 1956 | 1957 | ## Resources 1958 | 1959 | **Learning ES6** 1960 | 1961 | - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) 1962 | - [ExploringJS](http://exploringjs.com/) 1963 | - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) 1964 | - [Comprehensive Overview of ES6 Features](http://es6-features.org/) 1965 | 1966 | **Read This** 1967 | 1968 | - [Annotated ECMAScript 5.1](http://es5.github.com/) 1969 | 1970 | **Tools** 1971 | 1972 | - Code Style Linters 1973 | + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) 1974 | + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) 1975 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) 1976 | 1977 | **Other Style Guides** 1978 | 1979 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 1980 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 1981 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) 1982 | 1983 | **Other Styles** 1984 | 1985 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen 1986 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen 1987 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun 1988 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman 1989 | 1990 | **Further Reading** 1991 | 1992 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll 1993 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer 1994 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz 1995 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban 1996 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock 1997 | 1998 | **Books** 1999 | 2000 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford 2001 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov 2002 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz 2003 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders 2004 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas 2005 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw 2006 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig 2007 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch 2008 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault 2009 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg 2010 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy 2011 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon 2012 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov 2013 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman 2014 | - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke 2015 | 2016 | **Blogs** 2017 | 2018 | - [DailyJS](http://dailyjs.com/) 2019 | - [JavaScript Weekly](http://javascriptweekly.com/) 2020 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) 2021 | - [Bocoup Weblog](http://weblog.bocoup.com/) 2022 | - [Adequately Good](http://www.adequatelygood.com/) 2023 | - [NCZOnline](http://www.nczonline.net/) 2024 | - [Perfection Kills](http://perfectionkills.com/) 2025 | - [Ben Alman](http://benalman.com/) 2026 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) 2027 | - [Dustin Diaz](http://dustindiaz.com/) 2028 | - [nettuts](http://net.tutsplus.com/?s=javascript) 2029 | 2030 | **Podcasts** 2031 | 2032 | - [JavaScript Jabber](http://devchat.tv/js-jabber/) 2033 | 2034 | 2035 | **[⬆ 返回目录](#table-of-contents)** 2036 | 2037 | ## In the Wild 2038 | 2039 | This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. 2040 | 2041 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) 2042 | - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) 2043 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) 2044 | - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) 2045 | - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) 2046 | - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) 2047 | - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) 2048 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) 2049 | - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) 2050 | - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) 2051 | - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) 2052 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) 2053 | - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) 2054 | - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) 2055 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) 2056 | - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) 2057 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) 2058 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) 2059 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) 2060 | - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) 2061 | - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) 2062 | - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) 2063 | - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) 2064 | - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) 2065 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) 2066 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) 2067 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) 2068 | - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) 2069 | - **Muber**: [muber/javascript](https://github.com/muber/javascript) 2070 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) 2071 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) 2072 | - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) 2073 | - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) 2074 | - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) 2075 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) 2076 | - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) 2077 | - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) 2078 | - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) 2079 | - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) 2080 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) 2081 | - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) 2082 | - **Target**: [target/javascript](https://github.com/target/javascript) 2083 | - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) 2084 | - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) 2085 | - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) 2086 | - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) 2087 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) 2088 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) 2089 | 2090 | **[⬆ 返回目录](#table-of-contents)** 2091 | 2092 | ## Translation 2093 | 2094 | This style guide is also available in other languages: 2095 | 2096 | - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) 2097 | - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) 2098 | - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) 2099 | - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) 2100 | - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) 2101 | - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) 2102 | - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) 2103 | - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) 2104 | - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) 2105 | - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) 2106 | - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) 2107 | - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) 2108 | - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) 2109 | - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) 2110 | 2111 | ## The JavaScript Style Guide Guide 2112 | 2113 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 2114 | 2115 | ## Chat With Us About JavaScript 2116 | 2117 | - Find us on [gitter](https://gitter.im/airbnb/javascript). 2118 | 2119 | ## Contributors 2120 | 2121 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) 2122 | 2123 | 2124 | ## License 2125 | 2126 | (The MIT License) 2127 | 2128 | Copyright (c) 2014 Airbnb 2129 | 2130 | Permission is hereby granted, free of charge, to any person obtaining 2131 | a copy of this software and associated documentation files (the 2132 | 'Software'), to deal in the Software without restriction, including 2133 | without limitation the rights to use, copy, modify, merge, publish, 2134 | distribute, sublicense, and/or sell copies of the Software, and to 2135 | permit persons to whom the Software is furnished to do so, subject to 2136 | the following conditions: 2137 | 2138 | The above copyright notice and this permission notice shall be 2139 | included in all copies or substantial portions of the Software. 2140 | 2141 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 2142 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2143 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 2144 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 2145 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 2146 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 2147 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2148 | 2149 | **[⬆ 返回目录](#table-of-contents)** 2150 | 2151 | # }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Style Guide 2 | 3 | - [JavaScript Style Guide](JavaScript.md) 4 | - [React.js Style Guide](React.js.md) 5 | 6 | ## 代码校验工具 7 | 8 | - eslint 9 | - eslint-plugin-import 10 | - eslint-plugin-react 11 | - eslint-plugin-jsx-a11y 12 | -------------------------------------------------------------------------------- /React.js.md: -------------------------------------------------------------------------------- 1 | # React/JSX 编码规范 2 | 3 | ## 基本规范 4 | 5 | - 每个文件只包含的一个 React 组件: 6 | - 联系紧密的组件可以使用「命名空间」的形式; 7 | - 每个文件中可包含多个纯函数组件。 8 | - 始终使用 JSX 语法,不要使用 `React.createElement` 创建 ReactElement,以提高编写速度、可读性、可维护性(没有 JSX 转换的特殊场景例外,如在 `console` 中测试组件)。 9 | 10 | ## 组件创建方式 11 | 12 | React 中可以通过三种方式创建组件:ES6 `class`、[`createReactClass`](https://facebook.github.io/react/docs/react-without-es6.html)、[函数式组件](https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components) 。 13 | 14 | - 如果组件有内部状态,或者使用了生命周期方法,优先使用 `class extends React.Component` : 15 | 16 | ```js 17 | // bad 18 | var createReactClass = require('create-react-class'); 19 | var Greeting = createReactClass({ 20 | /// ... 21 | render() { 22 | return

Hello, {this.props.name}

; 23 | } 24 | }); 25 | 26 | // good 27 | class Greeting extends React.Component { 28 | // ... 29 | render() { 30 | return

Hello, {this.props.name}

; 31 | } 32 | } 33 | ``` 34 | 35 | 如果组件内部未使用状态或生命周期方法,优先使用普通函数(非箭头函数)创建组件: 36 | 37 | ```js 38 | // bad 39 | class Listing extends React.Component { 40 | render() { 41 | return
{this.props.hello}
; 42 | } 43 | } 44 | 45 | // bad (relying on function name inference is discouraged) 46 | const Listing = ({ hello }) => ( 47 |
{hello}
48 | ); 49 | 50 | // good 51 | function Listing({ hello }) { 52 | return
{hello}
; 53 | } 54 | ``` 55 | 56 | 参考链接: 57 | 58 | - [React.createClass versus extends React.Component](https://toddmotto.com/react-create-class-versus-component/) 59 | 60 | 61 | ## Mixins 62 | 63 | - [不要使用 mixins](https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html); 64 | - 使用 ES6 class 语法的 React 组件不支持 mixins(参考[高阶组件](https://facebook.github.io/react/docs/higher-order-components.html))。 65 | 66 | > Why? Mixins introduce implicit dependencies, cause name clashes, and cause snowballing complexity. Most use cases for mixins can be accomplished in better ways via components, higher-order components, or utility modules. 67 | 68 | 69 | ## 命名规范 70 | 71 | - **扩展名**:使用 `.jsx` 作为 React 组件的扩展名; 72 | - **文件名**:使用**大驼峰命名法(PascalCase)**,如 `MyComponent.jsx`; 73 | - **组件命名**:组件名称和文件名一致,如 `MyComponent.jsx` 里的组件名应该是 `MyComponent`;一个目录的根组件使用 `index.jsx` 命名,以目录名称作为组件名称; 74 | 75 | ```js 76 | // Use the filename as the component name 77 | 78 | // file contents 79 | class CheckBox extends React.Component { 80 | // ... 81 | } 82 | 83 | export default CheckBox; 84 | 85 | // in some other file 86 | // bad 87 | import CheckBox from './checkBox'; 88 | 89 | // bad 90 | import CheckBox from './check_box'; 91 | 92 | // good 93 | import CheckBox from './CheckBox'; 94 | 95 | 96 | // for root components of a directory, 97 | // use index.jsx as the filename and use the directory name as the component name 98 | 99 | // bad 100 | import Footer from './Footer/Footer.jsx'; 101 | 102 | // bad 103 | import Footer from './Footer/index.jsx'; 104 | 105 | // good 106 | import Footer from './Footer'; 107 | ``` 108 | - **引用命名**:React 组件使用**大驼峰命名法(PascalCase)**,组件实例使用**小驼峰命名法(camelCase)**; 109 | 110 | ```js 111 | // bad 112 | import reservationCard from './ReservationCard'; 113 | 114 | // good 115 | import ReservationCard from './ReservationCard'; 116 | 117 | // bad 118 | const ReservationItem = ; 119 | 120 | // good 121 | const reservationItem = ; 122 | 123 | // HTML tag 124 | const myDivElement =
; 125 | ReactDOM.render(myDivElement, mountNode); 126 | ``` 127 | 128 | - **高阶组件命名**: Use a composite of the higher-order component's name and the passed-in component's name as the `displayName` on the generated component. For example, the higher-order component `withFoo()`, when passed a component `Bar` should produce a component with a `displayName` of `withFoo(Bar)`. 129 | 130 | > Why? A component's `displayName` may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening. 131 | 132 | ```js 133 | // bad 134 | export default function withFoo(WrappedComponent) { 135 | return function WithFoo(props) { 136 | return ; 137 | } 138 | } 139 | 140 | // good 141 | export default function withFoo(WrappedComponent) { 142 | function WithFoo(props) { 143 | return ; 144 | } 145 | 146 | const wrappedComponentName = WrappedComponent.displayName 147 | || WrappedComponent.name 148 | || 'Component'; 149 | 150 | WithFoo.displayName = `withFoo(${wrappedComponentName})`; 151 | return WithFoo; 152 | } 153 | ``` 154 | 155 | ### 带命名空间的组件 156 | 157 | - 如果一个组件有许多关联子组件,可以以该组件作为命名空间编写、调用子组件。 158 | 159 | ```js 160 | class Form extends React.Component { 161 | // ... 162 | } 163 | 164 | class Row extends React.Component {} 165 | class Label extends React.Component {} 166 | class Input extends React.Component {} 167 | 168 | Form.Row = Row; 169 | Form.Label = Label; 170 | Form.Input = Input; 171 | 172 | export default Form; 173 | 174 | // refence Form component 175 | import Form from './Form'; 176 | 177 | const App = ( 178 |
179 | 180 | 181 | 182 | 183 |
184 | ); 185 | ``` 186 | 187 | ## 组件声明 188 | 189 | - 不要使用 `displayName` 来命名组件,通过引用来命名。 190 | 191 | ```js 192 | // bad 193 | export default React.createClass({ 194 | displayName: 'ReservationCard', 195 | // stuff goes here 196 | }); 197 | 198 | // good 199 | class ReservationCard extends React.Component { 200 | } 201 | 202 | export default ReservationCard; 203 | ``` 204 | 205 | ## 属性 206 | 207 | ### 属性命名 208 | 209 | - React 组件的属性使用**小驼峰命名法**; 210 | - 使用 `className` 代替 `class` 属性; 211 | - 使用 `htmlFor` 代替 `for` 属性; 212 | - 不要把 DOM 组件的属性用作其他用途。 213 | > Why? People expect props like `style` and `className` to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs. 214 | 215 | ```js 216 | // bad 217 | 218 | 219 | // good 220 | 221 | ``` 222 | 223 | **传递给 HTML 的属性:** 224 | 225 | - 传递给 HTML 元素的自定义属性,需要添加 `data-` 前缀,React 不会渲染非标准属性; 226 | - [无障碍](http://www.w3.org/WAI/intro/aria)属性 `aria-` 可以正常使用。 227 | 228 | ### 属性设置 229 | 230 | - 在组件行内设置属性(以便 `propTypes` 校验),不要在外部改变属性的值; 231 | - 属性较多使用 `{...this.props}` 语法; 232 | 233 | ```js 234 | const component = ; 235 | component.props.foo = x; // bad 236 | component.props.bar = y; // also bad 237 | 238 | // good 239 | const component = ; 240 | 241 | // good 242 | const props = {}; 243 | props.foo = x; 244 | props.bar = y; 245 | const component = ; 246 | ``` 247 | - 属性值明确为 `true` 时,省略值。 248 | 249 | ```js 250 | // bad 251 |