├── 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.
--------------------------------------------------------------------------------