├── jsx.md ├── react.md ├── react-native.md ├── .editorconfig ├── LICENSE ├── settings ├── vue │ └── .eslintrc.js └── react │ └── .eslintrc.js ├── settings.md ├── README.md └── airbnb.md /jsx.md: -------------------------------------------------------------------------------- 1 | # JSX相关规范 2 | 3 | WIP -------------------------------------------------------------------------------- /react.md: -------------------------------------------------------------------------------- 1 | # react相关规范 2 | 3 | WIP -------------------------------------------------------------------------------- /react-native.md: -------------------------------------------------------------------------------- 1 | # react-native相关规范 2 | 3 | WIP -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | # editorconfig-tools is unable to ignore longs strings or urls 11 | max_line_length = null -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2017 Airbnb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | MIT License 25 | 26 | Copyright (c) 2017 Chongyu Zhu 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in all 36 | copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 44 | SOFTWARE. 45 | -------------------------------------------------------------------------------- /settings/vue/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // http://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | extends: 'airbnb-base', 13 | // required to lint *.vue files 14 | plugins: [ 15 | 'html' 16 | ], 17 | // check if imports actually resolve 18 | 'settings': { 19 | 'import/resolver': { 20 | 'webpack': { 21 | 'config': 'build/webpack.base.conf.js' 22 | } 23 | } 24 | }, 25 | 'rules': { 26 | /* Waimai-BP-javascript-style-guide Custom Rules */ 27 | 28 | // don't require .vue extension when importing 29 | 'import/extensions': ['error', 'always', { 30 | 'js': 'never', 31 | 'vue': 'never' 32 | }], 33 | 34 | // allow debugger during development 35 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 36 | 37 | // suggest using template literals instead of string concatenation 38 | // http://eslint.org/docs/rules/prefer-template 39 | 'prefer-template': 'warn', 40 | 41 | // disallow unnecessary string escaping 42 | // http://eslint.org/docs/rules/no-useless-escape 43 | 'no-useless-escape': 'off', 44 | 45 | 46 | // use rest parameters instead of arguments 47 | // http://eslint.org/docs/rules/prefer-rest-params 48 | 'prefer-rest-params': 'off', 49 | 50 | // suggest using the spread operator instead of .apply() 51 | // http://eslint.org/docs/rules/prefer-spread 52 | 'prefer-spread': 'warn', 53 | 54 | // disallow arrow functions where they could be confused with comparisons 55 | // http://eslint.org/docs/rules/no-confusing-arrow 56 | 'no-confusing-arrow': 'off', 57 | 58 | // disallow use of unary operators, ++ and -- 59 | // http://eslint.org/docs/rules/no-plusplus 60 | 'no-plusplus': 'off', 61 | 62 | // disallow dangling underscores in identifiers 63 | 'no-underscore-dangle': ['warn', { allowAfterThis: false }], 64 | 65 | // enforce one true brace style 66 | 'brace-style': ['error', 'stroustrup', { allowSingleLine: false }], 67 | 68 | // encourages use of dot notation whenever possible 69 | 'dot-notation': ['warn', { allowKeywords: true }], 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /settings.md: -------------------------------------------------------------------------------- 1 | # 配置文档 2 | 3 | 4 | # 目录 5 | 6 | 1. [IDE配置](#ide-setting) 7 | 1. [webpack配置](#webpack-setting) 8 | 1. [fis配置](#fis-setting) 9 | 1. [格式化工具](formatter-setting) 10 | 11 | 12 | **[⬆ 返回目录](#table-of-contents)** 13 | 14 | 15 | 16 | # IDE配置 17 | 18 | ## .editorconfig配置(通用) 19 | 20 | 在根目录建立一个`.editorconfig`文件: 21 | 22 | ```bash 23 | root = true 24 | 25 | [*] 26 | indent_style = space 27 | indent_size = 2 28 | charset = utf-8 29 | trim_trailing_whitespace = true 30 | insert_final_newline = true 31 | end_of_line = lf 32 | ``` 33 | 34 | ## 安装全局依赖(通用) 35 | 36 | ```bash 37 | $ npm i -g eslint eslint-config-airbnb-base babel-eslint eslint-plugin-html eslint-plugin-import 38 | ``` 39 | 40 | ## VSCODE 41 | 42 | **安装.editorconfig支持** 43 | 44 | 在VSC命令中执行 45 | 46 | ``` 47 | $ ext install EditorConfig 48 | ``` 49 | 50 | **安装`eslint`** 51 | 52 | ```bash 53 | $ ext install vscode-eslint 54 | ``` 55 | 56 | 将`settings`目录对应的 [.eslintrc.js](https://github.com/clancyz/wm-bp-javascript/tree/master/settings/vue) 拷到开发目录下(这里以`vue`为例) 57 | 58 | **配置IDE** 59 | 60 | 参考[这里](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), 一般只需要在VSCODE的设置中增加两行: 61 | 62 | lint相关的文件,并可以在保存时**自动格式化** 63 | 64 | ```javascript 65 | { 66 | "eslint.autoFixOnSave": true, 67 | "eslint.validate": [ 68 | "javascript", "javascriptreact", "html","vue" 69 | ] 70 | } 71 | ``` 72 | 73 | 74 | ## Sublime Text 3 75 | 76 | Sublime Text 2版本,推荐升级到3 77 | 78 | **安装.editorconfig支持** 79 | 80 | `cmd/ctrl + shift + p` 输入install package, 回车确认,搜索`EditorConfig`, 回车安装 81 | 82 | **安装Sublimelinter** 83 | 84 | 同上方式,安装`sublimelinter`, 回车安装 85 | 86 | **安装SublimeLinter-contrib-eslint** 87 | 88 | 同上方式,安装`SublimeLinter-contrib-eslint` 89 | 90 | 将`settings`目录对应的 [.eslintrc.js](https://github.com/clancyz/wm-bp-javascript/tree/master/settings/vue) 拷到开发目录下(这里以`vue`为例) 91 | 92 | 基本就能用了,详细设置可以看[这里](http://sublimelinter.readthedocs.io/en/latest/settings.html) 93 | 94 | 附SublimeLinter-eslint地址:https://github.com/roadhump/SublimeLinter-eslint 95 | 96 | **安装ESLint-Formatter** 97 | 98 | 详细安装和配置请见[原repo](https://github.com/TheSavior/ESLint-Formatter), 也可以达到format on save的效果。 99 | 100 | 101 | **[⬆ 返回目录](#table-of-contents)** 102 | 103 | -------------------------------------------------------------------------------- /settings/react/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // WIP 2 | module.exports = { 3 | plugins: [ 4 | 'react', 5 | ], 6 | 7 | parserOptions: { 8 | ecmaFeatures: { 9 | jsx: true, 10 | }, 11 | }, 12 | 13 | // View link below for react rules documentation 14 | // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules 15 | rules: { 16 | // Specify whether double or single quotes should be used in JSX attributes 17 | // http://eslint.org/docs/rules/jsx-quotes 18 | 'jsx-quotes': ['error', 'prefer-double'], 19 | 20 | 'class-methods-use-this': ['error', { 21 | exceptMethods: [ 22 | 'render', 23 | 'getInitialState', 24 | 'getDefaultProps', 25 | 'getChildContext', 26 | 'componentWillMount', 27 | 'componentDidMount', 28 | 'componentWillReceiveProps', 29 | 'shouldComponentUpdate', 30 | 'componentWillUpdate', 31 | 'componentDidUpdate', 32 | 'componentWillUnmount', 33 | ], 34 | }], 35 | 36 | // Prevent missing displayName in a React component definition 37 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md 38 | 'react/display-name': ['off', { ignoreTranspilerName: false }], 39 | 40 | // Forbid certain propTypes (any, array, object) 41 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md 42 | 'react/forbid-prop-types': ['error', { forbid: ['any', 'array', 'object'] }], 43 | 44 | // Enforce boolean attributes notation in JSX 45 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md 46 | 'react/jsx-boolean-value': ['error', 'never'], 47 | 48 | // Validate closing bracket location in JSX 49 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md 50 | 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], 51 | 52 | // Validate closing tag location in JSX 53 | // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/jsx-closing-tag-location.md 54 | // TODO: enable, semver-minor 55 | 'react/jsx-closing-tag-location': 'off', 56 | 57 | // Enforce or disallow spaces inside of curly braces in JSX attributes 58 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md 59 | 'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }], 60 | 61 | // Enforce event handler naming conventions in JSX 62 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md 63 | 'react/jsx-handler-names': ['off', { 64 | eventHandlerPrefix: 'handle', 65 | eventHandlerPropPrefix: 'on', 66 | }], 67 | 68 | // Validate props indentation in JSX 69 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md 70 | 'react/jsx-indent-props': ['error', 2], 71 | 72 | // Validate JSX has key prop when in array or iterator 73 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md 74 | 'react/jsx-key': 'off', 75 | 76 | // Limit maximum of props on a single line in JSX 77 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md 78 | 'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }], 79 | 80 | // Prevent usage of .bind() in JSX props 81 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md 82 | 'react/jsx-no-bind': ['error', { 83 | ignoreRefs: true, 84 | allowArrowFunctions: true, 85 | allowBind: false, 86 | }], 87 | 88 | // Prevent duplicate props in JSX 89 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md 90 | 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }], 91 | 92 | // Prevent usage of unwrapped JSX strings 93 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md 94 | 'react/jsx-no-literals': 'off', 95 | 96 | // Disallow undeclared variables in JSX 97 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md 98 | 'react/jsx-no-undef': 'error', 99 | 100 | // Enforce PascalCase for user-defined JSX components 101 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md 102 | 'react/jsx-pascal-case': ['error', { 103 | allowAllCaps: true, 104 | ignore: [], 105 | }], 106 | 107 | // Enforce propTypes declarations alphabetical sorting 108 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md 109 | 'react/sort-prop-types': ['off', { 110 | ignoreCase: true, 111 | callbacksLast: false, 112 | requiredFirst: false, 113 | }], 114 | 115 | // Deprecated in favor of react/jsx-sort-props 116 | 'react/jsx-sort-prop-types': 'off', 117 | 118 | // Enforce props alphabetical sorting 119 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md 120 | 'react/jsx-sort-props': ['off', { 121 | ignoreCase: true, 122 | callbacksLast: false, 123 | shorthandFirst: false, 124 | shorthandLast: false, 125 | noSortAlphabetically: false, 126 | reservedFirst: true, 127 | }], 128 | 129 | // Prevent React to be incorrectly marked as unused 130 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md 131 | 'react/jsx-uses-react': ['error'], 132 | 133 | // Prevent variables used in JSX to be incorrectly marked as unused 134 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md 135 | 'react/jsx-uses-vars': 'error', 136 | 137 | // Prevent usage of dangerous JSX properties 138 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md 139 | 'react/no-danger': 'warn', 140 | 141 | // Prevent usage of deprecated methods 142 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md 143 | 'react/no-deprecated': ['error'], 144 | 145 | // Prevent usage of setState in componentDidMount 146 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md 147 | 'react/no-did-mount-set-state': 'error', 148 | 149 | // Prevent usage of setState in componentDidUpdate 150 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md 151 | 'react/no-did-update-set-state': 'error', 152 | 153 | // Prevent usage of setState in componentWillUpdate 154 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-will-update-set-state.md 155 | 'react/no-will-update-set-state': 'error', 156 | 157 | // Prevent direct mutation of this.state 158 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md 159 | 'react/no-direct-mutation-state': 'off', 160 | 161 | // Prevent usage of isMounted 162 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md 163 | 'react/no-is-mounted': 'error', 164 | 165 | // Prevent multiple component definition per file 166 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md 167 | 'react/no-multi-comp': ['error', { ignoreStateless: true }], 168 | 169 | // Prevent usage of setState 170 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md 171 | 'react/no-set-state': 'off', 172 | 173 | // Prevent using string references 174 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md 175 | 'react/no-string-refs': 'error', 176 | 177 | // Prevent usage of unknown DOM property 178 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md 179 | 'react/no-unknown-property': 'error', 180 | 181 | // Require ES6 class declarations over React.createClass 182 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md 183 | 'react/prefer-es6-class': ['error', 'always'], 184 | 185 | // Require stateless functions when not using lifecycle methods, setState or ref 186 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md 187 | 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }], 188 | 189 | // Prevent missing props validation in a React component definition 190 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md 191 | 'react/prop-types': ['error', { ignore: [], customValidators: [], skipUndeclared: false }], 192 | 193 | // Prevent missing React when using JSX 194 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md 195 | 'react/react-in-jsx-scope': 'error', 196 | 197 | // Require render() methods to return something 198 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md 199 | 'react/require-render-return': 'error', 200 | 201 | // Prevent extra closing tags for components without children 202 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md 203 | 'react/self-closing-comp': 'error', 204 | 205 | // Enforce component methods order 206 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md 207 | 'react/sort-comp': ['error', { 208 | order: [ 209 | 'static-methods', 210 | 'lifecycle', 211 | '/^on.+$/', 212 | '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', 213 | 'everything-else', 214 | '/^render.+$/', 215 | 'render' 216 | ], 217 | }], 218 | 219 | // Prevent missing parentheses around multilines JSX 220 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md 221 | 'react/jsx-wrap-multilines': ['error', { 222 | declaration: true, 223 | assignment: true, 224 | return: true, 225 | arrow: true, 226 | }], 227 | 228 | // Require that the first prop in a JSX element be on a new line when the element is multiline 229 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md 230 | 'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'], 231 | 232 | // Enforce spacing around jsx equals signs 233 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md 234 | 'react/jsx-equals-spacing': ['error', 'never'], 235 | 236 | // Enforce JSX indentation 237 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md 238 | 'react/jsx-indent': ['error', 2], 239 | 240 | // Disallow target="_blank" on links 241 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md 242 | 'react/jsx-no-target-blank': 'error', 243 | 244 | // only .jsx files may have JSX 245 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md 246 | 'react/jsx-filename-extension': ['error', { extensions: ['.jsx'] }], 247 | 248 | // prevent accidental JS comments from being injected into JSX as text 249 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md 250 | 'react/jsx-no-comment-textnodes': 'error', 251 | 252 | // disallow using React.render/ReactDOM.render's return value 253 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md 254 | 'react/no-render-return-value': 'error', 255 | 256 | // require a shouldComponentUpdate method, or PureRenderMixin 257 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md 258 | 'react/require-optimization': ['off', { allowDecorators: [] }], 259 | 260 | // warn against using findDOMNode() 261 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md 262 | 'react/no-find-dom-node': 'error', 263 | 264 | // Forbid certain props on Components 265 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md 266 | 'react/forbid-component-props': ['off', { forbid: [] }], 267 | 268 | // Forbid certain elements 269 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-elements.md 270 | 'react/forbid-elements': ['off', { forbid: [], }], 271 | 272 | // Prevent problem with children and props.dangerouslySetInnerHTML 273 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md 274 | 'react/no-danger-with-children': 'error', 275 | 276 | // Prevent unused propType definitions 277 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md 278 | 'react/no-unused-prop-types': ['error', { 279 | customValidators: [ 280 | ], 281 | skipShapeProps: true, 282 | }], 283 | 284 | // Require style prop value be an object or var 285 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/style-prop-object.md 286 | 'react/style-prop-object': 'error', 287 | 288 | // Prevent invalid characters from appearing in markup 289 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md 290 | 'react/no-unescaped-entities': 'error', 291 | 292 | // Prevent passing of children as props 293 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md 294 | 'react/no-children-prop': 'error', 295 | 296 | // Validate whitespace in and around the JSX opening and closing brackets 297 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md 298 | 'react/jsx-tag-spacing': ['error', { 299 | closingSlash: 'never', 300 | beforeSelfClosing: 'always', 301 | afterOpening: 'never' 302 | }], 303 | 304 | // Enforce spaces before the closing bracket of self-closing JSX elements 305 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md 306 | // Deprecated in favor of jsx-tag-spacing 307 | 'react/jsx-space-before-closing': ['off', 'always'], 308 | 309 | // Prevent usage of Array index in keys 310 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md 311 | 'react/no-array-index-key': 'error', 312 | 313 | // Enforce a defaultProps definition for every prop that is not a required prop 314 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-default-props.md 315 | 'react/require-default-props': 'error', 316 | 317 | // Forbids using non-exported propTypes 318 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-foreign-prop-types.md 319 | 'react/forbid-foreign-prop-types': 'off', 320 | 321 | // Prevent void DOM elements from receiving children 322 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md 323 | 'react/void-dom-elements-no-children': 'error', 324 | 325 | // Enforce all defaultProps have a corresponding non-required PropType 326 | // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/default-props-match-prop-types.md 327 | // TODO: enable, semver-minor 328 | 'react/default-props-match-prop-types': ['off', { allowRequiredDefaults: false }], 329 | 330 | // Prevent usage of shouldComponentUpdate when extending React.PureComponent 331 | // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/no-redundant-should-component-update.md 332 | // TODO: enable, semver-major 333 | 'react/no-redundant-should-component-update': 'off', 334 | }, 335 | 336 | settings: { 337 | 'import/resolver': { 338 | node: { 339 | extensions: ['.js', '.jsx', '.json'] 340 | } 341 | }, 342 | react: { 343 | pragma: 'React', 344 | version: '15.0' 345 | }, 346 | } 347 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Waimai Business Platform JavaScript Style Guide() { 2 | 3 | **基于[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)的javascript代码规范** 4 | 5 | 本文是[完整代码规范建议](airbnb.md)的**子集**,提及内容为`eslint`可检查部分。 6 | 7 | 补充规范: 8 | 9 | 1. [JSX相关规范](jsx.md) 10 | 2. [React相关规范](react.md) 11 | 3. [React Native相关规范](react-native.md) 12 | 13 | IDE/构建工具/格式化工具配置请看[配置文档](settings.md) 14 | 15 | 本文规则分为三类: 16 | 17 | - **强制:** 不符合规范 => `eslint error` 18 | - **建议 / warn:** 不符合规范 => `eslint warn` 警告提示 19 | - **建议 / off:** 建议你以这样的方式进行 => `eslint off` 不进行检查 20 | 21 | 建议通读完整规范,获得更好的代码书写指导建议。 22 | 23 | 修改的部分会以`[modified]`标注,无此说明的即与原文规约相同。 24 | 25 | 26 | ## 目录 27 | 28 | 1. [对象](#objects) 29 | 1. [数组](#arrays) 30 | 1. [解构](#destructuring) 31 | 1. [字符串](#strings) 32 | 1. [函数](#functions) 33 | 1. [箭头函数](#arrow-functions) 34 | 1. [构造函数](#constructors) 35 | 1. [模块](#modules) 36 | 1. [Iterators & Generators ](#iterators-and-generators) 37 | 1. [属性](#properties) 38 | 1. [变量](#variables) 39 | 1. [提升](#hoisting) 40 | 1. [比较运算符 & 等号](#comparison-operators--equality) 41 | 1. [代码块](#blocks) 42 | 1. [注释](#comments) 43 | 1. [空白](#whitespace) 44 | 1. [逗号](#commas) 45 | 1. [分号](#semicolons) 46 | 1. [类型转换](#type-casting--coercion) 47 | 1. [命名规则](#naming-conventions) 48 | 1. [存取器](#accessors) 49 | 1. [事件](#events) 50 | 1. [jQuery](#jquery) 51 | 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) 52 | 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) 53 | 54 | 55 | 56 | ## 引用 57 | 58 | - [2.1](#2.1) **[强制] 对所有的引用使用 `const` ;不要使用 `var`。** eslint: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html) 59 | 60 | 61 | > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 62 | 63 | ```javascript 64 | // bad 65 | var a = 1; 66 | var b = 2; 67 | 68 | // good 69 | const a = 1; 70 | const b = 2; 71 | ``` 72 | 73 | - [2.2](#2.2) **[强制] 如果你一定需要可变动的引用,使用 `let` 代替 `var`。** eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) 74 | 75 | > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 76 | 77 | ```javascript 78 | // bad 79 | var count = 1; 80 | if (true) { 81 | count += 1; 82 | } 83 | 84 | // good, use the let. 85 | let count = 1; 86 | if (true) { 87 | count += 1; 88 | } 89 | ``` 90 | 91 | **[⬆ 返回目录](#table-of-contents)** 92 | 93 | 94 | ## 对象 95 | 96 | - [3.1](#3.1) **[强制] 使用字面值创建对象。** eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) 97 | 98 | ```javascript 99 | // bad 100 | const item = new Object(); 101 | 102 | // good 103 | const item = {}; 104 | ``` 105 | 106 | 107 | 108 | - [3.4](#3.4) **[强制] 使用对象属性值的简写。** eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) 109 | 110 | > 为什么?因为这样更短更有描述性。 111 | 112 | ```javascript 113 | const lukeSkywalker = 'Luke Skywalker'; 114 | 115 | // bad 116 | const obj = { 117 | lukeSkywalker: lukeSkywalker, 118 | }; 119 | 120 | // good 121 | const obj = { 122 | lukeSkywalker, 123 | }; 124 | ``` 125 | 126 | 127 | - [3.6](#3.6) **[强制] 仅在对象属性有特殊符号时使用引号包裹。** eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) 128 | 129 | > 为什么? 这样看上去更加易读,另外还能有相关的代码高亮,也容易被许多JS引擎优化。 130 | 131 | ```javascript 132 | // bad 133 | const bad = { 134 | 'foo': 3, 135 | 'bar': 4, 136 | 'data-blah': 5, 137 | }; 138 | 139 | // good 140 | const good = { 141 | foo: 3, 142 | bar: 4, 143 | 'data-blah': 5, 144 | }; 145 | ``` 146 | 147 | **[⬆ 返回目录](#table-of-contents)** 148 | 149 | 150 | ## 数组 151 | 152 | - [4.1](#4.1) **[强制] 使用字面值创建数组。** eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) 153 | 154 | ```javascript 155 | // bad 156 | const items = new Array(); 157 | 158 | // good 159 | const items = []; 160 | ``` 161 | 162 | 163 | 164 | - [4.5](#arrays--callback-return) **[强制] 数组的相关方法使用return语句。如果函数体仅由一个带表达式且无副作用的语句组成,可以忽略return。** 参考[8.2](#arrows--implicit-return). eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) 165 | 166 | ```javascript 167 | // good 168 | [1, 2, 3].map((x) => { 169 | const y = x + 1; 170 | return x * y; 171 | }); 172 | 173 | // good 174 | [1, 2, 3].map(x => x + 1); 175 | 176 | // bad 177 | const flat = {}; 178 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { 179 | const flatten = memo.concat(item); 180 | flat[index] = flatten; 181 | }); 182 | 183 | // good 184 | const flat = {}; 185 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { 186 | const flatten = memo.concat(item); 187 | flat[index] = flatten; 188 | return flatten; 189 | }); 190 | 191 | // bad 192 | inbox.filter((msg) => { 193 | const { subject, author } = msg; 194 | if (subject === 'Mockingbird') { 195 | return author === 'Harper Lee'; 196 | } else { 197 | return false; 198 | } 199 | }); 200 | 201 | // good 202 | inbox.filter((msg) => { 203 | const { subject, author } = msg; 204 | if (subject === 'Mockingbird') { 205 | return author === 'Harper Lee'; 206 | } 207 | 208 | return false; 209 | }); 210 | ``` 211 | 212 | **[⬆ 返回目录](#table-of-contents)** 213 | 214 | 215 | 216 | 217 | ## 解构 218 | 219 | 220 | 221 | ## Strings 222 | 223 | - [6.1](#6.1) **[强制] 字符串使用单引号 `''` 。** eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) 224 | 225 | ```javascript 226 | // bad 227 | const name = "Capt. Janeway"; 228 | 229 | // good 230 | const name = 'Capt. Janeway'; 231 | ``` 232 | 233 | 234 | 235 | - [6.4](#6.4) `[modified]`**[建议 / warn]程序化生成字符串时,使用模板字符串代替字符串连接。** eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) jscs: [`requireTemplateStrings`](http://jscs.info/rule/requireTemplateStrings) 236 | 237 | 238 | > 为什么?模板字符串更为简洁,更具可读性。 239 | 240 | ```javascript 241 | // bad 242 | function sayHi(name) { 243 | return 'How are you, ' + name + '?'; 244 | } 245 | 246 | // bad 247 | function sayHi(name) { 248 | return ['How are you, ', name, '?'].join(); 249 | } 250 | 251 | // good 252 | function sayHi(name) { 253 | return `How are you, ${name}?`; 254 | } 255 | ``` 256 | 257 | 258 | 259 | - [6.4](#strings--eval) **[强制] 不要对字符串使用 `eval()`,会导致一系列问题。** eslint: [`no-eval`](http://eslint.org/docs/rules/no-eval) 260 | 261 | 262 | 263 | - [6.5](#strings--escaping) `[modified]` **[建议 / off] 不要对字符串使用不必要的escape操作。** eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) 264 | 265 | > 为什么? 反斜杠会影响可读性,仅在必须的时候使用它。 266 | 267 | ```javascript 268 | // bad 269 | const foo = '\'this\' \i\s \"quoted\"'; 270 | 271 | // good 272 | const foo = '\'this\' is "quoted"'; 273 | const foo = `my name is '${name}'`; 274 | ``` 275 | 276 | **[⬆ 返回目录](#table-of-contents)** 277 | 278 | 279 | ## 函数 280 | 281 | - [7.1](#7.1) `[modified]` **[建议 / off] 使用具名函数代替函数声明。** eslint: [`func-style`](http://eslint.org/docs/rules/func-style) jscs: [`disallowFunctionDeclarations`](http://jscs.info/rule/disallowFunctionDeclarations) 282 | 283 | > 为什么?因为函数声明会把函数提升(hoisted), 这样易使函数在定义前被引用。这会影响可读性和可维护性。如果一个函数的定义很长或很复杂,会干扰对文件剩余部分的理解,更好的方式是将它抽象在它自己的模块中。别忘了给函数表达式命名 - 匿名函数会使得在错误调用栈中定位问题变得困难。([讨论](https://github.com/airbnb/javascript/issues/794)) 284 | 285 | ```javascript 286 | // bad 287 | function foo() { 288 | // ... 289 | } 290 | 291 | // bad 292 | const foo = function () { 293 | // ... 294 | }; 295 | 296 | // good 297 | const foo = function bar() { 298 | // ... 299 | }; 300 | ``` 301 | 302 | 303 | 304 | - [7.2](#functions--iife) **[强制] 对于立即调用(IIFE)的函数表达式,用括号包裹函数体。** eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) 305 | 306 | > 为什么?立即调用函数是一个独立单元 - 用括号包裹函数体可以清晰地表达这一点。需要注意的是,在一个到处都是「模块」的世界,几乎从不需要IIFE。 307 | 308 | 309 | ```javascript 310 | // 立即调用的函数 (IIFE) 311 | (function () { 312 | console.log('Welcome to the Internet. Please follow me.'); 313 | }()); 314 | ``` 315 | 316 | - [7.3](#7.3) **[强制] 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。** eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) 317 | 318 | 319 | 320 | - [7.6](#7.6) `[modified]`**[建议 / off] 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。** eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) 321 | 322 | > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 323 | 324 | ```javascript 325 | // bad 326 | function concatenateAll() { 327 | const args = Array.prototype.slice.call(arguments); 328 | return args.join(''); 329 | } 330 | 331 | // good 332 | function concatenateAll(...args) { 333 | return args.join(''); 334 | } 335 | ``` 336 | 337 | 338 | 339 | - [7.10](#functions--constructor) **[强制] 永远不要使用Function构造函数来创建一个新函数。** eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) 340 | 341 | > 为什么?这种方式在分析字符串时与eval()类似,会带来各种问题。 342 | 343 | ```javascript 344 | // bad 345 | var add = new Function('a', 'b', 'return a + b'); 346 | 347 | // still bad 348 | var subtract = Function('a', 'b', 'return a - b'); 349 | ``` 350 | 351 | 352 | 353 | - [7.11](#functions--signature-spacing) **[强制] [强制] 在函数签名中使用空格。** eslint: [`space-before-function-paren`](http://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks) 354 | 355 | > 为什么?保持一致性是最佳实践, 另外如果在添加或删除名称时也不应增加/删除空格。 356 | 357 | ```javascript 358 | // bad 359 | const f = function(){}; 360 | const g = function (){}; 361 | const h = function() {}; 362 | 363 | // good 364 | const x = function () {}; 365 | const y = function a() {}; 366 | ``` 367 | 368 | 369 | 370 | - [7.12](#functions--mutate-params) **[强制] 永远不要改变(mutate)参数。** eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) 371 | 372 | > 为什么?对传入的参数进行操作会在原始调用带来不想要的变量副作用。 373 | 374 | ```javascript 375 | // bad 376 | function f1(obj) { 377 | obj.key = 1; 378 | } 379 | 380 | // good 381 | function f2(obj) { 382 | const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; 383 | } 384 | ``` 385 | 386 | 387 | 388 | - [7.13](#functions--reassign-params) **[强制] 永远不要对参数重新赋值(reassign)。** eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) 389 | 390 | > 为什么?对参数重新赋值会引起意料之外的行为,特别是对于`arguments`的访问。同时它会引起优化问题,特别在V8引擎中。 391 | 392 | 393 | ```javascript 394 | // bad 395 | function f1(a) { 396 | a = 1; 397 | // ... 398 | } 399 | 400 | function f2(a) { 401 | if (!a) { a = 1; } 402 | // ... 403 | } 404 | 405 | // good 406 | function f3(a) { 407 | const b = a || 1; 408 | // ... 409 | } 410 | 411 | function f4(a = 1) { 412 | // ... 413 | } 414 | ``` 415 | 416 | 417 | 418 | - [7.14](#functions--spread-vs-apply) `[modified]`**[建议 / warn] 推荐使用展开运算符 `...` 来调用可变参数的函数。** eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) 419 | 420 | > 为什么?这样更加清晰,也不用提供上下文,而且把`new`和 `apply` 组合的方式也比较蛋疼。 421 | 422 | 423 | ```javascript 424 | // bad 425 | const x = [1, 2, 3, 4, 5]; 426 | console.log.apply(console, x); 427 | 428 | // good 429 | const x = [1, 2, 3, 4, 5]; 430 | console.log(...x); 431 | 432 | // bad 433 | new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); 434 | 435 | // good 436 | new Date(...[2016, 8, 5]); 437 | ``` 438 | 439 | 440 | 441 | **[⬆ 返回目录](#table-of-contents)** 442 | 443 | 444 | ## 箭头函数 445 | 446 | - [8.1](#8.1) **[强制] 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。** eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) jscs: [`requireArrowFunctions`](http://jscs.info/rule/requireArrowFunctions) 447 | 448 | > 为什么?因为箭头函数创造了新的一个 `this` 执行环境(译注:参考 [Arrow functions - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 和 [ES6 arrow functions, syntax and lexical scoping](http://toddmotto.com/es6-arrow-functions-syntaxes-and-lexical-scoping/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 449 | 450 | > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 451 | 452 | ```javascript 453 | // bad 454 | [1, 2, 3].map(function (x) { 455 | const y = x + 1; 456 | return x * y; 457 | }); 458 | 459 | // good 460 | [1, 2, 3].map((x) => { 461 | const y = x + 1; 462 | return x * y; 463 | }); 464 | ``` 465 | 466 | - [8.2](#8.2) **[强制] 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。** eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam), [`requireShorthandArrowFunctions`](http://jscs.info/rule/requireShorthandArrowFunctions) 467 | 468 | > 为什么?语法糖。在链式调用中可读性很高。 469 | 470 | 471 | ```javascript 472 | // bad 473 | [1, 2, 3].map(number => { 474 | const nextNumber = number + 1; 475 | `A string containing the ${nextNumber}.`; 476 | }); 477 | 478 | // good 479 | [1, 2, 3].map(number => `A string containing the ${number}.`); 480 | 481 | // good 482 | [1, 2, 3].map((number) => { 483 | const nextNumber = number + 1; 484 | return `A string containing the ${nextNumber}.`; 485 | }); 486 | 487 | // good 488 | [1, 2, 3].map((number, index) => ({ 489 | [index]: number, 490 | })); 491 | 492 | // 当有副作用时,不要隐式return 493 | function foo(callback) { 494 | const val = callback(); 495 | if (val === true) { 496 | // Do something if callback returns true 497 | } 498 | } 499 | 500 | let bool = false; 501 | 502 | // bad 503 | foo(() => bool = true); 504 | 505 | // good 506 | foo(() => { 507 | bool = true; 508 | }); 509 | ``` 510 | 511 | 512 | 513 | - [8.4](#arrows--one-arg-parens) **[强制] 如果函数是单参数且不需要花括号`{}`,则要省略掉括号。否则,始终用括号包裹参数,这样更加清晰,可读性也更好。 注:始终使用括号包裹参数也可以。** 在eslint中使用 [“always” option](http://eslint.org/docs/rules/arrow-parens#always) 或在jscs中不引入 [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam)。 eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) 514 | 515 | > 为什么? 可以避免视觉上的混乱。 516 | 517 | ```javascript 518 | // bad 519 | [1, 2, 3].map((x) => x * x); 520 | 521 | // good 522 | [1, 2, 3].map(x => x * x); 523 | 524 | // good 525 | [1, 2, 3].map(number => ( 526 | `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` 527 | )); 528 | 529 | // bad 530 | [1, 2, 3].map(x => { 531 | const y = x + 1; 532 | return x * y; 533 | }); 534 | 535 | // good 536 | [1, 2, 3].map((x) => { 537 | const y = x + 1; 538 | return x * y; 539 | }); 540 | ``` 541 | 542 | 543 | 544 | - [8.5](#arrows--confusing) `[modified]`**[建议 / off] 在箭头函数后避免使用 (`<=`, `>=`)这样的比较操作符。** eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) 545 | 546 | ```javascript 547 | // bad 548 | const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; 549 | 550 | // bad 551 | const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; 552 | 553 | // good 554 | const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); 555 | 556 | // good 557 | const itemHeight = (item) => { 558 | const { height, largeSize, smallSize } = item; 559 | return height > 256 ? largeSize : smallSize; 560 | }; 561 | 562 | ``` 563 | 564 | **[⬆ 返回目录](#table-of-contents)** 565 | 566 | 567 | 568 | 569 | ## 类和构造器 570 | 571 | - [9.1](#9.1) **[强制] 总是使用 `class`。避免直接操作 `prototype` 。** 572 | 573 | > 为什么? 因为 `class` 语法更为简洁易读。 574 | 575 | 576 | ```javascript 577 | 578 | // bad 579 | function Queue(contents = []) { 580 | this._queue = [...contents]; 581 | } 582 | Queue.prototype.pop = function() { 583 | const value = this._queue[0]; 584 | this._queue.splice(0, 1); 585 | return value; 586 | } 587 | 588 | 589 | // good 590 | class Queue { 591 | constructor(contents = []) { 592 | this._queue = [...contents]; 593 | } 594 | pop() { 595 | const value = this._queue[0]; 596 | this._queue.splice(0, 1); 597 | return value; 598 | } 599 | } 600 | 601 | ``` 602 | 603 | 604 | 605 | - [9.5](#constructors--no-useless) **[强制]如果没有显式声明,类都有个默认的构造器(constructor)。一个空或者仅代理了父类的构造函数是不必要的。** eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) 606 | 607 | ```javascript 608 | // bad 609 | class Jedi { 610 | constructor() {} 611 | 612 | getName() { 613 | return this.name; 614 | } 615 | } 616 | 617 | // bad 618 | class Rey extends Jedi { 619 | constructor(...args) { 620 | super(...args); 621 | } 622 | } 623 | 624 | // good 625 | class Rey extends Jedi { 626 | constructor(...args) { 627 | super(...args); 628 | this.name = 'Rey'; 629 | } 630 | } 631 | ``` 632 | 633 | 634 | 635 | - [9.6](#classes--no-duplicate-members) **[强制] 避免重复类成员。** eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) 636 | 637 | > 为什么? 重复声明类成员,默认最后一个优先级最高 - 几乎肯定是个bug。 638 | 639 | ```javascript 640 | // bad 641 | class Foo { 642 | bar() { return 1; } 643 | bar() { return 2; } 644 | } 645 | 646 | // good 647 | class Foo { 648 | bar() { return 1; } 649 | } 650 | 651 | // good 652 | class Foo { 653 | bar() { return 2; } 654 | } 655 | ``` 656 | 657 | **[⬆ 返回目录](#table-of-contents)** 658 | 659 | 660 | ## 模块 661 | 662 | 663 | 664 | - [10.4](#modules--no-duplicate-imports) **[强制] 避免从同一个位置重复import.** 665 | eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) 666 | 667 | > 为什么? 这样会降低可维护性。 668 | 669 | ```javascript 670 | // bad 671 | import foo from 'foo'; 672 | // … some other imports … // 673 | import { named1, named2 } from 'foo'; 674 | 675 | // good 676 | import foo, { named1, named2 } from 'foo'; 677 | 678 | // good 679 | import foo, { 680 | named1, 681 | named2, 682 | } from 'foo'; 683 | ``` 684 | 685 | 686 | 687 | - [10.5](#modules--no-mutable-exports) **[强制] 不要将可变量export。** eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) 688 | 689 | > 为什么? 一般来说需要避免可变,特别是在export可变量时。虽然在某些特殊情况下需要这么做,但是一般来说只对常量的引用作export。 690 | 691 | ```javascript 692 | // bad 693 | let foo = 3; 694 | export { foo }; 695 | 696 | // good 697 | const foo = 3; 698 | export { foo }; 699 | ``` 700 | 701 | 702 | 703 | - [10.6](#modules--prefer-default-export) **[强制] 只有单个export的模块,推荐使用default export而不是具名export。** 704 | eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) 705 | 706 | ```javascript 707 | // bad 708 | export function foo() {} 709 | 710 | // good 711 | export default function foo() {} 712 | ``` 713 | 714 | 715 | 716 | - [10.7](#modules--imports-first) **[强制] 把所有的`import`放在非import句式前。** 717 | eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md) 718 | 719 | > 为什么? `import`存在提升(hoisted),把它们都置于文件顶部可以避免一些奇怪的行为。 720 | 721 | ```javascript 722 | // bad 723 | import foo from 'foo'; 724 | foo.init(); 725 | 726 | import bar from 'bar'; 727 | 728 | // good 729 | import foo from 'foo'; 730 | import bar from 'bar'; 731 | 732 | foo.init(); 733 | ``` 734 | 735 | 736 | 737 | - [10.9](#modules--no-webpack-loader-syntax) **[强制] 在import语句中不允许使用webpack loader语法。** 738 | eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) 739 | 740 | > 为什么? 使用这种语法的代码对打包工具有强依赖。推荐在`webpack.config.js`中使用这类语法。 741 | 742 | ```javascript 743 | // bad 744 | import fooSass from 'css!sass!foo.scss'; 745 | import barCss from 'style!css!bar.css'; 746 | 747 | // good 748 | import fooSass from 'foo.scss'; 749 | import barCss from 'bar.css'; 750 | ``` 751 | 752 | **[⬆ 返回目录](#table-of-contents)** 753 | 754 | 755 | ## 迭代器和生成器(Iterators and Generators) 756 | 757 | - [11.3](#generators--spacing) **[强制] 如果必须要使用generator, 那么要确保函数签名中使用正确的空格格式。** eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) 758 | 759 | > 为什么? `function` 和 `*` 都是概念上的关键字 - `*` 不是 `function` 的修饰符, `function*` 是一个统一结构体, 与`function`不同。 760 | 761 | ```javascript 762 | // bad 763 | function * foo() { 764 | // ... 765 | } 766 | 767 | // bad 768 | const bar = function * () { 769 | // ... 770 | }; 771 | 772 | // bad 773 | const baz = function *() { 774 | // ... 775 | }; 776 | 777 | // bad 778 | const quux = function*() { 779 | // ... 780 | }; 781 | 782 | // bad 783 | function*foo() { 784 | // ... 785 | } 786 | 787 | // bad 788 | function *foo() { 789 | // ... 790 | } 791 | 792 | // very bad 793 | function 794 | * 795 | foo() { 796 | // ... 797 | } 798 | 799 | // very bad 800 | const wat = function 801 | * 802 | () { 803 | // ... 804 | }; 805 | 806 | // good 807 | function* foo() { 808 | // ... 809 | } 810 | 811 | // good 812 | const foo = function* () { 813 | // ... 814 | }; 815 | ``` 816 | 817 | **[⬆ 返回目录](#table-of-contents)** 818 | 819 | 820 | ## 属性 821 | 822 | - [12.1](#12.1) **[建议 / warn] 使用 `.` 来访问对象的属性。** eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) 823 | 824 | ```javascript 825 | const luke = { 826 | jedi: true, 827 | age: 28, 828 | }; 829 | 830 | // bad 831 | const isJedi = luke['jedi']; 832 | 833 | // good 834 | const isJedi = luke.jedi; 835 | ``` 836 | 837 | **[⬆ 返回目录](#table-of-contents)** 838 | 839 | 840 | ## 变量 841 | 842 | - [13.1](#13.1) **[强制] 一直使用 `const` 或 `let` 来声明变量,如果不这样做就会产生全局变量。** 我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。)eslint: [`no-undef`](http://eslint.org/docs/rules/no-undef) [`prefer-const`](http://eslint.org/docs/rules/prefer-const) 843 | 844 | 845 | ```javascript 846 | // bad 847 | superPower = new SuperPower(); 848 | 849 | // good 850 | const superPower = new SuperPower(); 851 | ``` 852 | 853 | - [13.2](#13.2) **[强制] 使用 `const` 或 `let` 声明每一个变量。** eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) 854 | 855 | > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 856 | 857 | ```javascript 858 | // bad 859 | const items = getItems(), 860 | goSportsTeam = true, 861 | dragonball = 'z'; 862 | 863 | // bad 864 | // (compare to above, and try to spot the mistake) 865 | const items = getItems(), 866 | goSportsTeam = true; 867 | dragonball = 'z'; 868 | 869 | // good 870 | const items = getItems(); 871 | const goSportsTeam = true; 872 | const dragonball = 'z'; 873 | ``` 874 | 875 | 876 | 877 | - [13.5](#variables--no-chain-assignment) **[强制] 不要使用链式变量赋值.** 878 | 879 | > 为什么?链式变量赋值会创建隐式全局变量。 880 | 881 | ```javascript 882 | // bad 883 | (function example() { 884 | // JavaScript 解释器将这个语句解释为 885 | // let a = ( b = ( c = 1 ) ); 886 | // let关键字仅对a生效,b和c变成了全局变量。 887 | let a = b = c = 1; 888 | }()); 889 | 890 | console.log(a); // throws ReferenceError 891 | console.log(b); // 1 892 | console.log(c); // 1 893 | 894 | // good 895 | (function example() { 896 | let a = 1; 897 | let b = a; 898 | let c = a; 899 | }()); 900 | 901 | console.log(a); // throws ReferenceError 902 | console.log(b); // throws ReferenceError 903 | console.log(c); // throws ReferenceError 904 | 905 | // 对于 `const`也是一样的。 906 | ``` 907 | 908 | 909 | 910 | - [13.6](#variables--unary-increment-decrement) `[modified]`**[建议 / off] 避免使用一元增减运算符(++, --)。** eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) 911 | 912 | > 为什么? 根据eslint文档, 一元增减运算符会自动加入分号,在应用中会引起静默错误。使用`num += 1` 代替 `num++` 或 `num ++`来变更值会显得更加易读。 不使用一元增减运算符也可以避免在不注意的情况下值做了预增减(pre-incrementing/pre-decrementing),也会导致预期外的错误。 913 | 914 | ```javascript 915 | // bad 916 | 917 | const array = [1, 2, 3]; 918 | let num = 1; 919 | num++; 920 | --num; 921 | 922 | let sum = 0; 923 | let truthyCount = 0; 924 | for (let i = 0; i < array.length; i++) { 925 | let value = array[i]; 926 | sum += value; 927 | if (value) { 928 | truthyCount++; 929 | } 930 | } 931 | 932 | // good 933 | 934 | const array = [1, 2, 3]; 935 | let num = 1; 936 | num += 1; 937 | num -= 1; 938 | 939 | const sum = array.reduce((a, b) => a + b, 0); 940 | const truthyCount = array.filter(Boolean).length; 941 | ``` 942 | 943 | **[⬆ 返回目录](#table-of-contents)** 944 | 945 | 946 | ## Hoisting 947 | 948 | 949 | - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 950 | 951 | **[⬆ 返回目录](#table-of-contents)** 952 | 953 | 954 | ## 代码块 955 | 956 | - [16.2](#16.2) **[强制] 如果通过 `if` 和 `else` 使用多行代码块,把`else`另起一行。~~把 `else` 放在 `if` 代码块关闭括号的同一行。~~** eslint: [`brace-style`](http://eslint.org/docs/rules/brace-style.html) jscs: [`disallowNewlineBeforeBlockStatements`](http://jscs.info/rule/disallowNewlineBeforeBlockStatements) 957 | 958 | ```javascript 959 | // bad 960 | if (test) { 961 | thing1(); 962 | thing2(); 963 | } else { 964 | thing3(); 965 | } 966 | 967 | // good 968 | if (test) { 969 | thing1(); 970 | thing2(); 971 | } 972 | else { 973 | thing3(); 974 | } 975 | 976 | ``` 977 | 978 | 979 | **[⬆ 返回目录](#table-of-contents)** 980 | 981 | ## 控制语句 982 | 983 | 984 | 985 | **[⬆ 返回目录](#table-of-contents)** 986 | 987 | 988 | 989 | 990 | ## 注释 991 | 992 | - [18.3](#comments--spaces) **[强制] 所有注释开头加一个空格,增加可读性。** eslint: [`spaced-comment`](http://eslint.org/docs/rules/spaced-comment) 993 | 994 | ```javascript 995 | // bad 996 | //is current tab 997 | const active = true; 998 | 999 | // good 1000 | // is current tab 1001 | const active = true; 1002 | 1003 | // bad 1004 | /** 1005 | *make() returns a new element 1006 | *based on the passed-in tag name 1007 | */ 1008 | function make(tag) { 1009 | 1010 | // ... 1011 | 1012 | return element; 1013 | } 1014 | 1015 | // good 1016 | /** 1017 | * make() returns a new element 1018 | * based on the passed-in tag name 1019 | */ 1020 | function make(tag) { 1021 | 1022 | // ... 1023 | 1024 | return element; 1025 | } 1026 | ``` 1027 | 1028 | **[⬆ 返回目录](#table-of-contents)** 1029 | 1030 | 1031 | 1032 | ## 空格 1033 | 1034 | - [19.1](#19.1) **[强制] 使用 2 个空格作为缩进。** eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) 1035 | 1036 | ```javascript 1037 | // bad 1038 | function() { 1039 | ∙∙∙∙const name; 1040 | } 1041 | 1042 | // bad 1043 | function() { 1044 | ∙const name; 1045 | } 1046 | 1047 | // good 1048 | function() { 1049 | ∙∙const name; 1050 | } 1051 | ``` 1052 | 1053 | - [19.2](#19.2) **[强制] 在花括号前放一个空格。** eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) 1054 | 1055 | ```javascript 1056 | // bad 1057 | function test(){ 1058 | console.log('test'); 1059 | } 1060 | 1061 | // good 1062 | function test() { 1063 | console.log('test'); 1064 | } 1065 | 1066 | // bad 1067 | dog.set('attr',{ 1068 | age: '1 year', 1069 | breed: 'Bernese Mountain Dog', 1070 | }); 1071 | 1072 | // good 1073 | dog.set('attr', { 1074 | age: '1 year', 1075 | breed: 'Bernese Mountain Dog', 1076 | }); 1077 | ``` 1078 | 1079 | - [19.3](#19.3) **[强制] 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。** eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) 1080 | 1081 | ```javascript 1082 | // bad 1083 | if(isJedi) { 1084 | fight (); 1085 | } 1086 | 1087 | // good 1088 | if (isJedi) { 1089 | fight(); 1090 | } 1091 | 1092 | // bad 1093 | function fight () { 1094 | console.log ('Swooosh!'); 1095 | } 1096 | 1097 | // good 1098 | function fight() { 1099 | console.log('Swooosh!'); 1100 | } 1101 | ``` 1102 | 1103 | - [19.4](#19.4) **[强制] 使用空格把运算符隔开。** eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) jscs: [`requireSpaceBeforeBinaryOperators`](http://jscs.info/rule/requireSpaceBeforeBinaryOperators), [`requireSpaceAfterBinaryOperators`](http://jscs.info/rule/requireSpaceAfterBinaryOperators) 1104 | 1105 | ```javascript 1106 | // bad 1107 | const x=y+5; 1108 | 1109 | // good 1110 | const x = y + 5; 1111 | ``` 1112 | 1113 | - [19.5](#19.5) **[强制] 在文件末尾插入一个空行。** eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) 1114 | 1115 | ```javascript 1116 | // bad 1117 | (function(global) { 1118 | // ...stuff... 1119 | })(this); 1120 | ``` 1121 | 1122 | ```javascript 1123 | // bad 1124 | (function(global) { 1125 | // ...stuff... 1126 | })(this);↵ 1127 | ↵ 1128 | ``` 1129 | 1130 | ```javascript 1131 | // good 1132 | (function(global) { 1133 | // ...stuff... 1134 | })(this);↵ 1135 | ``` 1136 | 1137 | - [19.6](#19.6) **[强制] 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。** eslint: [`newline-per-chained-call`](http://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](http://eslint.org/docs/rules/no-whitespace-before-property) 1138 | 1139 | > 译注:当链式超过4个之后使用换行。 1140 | 1141 | ```javascript 1142 | // bad 1143 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 1144 | 1145 | // bad 1146 | $('#items'). 1147 | find('.selected'). 1148 | highlight(). 1149 | end(). 1150 | find('.open'). 1151 | updateCount(); 1152 | 1153 | // good 1154 | $('#items') 1155 | .find('.selected') 1156 | .highlight() 1157 | .end() 1158 | .find('.open') 1159 | .updateCount(); 1160 | 1161 | // bad 1162 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 1163 | .attr('width', (radius + margin) * 2).append('svg:g') 1164 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1165 | .call(tron.led); 1166 | 1167 | // good 1168 | const leds = stage.selectAll('.led') 1169 | .data(data) 1170 | .enter().append('svg:svg') 1171 | .classed('led', true) 1172 | .attr('width', (radius + margin) * 2) 1173 | .append('svg:g') 1174 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1175 | .call(tron.led); 1176 | ``` 1177 | 1178 | 1179 | 1180 | - [19.8](#whitespace--padded-blocks) **[强制]不要用代码块起始/结束位置加入空行。** eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) 1181 | 1182 | ```javascript 1183 | // bad 1184 | function bar() { 1185 | 1186 | console.log(foo); 1187 | 1188 | } 1189 | 1190 | // also bad 1191 | if (baz) { 1192 | 1193 | console.log(qux); 1194 | } else { 1195 | console.log(foo); 1196 | 1197 | } 1198 | 1199 | // good 1200 | function bar() { 1201 | console.log(foo); 1202 | } 1203 | 1204 | // good 1205 | if (baz) { 1206 | console.log(qux); 1207 | } else { 1208 | console.log(foo); 1209 | } 1210 | ``` 1211 | 1212 | 1213 | 1214 | - [19.9](#whitespace--in-parens) **[强制] 不要在括号前后加入空格。** eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) 1215 | 1216 | ```javascript 1217 | // bad 1218 | function bar( foo ) { 1219 | return foo; 1220 | } 1221 | 1222 | // good 1223 | function bar(foo) { 1224 | return foo; 1225 | } 1226 | 1227 | // bad 1228 | if ( foo ) { 1229 | console.log(foo); 1230 | } 1231 | 1232 | // good 1233 | if (foo) { 1234 | console.log(foo); 1235 | } 1236 | ``` 1237 | 1238 | 1239 | 1240 | - [19.10](#whitespace--in-brackets) **[强制] 不要在数组起始和尾部加入空格。** eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) 1241 | 1242 | ```javascript 1243 | // bad 1244 | const foo = [ 1, 2, 3 ]; 1245 | console.log(foo[ 0 ]); 1246 | 1247 | // good 1248 | const foo = [1, 2, 3]; 1249 | console.log(foo[0]); 1250 | ``` 1251 | 1252 | 1253 | 1254 | - [19.11](#whitespace--in-braces) **[强制] 在花括号首尾加入空格。** eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) 1255 | 1256 | ```javascript 1257 | // bad 1258 | const foo = {clark: 'kent'}; 1259 | 1260 | // good 1261 | const foo = { clark: 'kent' }; 1262 | ``` 1263 | 1264 | 1265 | 1266 | - [19.12](#whitespace--max-len) **[强制] 避免一行超过100个字符。** 备注:每个如上[above](#strings--line-length)的长字符串 可以不遵循这条规则。 eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) 1267 | 1268 | > 为什么?这样做可以增加可读性和可维护性。 1269 | 1270 | ```javascript 1271 | // bad 1272 | const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; 1273 | 1274 | // bad 1275 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); 1276 | 1277 | // good 1278 | const foo = jsonData 1279 | && jsonData.foo 1280 | && jsonData.foo.bar 1281 | && jsonData.foo.bar.baz 1282 | && jsonData.foo.bar.baz.quux 1283 | && jsonData.foo.bar.baz.quux.xyzzy; 1284 | 1285 | // good 1286 | $.ajax({ 1287 | method: 'POST', 1288 | url: 'https://airbnb.com/', 1289 | data: { name: 'John' }, 1290 | }) 1291 | .done(() => console.log('Congratulations!')) 1292 | .fail(() => console.log('You have failed this city.')); 1293 | ``` 1294 | 1295 | 1296 | **[⬆ 返回目录](#table-of-contents)** 1297 | 1298 | 1299 | ## 逗号 1300 | 1301 | - [20.1](#20.1) **[强制] 行首逗号:不需要。** eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) 1302 | 1303 | ```javascript 1304 | // bad 1305 | const story = [ 1306 | once 1307 | , upon 1308 | , aTime 1309 | ]; 1310 | 1311 | // good 1312 | const story = [ 1313 | once, 1314 | upon, 1315 | aTime, 1316 | ]; 1317 | 1318 | // bad 1319 | const hero = { 1320 | firstName: 'Ada' 1321 | , lastName: 'Lovelace' 1322 | , birthYear: 1815 1323 | , superPower: 'computers' 1324 | }; 1325 | 1326 | // good 1327 | const hero = { 1328 | firstName: 'Ada', 1329 | lastName: 'Lovelace', 1330 | birthYear: 1815, 1331 | superPower: 'computers', 1332 | }; 1333 | ``` 1334 | 1335 | - [20.2](#20.2) **[强制] 增加结尾的逗号: 需要。** eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) 1336 | 1337 | > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 1338 | 1339 | ```javascript 1340 | // bad - git diff without trailing comma 1341 | const hero = { 1342 | firstName: 'Florence', 1343 | - lastName: 'Nightingale' 1344 | + lastName: 'Nightingale', 1345 | + inventorOf: ['coxcomb graph', 'modern nursing'] 1346 | } 1347 | 1348 | // good - git diff with trailing comma 1349 | const hero = { 1350 | firstName: 'Florence', 1351 | lastName: 'Nightingale', 1352 | + inventorOf: ['coxcomb chart', 'modern nursing'], 1353 | } 1354 | 1355 | // bad 1356 | const hero = { 1357 | firstName: 'Dana', 1358 | lastName: 'Scully' 1359 | }; 1360 | 1361 | const heroes = [ 1362 | 'Batman', 1363 | 'Superman' 1364 | ]; 1365 | 1366 | // good 1367 | const hero = { 1368 | firstName: 'Dana', 1369 | lastName: 'Scully', 1370 | }; 1371 | 1372 | const heroes = [ 1373 | 'Batman', 1374 | 'Superman', 1375 | ]; 1376 | ``` 1377 | 1378 | **[⬆ 返回目录](#table-of-contents)** 1379 | 1380 | 1381 | ## 分号 1382 | 1383 | - [21.1](#21.1) **[强制]** **在语句末使用分号** eslint: [`semi`](http://eslint.org/docs/rules/semi.html) jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) 1384 | 1385 | 1386 | ```javascript 1387 | // bad 1388 | (function() { 1389 | const name = 'Skywalker' 1390 | return name 1391 | })() 1392 | 1393 | // good 1394 | (() => { 1395 | const name = 'Skywalker'; 1396 | return name; 1397 | })(); 1398 | 1399 | // good (防止函数在两个 IIFE 合并时被当成一个参数) 1400 | ;(() => { 1401 | const name = 'Skywalker'; 1402 | return name; 1403 | })(); 1404 | ``` 1405 | 1406 | [Read more](http://stackoverflow.com/a/7365214/1712802). 1407 | 1408 | **[⬆ 返回目录](#table-of-contents)** 1409 | 1410 | 1411 | ## 强制类型转换 1412 | 1413 | - [22.3](#22.3) **[强制] 对数字使用 `parseInt` 转换,并带上类型转换的基数。** eslint: [`radix`](http://eslint.org/docs/rules/radix) 1414 | 1415 | 1416 | ```javascript 1417 | const inputValue = '4'; 1418 | 1419 | // bad 1420 | const val = new Number(inputValue); 1421 | 1422 | // bad 1423 | const val = +inputValue; 1424 | 1425 | // bad 1426 | const val = inputValue >> 0; 1427 | 1428 | // bad 1429 | const val = parseInt(inputValue); 1430 | 1431 | // good 1432 | const val = Number(inputValue); 1433 | 1434 | // good 1435 | const val = parseInt(inputValue, 10); 1436 | ``` 1437 | 1438 | 1439 | **[⬆ 返回目录](#table-of-contents)** 1440 | 1441 | 1442 | ## 命名规则 1443 | 1444 | - [23.1](#23.1) `[modified]`**[建议 / off]避免单字母命名。命名应语义化。** eslint: [`id-length`](http://eslint.org/docs/rules/id-length) 1445 | 1446 | 1447 | ```javascript 1448 | // bad 1449 | function q() { 1450 | // ...stuff... 1451 | } 1452 | 1453 | // good 1454 | function query() { 1455 | // ..stuff.. 1456 | } 1457 | ``` 1458 | 1459 | - [23.2](#23.2) **[强制] 使用驼峰式命名对象、函数和实例。** eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) 1460 | 1461 | ```javascript 1462 | // bad 1463 | const OBJEcttsssss = {}; 1464 | const this_is_my_object = {}; 1465 | function c() {} 1466 | 1467 | // good 1468 | const thisIsMyObject = {}; 1469 | function thisIsMyFunction() {} 1470 | ``` 1471 | 1472 | - [23.3](#23.3) **[强制] 使用帕斯卡式命名构造函数或类。** eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) 1473 | 1474 | 1475 | ```javascript 1476 | // bad 1477 | function user(options) { 1478 | this.name = options.name; 1479 | } 1480 | 1481 | const bad = new user({ 1482 | name: 'nope', 1483 | }); 1484 | 1485 | // good 1486 | class User { 1487 | constructor(options) { 1488 | this.name = options.name; 1489 | } 1490 | } 1491 | 1492 | const good = new User({ 1493 | name: 'yup', 1494 | }); 1495 | ``` 1496 | 1497 | - [23.4](#23.4) `[modified]`**[建议 / warn] 不要使用下划线 `_` 结尾或开头来命名属性和方法。** eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) 1498 | 1499 | > 为什么? Javascript对属性或方法而言并没有「私有」的定义。虽然用大多人用下划线开头表示“私有”, 但是实际上这些方法是完全公有的,是公共API的一部分。这种方式会让开发者误认为修改不会影响到它,或者不需要测试。如果你需要一些“私有”定义,那么它们不应该这样显眼。 1500 | 1501 | 1502 | ```javascript 1503 | // bad 1504 | this.__firstName__ = 'Panda'; 1505 | this.firstName_ = 'Panda'; 1506 | this._firstName = 'Panda'; 1507 | 1508 | // good 1509 | this.firstName = 'Panda'; 1510 | ``` 1511 | 1512 | - [23.5](#23.5) **[强制] 别保存 `this` 的引用。使用箭头函数或 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).** jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) 1513 | 1514 | 1515 | ```javascript 1516 | // bad 1517 | function foo() { 1518 | const self = this; 1519 | return function() { 1520 | console.log(self); 1521 | }; 1522 | } 1523 | 1524 | // bad 1525 | function foo() { 1526 | const that = this; 1527 | return function() { 1528 | console.log(that); 1529 | }; 1530 | } 1531 | 1532 | // good 1533 | function foo() { 1534 | return () => { 1535 | console.log(this); 1536 | }; 1537 | } 1538 | ``` 1539 | 1540 | **[⬆ 返回目录](#table-of-contents)** 1541 | 1542 | 1543 | ## 存取器 1544 | 1545 | 1546 | **[⬆ 返回目录](#table-of-contents)** 1547 | 1548 | 1549 | ## 事件 1550 | 1551 | **[⬆ 返回目录](#table-of-contents)** 1552 | 1553 | 1554 | ## jQuery 1555 | 1556 | **[⬆ 返回目录](#table-of-contents)** 1557 | 1558 | 1559 | ## ECMAScript 5 兼容性 1560 | 1561 | - [27.1](#27.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). 1562 | 1563 | **[⬆ 返回目录](#table-of-contents)** 1564 | 1565 | 1566 | ## ECMAScript 6 规范 1567 | 1568 | - [28.1](#28.1) 以下是链接到 ES6 的各个特性的列表。 1569 | 1570 | 1. [Arrow Functions](#arrow-functions) 1571 | 1. [Classes](#constructors) 1572 | 1. [Object Shorthand](#es6-object-shorthand) 1573 | 1. [Object Concise](#es6-object-concise) 1574 | 1. [Object Computed Properties](#es6-computed-properties) 1575 | 1. [Template Strings](#es6-template-literals) 1576 | 1. [Destructuring](#destructuring) 1577 | 1. [Default Parameters](#es6-default-parameters) 1578 | 1. [Rest](#es6-rest) 1579 | 1. [Array Spreads](#es6-array-spreads) 1580 | 1. [Let and Const](#references) 1581 | 1. [Iterators and Generators](#iterators-and-generators) 1582 | 1. [Modules](#modules) 1583 | 1584 | 1585 | 1586 | 1587 | - [28.2](#tc39-proposals) `[modified]`**[建议]** 不要使用未到stage 3的 [TC39提案](https://github.com/tc39/proposals) 。 1588 | 1589 | > 为什么? [它们还不是终稿](https://tc39.github.io/process-document/), 有可能被改动或废弃。我们使用的是Javascript, 但是提案暂时还不是Javascript。 1590 | 1591 | 1592 | -------------------------------------------------------------------------------- /airbnb.md: -------------------------------------------------------------------------------- 1 | # Airbnb JavaScript Style Guide() { 2 | 3 | 以下是完整的[Airbnb Javascript Style Guide](https://github.com/airbnb/javascript)的中文翻译。 4 | 5 | 对应源repo的commit至[2666db5](https://github.com/airbnb/javascript/commit/2666db559dc0ef41887a1138cff4f59b3879892a) 6 | 7 | 8 | ## 目录 9 | 10 | 1. [类型](#types) 11 | 1. [引用](#references) 12 | 1. [对象](#objects) 13 | 1. [数组](#arrays) 14 | 1. [解构](#destructuring) 15 | 1. [字符串](#strings) 16 | 1. [函数](#functions) 17 | 1. [箭头函数](#arrow-functions) 18 | 1. [构造函数](#constructors) 19 | 1. [模块](#modules) 20 | 1. [Iterators & Generators ](#iterators-and-generators) 21 | 1. [属性](#properties) 22 | 1. [变量](#variables) 23 | 1. [提升](#hoisting) 24 | 1. [比较运算符 & 等号](#comparison-operators--equality) 25 | 1. [代码块](#blocks) 26 | 1. [注释](#comments) 27 | 1. [空白](#whitespace) 28 | 1. [逗号](#commas) 29 | 1. [分号](#semicolons) 30 | 1. [类型转换](#type-casting--coercion) 31 | 1. [命名规则](#naming-conventions) 32 | 1. [存取器](#accessors) 33 | 1. [事件](#events) 34 | 1. [jQuery](#jquery) 35 | 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) 36 | 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) 37 | 1. [测试](#testing) 38 | 1. [性能](#performance) 39 | 1. [资源](#resources) 40 | 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) 41 | 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) 42 | 1. [Contributors](#contributors) 43 | 1. [License](#license) 44 | 45 | 46 | ## 类型 47 | 48 | - [1.1](#1.1) **基本类型**: 直接存取基本类型。 49 | 50 | + `字符串` 51 | + `数值` 52 | + `布尔类型` 53 | + `null` 54 | + `undefined` 55 | 56 | 57 | ```javascript 58 | const foo = 1; 59 | let bar = foo; 60 | 61 | bar = 9; 62 | 63 | console.log(foo, bar); // => 1, 9 64 | ``` 65 | 66 | - [1.2](#1.2) **复杂类型**: 通过引用的方式存取复杂类型。 67 | 68 | + `对象` 69 | + `数组` 70 | + `函数` 71 | 72 | 73 | ```javascript 74 | const foo = [1, 2]; 75 | const bar = foo; 76 | 77 | bar[0] = 9; 78 | 79 | console.log(foo[0], bar[0]); // => 9, 9 80 | ``` 81 | 82 | **[⬆ 返回目录](#table-of-contents)** 83 | 84 | 85 | ## 引用 86 | 87 | - [2.1](#2.1) 对所有的引用使用 `const` ;不要使用 `var`。eslint: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html) 88 | 89 | 90 | > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 91 | 92 | ```javascript 93 | // bad 94 | var a = 1; 95 | var b = 2; 96 | 97 | // good 98 | const a = 1; 99 | const b = 2; 100 | ``` 101 | 102 | - [2.2](#2.2) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) 103 | 104 | > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 105 | 106 | ```javascript 107 | // bad 108 | var count = 1; 109 | if (true) { 110 | count += 1; 111 | } 112 | 113 | // good, use the let. 114 | let count = 1; 115 | if (true) { 116 | count += 1; 117 | } 118 | ``` 119 | 120 | - [2.3](#2.3) 注意 `let` 和 `const` 都是块级作用域。 121 | 122 | ```javascript 123 | // const 和 let 只存在于它们被定义的区块内。 124 | { 125 | let a = 1; 126 | const b = 1; 127 | } 128 | console.log(a); // ReferenceError 129 | console.log(b); // ReferenceError 130 | ``` 131 | 132 | **[⬆ 返回目录](#table-of-contents)** 133 | 134 | 135 | ## 对象 136 | 137 | - [3.1](#3.1) 使用字面值创建对象。eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) 138 | 139 | ```javascript 140 | // bad 141 | const item = new Object(); 142 | 143 | // good 144 | const item = {}; 145 | ``` 146 | 147 | 148 | 149 | - [3.2](#3.2) 创建有动态属性名的对象时,使用可被计算的属性名称。 150 | 151 | > 为什么?因为这样可以让你在一个地方定义所有的对象属性。 152 | 153 | ```javascript 154 | function getKey(k) { 155 | return `a key named ${k}`; 156 | } 157 | 158 | // bad 159 | const obj = { 160 | id: 5, 161 | name: 'San Francisco', 162 | }; 163 | obj[getKey('enabled')] = true; 164 | 165 | // good 166 | const obj = { 167 | id: 5, 168 | name: 'San Francisco', 169 | [getKey('enabled')]: true, 170 | }; 171 | ``` 172 | 173 | 174 | - [3.3](#3.3) 使用对象方法的简写。 175 | 176 | ```javascript 177 | // bad 178 | const atom = { 179 | value: 1, 180 | 181 | addValue: function (value) { 182 | return atom.value + value; 183 | }, 184 | }; 185 | 186 | // good 187 | const atom = { 188 | value: 1, 189 | 190 | addValue(value) { 191 | return atom.value + value; 192 | }, 193 | }; 194 | ``` 195 | 196 | 197 | - [3.4](#3.4) 使用对象属性值的简写。eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) 198 | 199 | > 为什么?因为这样更短更有描述性。 200 | 201 | ```javascript 202 | const lukeSkywalker = 'Luke Skywalker'; 203 | 204 | // bad 205 | const obj = { 206 | lukeSkywalker: lukeSkywalker, 207 | }; 208 | 209 | // good 210 | const obj = { 211 | lukeSkywalker, 212 | }; 213 | ``` 214 | 215 | - [3.5](#3.5) 在对象属性声明前把简写的属性分组。 216 | 217 | > 为什么?因为这样能清楚地看出哪些属性使用了简写。 218 | 219 | ```javascript 220 | const anakinSkywalker = 'Anakin Skywalker'; 221 | const lukeSkywalker = 'Luke Skywalker'; 222 | 223 | // bad 224 | const obj = { 225 | episodeOne: 1, 226 | twoJedisWalkIntoACantina: 2, 227 | lukeSkywalker, 228 | episodeThree: 3, 229 | mayTheFourth: 4, 230 | anakinSkywalker, 231 | }; 232 | 233 | // good 234 | const obj = { 235 | lukeSkywalker, 236 | anakinSkywalker, 237 | episodeOne: 1, 238 | twoJedisWalkIntoACantina: 2, 239 | episodeThree: 3, 240 | mayTheFourth: 4, 241 | }; 242 | ``` 243 | 244 | 245 | - [3.6](#3.6) 仅在对象属性有特殊符号时使用引号包裹。 eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) 246 | 247 | > 为什么? 这样看上去更加易读,另外还能有相关的代码高亮,也容易被许多JS引擎优化。 248 | 249 | ```javascript 250 | // bad 251 | const bad = { 252 | 'foo': 3, 253 | 'bar': 4, 254 | 'data-blah': 5, 255 | }; 256 | 257 | // good 258 | const good = { 259 | foo: 3, 260 | bar: 4, 261 | 'data-blah': 5, 262 | }; 263 | ``` 264 | 265 | 266 | - [3.7](#objects--prototype-builtins) 不要直接使用`Object.prototype`相关语法,如 `hasOwnProperty`, `propertyIsEnumerable`, 和 `isPrototypeOf`. 267 | 268 | > 为什么? 这些方法有可能被属性覆盖 - 如 `{ hasOwnProperty: false }` - 或者,对象可能是个空对象(`Object.create(null)`). 269 | 270 | ```javascript 271 | // bad 272 | console.log(object.hasOwnProperty(key)); 273 | 274 | // good 275 | console.log(Object.prototype.hasOwnProperty.call(object, key)); 276 | 277 | // best 278 | const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. 279 | /* or */ 280 | import has from 'has'; 281 | // ... 282 | console.log(has.call(object, key)); 283 | ``` 284 | 285 | 286 | - [3.8](#objects--rest-spread) 在浅拷贝时,推荐使用对象展开运算符而不是[`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)。在对象解构时,使用对象的剩余运算符来获得一个新对象。 287 | 288 | ```javascript 289 | // very bad 290 | const original = { a: 1, b: 2 }; 291 | const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ 292 | delete copy.a; // so does this 293 | 294 | // bad 295 | const original = { a: 1, b: 2 }; 296 | const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } 297 | 298 | // good 299 | const original = { a: 1, b: 2 }; 300 | const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } 301 | 302 | const { a, ...noA } = copy; // noA => { b: 2, c: 3 } 303 | 304 | ``` 305 | 306 | **[⬆ 返回目录](#table-of-contents)** 307 | 308 | 309 | ## 数组 310 | 311 | - [4.1](#4.1) 使用字面值创建数组。eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) 312 | 313 | ```javascript 314 | // bad 315 | const items = new Array(); 316 | 317 | // good 318 | const items = []; 319 | ``` 320 | 321 | - [4.2](#4.2) 向数组添加元素时使用 Arrary#push 替代直接赋值。 322 | 323 | ```javascript 324 | const someStack = []; 325 | 326 | // bad 327 | someStack[someStack.length] = 'abracadabra'; 328 | 329 | // good 330 | someStack.push('abracadabra'); 331 | ``` 332 | 333 | 334 | - [4.3](#4.3) 使用拓展运算符 `...` 复制数组。 335 | 336 | ```javascript 337 | // bad 338 | const len = items.length; 339 | const itemsCopy = []; 340 | let i; 341 | 342 | for (i = 0; i < len; i++) { 343 | itemsCopy[i] = items[i]; 344 | } 345 | 346 | // good 347 | const itemsCopy = [...items]; 348 | 349 | ``` 350 | 351 | - [4.4](#4.4) 使用 Array.from 把一个类数组对象转换成数组。 352 | 353 | ```javascript 354 | const foo = document.querySelectorAll('.foo'); 355 | const nodes = Array.from(foo); 356 | ``` 357 | 358 | 359 | 360 | - [4.5](#arrays--callback-return) 数组的相关方法使用return语句。如果函数体仅由一个带表达式且无副作用的语句组成,可以忽略return。参考[8.2](#arrows--implicit-return). eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) 361 | 362 | ```javascript 363 | // good 364 | [1, 2, 3].map((x) => { 365 | const y = x + 1; 366 | return x * y; 367 | }); 368 | 369 | // good 370 | [1, 2, 3].map(x => x + 1); 371 | 372 | // bad 373 | const flat = {}; 374 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { 375 | const flatten = memo.concat(item); 376 | flat[index] = flatten; 377 | }); 378 | 379 | // good 380 | const flat = {}; 381 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { 382 | const flatten = memo.concat(item); 383 | flat[index] = flatten; 384 | return flatten; 385 | }); 386 | 387 | // bad 388 | inbox.filter((msg) => { 389 | const { subject, author } = msg; 390 | if (subject === 'Mockingbird') { 391 | return author === 'Harper Lee'; 392 | } else { 393 | return false; 394 | } 395 | }); 396 | 397 | // good 398 | inbox.filter((msg) => { 399 | const { subject, author } = msg; 400 | if (subject === 'Mockingbird') { 401 | return author === 'Harper Lee'; 402 | } 403 | 404 | return false; 405 | }); 406 | ``` 407 | 408 | **[⬆ 返回目录](#table-of-contents)** 409 | 410 | 411 | - [4.6](#arrays--bracket-newline) 如果数组有多行,在数组括号起始和结束位置使用换行。 412 | 413 | ```javascript 414 | // bad 415 | const arr = [ 416 | [0, 1], [2, 3], [4, 5], 417 | ]; 418 | 419 | const objectInArray = [{ 420 | id: 1, 421 | }, { 422 | id: 2, 423 | }]; 424 | 425 | const numberInArray = [ 426 | 1, 2, 427 | ]; 428 | 429 | // good 430 | const arr = [[0, 1], [2, 3], [4, 5]]; 431 | 432 | const objectInArray = [ 433 | { 434 | id: 1, 435 | }, 436 | { 437 | id: 2, 438 | }, 439 | ]; 440 | 441 | const numberInArray = [ 442 | 1, 443 | 2, 444 | ]; 445 | ``` 446 | 447 | **[⬆ 返回目录](#table-of-contents)** 448 | 449 | 450 | 451 | 452 | ## 解构 453 | 454 | - [5.1](#5.1) 使用解构存取和使用多属性对象。jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) 455 | 456 | > 为什么?因为解构能减少临时引用属性。 457 | 458 | ```javascript 459 | // bad 460 | function getFullName(user) { 461 | const firstName = user.firstName; 462 | const lastName = user.lastName; 463 | 464 | return `${firstName} ${lastName}`; 465 | } 466 | 467 | // good 468 | function getFullName(obj) { 469 | const { firstName, lastName } = obj; 470 | return `${firstName} ${lastName}`; 471 | } 472 | 473 | // best 474 | function getFullName({ firstName, lastName }) { 475 | return `${firstName} ${lastName}`; 476 | } 477 | ``` 478 | 479 | - [5.2](#5.2) 对数组使用解构赋值。 480 | 481 | ```javascript 482 | const arr = [1, 2, 3, 4]; 483 | 484 | // bad 485 | const first = arr[0]; 486 | const second = arr[1]; 487 | 488 | // good 489 | const [first, second] = arr; 490 | ``` 491 | 492 | - [5.3](#5.3) 需要回传多个值时,使用对象解构,而不是数组解构。 jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) 493 | 494 | > 为什么?增加属性或者改变排序不会改变调用时的位置。 495 | 496 | ```javascript 497 | // bad 498 | function processInput(input) { 499 | // then a miracle occurs 500 | return [left, right, top, bottom]; 501 | } 502 | 503 | // 调用时需要考虑回调数据的顺序。 504 | const [left, __, top] = processInput(input); 505 | 506 | // good 507 | function processInput(input) { 508 | // then a miracle occurs 509 | return { left, right, top, bottom }; 510 | } 511 | 512 | // 调用时只选择需要的数据 513 | const { left, right } = processInput(input); 514 | ``` 515 | 516 | 517 | **[⬆ 返回目录](#table-of-contents)** 518 | 519 | 520 | ## Strings 521 | 522 | - [6.1](#6.1) 字符串使用单引号 `''` 。eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) 523 | 524 | ```javascript 525 | // bad 526 | const name = "Capt. Janeway"; 527 | 528 | // good 529 | const name = 'Capt. Janeway'; 530 | ``` 531 | 532 | - [6.2](#6.2) 字符串超过 100 个字节应该使用字符串连接号换行。 533 | - [6.3](#6.3) 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). 534 | 535 | ```javascript 536 | // bad 537 | 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.'; 538 | 539 | // bad 540 | const errorMessage = 'This is a super long error that was thrown because \ 541 | of Batman. When you stop to think about how Batman had anything to do \ 542 | with this, you would get nowhere \ 543 | fast.'; 544 | 545 | // good 546 | const errorMessage = 'This is a super long error that was thrown because ' + 547 | 'of Batman. When you stop to think about how Batman had anything to do ' + 548 | 'with this, you would get nowhere fast.'; 549 | ``` 550 | 551 | 552 | - [6.4](#6.4) 程序化生成字符串时,使用模板字符串代替字符串连接。eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) jscs: [`requireTemplateStrings`](http://jscs.info/rule/requireTemplateStrings) 553 | 554 | 555 | > 为什么?模板字符串更为简洁,更具可读性。 556 | 557 | ```javascript 558 | // bad 559 | function sayHi(name) { 560 | return 'How are you, ' + name + '?'; 561 | } 562 | 563 | // bad 564 | function sayHi(name) { 565 | return ['How are you, ', name, '?'].join(); 566 | } 567 | 568 | // good 569 | function sayHi(name) { 570 | return `How are you, ${name}?`; 571 | } 572 | ``` 573 | 574 | 575 | 576 | - [6.4](#strings--eval) 不要对字符串使用 `eval()`,会导致一系列问题。 eslint: [`no-eval`](http://eslint.org/docs/rules/no-eval) 577 | 578 | 579 | - [6.5](#strings--escaping) 不要对字符串使用不必要的escape操作。 eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) 580 | 581 | > 为什么? 反斜杠会影响可读性,仅在必须的时候使用它。 582 | 583 | ```javascript 584 | // bad 585 | const foo = '\'this\' \i\s \"quoted\"'; 586 | 587 | // good 588 | const foo = '\'this\' is "quoted"'; 589 | const foo = `my name is '${name}'`; 590 | ``` 591 | 592 | **[⬆ 返回目录](#table-of-contents)** 593 | 594 | 595 | ## 函数 596 | 597 | - [7.1](#7.1) 使用具名函数代替函数声明。 eslint: [`func-style`](http://eslint.org/docs/rules/func-style) jscs: [`disallowFunctionDeclarations`](http://jscs.info/rule/disallowFunctionDeclarations) 598 | 599 | > 为什么?因为函数声明会把函数提升(hoisted), 这样易使函数在定义前被引用。这会影响可读性和可维护性。如果一个函数的定义很长或很复杂,会干扰对文件剩余部分的理解,更好的方式是将它抽象在它自己的模块中。别忘了给函数表达式命名 - 匿名函数会使得在错误调用栈中定位问题变得困难。([讨论](https://github.com/airbnb/javascript/issues/794)) 600 | 601 | ```javascript 602 | // bad 603 | function foo() { 604 | // ... 605 | } 606 | 607 | // bad 608 | const foo = function () { 609 | // ... 610 | }; 611 | 612 | // good 613 | const foo = function bar() { 614 | // ... 615 | }; 616 | ``` 617 | 618 | 619 | 620 | - [7.2](#functions--iife) 对于立即调用(IIFE)的函数表达式,用括号包裹函数体。 eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) 621 | 622 | > 为什么?立即调用函数是一个独立单元 - 用括号包裹函数体可以清晰地表达这一点。需要注意的是,在一个到处都是「模块」的世界,几乎从不需要IIFE。 623 | 624 | 625 | ```javascript 626 | // 立即调用的函数 (IIFE) 627 | (function () { 628 | console.log('Welcome to the Internet. Please follow me.'); 629 | }()); 630 | ``` 631 | 632 | - [7.3](#7.3) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 633 | 634 | - [7.4](#7.4) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 635 | 636 | ```javascript 637 | // bad 638 | if (currentUser) { 639 | function test() { 640 | console.log('Nope.'); 641 | } 642 | } 643 | 644 | // good 645 | let test; 646 | if (currentUser) { 647 | test = () => { 648 | console.log('Yup.'); 649 | }; 650 | } 651 | ``` 652 | 653 | - [7.5](#7.5) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 654 | 655 | ```javascript 656 | // bad 657 | function nope(name, options, arguments) { 658 | // ...stuff... 659 | } 660 | 661 | // good 662 | function yup(name, options, args) { 663 | // ...stuff... 664 | } 665 | ``` 666 | 667 | 668 | - [7.6](#7.6) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 669 | 670 | > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 671 | 672 | ```javascript 673 | // bad 674 | function concatenateAll() { 675 | const args = Array.prototype.slice.call(arguments); 676 | return args.join(''); 677 | } 678 | 679 | // good 680 | function concatenateAll(...args) { 681 | return args.join(''); 682 | } 683 | ``` 684 | 685 | 686 | 687 | - [7.7](#7.7) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 688 | 689 | ```javascript 690 | // really bad 691 | function handleThings(opts) { 692 | // 不!我们不应该改变函数参数。 693 | // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 694 | // 但这样的写法会造成一些 Bugs。 695 | //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) 696 | opts = opts || {}; 697 | // ... 698 | } 699 | 700 | // still bad 701 | function handleThings(opts) { 702 | if (opts === void 0) { 703 | opts = {}; 704 | } 705 | // ... 706 | } 707 | 708 | // good 709 | function handleThings(opts = {}) { 710 | // ... 711 | } 712 | ``` 713 | 714 | - [7.8](#7.8) 直接给函数参数赋值时需要避免副作用。 715 | 716 | > 为什么?因为这样的写法让人感到很困惑。 717 | 718 | ```javascript 719 | var b = 1; 720 | // bad 721 | function count(a = b++) { 722 | console.log(a); 723 | } 724 | count(); // 1 725 | count(); // 2 726 | count(3); // 3 727 | count(); // 3 728 | ``` 729 | 730 | 731 | - [7.9](#functions--defaults-last) Always put default parameters last. 732 | 733 | ```javascript 734 | // bad 735 | function handleThings(opts = {}, name) { 736 | // ... 737 | } 738 | 739 | // good 740 | function handleThings(name, opts = {}) { 741 | // ... 742 | } 743 | ``` 744 | 745 | 746 | - [7.10](#functions--constructor) 永远不要使用Function构造函数来创建一个新函数。 eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) 747 | 748 | > 为什么?这种方式在分析字符串时与eval()类似,会带来各种问题。 749 | 750 | ```javascript 751 | // bad 752 | var add = new Function('a', 'b', 'return a + b'); 753 | 754 | // still bad 755 | var subtract = Function('a', 'b', 'return a - b'); 756 | ``` 757 | 758 | 759 | 760 | - [7.11](#functions--signature-spacing) 在函数签名中使用空格. eslint: [`space-before-function-paren`](http://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks) 761 | 762 | > 为什么?保持一致性是最佳实践, 另外如果在添加或删除名称时也不应增加/删除空格。 763 | 764 | ```javascript 765 | // bad 766 | const f = function(){}; 767 | const g = function (){}; 768 | const h = function() {}; 769 | 770 | // good 771 | const x = function () {}; 772 | const y = function a() {}; 773 | ``` 774 | 775 | 776 | - [7.12](#functions--mutate-params) 永远不要改变(mutate)参数。 eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) 777 | 778 | > 为什么?对传入的参数进行操作会在原始调用带来不想要的变量副作用。 779 | 780 | ```javascript 781 | // bad 782 | function f1(obj) { 783 | obj.key = 1; 784 | } 785 | 786 | // good 787 | function f2(obj) { 788 | const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; 789 | } 790 | ``` 791 | 792 | 793 | - [7.13](#functions--reassign-params) 永远不要对参数重新赋值(reassign)。 eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) 794 | 795 | > 为什么?对参数重新赋值会引起意料之外的行为,特别是对于`arguments`的访问。同时它会引起优化问题,特别在V8引擎中。 796 | 797 | 798 | ```javascript 799 | // bad 800 | function f1(a) { 801 | a = 1; 802 | // ... 803 | } 804 | 805 | function f2(a) { 806 | if (!a) { a = 1; } 807 | // ... 808 | } 809 | 810 | // good 811 | function f3(a) { 812 | const b = a || 1; 813 | // ... 814 | } 815 | 816 | function f4(a = 1) { 817 | // ... 818 | } 819 | ``` 820 | 821 | 822 | - [7.14](#functions--spread-vs-apply) 推荐使用展开运算符 `...` 来调用可变参数的函数。 eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) 823 | 824 | > 为什么?这样更加清晰,也不用提供上下文,而且把`new`和 `apply` 组合的方式也比较蛋疼。 825 | 826 | 827 | ```javascript 828 | // bad 829 | const x = [1, 2, 3, 4, 5]; 830 | console.log.apply(console, x); 831 | 832 | // good 833 | const x = [1, 2, 3, 4, 5]; 834 | console.log(...x); 835 | 836 | // bad 837 | new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); 838 | 839 | // good 840 | new Date(...[2016, 8, 5]); 841 | ``` 842 | 843 | 844 | 845 | - [7.15](#functions--signature-invocation-indentation) 多行签名或调用的函数, 应该和其他多行列表一样保持缩进:每项都占一行,最后一项也应有逗号。 846 | 847 | ```javascript 848 | // bad 849 | function foo(bar, 850 | baz, 851 | quux) { 852 | // ... 853 | } 854 | 855 | // good 856 | function foo( 857 | bar, 858 | baz, 859 | quux, 860 | ) { 861 | // ... 862 | } 863 | 864 | // bad 865 | console.log(foo, 866 | bar, 867 | baz); 868 | 869 | // good 870 | console.log( 871 | foo, 872 | bar, 873 | baz, 874 | ); 875 | ``` 876 | 877 | **[⬆ 返回目录](#table-of-contents)** 878 | 879 | 880 | ## 箭头函数 881 | 882 | - [8.1](#8.1) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) jscs: [`requireArrowFunctions`](http://jscs.info/rule/requireArrowFunctions) 883 | 884 | > 为什么?因为箭头函数创造了新的一个 `this` 执行环境(译注:参考 [Arrow functions - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 和 [ES6 arrow functions, syntax and lexical scoping](http://toddmotto.com/es6-arrow-functions-syntaxes-and-lexical-scoping/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 885 | 886 | > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 887 | 888 | ```javascript 889 | // bad 890 | [1, 2, 3].map(function (x) { 891 | const y = x + 1; 892 | return x * y; 893 | }); 894 | 895 | // good 896 | [1, 2, 3].map((x) => { 897 | const y = x + 1; 898 | return x * y; 899 | }); 900 | ``` 901 | 902 | - [8.2](#8.2) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam), [`requireShorthandArrowFunctions`](http://jscs.info/rule/requireShorthandArrowFunctions) 903 | 904 | > 为什么?语法糖。在链式调用中可读性很高。 905 | 906 | 907 | ```javascript 908 | // bad 909 | [1, 2, 3].map(number => { 910 | const nextNumber = number + 1; 911 | `A string containing the ${nextNumber}.`; 912 | }); 913 | 914 | // good 915 | [1, 2, 3].map(number => `A string containing the ${number}.`); 916 | 917 | // good 918 | [1, 2, 3].map((number) => { 919 | const nextNumber = number + 1; 920 | return `A string containing the ${nextNumber}.`; 921 | }); 922 | 923 | // good 924 | [1, 2, 3].map((number, index) => ({ 925 | [index]: number, 926 | })); 927 | 928 | // 当有副作用时,不要隐式return 929 | function foo(callback) { 930 | const val = callback(); 931 | if (val === true) { 932 | // Do something if callback returns true 933 | } 934 | } 935 | 936 | let bool = false; 937 | 938 | // bad 939 | foo(() => bool = true); 940 | 941 | // good 942 | foo(() => { 943 | bool = true; 944 | }); 945 | ``` 946 | 947 | 948 | 949 | - [8.3](#arrows--paren-wrap) 如果表达式是多行的, 用括号包裹来获得更好的可读性。 950 | 951 | > 为什么?这样能更清晰显示出函数的起始和结束。 952 | 953 | ```javascript 954 | // bad 955 | ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( 956 | httpMagicObjectWithAVeryLongName, 957 | httpMethod, 958 | ) 959 | ); 960 | 961 | // good 962 | ['get', 'post', 'put'].map(httpMethod => ( 963 | Object.prototype.hasOwnProperty.call( 964 | httpMagicObjectWithAVeryLongName, 965 | httpMethod, 966 | ) 967 | )); 968 | ``` 969 | 970 | 971 | 972 | - [8.4](#arrows--one-arg-parens) 如果函数是单参数且不需要花括号`{}`,则要省略掉括号。否则,始终用括号包裹参数,这样更加清晰,可读性也更好。 注:始终使用括号包裹参数也可以。 在eslint中使用 [“always” option](http://eslint.org/docs/rules/arrow-parens#always) 或在jscs中不引入 [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam)。 eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) 973 | 974 | > 为什么? 可以避免视觉上的混乱。 975 | 976 | ```javascript 977 | // bad 978 | [1, 2, 3].map((x) => x * x); 979 | 980 | // good 981 | [1, 2, 3].map(x => x * x); 982 | 983 | // good 984 | [1, 2, 3].map(number => ( 985 | `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` 986 | )); 987 | 988 | // bad 989 | [1, 2, 3].map(x => { 990 | const y = x + 1; 991 | return x * y; 992 | }); 993 | 994 | // good 995 | [1, 2, 3].map((x) => { 996 | const y = x + 1; 997 | return x * y; 998 | }); 999 | ``` 1000 | 1001 | 1002 | 1003 | - [8.5](#arrows--confusing) Avoid confusing arrow function syntax (`=>`) with comparison operators (`<=`, `>=`). eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) 1004 | 1005 | ```javascript 1006 | // bad 1007 | const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; 1008 | 1009 | // bad 1010 | const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; 1011 | 1012 | // good 1013 | const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); 1014 | 1015 | // good 1016 | const itemHeight = (item) => { 1017 | const { height, largeSize, smallSize } = item; 1018 | return height > 256 ? largeSize : smallSize; 1019 | }; 1020 | 1021 | ``` 1022 | 1023 | **[⬆ 返回目录](#table-of-contents)** 1024 | 1025 | 1026 | 1027 | 1028 | ## 类和构造器 1029 | 1030 | - [9.1](#9.1) 总是使用 `class`。避免直接操作 `prototype` 。 1031 | 1032 | > 为什么? 因为 `class` 语法更为简洁易读。 1033 | 1034 | 1035 | ```javascript 1036 | 1037 | // bad 1038 | function Queue(contents = []) { 1039 | this._queue = [...contents]; 1040 | } 1041 | Queue.prototype.pop = function() { 1042 | const value = this._queue[0]; 1043 | this._queue.splice(0, 1); 1044 | return value; 1045 | } 1046 | 1047 | 1048 | // good 1049 | class Queue { 1050 | constructor(contents = []) { 1051 | this._queue = [...contents]; 1052 | } 1053 | pop() { 1054 | const value = this._queue[0]; 1055 | this._queue.splice(0, 1); 1056 | return value; 1057 | } 1058 | } 1059 | 1060 | ``` 1061 | 1062 | 1063 | - [9.2](#9.2) 使用 `extends` 继承。 1064 | 1065 | > 为什么?因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 1066 | 1067 | ```javascript 1068 | // bad 1069 | const inherits = require('inherits'); 1070 | function PeekableQueue(contents) { 1071 | Queue.apply(this, contents); 1072 | } 1073 | inherits(PeekableQueue, Queue); 1074 | PeekableQueue.prototype.peek = function() { 1075 | return this._queue[0]; 1076 | } 1077 | 1078 | // good 1079 | class PeekableQueue extends Queue { 1080 | peek() { 1081 | return this._queue[0]; 1082 | } 1083 | } 1084 | ``` 1085 | 1086 | - [9.3](#9.3) 方法可以返回 `this` 来帮助链式调用。 1087 | 1088 | ```javascript 1089 | // bad 1090 | Jedi.prototype.jump = function() { 1091 | this.jumping = true; 1092 | return true; 1093 | }; 1094 | 1095 | Jedi.prototype.setHeight = function(height) { 1096 | this.height = height; 1097 | }; 1098 | 1099 | const luke = new Jedi(); 1100 | luke.jump(); // => true 1101 | luke.setHeight(20); // => undefined 1102 | 1103 | // good 1104 | class Jedi { 1105 | jump() { 1106 | this.jumping = true; 1107 | return this; 1108 | } 1109 | 1110 | setHeight(height) { 1111 | this.height = height; 1112 | return this; 1113 | } 1114 | } 1115 | 1116 | const luke = new Jedi(); 1117 | 1118 | luke.jump() 1119 | .setHeight(20); 1120 | ``` 1121 | 1122 | 1123 | - [9.4](#9.4) 可以写一个自定义的 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 1124 | 1125 | ```javascript 1126 | class Jedi { 1127 | constructor(options = {}) { 1128 | this.name = options.name || 'no name'; 1129 | } 1130 | 1131 | getName() { 1132 | return this.name; 1133 | } 1134 | 1135 | toString() { 1136 | return `Jedi - ${this.getName()}`; 1137 | } 1138 | } 1139 | ``` 1140 | 1141 | 1142 | 1143 | - [9.5](#constructors--no-useless) 如果没有显式声明,类都有个默认的构造器(constructor)。一个空或者仅代理了父类的构造函数是不必要的。 eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) 1144 | 1145 | ```javascript 1146 | // bad 1147 | class Jedi { 1148 | constructor() {} 1149 | 1150 | getName() { 1151 | return this.name; 1152 | } 1153 | } 1154 | 1155 | // bad 1156 | class Rey extends Jedi { 1157 | constructor(...args) { 1158 | super(...args); 1159 | } 1160 | } 1161 | 1162 | // good 1163 | class Rey extends Jedi { 1164 | constructor(...args) { 1165 | super(...args); 1166 | this.name = 'Rey'; 1167 | } 1168 | } 1169 | ``` 1170 | 1171 | 1172 | 1173 | - [9.6](#classes--no-duplicate-members) 避免重复类成员。 eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) 1174 | 1175 | > 为什么? 重复声明类成员,默认最后一个优先级最高 - 几乎肯定是个bug。 1176 | 1177 | ```javascript 1178 | // bad 1179 | class Foo { 1180 | bar() { return 1; } 1181 | bar() { return 2; } 1182 | } 1183 | 1184 | // good 1185 | class Foo { 1186 | bar() { return 1; } 1187 | } 1188 | 1189 | // good 1190 | class Foo { 1191 | bar() { return 2; } 1192 | } 1193 | ``` 1194 | 1195 | **[⬆ 返回目录](#table-of-contents)** 1196 | 1197 | 1198 | ## 模块 1199 | 1200 | - [10.1](#10.1) 总是使用模组 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 1201 | 1202 | > 为什么?模块就是未来,让我们开始迈向未来吧。 1203 | 1204 | ```javascript 1205 | // bad 1206 | const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 1207 | module.exports = AirbnbStyleGuide.es6; 1208 | 1209 | // ok 1210 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 1211 | export default AirbnbStyleGuide.es6; 1212 | 1213 | // best 1214 | import { es6 } from './AirbnbStyleGuide'; 1215 | export default es6; 1216 | ``` 1217 | 1218 | - [10.2](#10.2) 不要使用通配符 import。 1219 | 1220 | > 为什么?这样能确保你只有一个默认 export。 1221 | 1222 | ```javascript 1223 | // bad 1224 | import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 1225 | 1226 | // good 1227 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 1228 | ``` 1229 | 1230 | - [10.3](#10.3) 不要从 import 中直接 export。 1231 | 1232 | > 为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 1233 | 1234 | ```javascript 1235 | // bad 1236 | // filename es6.js 1237 | export { es6 as default } from './airbnbStyleGuide'; 1238 | 1239 | // good 1240 | // filename es6.js 1241 | import { es6 } from './AirbnbStyleGuide'; 1242 | export default es6; 1243 | ``` 1244 | 1245 | 1246 | 1247 | - [10.4](#modules--no-duplicate-imports) 避免从同一个位置重复import. 1248 | eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) 1249 | 1250 | > 为什么? 这样会降低可维护性。 1251 | 1252 | ```javascript 1253 | // bad 1254 | import foo from 'foo'; 1255 | // … some other imports … // 1256 | import { named1, named2 } from 'foo'; 1257 | 1258 | // good 1259 | import foo, { named1, named2 } from 'foo'; 1260 | 1261 | // good 1262 | import foo, { 1263 | named1, 1264 | named2, 1265 | } from 'foo'; 1266 | ``` 1267 | 1268 | 1269 | 1270 | - [10.5](#modules--no-mutable-exports) 不要将可变量export。eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) 1271 | 1272 | > 为什么? 一般来说需要避免可变,特别是在export可变量时。虽然在某些特殊情况下需要这么做,但是一般来说只对常量的引用作export。 1273 | 1274 | ```javascript 1275 | // bad 1276 | let foo = 3; 1277 | export { foo }; 1278 | 1279 | // good 1280 | const foo = 3; 1281 | export { foo }; 1282 | ``` 1283 | 1284 | 1285 | 1286 | - [10.6](#modules--prefer-default-export) 只有单个export的模块,推荐使用default export而不是具名export。 1287 | eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) 1288 | 1289 | ```javascript 1290 | // bad 1291 | export function foo() {} 1292 | 1293 | // good 1294 | export default function foo() {} 1295 | ``` 1296 | 1297 | 1298 | 1299 | - [10.7](#modules--imports-first) 把所有的`import`放在非import句式前。 1300 | eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md) 1301 | 1302 | > 为什么? `import`存在提升(hoisted),把它们都置于文件顶部可以避免一些奇怪的行为。 1303 | 1304 | ```javascript 1305 | // bad 1306 | import foo from 'foo'; 1307 | foo.init(); 1308 | 1309 | import bar from 'bar'; 1310 | 1311 | // good 1312 | import foo from 'foo'; 1313 | import bar from 'bar'; 1314 | 1315 | foo.init(); 1316 | ``` 1317 | 1318 | 1319 | 1320 | - [10.8](#modules--multiline-imports-over-newlines) 多行的import应该像多行Array/object一样保持缩进。 1321 | 1322 | > 为什么? 这条规则在style guide里面的其他缩进规则保持一致,包括尾部的逗号。 1323 | 1324 | ```javascript 1325 | // bad 1326 | import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; 1327 | 1328 | // good 1329 | import { 1330 | longNameA, 1331 | longNameB, 1332 | longNameC, 1333 | longNameD, 1334 | longNameE, 1335 | } from 'path'; 1336 | ``` 1337 | 1338 | 1339 | 1340 | - [10.9](#modules--no-webpack-loader-syntax) 在import语句中不允许使用webpack loader语法。 1341 | eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) 1342 | 1343 | > 为什么? 使用这种语法的代码对打包工具有强依赖。推荐在`webpack.config.js`中使用这类语法。 1344 | 1345 | ```javascript 1346 | // bad 1347 | import fooSass from 'css!sass!foo.scss'; 1348 | import barCss from 'style!css!bar.css'; 1349 | 1350 | // good 1351 | import fooSass from 'foo.scss'; 1352 | import barCss from 'bar.css'; 1353 | ``` 1354 | 1355 | **[⬆ 返回目录](#table-of-contents)** 1356 | 1357 | 1358 | ## 迭代器和生成器(Iterators and Generators) 1359 | 1360 | - [11.1](#11.1) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-of`。 1361 | 1362 | > 为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 1363 | 1364 | ```javascript 1365 | const numbers = [1, 2, 3, 4, 5]; 1366 | 1367 | // bad 1368 | let sum = 0; 1369 | for (let num of numbers) { 1370 | sum += num; 1371 | } 1372 | 1373 | sum === 15; 1374 | 1375 | // good 1376 | let sum = 0; 1377 | numbers.forEach((num) => sum += num); 1378 | sum === 15; 1379 | 1380 | // best (use the functional force) 1381 | const sum = numbers.reduce((total, num) => total + num, 0); 1382 | sum === 15; 1383 | ``` 1384 | 1385 | - [11.2](#11.2) 现在还不要使用 generators。 1386 | 1387 | > 为什么?因为它们现在还没法很好地编译到 ES5。 1388 | 1389 | 1390 | 1391 | - [11.3](#generators--spacing) 如果必须要使用generator, 那么要确保函数签名中使用正确的空格格式。 eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) 1392 | 1393 | > 为什么? `function` 和 `*` 都是概念上的关键字 - `*` 不是 `function` 的修饰符, `function*` 是一个统一结构体, 与`function`不同。 1394 | 1395 | ```javascript 1396 | // bad 1397 | function * foo() { 1398 | // ... 1399 | } 1400 | 1401 | // bad 1402 | const bar = function * () { 1403 | // ... 1404 | }; 1405 | 1406 | // bad 1407 | const baz = function *() { 1408 | // ... 1409 | }; 1410 | 1411 | // bad 1412 | const quux = function*() { 1413 | // ... 1414 | }; 1415 | 1416 | // bad 1417 | function*foo() { 1418 | // ... 1419 | } 1420 | 1421 | // bad 1422 | function *foo() { 1423 | // ... 1424 | } 1425 | 1426 | // very bad 1427 | function 1428 | * 1429 | foo() { 1430 | // ... 1431 | } 1432 | 1433 | // very bad 1434 | const wat = function 1435 | * 1436 | () { 1437 | // ... 1438 | }; 1439 | 1440 | // good 1441 | function* foo() { 1442 | // ... 1443 | } 1444 | 1445 | // good 1446 | const foo = function* () { 1447 | // ... 1448 | }; 1449 | ``` 1450 | 1451 | **[⬆ 返回目录](#table-of-contents)** 1452 | 1453 | 1454 | ## 属性 1455 | 1456 | - [12.1](#12.1) 使用 `.` 来访问对象的属性。eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) 1457 | 1458 | ```javascript 1459 | const luke = { 1460 | jedi: true, 1461 | age: 28, 1462 | }; 1463 | 1464 | // bad 1465 | const isJedi = luke['jedi']; 1466 | 1467 | // good 1468 | const isJedi = luke.jedi; 1469 | ``` 1470 | 1471 | - [12.2](#12.2) 当通过变量访问属性时使用中括号 `[]`。 1472 | 1473 | ```javascript 1474 | const luke = { 1475 | jedi: true, 1476 | age: 28, 1477 | }; 1478 | 1479 | function getProp(prop) { 1480 | return luke[prop]; 1481 | } 1482 | 1483 | const isJedi = getProp('jedi'); 1484 | ``` 1485 | 1486 | **[⬆ 返回目录](#table-of-contents)** 1487 | 1488 | 1489 | ## 变量 1490 | 1491 | - [13.1](#13.1) 一直使用 `const` 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。)eslint: [`no-undef`](http://eslint.org/docs/rules/no-undef) [`prefer-const`](http://eslint.org/docs/rules/prefer-const) 1492 | 1493 | 1494 | ```javascript 1495 | // bad 1496 | superPower = new SuperPower(); 1497 | 1498 | // good 1499 | const superPower = new SuperPower(); 1500 | ``` 1501 | 1502 | - [13.2](#13.2) 使用 `const` 声明每一个变量。eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) 1503 | 1504 | > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 1505 | 1506 | ```javascript 1507 | // bad 1508 | const items = getItems(), 1509 | goSportsTeam = true, 1510 | dragonball = 'z'; 1511 | 1512 | // bad 1513 | // (compare to above, and try to spot the mistake) 1514 | const items = getItems(), 1515 | goSportsTeam = true; 1516 | dragonball = 'z'; 1517 | 1518 | // good 1519 | const items = getItems(); 1520 | const goSportsTeam = true; 1521 | const dragonball = 'z'; 1522 | ``` 1523 | 1524 | - [13.3](#13.3) 将所有的 `const` 和 `let` 分组 1525 | 1526 | > 为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。 1527 | 1528 | ```javascript 1529 | // bad 1530 | let i, len, dragonball, 1531 | items = getItems(), 1532 | goSportsTeam = true; 1533 | 1534 | // bad 1535 | let i; 1536 | const items = getItems(); 1537 | let dragonball; 1538 | const goSportsTeam = true; 1539 | let len; 1540 | 1541 | // good 1542 | const goSportsTeam = true; 1543 | const items = getItems(); 1544 | let dragonball; 1545 | let i; 1546 | let length; 1547 | ``` 1548 | 1549 | - [13.4](#13.4) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 1550 | 1551 | > 为什么?`let` 和 `const` 是块级作用域而不是函数作用域。 1552 | 1553 | ```javascript 1554 | // good 1555 | function() { 1556 | test(); 1557 | console.log('doing stuff..'); 1558 | 1559 | //..other stuff.. 1560 | 1561 | const name = getName(); 1562 | 1563 | if (name === 'test') { 1564 | return false; 1565 | } 1566 | 1567 | return name; 1568 | } 1569 | 1570 | // bad - unnecessary function call 1571 | function(hasName) { 1572 | const name = getName(); 1573 | 1574 | if (!hasName) { 1575 | return false; 1576 | } 1577 | 1578 | this.setFirstName(name); 1579 | 1580 | return true; 1581 | } 1582 | 1583 | // good 1584 | function(hasName) { 1585 | if (!hasName) { 1586 | return false; 1587 | } 1588 | 1589 | const name = getName(); 1590 | this.setFirstName(name); 1591 | 1592 | return true; 1593 | } 1594 | ``` 1595 | 1596 | 1597 | 1598 | - [13.5](#variables--no-chain-assignment) 不要使用链式变量赋值. 1599 | 1600 | > 为什么?链式变量赋值会创建隐式全局变量。 1601 | 1602 | ```javascript 1603 | // bad 1604 | (function example() { 1605 | // JavaScript 解释器将这个语句解释为 1606 | // let a = ( b = ( c = 1 ) ); 1607 | // let关键字仅对a生效,b和c变成了全局变量。 1608 | let a = b = c = 1; 1609 | }()); 1610 | 1611 | console.log(a); // throws ReferenceError 1612 | console.log(b); // 1 1613 | console.log(c); // 1 1614 | 1615 | // good 1616 | (function example() { 1617 | let a = 1; 1618 | let b = a; 1619 | let c = a; 1620 | }()); 1621 | 1622 | console.log(a); // throws ReferenceError 1623 | console.log(b); // throws ReferenceError 1624 | console.log(c); // throws ReferenceError 1625 | 1626 | // 对于 `const`也是一样的。 1627 | ``` 1628 | 1629 | 1630 | 1631 | - [13.6](#variables--unary-increment-decrement) 避免使用一元增减运算符(++, --)。 eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) 1632 | 1633 | > 为什么? 根据eslint文档, 一元增减运算符会自动加入分号,在应用中会引起静默错误。使用`num += 1` 代替 `num++` 或 `num ++`来变更值会显得更加易读。 不使用一元增减运算符也可以避免在不注意的情况下值做了预增减(pre-incrementing/pre-decrementing),也会导致预期外的错误。 1634 | 1635 | ```javascript 1636 | // bad 1637 | 1638 | const array = [1, 2, 3]; 1639 | let num = 1; 1640 | num++; 1641 | --num; 1642 | 1643 | let sum = 0; 1644 | let truthyCount = 0; 1645 | for (let i = 0; i < array.length; i++) { 1646 | let value = array[i]; 1647 | sum += value; 1648 | if (value) { 1649 | truthyCount++; 1650 | } 1651 | } 1652 | 1653 | // good 1654 | 1655 | const array = [1, 2, 3]; 1656 | let num = 1; 1657 | num += 1; 1658 | num -= 1; 1659 | 1660 | const sum = array.reduce((a, b) => a + b, 0); 1661 | const truthyCount = array.filter(Boolean).length; 1662 | ``` 1663 | 1664 | **[⬆ 返回目录](#table-of-contents)** 1665 | 1666 | 1667 | ## Hoisting 1668 | 1669 | - [14.1](#14.1) `var` 声明会被提升至该作用域的顶部,但它们赋值不会提升。`let` 和 `const` 被赋予了一种称为「[暂时性死区(Temporal Dead Zones, TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let)」的概念。这对于了解为什么 [type of 不再安全](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15)相当重要。 1670 | 1671 | ```javascript 1672 | // 我们知道这样运行不了 1673 | // (假设 notDefined 不是全局变量) 1674 | function example() { 1675 | console.log(notDefined); // => throws a ReferenceError 1676 | } 1677 | 1678 | // 由于变量提升的原因, 1679 | // 在引用变量后再声明变量是可以运行的。 1680 | // 注:变量的赋值 `true` 不会被提升。 1681 | function example() { 1682 | console.log(declaredButNotAssigned); // => undefined 1683 | var declaredButNotAssigned = true; 1684 | } 1685 | 1686 | // 编译器会把函数声明提升到作用域的顶层, 1687 | // 这意味着我们的例子可以改写成这样: 1688 | function example() { 1689 | let declaredButNotAssigned; 1690 | console.log(declaredButNotAssigned); // => undefined 1691 | declaredButNotAssigned = true; 1692 | } 1693 | 1694 | // 使用 const 和 let 1695 | function example() { 1696 | console.log(declaredButNotAssigned); // => throws a ReferenceError 1697 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError 1698 | const declaredButNotAssigned = true; 1699 | } 1700 | ``` 1701 | 1702 | - [14.2](#14.2) 匿名函数表达式的变量名会被提升,但函数内容并不会。 1703 | 1704 | ```javascript 1705 | function example() { 1706 | console.log(anonymous); // => undefined 1707 | 1708 | anonymous(); // => TypeError anonymous is not a function 1709 | 1710 | var anonymous = function() { 1711 | console.log('anonymous function expression'); 1712 | }; 1713 | } 1714 | ``` 1715 | 1716 | - [14.3](#14.3) 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。 1717 | 1718 | ```javascript 1719 | function example() { 1720 | console.log(named); // => undefined 1721 | 1722 | named(); // => TypeError named is not a function 1723 | 1724 | superPower(); // => ReferenceError superPower is not defined 1725 | 1726 | var named = function superPower() { 1727 | console.log('Flying'); 1728 | }; 1729 | } 1730 | 1731 | // the same is true when the function name 1732 | // is the same as the variable name. 1733 | function example() { 1734 | console.log(named); // => undefined 1735 | 1736 | named(); // => TypeError named is not a function 1737 | 1738 | var named = function named() { 1739 | console.log('named'); 1740 | } 1741 | } 1742 | ``` 1743 | 1744 | - [14.4](#14.4) 函数声明的名称和函数体都会被提升。 1745 | 1746 | ```javascript 1747 | function example() { 1748 | superPower(); // => Flying 1749 | 1750 | function superPower() { 1751 | console.log('Flying'); 1752 | } 1753 | } 1754 | ``` 1755 | 1756 | - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 1757 | 1758 | **[⬆ 返回目录](#table-of-contents)** 1759 | 1760 | 1761 | ## 代码块 1762 | 1763 | - [16.1](#16.1) 使用大括号包裹所有的多行代码块。 1764 | 1765 | ```javascript 1766 | // bad 1767 | if (test) 1768 | return false; 1769 | 1770 | // good 1771 | if (test) return false; 1772 | 1773 | // good 1774 | if (test) { 1775 | return false; 1776 | } 1777 | 1778 | // bad 1779 | function() { return false; } 1780 | 1781 | // good 1782 | function() { 1783 | return false; 1784 | } 1785 | ``` 1786 | 1787 | - [16.2](#16.2) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。eslint: [`brace-style`](http://eslint.org/docs/rules/brace-style.html) jscs: [`disallowNewlineBeforeBlockStatements`](http://jscs.info/rule/disallowNewlineBeforeBlockStatements) 1788 | 1789 | ```javascript 1790 | // bad 1791 | if (test) { 1792 | thing1(); 1793 | thing2(); 1794 | } 1795 | else { 1796 | thing3(); 1797 | } 1798 | 1799 | // good 1800 | if (test) { 1801 | thing1(); 1802 | thing2(); 1803 | } else { 1804 | thing3(); 1805 | } 1806 | ``` 1807 | 1808 | 1809 | **[⬆ 返回目录](#table-of-contents)** 1810 | 1811 | ## 控制语句 1812 | 1813 | 1814 | - [17.1](#control-statements) 在控制语句中 (`if`, `while` 等),如果超过了一行的最大长度,应该对每个分组换行。逻辑运算符应该在行首或行尾取决于你自己。 1815 | 1816 | ```javascript 1817 | // bad 1818 | if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { 1819 | thing1(); 1820 | } 1821 | 1822 | // bad 1823 | if (foo === 123 && 1824 | bar === 'abc') { 1825 | thing1(); 1826 | } 1827 | 1828 | // bad 1829 | if (foo === 123 1830 | && bar === 'abc') { 1831 | thing1(); 1832 | } 1833 | 1834 | // good 1835 | if ( 1836 | (foo === 123 || bar === "abc") && 1837 | doesItLookGoodWhenItBecomesThatLong() && 1838 | isThisReallyHappening() 1839 | ) { 1840 | thing1(); 1841 | } 1842 | 1843 | // good 1844 | if (foo === 123 && bar === 'abc') { 1845 | thing1(); 1846 | } 1847 | 1848 | // good 1849 | if ( 1850 | foo === 123 && 1851 | bar === 'abc' 1852 | ) { 1853 | thing1(); 1854 | } 1855 | 1856 | // good 1857 | if ( 1858 | foo === 123 1859 | && bar === 'abc' 1860 | ) { 1861 | thing1(); 1862 | } 1863 | ``` 1864 | 1865 | 1866 | 1867 | 1868 | ## 注释 1869 | 1870 | - [18.1](#18.1) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 1871 | 1872 | ```javascript 1873 | // bad 1874 | // make() returns a new element 1875 | // based on the passed in tag name 1876 | // 1877 | // @param {String} tag 1878 | // @return {Element} element 1879 | function make(tag) { 1880 | 1881 | // ...stuff... 1882 | 1883 | return element; 1884 | } 1885 | 1886 | // good 1887 | /** 1888 | * make() returns a new element 1889 | * based on the passed in tag name 1890 | * 1891 | * @param {String} tag 1892 | * @return {Element} element 1893 | */ 1894 | function make(tag) { 1895 | 1896 | // ...stuff... 1897 | 1898 | return element; 1899 | } 1900 | ``` 1901 | 1902 | - [18.2](#18.2) 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 1903 | 1904 | ```javascript 1905 | // bad 1906 | const active = true; // is current tab 1907 | 1908 | // good 1909 | // is current tab 1910 | const active = true; 1911 | 1912 | // bad 1913 | function getType() { 1914 | console.log('fetching type...'); 1915 | // set the default type to 'no type' 1916 | const type = this._type || 'no type'; 1917 | 1918 | return type; 1919 | } 1920 | 1921 | // good 1922 | function getType() { 1923 | console.log('fetching type...'); 1924 | 1925 | // set the default type to 'no type' 1926 | const type = this._type || 'no type'; 1927 | 1928 | return type; 1929 | } 1930 | ``` 1931 | 1932 | - [18.3](#comments--spaces) 所有注释开头加一个空格,增加可读性。eslint: [`spaced-comment`](http://eslint.org/docs/rules/spaced-comment) 1933 | 1934 | ```javascript 1935 | // bad 1936 | //is current tab 1937 | const active = true; 1938 | 1939 | // good 1940 | // is current tab 1941 | const active = true; 1942 | 1943 | // bad 1944 | /** 1945 | *make() returns a new element 1946 | *based on the passed-in tag name 1947 | */ 1948 | function make(tag) { 1949 | 1950 | // ... 1951 | 1952 | return element; 1953 | } 1954 | 1955 | // good 1956 | /** 1957 | * make() returns a new element 1958 | * based on the passed-in tag name 1959 | */ 1960 | function make(tag) { 1961 | 1962 | // ... 1963 | 1964 | return element; 1965 | } 1966 | ``` 1967 | 1968 | - [18.4](#18.4) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 1969 | 1970 | - [18.5](#18.5) 使用 `// FIXME`: 标注问题。 1971 | 1972 | ```javascript 1973 | class Calculator { 1974 | constructor() { 1975 | // FIXME: shouldn't use a global here 1976 | total = 0; 1977 | } 1978 | } 1979 | ``` 1980 | 1981 | - [18.6](#18.6) 使用 `// TODO`: 标注问题的解决方式。 1982 | 1983 | ```javascript 1984 | class Calculator { 1985 | constructor() { 1986 | // TODO: total should be configurable by an options param 1987 | this.total = 0; 1988 | } 1989 | } 1990 | ``` 1991 | 1992 | **[⬆ 返回目录](#table-of-contents)** 1993 | 1994 | 1995 | 1996 | ## 空格 1997 | 1998 | - [19.1](#19.1) 使用 2 个空格作为缩进。eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) 1999 | 2000 | ```javascript 2001 | // bad 2002 | function() { 2003 | ∙∙∙∙const name; 2004 | } 2005 | 2006 | // bad 2007 | function() { 2008 | ∙const name; 2009 | } 2010 | 2011 | // good 2012 | function() { 2013 | ∙∙const name; 2014 | } 2015 | ``` 2016 | 2017 | - [19.2](#19.2) 在花括号前放一个空格。eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) 2018 | 2019 | ```javascript 2020 | // bad 2021 | function test(){ 2022 | console.log('test'); 2023 | } 2024 | 2025 | // good 2026 | function test() { 2027 | console.log('test'); 2028 | } 2029 | 2030 | // bad 2031 | dog.set('attr',{ 2032 | age: '1 year', 2033 | breed: 'Bernese Mountain Dog', 2034 | }); 2035 | 2036 | // good 2037 | dog.set('attr', { 2038 | age: '1 year', 2039 | breed: 'Bernese Mountain Dog', 2040 | }); 2041 | ``` 2042 | 2043 | - [19.3](#19.3) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) 2044 | 2045 | ```javascript 2046 | // bad 2047 | if(isJedi) { 2048 | fight (); 2049 | } 2050 | 2051 | // good 2052 | if (isJedi) { 2053 | fight(); 2054 | } 2055 | 2056 | // bad 2057 | function fight () { 2058 | console.log ('Swooosh!'); 2059 | } 2060 | 2061 | // good 2062 | function fight() { 2063 | console.log('Swooosh!'); 2064 | } 2065 | ``` 2066 | 2067 | - [19.4](#19.4) 使用空格把运算符隔开。eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) jscs: [`requireSpaceBeforeBinaryOperators`](http://jscs.info/rule/requireSpaceBeforeBinaryOperators), [`requireSpaceAfterBinaryOperators`](http://jscs.info/rule/requireSpaceAfterBinaryOperators) 2068 | 2069 | ```javascript 2070 | // bad 2071 | const x=y+5; 2072 | 2073 | // good 2074 | const x = y + 5; 2075 | ``` 2076 | 2077 | - [19.5](#19.5) 在文件末尾插入一个空行。 eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) 2078 | 2079 | ```javascript 2080 | // bad 2081 | (function(global) { 2082 | // ...stuff... 2083 | })(this); 2084 | ``` 2085 | 2086 | ```javascript 2087 | // bad 2088 | (function(global) { 2089 | // ...stuff... 2090 | })(this);↵ 2091 | ↵ 2092 | ``` 2093 | 2094 | ```javascript 2095 | // good 2096 | (function(global) { 2097 | // ...stuff... 2098 | })(this);↵ 2099 | ``` 2100 | 2101 | - [19.6](#19.6) 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 eslint: [`newline-per-chained-call`](http://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](http://eslint.org/docs/rules/no-whitespace-before-property) 2102 | 2103 | ```javascript 2104 | // bad 2105 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 2106 | 2107 | // bad 2108 | $('#items'). 2109 | find('.selected'). 2110 | highlight(). 2111 | end(). 2112 | find('.open'). 2113 | updateCount(); 2114 | 2115 | // good 2116 | $('#items') 2117 | .find('.selected') 2118 | .highlight() 2119 | .end() 2120 | .find('.open') 2121 | .updateCount(); 2122 | 2123 | // bad 2124 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 2125 | .attr('width', (radius + margin) * 2).append('svg:g') 2126 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 2127 | .call(tron.led); 2128 | 2129 | // good 2130 | const leds = stage.selectAll('.led') 2131 | .data(data) 2132 | .enter().append('svg:svg') 2133 | .classed('led', true) 2134 | .attr('width', (radius + margin) * 2) 2135 | .append('svg:g') 2136 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 2137 | .call(tron.led); 2138 | ``` 2139 | 2140 | - [19.7](#19.7) 在块末和新语句前插入空行。jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) 2141 | 2142 | 2143 | ```javascript 2144 | // bad 2145 | if (foo) { 2146 | return bar; 2147 | } 2148 | return baz; 2149 | 2150 | // good 2151 | if (foo) { 2152 | return bar; 2153 | } 2154 | 2155 | return baz; 2156 | 2157 | // bad 2158 | const obj = { 2159 | foo() { 2160 | }, 2161 | bar() { 2162 | }, 2163 | }; 2164 | return obj; 2165 | 2166 | // good 2167 | const obj = { 2168 | foo() { 2169 | }, 2170 | 2171 | bar() { 2172 | }, 2173 | }; 2174 | 2175 | return obj; 2176 | ``` 2177 | 2178 | 2179 | 2180 | - [19.8](#whitespace--padded-blocks) 不要用代码块起始/结束位置加入空行。eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) 2181 | 2182 | ```javascript 2183 | // bad 2184 | function bar() { 2185 | 2186 | console.log(foo); 2187 | 2188 | } 2189 | 2190 | // also bad 2191 | if (baz) { 2192 | 2193 | console.log(qux); 2194 | } else { 2195 | console.log(foo); 2196 | 2197 | } 2198 | 2199 | // good 2200 | function bar() { 2201 | console.log(foo); 2202 | } 2203 | 2204 | // good 2205 | if (baz) { 2206 | console.log(qux); 2207 | } else { 2208 | console.log(foo); 2209 | } 2210 | ``` 2211 | 2212 | 2213 | 2214 | - [19.9](#whitespace--in-parens) 不要在括号前后加入空格。eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) 2215 | 2216 | ```javascript 2217 | // bad 2218 | function bar( foo ) { 2219 | return foo; 2220 | } 2221 | 2222 | // good 2223 | function bar(foo) { 2224 | return foo; 2225 | } 2226 | 2227 | // bad 2228 | if ( foo ) { 2229 | console.log(foo); 2230 | } 2231 | 2232 | // good 2233 | if (foo) { 2234 | console.log(foo); 2235 | } 2236 | ``` 2237 | 2238 | 2239 | 2240 | - [19.10](#whitespace--in-brackets) 不要在数组起始和尾部加入空格。 eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) 2241 | 2242 | ```javascript 2243 | // bad 2244 | const foo = [ 1, 2, 3 ]; 2245 | console.log(foo[ 0 ]); 2246 | 2247 | // good 2248 | const foo = [1, 2, 3]; 2249 | console.log(foo[0]); 2250 | ``` 2251 | 2252 | 2253 | 2254 | - [19.11](#whitespace--in-braces) 在花括号首尾加入空格。 eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) 2255 | 2256 | ```javascript 2257 | // bad 2258 | const foo = {clark: 'kent'}; 2259 | 2260 | // good 2261 | const foo = { clark: 'kent' }; 2262 | ``` 2263 | 2264 | 2265 | 2266 | - [19.12](#whitespace--max-len) 避免一行超过100个字符。备注:每个如上[above](#strings--line-length)的长字符串 可以不遵循这条规则。 eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) 2267 | 2268 | > 为什么?这样做可以增加可读性和可维护性。 2269 | 2270 | ```javascript 2271 | // bad 2272 | const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; 2273 | 2274 | // bad 2275 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); 2276 | 2277 | // good 2278 | const foo = jsonData 2279 | && jsonData.foo 2280 | && jsonData.foo.bar 2281 | && jsonData.foo.bar.baz 2282 | && jsonData.foo.bar.baz.quux 2283 | && jsonData.foo.bar.baz.quux.xyzzy; 2284 | 2285 | // good 2286 | $.ajax({ 2287 | method: 'POST', 2288 | url: 'https://airbnb.com/', 2289 | data: { name: 'John' }, 2290 | }) 2291 | .done(() => console.log('Congratulations!')) 2292 | .fail(() => console.log('You have failed this city.')); 2293 | ``` 2294 | 2295 | 2296 | **[⬆ 返回目录](#table-of-contents)** 2297 | 2298 | 2299 | ## 逗号 2300 | 2301 | - [20.1](#20.1) 行首逗号:**不需要**。eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) 2302 | 2303 | ```javascript 2304 | // bad 2305 | const story = [ 2306 | once 2307 | , upon 2308 | , aTime 2309 | ]; 2310 | 2311 | // good 2312 | const story = [ 2313 | once, 2314 | upon, 2315 | aTime, 2316 | ]; 2317 | 2318 | // bad 2319 | const hero = { 2320 | firstName: 'Ada' 2321 | , lastName: 'Lovelace' 2322 | , birthYear: 1815 2323 | , superPower: 'computers' 2324 | }; 2325 | 2326 | // good 2327 | const hero = { 2328 | firstName: 'Ada', 2329 | lastName: 'Lovelace', 2330 | birthYear: 1815, 2331 | superPower: 'computers', 2332 | }; 2333 | ``` 2334 | 2335 | - [20.2](#20.2) 增加结尾的逗号: **需要**。eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) 2336 | 2337 | > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 2338 | 2339 | ```javascript 2340 | // bad - git diff without trailing comma 2341 | const hero = { 2342 | firstName: 'Florence', 2343 | - lastName: 'Nightingale' 2344 | + lastName: 'Nightingale', 2345 | + inventorOf: ['coxcomb graph', 'modern nursing'] 2346 | } 2347 | 2348 | // good - git diff with trailing comma 2349 | const hero = { 2350 | firstName: 'Florence', 2351 | lastName: 'Nightingale', 2352 | + inventorOf: ['coxcomb chart', 'modern nursing'], 2353 | } 2354 | 2355 | // bad 2356 | const hero = { 2357 | firstName: 'Dana', 2358 | lastName: 'Scully' 2359 | }; 2360 | 2361 | const heroes = [ 2362 | 'Batman', 2363 | 'Superman' 2364 | ]; 2365 | 2366 | // good 2367 | const hero = { 2368 | firstName: 'Dana', 2369 | lastName: 'Scully', 2370 | }; 2371 | 2372 | const heroes = [ 2373 | 'Batman', 2374 | 'Superman', 2375 | ]; 2376 | ``` 2377 | 2378 | **[⬆ 返回目录](#table-of-contents)** 2379 | 2380 | 2381 | ## 分号 2382 | 2383 | - [21.1](#21.1) **使用分号** eslint: [`semi`](http://eslint.org/docs/rules/semi.html) jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) 2384 | 2385 | 2386 | ```javascript 2387 | // bad 2388 | (function() { 2389 | const name = 'Skywalker' 2390 | return name 2391 | })() 2392 | 2393 | // good 2394 | (() => { 2395 | const name = 'Skywalker'; 2396 | return name; 2397 | })(); 2398 | 2399 | // good (防止函数在两个 IIFE 合并时被当成一个参数) 2400 | ;(() => { 2401 | const name = 'Skywalker'; 2402 | return name; 2403 | })(); 2404 | ``` 2405 | 2406 | [Read more](http://stackoverflow.com/a/7365214/1712802). 2407 | 2408 | **[⬆ 返回目录](#table-of-contents)** 2409 | 2410 | 2411 | ## 强制类型转换 2412 | 2413 | - [22.1](#22.1) 在语句开始时执行类型转换。 2414 | - [22.2](#22.2) 字符串: 2415 | 2416 | ```javascript 2417 | // => this.reviewScore = 9; 2418 | 2419 | // bad 2420 | const totalScore = this.reviewScore + ''; 2421 | 2422 | // good 2423 | const totalScore = String(this.reviewScore); 2424 | ``` 2425 | 2426 | - [22.3](#22.3) 对数字使用 `parseInt` 转换,并带上类型转换的基数。eslint: [`radix`](http://eslint.org/docs/rules/radix) 2427 | 2428 | 2429 | ```javascript 2430 | const inputValue = '4'; 2431 | 2432 | // bad 2433 | const val = new Number(inputValue); 2434 | 2435 | // bad 2436 | const val = +inputValue; 2437 | 2438 | // bad 2439 | const val = inputValue >> 0; 2440 | 2441 | // bad 2442 | const val = parseInt(inputValue); 2443 | 2444 | // good 2445 | const val = Number(inputValue); 2446 | 2447 | // good 2448 | const val = parseInt(inputValue, 10); 2449 | ``` 2450 | 2451 | - [22.4](#22.4) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 2452 | 2453 | ```javascript 2454 | // good 2455 | /** 2456 | * 使用 parseInt 导致我的程序变慢, 2457 | * 改成使用位操作转换数字快多了。 2458 | */ 2459 | const val = inputValue >> 0; 2460 | ``` 2461 | 2462 | - [22.5](#22.5) **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([参考](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[关于这个问题的讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: 2463 | 2464 | ```javascript 2465 | 2147483647 >> 0 //=> 2147483647 2466 | 2147483648 >> 0 //=> -2147483648 2467 | 2147483649 >> 0 //=> -2147483647 2468 | ``` 2469 | 2470 | - [22.6](#22.6) 布尔: 2471 | 2472 | ```javascript 2473 | const age = 0; 2474 | 2475 | // bad 2476 | const hasAge = new Boolean(age); 2477 | 2478 | // good 2479 | const hasAge = Boolean(age); 2480 | 2481 | // good 2482 | const hasAge = !!age; 2483 | ``` 2484 | 2485 | **[⬆ 返回目录](#table-of-contents)** 2486 | 2487 | 2488 | ## 命名规则 2489 | 2490 | - [23.1](#23.1) 避免单字母命名。命名应语义化。eslint: [`id-length`](http://eslint.org/docs/rules/id-length) 2491 | 2492 | 2493 | ```javascript 2494 | // bad 2495 | function q() { 2496 | // ...stuff... 2497 | } 2498 | 2499 | // good 2500 | function query() { 2501 | // ..stuff.. 2502 | } 2503 | ``` 2504 | 2505 | - [23.2](#23.2) 使用驼峰式命名对象、函数和实例。eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) 2506 | 2507 | ```javascript 2508 | // bad 2509 | const OBJEcttsssss = {}; 2510 | const this_is_my_object = {}; 2511 | function c() {} 2512 | 2513 | // good 2514 | const thisIsMyObject = {}; 2515 | function thisIsMyFunction() {} 2516 | ``` 2517 | 2518 | - [23.3](#23.3) 使用帕斯卡式命名构造函数或类。eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) 2519 | 2520 | 2521 | ```javascript 2522 | // bad 2523 | function user(options) { 2524 | this.name = options.name; 2525 | } 2526 | 2527 | const bad = new user({ 2528 | name: 'nope', 2529 | }); 2530 | 2531 | // good 2532 | class User { 2533 | constructor(options) { 2534 | this.name = options.name; 2535 | } 2536 | } 2537 | 2538 | const good = new User({ 2539 | name: 'yup', 2540 | }); 2541 | ``` 2542 | 2543 | - [23.4](#23.4) 不要使用下划线 `_` 结尾或开头来命名属性和方法。eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) 2544 | 2545 | > 为什么? Javascript对属性或方法而言并没有「私有」的定义。虽然用大多人用下划线开头表示“私有”, 但是实际上这些方法是完全公有的,是公共API的一部分。这种方式会让开发者误认为修改不会影响到它,或者不需要测试。如果你需要一些“私有”定义,那么它们不应该这样显眼。 2546 | 2547 | 2548 | ```javascript 2549 | // bad 2550 | this.__firstName__ = 'Panda'; 2551 | this.firstName_ = 'Panda'; 2552 | this._firstName = 'Panda'; 2553 | 2554 | // good 2555 | this.firstName = 'Panda'; 2556 | ``` 2557 | 2558 | - [23.5](#23.5) 别保存 `this` 的引用。使用箭头函数或 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) 2559 | 2560 | 2561 | ```javascript 2562 | // bad 2563 | function foo() { 2564 | const self = this; 2565 | return function() { 2566 | console.log(self); 2567 | }; 2568 | } 2569 | 2570 | // bad 2571 | function foo() { 2572 | const that = this; 2573 | return function() { 2574 | console.log(that); 2575 | }; 2576 | } 2577 | 2578 | // good 2579 | function foo() { 2580 | return () => { 2581 | console.log(this); 2582 | }; 2583 | } 2584 | ``` 2585 | 2586 | - [23.6](#23.6) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 2587 | 2588 | ```javascript 2589 | // file 1 contents 2590 | class CheckBox { 2591 | // ... 2592 | } 2593 | export default CheckBox; 2594 | 2595 | // file 2 contents 2596 | export default function fortyTwo() { return 42; } 2597 | 2598 | // file 3 contents 2599 | export default function insideDirectory() {} 2600 | 2601 | // in some other file 2602 | // bad 2603 | import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename 2604 | import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export 2605 | import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export 2606 | 2607 | // bad 2608 | import CheckBox from './check_box'; // PascalCase import/export, snake_case filename 2609 | import forty_two from './forty_two'; // snake_case import/filename, camelCase export 2610 | import inside_directory from './inside_directory'; // snake_case import, camelCase export 2611 | import index from './inside_directory/index'; // requiring the index file explicitly 2612 | import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly 2613 | 2614 | // good 2615 | import CheckBox from './CheckBox'; // PascalCase export/import/filename 2616 | import fortyTwo from './fortyTwo'; // camelCase export/import/filename 2617 | import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" 2618 | // ^ supports both insideDirectory.js and insideDirectory/index.js 2619 | ``` 2620 | 2621 | - [23.7](#23.7) 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。 2622 | 2623 | ```javascript 2624 | function makeStyleGuide() { 2625 | } 2626 | 2627 | export default makeStyleGuide; 2628 | ``` 2629 | 2630 | - [23.8](#23.8) 当你导出单例、函数库、空对象时使用帕斯卡式命名。 2631 | 2632 | ```javascript 2633 | const AirbnbStyleGuide = { 2634 | es6: { 2635 | } 2636 | }; 2637 | 2638 | export default AirbnbStyleGuide; 2639 | ``` 2640 | 2641 | 2642 | 2643 | - [23.9](#naming--Acronyms-and-Initialisms) 缩略词应该全部大写或全部小写. 2644 | 2645 | > 为什么?命名是为了可读性。 2646 | 2647 | ```javascript 2648 | // bad 2649 | import SmsContainer from './containers/SmsContainer'; 2650 | 2651 | // bad 2652 | const HttpRequests = [ 2653 | // ... 2654 | ]; 2655 | 2656 | // good 2657 | import SMSContainer from './containers/SMSContainer'; 2658 | 2659 | // good 2660 | const HTTPRequests = [ 2661 | // ... 2662 | ]; 2663 | 2664 | // best 2665 | import TextMessageContainer from './containers/TextMessageContainer'; 2666 | 2667 | // best 2668 | const Requests = [ 2669 | // ... 2670 | ]; 2671 | ``` 2672 | 2673 | 2674 | **[⬆ 返回目录](#table-of-contents)** 2675 | 2676 | 2677 | ## 存取器 2678 | 2679 | - [24.1](#24.1) 属性的存取函数不是必须的。 2680 | - [24.2](#24.2) 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 2681 | 2682 | ```javascript 2683 | // bad 2684 | dragon.age(); 2685 | 2686 | // good 2687 | dragon.getAge(); 2688 | 2689 | // bad 2690 | dragon.age(25); 2691 | 2692 | // good 2693 | dragon.setAge(25); 2694 | ``` 2695 | 2696 | - [24.3](#24.3) 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 2697 | 2698 | ```javascript 2699 | // bad 2700 | if (!dragon.age()) { 2701 | return false; 2702 | } 2703 | 2704 | // good 2705 | if (!dragon.hasAge()) { 2706 | return false; 2707 | } 2708 | ``` 2709 | 2710 | - [24.4](#24.4) 创建 `get()` 和 `set()` 函数是可以的,但要保持一致。 2711 | 2712 | ```javascript 2713 | class Jedi { 2714 | constructor(options = {}) { 2715 | const lightsaber = options.lightsaber || 'blue'; 2716 | this.set('lightsaber', lightsaber); 2717 | } 2718 | 2719 | set(key, val) { 2720 | this[key] = val; 2721 | } 2722 | 2723 | get(key) { 2724 | return this[key]; 2725 | } 2726 | } 2727 | ``` 2728 | 2729 | **[⬆ 返回目录](#table-of-contents)** 2730 | 2731 | 2732 | ## 事件 2733 | 2734 | - [25.1](#25.1) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: 2735 | 2736 | ```javascript 2737 | // bad 2738 | $(this).trigger('listingUpdated', listing.id); 2739 | 2740 | ... 2741 | 2742 | $(this).on('listingUpdated', function(e, listingId) { 2743 | // do something with listingId 2744 | }); 2745 | ``` 2746 | 2747 | 更好的写法: 2748 | 2749 | ```javascript 2750 | // good 2751 | $(this).trigger('listingUpdated', { listingId : listing.id }); 2752 | 2753 | ... 2754 | 2755 | $(this).on('listingUpdated', function(e, data) { 2756 | // do something with data.listingId 2757 | }); 2758 | ``` 2759 | 2760 | **[⬆ 返回目录](#table-of-contents)** 2761 | 2762 | 2763 | ## jQuery 2764 | 2765 | - [26.1](#26.1) 使用 `$` 作为存储 jQuery 对象的变量名前缀。jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) 2766 | 2767 | ```javascript 2768 | // bad 2769 | const sidebar = $('.sidebar'); 2770 | 2771 | // good 2772 | const $sidebar = $('.sidebar'); 2773 | ``` 2774 | 2775 | - [26.2](#26.2) 缓存 jQuery 查询。 2776 | 2777 | ```javascript 2778 | // bad 2779 | function setSidebar() { 2780 | $('.sidebar').hide(); 2781 | 2782 | // ...stuff... 2783 | 2784 | $('.sidebar').css({ 2785 | 'background-color': 'pink' 2786 | }); 2787 | } 2788 | 2789 | // good 2790 | function setSidebar() { 2791 | const $sidebar = $('.sidebar'); 2792 | $sidebar.hide(); 2793 | 2794 | // ...stuff... 2795 | 2796 | $sidebar.css({ 2797 | 'background-color': 'pink' 2798 | }); 2799 | } 2800 | ``` 2801 | 2802 | - [26.3](#26.3) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 2803 | - [26.4](#26.4) 对有作用域的 jQuery 对象查询使用 `find`。 2804 | 2805 | ```javascript 2806 | // bad 2807 | $('ul', '.sidebar').hide(); 2808 | 2809 | // bad 2810 | $('.sidebar').find('ul').hide(); 2811 | 2812 | // good 2813 | $('.sidebar ul').hide(); 2814 | 2815 | // good 2816 | $('.sidebar > ul').hide(); 2817 | 2818 | // good 2819 | $sidebar.find('ul').hide(); 2820 | ``` 2821 | 2822 | **[⬆ 返回目录](#table-of-contents)** 2823 | 2824 | 2825 | ## ECMAScript 5 兼容性 2826 | 2827 | - [27.1](#27.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). 2828 | 2829 | **[⬆ 返回目录](#table-of-contents)** 2830 | 2831 | 2832 | ## ECMAScript 6 规范 2833 | 2834 | - [28.1](#28.1) 以下是链接到 ES6 的各个特性的列表。 2835 | 2836 | 1. [Arrow Functions](#arrow-functions) 2837 | 1. [Classes](#constructors) 2838 | 1. [Object Shorthand](#es6-object-shorthand) 2839 | 1. [Object Concise](#es6-object-concise) 2840 | 1. [Object Computed Properties](#es6-computed-properties) 2841 | 1. [Template Strings](#es6-template-literals) 2842 | 1. [Destructuring](#destructuring) 2843 | 1. [Default Parameters](#es6-default-parameters) 2844 | 1. [Rest](#es6-rest) 2845 | 1. [Array Spreads](#es6-array-spreads) 2846 | 1. [Let and Const](#references) 2847 | 1. [Iterators and Generators](#iterators-and-generators) 2848 | 1. [Modules](#modules) 2849 | 2850 | 2851 | 2852 | - [28.2](#tc39-proposals) 不要使用未到stage 3的 [TC39提案](https://github.com/tc39/proposals) 。 2853 | 2854 | > 为什么? [它们还不是终稿](https://tc39.github.io/process-document/), 有可能被改动或废弃。我们使用的是Javascript, 但是提案暂时还不是Javascript。 2855 | 2856 | 2857 | **[⬆ 返回目录](#table-of-contents)** 2858 | 2859 | 2860 | ## 测试 2861 | 2862 | 2863 | - [29.1](#testing--yup) **Yup.** 2864 | 2865 | ```javascript 2866 | function foo() { 2867 | return true; 2868 | } 2869 | ``` 2870 | 2871 | 2872 | - [29.2](#testing--for-real) **不是强制的,但是建议**: 2873 | - 无论使用哪个测试框架,需要编写测试用例。 2874 | - 尽力去编写小的纯函数,控制可变性。 2875 | - 留意stubs和mocks - 它们会让测试变得脆弱. 2876 | - 在Airbnb我们主要使用 [`mocha`](https://www.npmjs.com/package/mocha) / [`tape`](https://www.npmjs.com/package/tape) 也被用于小的,独立的模块测试。 2877 | - 努力达到100%的测试覆盖率是一个好目标,虽然达到这个目标在某些情况下不太实际。 2878 | - 当你解决完一个bug, 写个回归测试。如果没有回归,这个bug在后面可能引起新的问题。 2879 | 2880 | **[⬆ 返回目录](#table-of-contents)** 2881 | 2882 | 2883 | ## 性能 2884 | 2885 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 2886 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 2887 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 2888 | - [Bang Function](http://jsperf.com/bang-function) 2889 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 2890 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 2891 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 2892 | - Loading... 2893 | 2894 | **[⬆ 返回目录](#table-of-contents)** 2895 | 2896 | 2897 | ## 资源 2898 | 2899 | **Learning ES6** 2900 | 2901 | - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) 2902 | - [ExploringJS](http://exploringjs.com/) 2903 | - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) 2904 | - [Comprehensive Overview of ES6 Features](http://es6-features.org/) 2905 | 2906 | **Read This** 2907 | 2908 | - [Annotated ECMAScript 5.1](http://es5.github.com/) 2909 | 2910 | **Tools** 2911 | 2912 | - Code Style Linters 2913 | + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) 2914 | + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) 2915 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) 2916 | 2917 | **Other Styleguides** 2918 | 2919 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 2920 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 2921 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) 2922 | 2923 | **Other Styles** 2924 | 2925 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen 2926 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen 2927 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun 2928 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman 2929 | 2930 | **Further Reading** 2931 | 2932 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll 2933 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer 2934 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz 2935 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban 2936 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock 2937 | 2938 | **Books** 2939 | 2940 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford 2941 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov 2942 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz 2943 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders 2944 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas 2945 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw 2946 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig 2947 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch 2948 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault 2949 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg 2950 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy 2951 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon 2952 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov 2953 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman 2954 | - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke 2955 | 2956 | **Blogs** 2957 | 2958 | - [DailyJS](http://dailyjs.com/) 2959 | - [JavaScript Weekly](http://javascriptweekly.com/) 2960 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) 2961 | - [Bocoup Weblog](http://weblog.bocoup.com/) 2962 | - [Adequately Good](http://www.adequatelygood.com/) 2963 | - [NCZOnline](http://www.nczonline.net/) 2964 | - [Perfection Kills](http://perfectionkills.com/) 2965 | - [Ben Alman](http://benalman.com/) 2966 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) 2967 | - [Dustin Diaz](http://dustindiaz.com/) 2968 | - [nettuts](http://net.tutsplus.com/?s=javascript) 2969 | 2970 | **Podcasts** 2971 | 2972 | - [JavaScript Jabber](http://devchat.tv/js-jabber/) 2973 | 2974 | 2975 | **[⬆ 返回目录](#table-of-contents)** 2976 | 2977 | 2978 | ## The JavaScript Style Guide Guide 2979 | 2980 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 2981 | 2982 | ## Chat With Us About JavaScript 2983 | 2984 | - Find us on [gitter](https://gitter.im/airbnb/javascript). 2985 | 2986 | ## Contributors 2987 | 2988 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) 2989 | 2990 | ## License 2991 | 2992 | (The MIT License) 2993 | 2994 | Copyright (c) 2014-2017 Airbnb 2995 | 2996 | Permission is hereby granted, free of charge, to any person obtaining 2997 | a copy of this software and associated documentation files (the 2998 | 'Software'), to deal in the Software without restriction, including 2999 | without limitation the rights to use, copy, modify, merge, publish, 3000 | distribute, sublicense, and/or sell copies of the Software, and to 3001 | permit persons to whom the Software is furnished to do so, subject to 3002 | the following conditions: 3003 | 3004 | The above copyright notice and this permission notice shall be 3005 | included in all copies or substantial portions of the Software. 3006 | 3007 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 3008 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 3009 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 3010 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 3011 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 3012 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 3013 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------