From 331353e59b140d702ad68fc59771c0bf075ad1e4 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Tue, 1 Nov 2016 11:02:35 +0800 Subject: [PATCH 01/33] update --- linters/.eslintrc_backup | 231 +++++++++++++++++++++++++++++++++++++++ linters/README.md | 2 + 2 files changed, 233 insertions(+) create mode 100644 linters/.eslintrc_backup create mode 100644 linters/README.md diff --git a/linters/.eslintrc_backup b/linters/.eslintrc_backup new file mode 100644 index 0000000000..f9a0e18396 --- /dev/null +++ b/linters/.eslintrc_backup @@ -0,0 +1,231 @@ + +// copy https://github.com/strikingly/javascript/blob/master/linters/.eslintrc + +{ + "parser": "babel-eslint", // https://github.com/babel/babel-eslint + "plugins": [ + "react", // https://github.com/yannickcr/eslint-plugin-react + "filenames" + ], + "env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments + "browser": true, // browser global variables + "node": true // Node.js global variables and Node.js-specific rules + }, + "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "forOf": true, + "generators": false, + "modules": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": false, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "spread": true, + "superInFunctions": true, + "templateStrings": true, + "jsx": true + }, + "rules": { + /** + * Strict mode + */ + // babel inserts "use strict"; for us + "strict": [2, "never"], // http://eslint.org/docs/rules/strict + + /** + * ES6 + */ + "no-var": 2, // http://eslint.org/docs/rules/no-var + "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const + + /** + * Variables + */ + "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow + "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names + "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars + "vars": "local", + "args": "after-used" + }], + "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define + + /** + * Possible errors + */ + "comma-dangle": [2, "always-multiline"], // http://eslint.org/docs/rules/comma-dangle + "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign + "no-console": 1, // http://eslint.org/docs/rules/no-console + "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger + "no-alert": 1, // http://eslint.org/docs/rules/no-alert + "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition + "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys + "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case + "no-empty": 2, // http://eslint.org/docs/rules/no-empty + "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign + "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast + "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi + "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign + "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations + "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp + "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace + "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls + "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays + "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable + "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan + "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var + + /** + * Best practices + */ + "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return + "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly + "default-case": 2, // http://eslint.org/docs/rules/default-case + "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation + "allowKeywords": true + }], + "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq + "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in + "no-caller": 2, // http://eslint.org/docs/rules/no-caller + "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return + "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null + "no-eval": 2, // http://eslint.org/docs/rules/no-eval + "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native + "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind + "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough + "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal + "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval + "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks + "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func + "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str + "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign + "no-new": 2, // http://eslint.org/docs/rules/no-new + "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func + "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers + "no-octal": 2, // http://eslint.org/docs/rules/no-octal + "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape + "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign + "no-proto": 2, // http://eslint.org/docs/rules/no-proto + "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare + "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign + "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url + "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare + "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences + "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal + "no-with": 2, // http://eslint.org/docs/rules/no-with + "radix": 2, // http://eslint.org/docs/rules/radix + "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top + "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife + "yoda": 2, // http://eslint.org/docs/rules/yoda + + /** + * Style + */ + "indent": [2, 2], // http://eslint.org/docs/rules/indent + "brace-style": [2, // http://eslint.org/docs/rules/brace-style + "1tbs", { + "allowSingleLine": true + }], + "camelcase": [2, { // http://eslint.org/docs/rules/camelcase + "properties": "never" + }], + "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing + "before": false, + "after": true + }], + "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style + "eol-last": 2, // http://eslint.org/docs/rules/eol-last + "func-names": 1, // http://eslint.org/docs/rules/func-names + "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing + "beforeColon": false, + "afterColon": true + }], + "new-cap": [0, { // http://eslint.org/docs/rules/new-cap + "newIsCap": true + }], + "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines + "max": 2 + }], + "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary + "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object + "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func + "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces + "no-extra-parens": 0, // http://eslint.org/docs/rules/no-extra-parens + "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle + "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var + "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks + "semi": [2, "never"], // http://eslint.org/docs/rules/semi + "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing + "before": false, + "after": true + }], + "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords + "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks + "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren + "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops + "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case + "spaced-comment": 2, // http://eslint.org/docs/rules/spaced-comment + "no-unused-expressions": 1, + "filenames/filenames": [2, "^[a-zA-Z]+$"], + + /** + * JSX style + */ + "jsx-quotes": 2, + "react/display-name": 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md + "react/jsx-boolean-value": [2, "always"], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md + "react/jsx-no-undef": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md + "react/jsx-sort-props": 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md + "react/jsx-sort-prop-types": 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md + "react/jsx-uses-react": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md + "react/jsx-uses-vars": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md + "react/no-did-mount-set-state": [0, "allow-in-func"], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md + "react/no-did-update-set-state": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md + "react/no-multi-comp": 1, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md + "react/no-unknown-property": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md + "react/prop-types": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md + "react/react-in-jsx-scope": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md + "react/self-closing-comp": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md + "react/wrap-multilines": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md + "react/sort-comp": [2, { // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md + "order": [ + "lifecycle", + "/^on.+$/", + "/^get.+$/", + "everything-else", + "rendering" + ], + "groups": { + "lifecycle": [ + "displayName", + "propTypes", + "contextTypes", + "childContextTypes", + "mixins", + "statics", + "defaultProps", + "constructor", + "getDefaultProps", + "getInitialState", + "getDefaultMetaState", + "getDefaultState", + "getChildContext", + "componentWillMount", + "componentDidMount", + "componentWillReceiveProps", + "shouldComponentUpdate", + "componentWillUpdate", + "componentDidUpdate", + "componentWillUnmount" + ], + "rendering": [ + "/^render.+$/", + "render" + ] + } + }] + } +} diff --git a/linters/README.md b/linters/README.md new file mode 100644 index 0000000000..2af6b5eb40 --- /dev/null +++ b/linters/README.md @@ -0,0 +1,2 @@ + +# 推荐使用 .eslintrc.js From b014be247d96bb05bd262d5ac92e2f4f917dd31f Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Tue, 1 Nov 2016 18:53:52 +0800 Subject: [PATCH 02/33] =?UTF-8?q?eslint=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linters/.eslintrc.js | 853 ++++++++++++++++++++++++++++++++++++ linters/.eslintrc.simple.js | 63 +++ 2 files changed, 916 insertions(+) create mode 100644 linters/.eslintrc.js create mode 100644 linters/.eslintrc.simple.js diff --git a/linters/.eslintrc.js b/linters/.eslintrc.js new file mode 100644 index 0000000000..2acab9bb86 --- /dev/null +++ b/linters/.eslintrc.js @@ -0,0 +1,853 @@ + + +// 此文件可以设置为全局 Eslint 配置 + + +/* global aa */ +// http://eslint.org/docs/rules/no-undef + +/** + * 参看文档 + * https://github.com/feross/eslint-config-standard/blob/master/eslintrc.json + * + * https://segmentfault.com/a/1190000004468428 + * http://www.tuicool.com/articles/rIFBfey + * http://blog.csdn.net/helpzp2008/article/details/51507428 + * http://www.cnblogs.com/allin123/p/5754500.html + */ + + +//文件中关闭验证 + +/*eslint-disable */ + +//suppress all warnings between comments +//alert('foo'); + +/*eslint-enable */ + + + +//文件中指定规则不验证 + +/*eslint-disable no-alert, no-console */ + +// alert('foo'); +// console.log('bar'); + +/*eslint-enable no-alert */ + + +module.exports = { + // 环境定义了预定义的全局变量, 更多在官网查看 + "env": { + "browser": true, + "node": true, + "commonjs": true, + "amd": true, + "es6": true, + "mocha": true, + }, + + // "plugins": [ + // "standard", + // "promise" + // ], + + "globals": { + "_": false, + "$": false, + "define": false, + "document": false, + "navigator": false, + "window": false, + }, + + //表示使用默认的规则进行校验 + "extends": "eslint:recommended", + // "extends": 'standard', + + // JavaScript 语言选项 + "parserOptions": { + // ECMAScript 版本 + // "ecmaVersion": 8, + // 想使用的额外的语言特性: + "ecmaFeatures": { + // 允许在全局作用域下使用 return 语句 + // "globalReturn": true, + // impliedStric + // "impliedStrict": true, + // 启用 JSX + // "jsx": true + // lambda表达式 + "arrowFunctions": true, + // 块级作用域,允许使用let const + "blockBindings": true, + // class + "classes": true, + // http://es6.ruanyifeng.com/#docs/function#函数参数的默认值 + "defaultParams": true, + // 解构赋值 + "destructuring": true, + // http://es6.ruanyifeng.com/#docs/object#对象的扩展运算符 + "experimentalObjectRestSpread": true, + // http://es6.ruanyifeng.com/#docs/iterator#for---of循环 + "forOf": true, + // http://es6.ruanyifeng.com/#docs/generator + "generators": true, + // 允许使用模块,模块内默认严格模式 + "modules": true, + // 允许字面量定义对象时,用表达式做属性名 + // http://es6.ruanyifeng.com/#docs/object#属性名表达式 + "objectLiteralComputedProperties": true, + // 允许对象字面量方法名简写 + /*var o = { + method() { + return "Hello!"; + } + }; + + 等同于 + + var o = { + method: function() { + return "Hello!"; + } + }; + */ + "objectLiteralShorthandMethods": true, + /* + 对象字面量属性名简写 + var foo = 'bar'; + var baz = {foo}; + baz // {foo: "bar"} + + // 等同于 + var baz = {foo: foo}; + */ + "objectLiteralShorthandProperties": true, + // http://es6.ruanyifeng.com/#docs/function#rest参数 + "restParams": true, + // http://es6.ruanyifeng.com/#docs/function#扩展运算符 + "spread": true, + "superInFunctions": true, + // http://es6.ruanyifeng.com/#docs/string#模板字符串 + "templateStrings": true, + "unicodeCodePointEscapes": true, + }, + "sourceType": "module", //script + }, + + + /** 规则的严重性(rule severity) + * 0 或 0 - 关闭规则 + * turn the rule off 不验证 "warn" + * "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出), + * turn the rule on as a warning(doesn’ t affect exit code) 警告 2 + * 2 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出) + * turn the rule on as an error(exit code is 1 when triggered) 错误( 如有)规则的选项(additional options) + */ + + /** rules 核心配置 + * "camelcase": 2, + * "curly": 2, + * "brace-style": [2, "1tbs"], + * "quotes": [2, "single"], + * "semi": [2, "always"], + * "space-in-brackets": [2, "never"], + * "space-infix-ops": 2, + */ + + "rules": { + + ///////////////// + /// 可能的错误 /// + //////////////// + + // 定义对象的set存取器属性时,强制定义get(在声明对象时getter和setter需成对出现) + "accessor-pairs": 2, + // 指定数组的元素之间要以空格隔开(, 后面), + // 用数组字面量定义数组时数组元素前后是否加空格 + // never 参数:默认,数组元素前后不能带空格,但可以有换行符 + // always参数:数组元素前后必须留空格 + "array-bracket-spacing": [1, "never"], + // 强制数组方法的回调函数中有 return 语句 + "array-callback-return": 0, + // 要求箭头函数体使用大括号 + "arrow-body-style": 2, + // 要求箭头函数的参数使用圆括号 + "arrow-parens": 2, + "arrow-spacing": [1, { "before": true, "after": true }], + // 在块级作用域外访问块内定义的变量是否报错提示 + // 强制把变量的使用限制在其定义的作用域范围内 + // 当在代码块中用var声明变量,并在代码块外使用时报错 + "block-scoped-var": 0, + // 禁止或强制在单行代码块中使用空格(禁用) + // 在单行代码块中,代码块前后是否需要留空格 + // always参数:默认,前后必须留空格 + // never 参数: 前后不能带空格 + "block-spacing":[2, "never"], + // 强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab, + // if while function 后面的 { 必须与if在同一行,java风格。 + // 大括号的样式,比如下面的大括号语法采用『1tbs』,允许单行样式 + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + // require return statements after callbacks + // 强制回调后return,避免多次调用回调 + "callback-return": 1, + // 强制使用双峰驼命名格式 + "camelcase": 0, + // "camelcase": [2, { "properties": "never" }], + "class-methods-use-this": 2, + // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号, + // always-multiline:多行模式必须带逗号,单行模式不能带逗号(I don't care about IE8) + // "comma-dangle": 0, + // "comma-dangle": [1, "never"], // always-multiline + // "comma-dangle": [1, "always-multiline"], // never + // 控制逗号前后的空格 + // 规定了逗号前后的空白,默认配置规定逗号前面没有空白,而逗号后面需要留空白 + "comma-spacing": [1, { "before": false, "after": true }], + // 控制逗号在行尾出现还是在行首出现 (默认行尾) + // http://eslint.org/docs/rules/comma-style + // 规定了逗号放的位置,默认配置逗号应该放在行末,如果设置为first,逗号就应放在行首 + "comma-style": [2, "last"], + // 限制圈复杂度,也就是类似if else能连续接多少个 + "complexity": [2, 5], + // "SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平 + // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always + // 是否在对象的动态属性(computed properties: ES6引入)中添加空白,默认配置不添加空白 + "computed-property-spacing": [2, "never"], + // 要求 return 语句要么总是指定返回的值,要么不指定 + // 不同分支的return语句不能返回不同的类型,要么一致要么都没有 + "consistent-return": 0, + // 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了 + // e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值 + // 统一this的别名(this赋值的变量名)保证整个应用程序代码的统一。 + // 如果一个变量被指定为this对象的别名,那么这个变量就不能够用来赋其他值,只能够用来保存this对象。 + // 如果this对象明确被赋值给了一个变量,那么这个变量应该是配置中指定的那个变量名。 + "consistent-this": [1, "self"], + // "consistent-this": [1, "that"], + // 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示 + "constructor-super": 2, + // if else while for do后面的代码块是否需要{ }包围,参数: + // multi 只有块中有多行语句时才需要{ }包围 + // multi-line 只有块中有多行语句时才需要{ }包围, 但是块中的执行语句只有一行时, + // 块中的语句只能跟和if语句在同一行。if (foo) foo++; else doSomething(); + // multi-or-nest 只有块中有多行语句时才需要{ }包围, 如果块中的执行语句只有一行,执行语句可以零另起一行也可以跟在if语句后面 + // [2, "multi", "consistent"] 保持前后语句的{ }一致 + // default: [2, "all"] 全都需要{ }包围 + // 强制所有控制语句使用一致的括号风格 + "curly": [2, "multi-line"], + // switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告 + // 所有的switch语句都必须要有一个default分支 + "default-case": 2, + // 在书写对象的属性或方法时,新的一行代码可以以. 开头,也可以以. 结束。 + // 强制object.key 中 . 的位置,参数: + // property, '.'号应与属性在同一行 + // object, '.'号应与对象名在同一行 + "dot-location": [2, "property"], + // 强制使用.号取属性 + // 参数: allowKeywords: true (默认)使用保留字做属性名时,只能使用.方式取属性 + // false 使用保留字做属性名时, 只能使用[]方式取属性 + // e.g [2, {"allowKeywords": false}] + // allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 + // e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}] + "dot-notation": 0, + // "dot-notation": [2, {"allowKeywords": true}], + // 文件末尾强制换行 + // 该规则规定文件最后强制换行,仅需留一空行 + "eol-last": 2, + // 在进行比较时,必须使用全等=== 和完全不等!== + // allow-null 允许 null 和 undefined == + "eqeqeq": [2, "allow-null"], + "func-call-spacing": [2, "never"], + "func-name-matching": 2, + // 强制使用命名的 function 表达式 + // 要求给函数表达式命名,便于 debug + "func-names": 0, + //函数声明就是把function关键词写在最前面,后面跟一个函数名。我们可以在函数申明代码前调用函数 + //函数表达式是通过var等声明变量的关键字开头,然后跟函数名,再后面是function本身。在使用函数表达式定义函数前调用函数会报错 + // 统一定义函数是所采用的方式,参数: + // 强制一致地使用函数声明或函数表达式,方法定义风格,参数: + // declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"] + // expression: 强制使用方法表达式的方式,默认方式,var f = function() {} e.g [2, "expression"] + // allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", {"allowArrowFunctions":true}] + "func-style": 0, + // "func-style": [2, "expression"], + // 强制 generator 函数中 * 号周围使用一致的空格 + "generator-star-spacing": [2, { "before": true, "after": true }], + // 要求 require() 出现在顶层模块作用域中 + // 强制 require() 出现在模块作用域的顶部 + "global-require": 0, + // "global-require": 1, + // 要求 for-in 循环中必须有一个 if 语句 + // for (key in foo) { + // doSomething(key); + // } error + // 确保 foo.hasOwnProperty(key) => + // for (key in foo) { + // if ({}.hasOwnProperty.call(foo, key)) { + // doSomething(key); + // } + // } + "guard-for-in": 1, + // 要求回调函数中有容错处理 + // 如果函数有err入参(err或者error),在函数体内必须进行处理 + "handle-callback-err": [2, "^(err|error)$" ], + // 禁止使用指定的标识符 + // 规定了标识符命名的黑名单 + "id-blacklist": 1, + // "id-blacklist": [2, "data", "err", "e", "cb", "callback"], + // 规定标识符的最小及最大长度,默认配置标识符最少两个字符 + // 这里设定为 1,如 $ _ 等 + "id-length": [2, { "min": 1 }], + // 要求标识符匹配一个指定的正则表达式 + // 命名检测,标识符命名需和配置中的正则表达式匹配,但是该规则对函数调用无效 + "id-match": 1, + // "id-match": [2, "^[a-z]+([A-Z][a-z]+)*$", { "properties": false }], + // 统一代码缩进方式,默认值是4 spaces + "indent": [2, 2, { "SwitchCase": 1 }], + // "indent": 0, + // 要求或禁止 var 声明中的初始化(初值) + // 变量声明时必须赋初值 + "init-declarations": 0, + // 强制在 JSX 属性中一致地使用双引号或单引号 + // 规定了在JSX中的属性值是使用单引号还是双引号,默认使用双引号 + "jsx-quotes": [2, "prefer-double"], + // 强制在对象字面量的属性中键和值之间使用一致的间距 + // 该规则规定了在对象字面量语法中key和value之间的空白,冒号前不要留空格,冒号后面需留一个空格 + // "key-spacing": 0, + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + // 强制在关键字前后使用一致的空格 (前后都需要) + // 规定了keyword前后是否需要留一个空格 + "keyword-spacing": 0, + // "keyword-spacing": [2, { "before": true, "after": true, "overrides": {} }], + // "keyword-spacing": [2, { "before": true, "after": true }], + // 规定了单行注释的位置,默认: "position": "above"; 可选: beside + "line-comment-position": [1, { "position": "above" }], + // 强制使用一致的换行风格 + // 统一换行符,"\n" unix(for LF) and "\r\n" for windows(CRLF),默认unix + "linebreak-style": [1, "unix"], + // 要求在注释周围有空行 ( 要求在块级注释之前有一空行) + // 规定注释和代码块之间是否留空行 + "lines-around-comment": [1, { "beforeBlockComment": true }], + // "lines-around-comment": 2, + "lines-around-directive": 2, + // 规定代码最多可以嵌套多少层 + "max-depth": [2, 4], + // 规定了代码单行的最大长度 + "max-len": [1, 240], + // "max-len": [2, 80, 4], + // 强制最大行数 + "max-lines": 0, + // 强制回调函数最大嵌套深度 5层 + "max-nested-callbacks": [1, 5], + // 强制 function 定义中最多允许的参数数量 + // 规定了函数参数的最大个数 + "max-params": [1, 7], + // 强制 function 块最多允许的的语句数量 + // 规定了函数中代码不能够超过多少行 + "max-statements": [1, 200], + // 强制每一行中所允许的最大语句数量 + "max-statements-per-line": 0, + "multiline-ternary": [2, "never"], + // 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。) + "new-cap": [2, { "newIsCap": true, "capIsNew": false }], + // 要求调用无参构造函数时有圆括号 + // 使用构造函数(new)时必须圆括号不能省略 + "new-parens": 2, + // 要求或禁止 var 声明语句后有一行空行 + // 规定了变量声明后是否需要空行 + "newline-after-var": 0, + // 要求 return 语句之前有一空行 + // 规定了return语句前是否是否需要空行 + "newline-before-return": 0, + // 要求方法链中每个调用都有一个换行符 + // 规定了方法链式调用时是否需换行 + "newline-per-chained-call": 1, + // 禁用 alert、confirm 和 prompt + "no-alert": 0, + // 禁止使用 Array 构造函数 + "no-array-constructor": 2, + // 禁用按位运算符 + "no-bitwise": 0, + // 禁用 arguments.caller 和 arguments.callee + "no-caller": 2, + // 不允许在 case 子句中使用词法声明 + // 禁止在case/default语句中使用 lexical declarations,例如let, const, function and class + // 因为在case/default中的声明,在整个switch语句中都能够访问到,如果需要声明变量,可以加大括号。 + "no-case-declarations": 2, + // 不允许 catch 子句的参数与外层作用域中的变量同名 + // In IE 8 and earlier,禁止catch子句参数与外部作用域变量同名 + "no-catch-shadow": 0, + // 禁止修改类声明的变量 + "no-class-assign": 2, + // 不允许箭头功能,在那里他们可以混淆的比较 + "no-confusing-arrow": 0, + // 禁止条件表达式中出现赋值操作符 + "no-cond-assign": 2, + // 禁用 console + "no-console": 0, + // 禁止修改 const 声明的变量 + "no-const-assign":2, + // 禁止在条件中使用常量表达式 if(true) if(1) + // if (false) { + // doSomethingUnfinished(); + // } //cuowu + "no-constant-condition": 1, + // "no-constant-condition": [2, { "checkLoops": false }], + // 禁用 continue 语句 + "no-continue": 0, + // 禁止在正则表达式中使用控制字符 :new RegExp("\x1f") + "no-control-regex": 2, + // 禁止使用 debugger 语句 + // allow debugger during development + // "no-debugger": 1, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + // 禁止删除变量 + // 禁止使用 delete 删除 var声明的变量 + "no-delete-var": 2, + // 禁止除法操作符显式的出现在正则表达式开始的位置 + // 不能使用看起来像除法的正则表达式 + // 用来消除/ (除号)操作符对程序员的迷惑,比如在正则表达式/=foo/中,我们并不能够确定第一个/是除号还是正则表达式,因此我们需要在等号前面加一个转移符/\=foo/ + "no-div-regex": 2, + // 禁止 function 定义中出现重名参数(函数参数禁止重名) + "no-dupe-args": 2, + // 禁止类成员中出现重复的名称(在创建对象字面量时不允许键重复 ) + "no-dupe-class-members": 2, + // 禁止对象字面量中出现重复的 key + "no-dupe-keys": 2, + // 禁止重复的 case 标签(在switch语句中禁止重复的case) + "no-duplicate-case": 2, + // 不允许复制模块的进口 + "no-duplicate-imports": 2, + // 禁止 if 语句中有 return 之后有 else + // 在if else语句中,如果else语句中只含有一个return语句,那么完全可以不使用else语句,直接return。 + "no-else-return": 2, + // 禁止空代码块,代码块的内容不能为空 + "no-empty": 1, + // 禁止在正则表达式中使用空字符集 (正则表达式的内容不能为空 /^abc[]/) + "no-empty-character-class": 2, + // 禁止出现空函数 如果一个函数包含了一条注释,它将不会被认为有问题 + "no-empty-function": 2, + // 禁止使用空解构模式no-empty-pattern + // 在结构赋值时,模式不能为空。在ECMAScript2015的结构赋值中,模式为空是不会报错的,只是这样的结构赋值没有任何效果,该条规则就保证了模式不能为空,也就保证了结构赋值的有效性。 + "no-empty-pattern": 2, + // 禁止在没有类型检查操作符的情况下与 null 进行比较 + // 保证了在和null比较时使用===和!==,而不能够使用==和!= + "no-eq-null": 1, + // 禁用 eval() 函数 + "no-eval": 2, + // 禁止对catch语句中的异常(即参数)进行赋值 + "no-ex-assign": 2, + // 禁止扩展原生类型,不能向native的对象上面添加属性 + "no-extend-native": 2, + // 禁止不必要的 .bind() 调用 + // 保证了调用 bind 方法的函数体内有this对象。规避了不必要的使用bind方法的情况。 + // 箭头函数中没有 this 对象,也就不能够使用 bind()方法。该规则保证了在所有的箭头函数中使用 bind 方法将被视为错误。 + "no-extra-bind": 2, + // 禁止不必要的布尔转换 + "no-extra-boolean-cast":2, + // 禁用不必要的标签 + // 如果 loop中没有内嵌的 loops或 switches, loop标签是不必要的 + "no-extra-label": 1, + // 禁止不必要的圆括号,默认 all 时 + // incorrect (a * b) + c; a = (b + c); + // correct (0).toString(); typeof (a); + "no-extra-parens": [1, "functions"], + // "no-extra-parens": [2, "functions"], + // 禁止不必要的分号, + // incorrect => + // function foo() { + // // code + // }; + // correct => + // var foo = function() { + // // code + // }; + "no-extra-semi": 1, + // 禁止 case 语句落空 + // 在case语句中尽量加break,避免不必要的fallthrough错误,消除从一个case到另一个case的非故意的「fall through」。 + // 如果没有添加 break 等终止语句或者没有添加注释语句,将会抛出错误 + "no-fallthrough": 2, + // 禁止数字字面量中使用前导和末尾小数点 + // 在使用浮点小数时,不能够省略小数点前面的数或者后面的数,必须写。比如.2 2. 应该写2.2 2.0 + "no-floating-decimal": 2, + // 禁止对 function 声明重新赋值(禁止重复的函数声明) + "no-func-assign": 2, + // 禁止使用短符号进行类型转换(!!fOO) + // 禁止隐式转换,为了消除简写的类型转换 + "no-implicit-coercion": 0, + // 禁止在全局范围内使用 var 和 function 声明,消除全局变量、全局函数 + "no-implicit-globals": 1, + // 禁止使用类似 eval() 的方法 + // 在 setTimeout(), setInterval() or execScript()中消除隐式eval的使用 + "no-implied-eval": 2, + // 禁止在代码行后使用内联注释(行内注释) + "no-inline-comments": 0, + // 禁止在嵌套的块中出现 function 或 var 声明(禁止在块语句中声明变量或函数) + "no-inner-declarations": [2, "functions"], + // 禁止 RegExp 构造函数中无效的正则表达式字符串(禁止使用无效的正则语句) + "no-invalid-regexp": 2, + // 禁止 this 关键字出现在类和类对象之外 + // 禁止无效的this,只能用在构造器,类,对象字面量 + "no-invalid-this": 0, + // 禁止在字符串和注释之外不规则的空白(禁止使用不合法或者不规则的空白符) + "no-irregular-whitespace": 2, + // 禁用 __iterator__ 属性 + "no-iterator": 2, + // 防止 label 和 声明的变量 重名 + "no-label-var": 2, + // 禁用标签语句 + // 禁止使用 label 语句,以避免无限循环 + "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], + // 禁用不必要的嵌套代码块 + "no-lone-blocks": 2, + // 禁止 if 作为唯一的语句出现在 else 语句中 + // 禁止在if-else控制语句中,else代码块中仅包含一个if语句 + "no-lonely-if": 0, + // 禁止在循环中出现 function 声明和表达式 + // 禁止在循环体中定义函数并且函数引用了外部变量 + // 在循环中定义了函数,但是函数内部没有引用外部变量,或者使用let定义的代码块变量,视为合法 + "no-loop-func": 2, + // 禁用'Magic numbers',建议使用常量来命名(如 3.14 或 max: 10 之类的数字) + // 忽视 数组索引 以及 数字 1 0 -1等 + "no-magic-numbers": 0, + // "no-magic-numbers": [1, { "ignore": [0, -1, 1], "ignoreArrayIndexes": true }], + // 禁止混合使用不同的操作符 + "no-mixed-operators": 0, + // 禁止混合常规 var 声明和 require 调用 + // 声明时不能混用声明类型 + "no-mixed-requires": 0, + // 禁止混用 空格 和 tab 缩进 + "no-mixed-spaces-and-tabs": 2, + // 禁止使用多个空格 + // 保证了在逻辑表达式、条件表达式、申明语句、数组元素、对象属性、sequences、函数参数中不使用超过一个的空白符 + "no-multi-spaces": 1, + // 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串 + // 该规则保证了字符串不分行书写 + "no-multi-str": 2, + // 不允许多个空行 + // 不要留超过规定数目的空白行 + "no-multiple-empty-lines": [1, { "max": 5 }], + // 禁止对原生对象赋值 + // 该规则保证了不重写原生对象 + "no-native-reassign": 2, + // 不允许否定的表达式 + // 在if语句中使用了否定表达式,同时else语句又不为空,那么这样的if-else语句将被视为不合法, + // 为什么不将其反过来这样代码更容易理解(虽更易理解,但可能否定分支进入频率更高,所以提到前面用), + // 该规则同样适用于三元操作符 + "no-negated-condition": 0, + // 禁止在 in 表达式中出现否定的左操作数(在in操作符左边的操作项不能用! ) + // 例如这样写不对的:if ( !a in b) { //dosomething } + "no-negated-in-lhs": 2, + // 禁止使用嵌套的三元表达式 + "no-nested-ternary": 1, + // 禁止在非赋值或条件语句中使用 new 操作符 + // 在使用new来调用构造函数后,必须把生成的实例赋值给一个变量 + "no-new": 2, + // 禁止对 Function 对象使用 new 操作符 + // 禁止使用 new Function(); 语句 + "no-new-func": 0, + // 禁止使用 new Object() 来构造函数 + "no-new-object": 2, + // 禁止调用 require 时使用 new 操作符 + // 禁止把 require方法 和 new操作符 一起使用 + "no-new-require": 2, + // 禁止 Symbol 的构造函数 + "no-new-symbol": 2, + // 禁止对 String,Number 和 Boolean 使用 new 操作符 + // 禁止使用 new 创建 String, Number, and Boolean实例 + "no-new-wrappers": 2, + // 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math() 以及 JSON(); + "no-obj-calls": 2, + // 禁用八进制字面量(八进制数字) + "no-octal": 2, + // 禁止在字符串中使用八进制转义序列,比如 var foo = "Copyright \251"; + "no-octal-escape": 2, + // 不允许对 function 的参数进行重新赋值 + // 禁止对函数的参数重新进行无意义的赋值 + "no-param-reassign": 1, + // 禁止对 __dirname 和 __filename 进行字符串连接(路径拼接) + "no-path-concat": 0, + // 禁止使用一元操作符 ++ 和 -- + "no-plusplus": 0, + // 禁用 process.env + "no-process-env": 0, + // 禁用 process.exit() + "no-process-exit": 0, + // 禁用 __proto__ 属性 + "no-proto": 2, + // 禁止直接使用 Object.prototypes 的内置属性 + "no-prototype-builtins": 1, + // 禁止使用 var 多次声明同一变量,避免重复声明一个变量 + "no-redeclare": [2, { "builtinGlobals": true }], + // 禁止正则表达式字面量中出现多个空格 /foo bar/ + "no-regex-spaces": 2, + // 禁用特定的全局变量 + "no-restricted-globals": [2, "event"], + // 允许指定模块加载时的进口 + "no-restricted-imports": 0, + // 使用了就会报错 + // "no-restricted-modules": [2, "fs"], + "no-restricted-modules": 2, + "no-restricted-properties": 2, + // 禁止使用特定的语法 + // 禁止使用某些特定的JavaScript语法,例如FunctionDeclaration 和 WithStatement + "no-restricted-syntax": 0, + // "no-restricted-syntax": [1, "FunctionExpression", "WithStatement"], + // 禁用指定的通过 require 加载的模块 + // 不要在return语句中使用赋值语句 + "no-return-assign": [2, "always"], + // "no-return-assign": [2, "except-parens"], + // 禁止使用 javascript: url + // 禁止代码中使用类似 javascript:void(0) 的javascript: urls. + "no-script-url": 0, + // 禁止自我赋值 禁止给自身赋值 + "no-self-assign": 2, + // 禁止自身比较 禁止和自身作比较 + "no-self-compare": 2, + // 禁用逗号操作符 + // 禁止可能导致结果不明确的逗号操作符 + "no-sequences": 2, + // 禁止 var 声明 与外层作用域的变量同名 + // 禁止声明外部作用域中已定义的变量 + "no-shadow": 0, + // 禁止覆盖受限制的标识符 + // 声明变量时禁止覆盖JavaScript中的一些保留关键字,比如 NaN、Infinity、undefined、eval、arguments等 + "no-shadow-restricted-names": 2, + // 禁止稀疏数组,清除多余的逗号申明 比如 [1,,2] + "no-sparse-arrays": 2, + // 禁止 function 标识符和括号之间出现空格 + // 函数调用时,函数名和圆括号之间不能有空格 + "no-spaced-func": 2, + // 禁止使用同步方法,建议使用异步方法 + "no-sync": 1, + "no-tabs": 2, + "no-template-curly-in-string": 2, + // 禁止使用三元操作符 + "no-ternary": 0, + // 禁止抛出非异常字面量 + // 通过throw语句抛出的对象必须是Error对象本身或者通过Error对象定义的对象。有些情况除外,见官网 + "no-throw-literal": 2, + // 禁止在构造函数中,在调用 super() 之前使用 this 或 super + "no-this-before-super": 2, + // 禁用行尾空格,(默认 false)不允许尾随空格空行, + // 注:编辑器保存代码时会去除尾空格,不用担心 + "no-trailing-spaces": 1, + // "no-trailing-spaces": [1, { "skipBlankLines": true }], + // 禁用未声明的变量,除非它们在 /*global */ 注释中被提到 + // 禁止使用未被定义的变量,除非已在配置文件的 global 中进行了说明 + "no-undef": 2, + // "no-undef": [2, { "typeof": true }], + // 禁止将变量初始化为 undefined + "no-undef-init": 2, + // 禁止将 undefined 作为标识符(变量名) + "no-undefined": 0, + // 禁止标识符中有悬空下划线 _bar + // 禁止在标识符前后使用下划线 + "no-underscore-dangle": 1, + // 禁止出现令人困惑的多行表达式(为了保证两行不相关的代码不会意外的被当做一行代码来解析) + "no-unexpected-multiline": 2, + // 禁用一成不变的循环条件 + // 禁止使用不被修改的循环条件 + "no-unmodified-loop-condition": 2, + // 禁止可以在有更简单的可替代的表达式时使用三元操作符 + // 禁止使用没有必要的三元操作符,因为用有些三元操作符可以使用其他语句替换 + "no-unneeded-ternary": [2, { "defaultAssignment": false }], + // 禁止在return、throw、continue 和 break语句之后出现不可达代码 (禁止有执行不到的代码) + /* + function foo() { + return true; + console.log("done"); + } //错误 + */ + "no-unreachable": 2, + // 禁止出现未使用过的表达式 + // 禁止在代码中出现没有被使用到的表达式或值 + "no-unused-expressions": [2, { "allowShortCircuit": true, "allowTernary": true }], + // "no-unused-expressions": 1, + // 禁用未使用过的标签 + // 禁止在代码中出现没有被使用到的标签 + "no-unused-labels": 2, + // 禁止出现未使用过的变量 + // 不允许定义的变量在后面的代码中没有被使用到 + "no-unused-vars": [1, { "vars": "all", "args": "none" }], + // 不允许在变量定义之前使用它们(所有的变量都应该先定义后使用) + "no-use-before-define": 2, + // 禁止不必要的(没有意义的) .call() 和 .apply() + "no-useless-call": 2, + // 禁止不必要的计算性能键对象的文字 + "no-useless-computed-key": 2, + // 禁止不必要的字符串字面量或模板字面量的连接 + // 避免使用不必要的字符串拼接 如:var str = "Hello, " + "World!"; + "no-useless-concat": 2, + "no-useless-constructor": 2, + // 禁用不必要的转义字符 + "no-useless-escape": 2, + "no-useless-rename": 2, + "no-useless-return": 2, + // 要求使用 let 或 const 而不是 var + "no-var": 0, + // 禁用 void 操作符 + "no-void": 2, + // 禁止在注释中使用特定的警告术语 + // 生产代码中不能出现 warning-comments 包含的注释(默认 location: "start") + "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "anywhere" }], + // "no-warning-comments": 1, + // 禁止属性前有空白 + // 禁止属性操作符 . 的前后和 [ 之前有空格 + "no-whitespace-before-property": 1, + // 禁用 with 语句 + "no-with": 2, + // 强制花括号内换行符的一致性 + "object-curly-newline": 0, + // 强制在花括号中使用一致的空格 + // 规定对象字面量中大括号内是否允许加空格,也适用于ES6中的结构赋值和模块import和export + "object-curly-spacing": 0, + // "object-curly-spacing": [2, "never"], + // 强制将对象的属性放在不同的行上 + "object-property-newline": 0, + // "object-property-newline": [2, { "allowMultiplePropertiesPerLine": true }], + // 要求或禁止对象字面量中方法和属性使用简写语法 + "object-shorthand": 0, + // 强制函数中的变量要么一起声明要么分开声明 + // 规定了在每个函数中声明变量是否只使用一次var,该规则同样适用于 let 和 const + "one-var": [2, { "initialized": "never" }], + // 要求或禁止在 var 声明周围换行 + "one-var-declaration-per-line": 2, + // 要求或禁止在可能的情况下要求使用简化的赋值操作符 + // 规定了使用赋值操作符的简写形式 + "operator-assignment": 2, + // "operator-assignment": [2, "always"], + // 强制操作符使用一致的换行符 + // 在换行时操作符应该放在行首还是行尾。还可对某些操作符进行重写 + "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], + // 要求或禁止块内填充 + // 在代码块中,代码块的开始和结尾是否应该留一个空行 + "padded-blocks": 0, + // "padded-blocks": [2, "never"], + // 要求使用箭头函数作为回调 + "prefer-arrow-callback": 0, + // 要求使用 const 声明那些声明后不再被修改的变量 + "prefer-const": 0, + "prefer-numeric-literals": 2, + // 要求在合适的地方使用 Reflect 方法 + "prefer-reflect": 0, + // 要求使用扩展运算符而非 .apply() + "prefer-spread": 0, + // 要求使用模板字面量而非字符串连接 + // var str = "Hello, " + name + "!"; + // 改为模板字面量 + // var str = `Hello, ${name}!`; + "prefer-template": 0, + // Suggest using the rest parameters instead of arguments + "prefer-rest-params": 0, + "prefer-spread": 2, + // 要求对象字面量属性名称用引号括起来 + // 对象的属性名是否强制加双引号 + "quote-props": 0, + // "quote-props": [2, "always"], + // 强制使用一致的反勾号、双引号或单引号 + // 规定了字符串定义的方式,在JavaScript中有三种方式定义字符串, + // 双引号 double、单引号 single、反义符 backtick ES6 only(ECMAScript2015)。 + "quotes": 0, + // "quotes": [1, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], + // 强制在parseInt()使用基数参数 + // 在使用parseInt()方法时,必须要传递第二个参数来帮助解析 + "radix": 0, + // 要求使用 JSDoc 注释 + // 注释格式要求JSDoc格式 + // "require-jsdoc": 1, + "require-jsdoc": [0, { + "require": { + "FunctionDeclaration": true, + "MethodDefinition": false, + "ClassDeclaration": false + } + }], + // 要求generator 函数内有 yield + "require-yield": 0, + // enforce spacing between rest and spread operators and their expressions + "rest-spread-spacing": [2, "never"], + // 要求或禁止使用分号而不是使用 ASI(这个才是控制行尾部分号的,) + // JavaScript不要求在每行末尾加上分号,这是因为JavaScript引擎会决定是否需要在行末加上分号,然后自动帮我们在行末加上分号,这一特性被成为ASI(automatic semicolon insertion),也是JavaScript语言最富争议的特性之一 + // 尽管ASI允许我们使用更加自由的代码风格,但是它也可能使得你的代码并不是按你期许的方式运行 + // 两个可选参数,always 和never + // 默认配置always,要求在行末加上分号。 + "semi": [1, "always"], + // "semi": [1, "never"], + // 强制分号之前和之后使用一致的空格 + // 该规则用来规定分号前后是否加空格,默认配置如下 + "semi-spacing": 0, + // "semi-spacing": [2, { "before": false, "after": true }], + // 强制模块内的 import 排序 + // 要求对同一个模块里的import声明按字母排序 + "sort-imports": 0, + "sort-keys": 0, + // 要求同一个声明块中的变量按顺序排列 + // 规定在同一个变量声明代码块中,要对变量的声明按字母排序 + "sort-vars": 0, + // 要求关键词后必须有空格 + "space-after-keywords": 0, + // "space-after-keywords": [2, "always"], + // 强制在块之前使用一致的空格 + // 规定了在代码块前是否需要加空格 + "space-before-blocks": 0, + // "space-before-blocks": [2, "always"], + // 强制在 function的左括号之前使用一致的空格 + // 函数定义时,function关键字后面的小括号前是否需要加空格 + "space-before-function-paren": 0, + // "space-before-function-paren": [2, "always"], + // 强制在圆括号内使用一致的空格 + // 规定圆括号内部的空格。规定是否需要在(右边,或者)左边加空格 + "space-in-parens": 0, + // "space-in-parens": [2, "never"], + // 要求操作符周围有空格, 如:a+b => a + b + "space-infix-ops": 0, + //This rule was removed in ESLint v2.0 and replaced by the keyword-spacing rule. + // "space-return-throw-case": 2, + // 强制在一元操作符前后使用一致的空格 + "space-unary-ops": [2, { "words": true, "nonwords": false }], + // 强制在注释中 // 或 /* 使用一致的空格 + "spaced-comment": 0, + // "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }], + // "spaced-comment": [2, "always", { "line": { "markers": ["*package", "!", ","] }, "block": { "balanced": true, "markers": ["*package", "!", ","], "exceptions": ["*"] } }], + // 要求或禁止使用严格模式指令(使用严格模式) + "strict": 0, + "symbol-description": 2, + // 要求或禁止模板字符串中的嵌入表达式周围空格的使用 + "template-curly-spacing": 1, + // "template-curly-spacing": [2, "never"], + // 要求或禁止 Unicode BOM + "unicode-bom": [2, "never"], + // 禁止和 NaN 作比较,要求使用 isNaN() 检查 + "use-isnan": 2, + // 强制使用有效的 JSDoc 注释(用来检测JSDoc是否完整和合法) + "valid-jsdoc": 1, + // 强制 typeof 表达式与有效的字符串进行比较 + // typeof 操作符返回的结果会是 "undefined", "object", "boolean", "number", "string", 和 "function"之一。 + // 保证 typeof 操作符返回的结果必须和上面六个字符串作比较 + // typeof foo === "undefimed" 错误 + "valid-typeof": 2, + // 要求所有的 var 声明出现在它们所在的作用域顶部 + // 在通过var声明变量时,应该放在代码所在作用域的顶部 + "vars-on-top": 0, + // 要求 IIFE 使用括号括起来 + // 立即执行函数需要通过圆括号包围 + "wrap-iife": [2, "any", { "functionPrototypeMethods": true }], + // 要求正则表达式被括号括起来 + "wrap-regex": 0, + // 强制在 yield* 表达式中 * 周围使用空格 + "yield-star-spacing": [2, "both"], + // 要求或禁止 “Yoda” 条件 + // yoda条件语句就是对象字面量应该写在比较操作符的左边,而变量应该写在比较操作符的右边 + // 默认的规则要求,变量写在左边而字面量写在右边 + "yoda": [2, "never"], + + // "standard/object-curly-even-spacing": [2, "either"], + // "standard/array-bracket-even-spacing": [2, "either"], + // "standard/computed-property-even-spacing": [2, "even"], + // + // "promise/param-names": 2 + } +}; diff --git a/linters/.eslintrc.simple.js b/linters/.eslintrc.simple.js new file mode 100644 index 0000000000..3de58779b8 --- /dev/null +++ b/linters/.eslintrc.simple.js @@ -0,0 +1,63 @@ +module.exports = { + // 环境定义了预定义的全局变量, 更多在官网查看 + "env": { + "browser": true, + "node": true, + "commonjs": true, + "amd": true, + "es6": true, + "mocha": true, + }, + "globals": { + "_": false, + "$": false, + "define": false, + "document": false, + "navigator": false, + "window": false, + "getViewTemplatePath": false, + }, + "rules": { + // 强制使用双峰驼命名格式 + "camelcase": 0, + // "camelcase": [2, { "properties": "never" }], + // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号, + // always-multiline:多行模式必须带逗号,单行模式不能带逗号(I don't care about IE8) + "comma-dangle": [1, "always-multiline"], // never + // 控制逗号前后的空格 + // 规定了逗号前后的空白,默认配置规定逗号前面没有空白,而逗号后面需要留空白 + "comma-spacing": [1, { "before": false, "after": true }], + // 规定了标识符命名的黑名单 + "id-blacklist": [1, "err", "e", "cb"], + // 强制在对象字面量的属性中键和值之间使用一致的间距 + // 该规则规定了在对象字面量语法中key和value之间的空白,冒号前不要留空格,冒号后面需留一个空格 + // "key-spacing": 0, + "key-spacing": [1, { "beforeColon": false, "afterColon": true }], + // 强制在关键字前后使用一致的空格 (前后都需要) + // 规定了keyword前后是否需要留一个空格 + "keyword-spacing": 0, + // "keyword-spacing": [1, { "before": true, "after": true, "overrides": {} }], + // 禁止使用某些特定的JavaScript语法,例如FunctionDeclaration 和 WithStatement + "no-restricted-syntax": 0, + // "no-restricted-syntax": [1, "FunctionExpression", "WithStatement"], + // 禁止 this 关键字出现在类和类对象之外 + // 禁止无效的this,只能用在构造器,类,对象字面量 + "no-invalid-this": 0, + // 在代码块中,代码块的开始和结尾是否应该留一个空行 + "padded-blocks": 0, + // "padded-blocks": [2, "never"], + // 要求关键词后必须有空格 + "space-after-keywords": 0, + // 强制在注释中 // 或 /* 使用一致的空格 + "spaced-comment": 0, + // "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }], + // "spaced-comment": [2, "always", { "line": { "markers": ["*package", "!", ","] }, "block": { "balanced": true, "markers": ["*package", "!", ","], "exceptions": ["*"] } }], + // 要求或禁止使用严格模式指令(使用严格模式) + "strict": 0, + // 要求所有的 var 声明出现在它们所在的作用域顶部 + // 在通过var声明变量时,应该放在代码所在作用域的顶部 + "vars-on-top": 0, + // 要求正则表达式被括号括起来 + "wrap-regex": 0, + } +} From 6c566bce57c5f7eff353ecd0f257ecb9893133de Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Tue, 1 Nov 2016 18:57:48 +0800 Subject: [PATCH 03/33] update .eslintrc.js --- linters/.eslintrc.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/linters/.eslintrc.js b/linters/.eslintrc.js index 2acab9bb86..097fcffc2f 100644 --- a/linters/.eslintrc.js +++ b/linters/.eslintrc.js @@ -149,13 +149,7 @@ module.exports = { */ /** rules 核心配置 - * "camelcase": 2, - * "curly": 2, - * "brace-style": [2, "1tbs"], - * "quotes": [2, "single"], - * "semi": [2, "always"], - * "space-in-brackets": [2, "never"], - * "space-infix-ops": 2, + * 这里也许可以抽取一个精简版 */ "rules": { From 46bc6da092fd52733ee4c64ff4af3ae249bd4f1e Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Wed, 8 Feb 2017 14:36:09 +0800 Subject: [PATCH 04/33] =?UTF-8?q?copy=20es6=20=E4=B8=AD=E6=96=87=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es5/zh-cn_v1.md | 1740 +++++++++++++++++++++++++++++++++++++ es5/zh-cn_v2.md | 1328 ++++++++++++++++++++++++++++ es6_zh-cn.md | 2192 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 5260 insertions(+) create mode 100644 es5/zh-cn_v1.md create mode 100644 es5/zh-cn_v2.md create mode 100644 es6_zh-cn.md diff --git a/es5/zh-cn_v1.md b/es5/zh-cn_v1.md new file mode 100644 index 0000000000..2a29c76a45 --- /dev/null +++ b/es5/zh-cn_v1.md @@ -0,0 +1,1740 @@ +# Airbnb JavaScript Style Guide() { + +*用更合理的方式写 JavaScript* + +## 目录 + + 1. [类型](#types) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转化](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [构造函数](#constructors) + 1. [事件](#events) + 1. [模块](#modules) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [谁在使用](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 风格指南说明](#the-javascript-style-guide-guide) + 1. [与我们讨论 JavaScript](#chat-with-us-about-javascript) + 1. [贡献者](#contributors) + 1. [许可](#license) + +## 类型 + + - **原始值**: 存取直接作用于它自身。 + + + `string` + + `number` + + `boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1; + var bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **复杂类型**: 存取时作用于它自身值的引用。 + + + `object` + + `array` + + `function` + + ```javascript + var foo = [1, 2]; + var bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 对象 + + - 使用直接量创建对象。 + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - 不要使用[保留字](http://es5.github.io/#x7.6.1)作为键名,它们在 IE8 下不工作。[更多信息](https://github.com/airbnb/javascript/issues/61)。 + + ```javascript + // bad + var superman = { + default: { clark: 'kent' }, + private: true + }; + + // good + var superman = { + defaults: { clark: 'kent' }, + hidden: true + }; + ``` + + - 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + var superman = { + class: 'alien' + }; + + // bad + var superman = { + klass: 'alien' + }; + + // good + var superman = { + type: 'alien' + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 数组 + + - 使用直接量创建数组。 + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - 向数组增加元素时使用 Array#push 来替代直接赋值。 + + ```javascript + var someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + - 当你需要拷贝数组时,使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + + ```javascript + var len = items.length; + var itemsCopy = []; + var i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + itemsCopy = items.slice(); + ``` + + - 使用 Array#slice 将类数组对象转换成数组。 + + ```javascript + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 字符串 + + - 使用单引号 `''` 包裹字符串。 + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob " + this.lastName; + + // good + var fullName = 'Bob ' + this.lastName; + ``` + + - 超过 100 个字符的字符串应该使用连接符写成多行。 + - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[jsPerf](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that was thrown because \ + of Batman. When you stop to think about how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + // good + var errorMessage = 'This is a super long error that was thrown because ' + + 'of Batman. When you stop to think about how Batman had anything to do ' + + 'with this, you would get nowhere fast.'; + ``` + + - 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:[jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items; + var messages; + var length; + var i; + + messages = [{ + state: 'success', + message: 'This one worked.' + }, { + state: 'success', + message: 'This one worked as well.' + }, { + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = ''; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + // use direct assignment in this case because we're micro-optimizing. + items[i] = '
  • ' + messages[i].message + '
  • '; + } + + return ''; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 函数 + + - 函数表达式: + + ```javascript + // 匿名函数表达式 + var anonymous = function() { + return true; + }; + + // 命名函数表达式 + var named = function named() { + return true; + }; + + // 立即调用的函数表达式(IIFE) + (function () { + console.log('Welcome to the Internet. Please follow me.'); + }()); + ``` + + - 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + - **注:** ECMA-262 把 `块` 定义为一组语句。函数声明不是语句。[阅读对 ECMA-262 这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + var test; + if (currentUser) { + test = function test() { + console.log('Yup.'); + }; + } + ``` + + - 永远不要把参数命名为 `arguments`。这将取代函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 属性 + + - 使用 `.` 来访问对象的属性。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + // bad + var isJedi = luke['jedi']; + + // good + var isJedi = luke.jedi; + ``` + + - 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 变量 + + - 总是使用 `var` 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。 + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - 使用 `var` 声明每一个变量。 + 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + + ```javascript + // bad + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (跟上面的代码比较一下,看看哪里错了) + var items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + ``` + + - 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。 + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i; + var items = getItems(); + var dragonball; + var goSportsTeam = true; + var len; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball; + var length; + var i; + ``` + + - 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。 + + ```javascript + // bad + function () { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function () { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad - 不必要的函数调用 + function () { + var name = getName(); + + if (!arguments.length) { + return false; + } + + this.setFirstName(name); + + return true; + } + + // good + function () { + var name; + + if (!arguments.length) { + return false; + } + + name = getName(); + this.setFirstName(name); + + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 提升 + + - 变量声明会提升至作用域顶部,但赋值不会。 + + ```javascript + // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。 + // 注:变量赋值为 `true` 不会提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: + function example() { + var declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + ``` + + - 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function () { + console.log('anonymous function expression'); + }; + } + ``` + + - 命名函数表达式会提升变量名,但不会提升函数名或函数体。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // 当函数名跟变量名一样时,表现也是如此。 + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + - 函数声明提升它们的名字和函数体。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - 了解更多信息在 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 比较运算符 & 等号 + + - 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. + - 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0、-0 或 NaN** 被计算为 **false**,否则为 **true** + + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0]) { + // true + // 一个数组就是一个对象,对象被计算为 true + } + ``` + + - 使用快捷方式。 + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - 了解更多信息在 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. + +**[⬆ 回到顶部](#table-of-contents)** + + +## + + - 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function () { return false; } + + // good + function () { + return false; + } + ``` + + - 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + + +## 注释 + + - 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + ``` + + - 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - 使用 `// FIXME:` 标注问题。 + + ```javascript + function Calculator() { + + // FIXME: shouldn't use a global here + total = 0; + + return this; + } + ``` + + - 使用 `// TODO:` 标注问题的解决方式。 + + ```javascript + function Calculator() { + + // TODO: total should be configurable by an options param + this.total = 0; + + return this; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 空白 + + - 使用 2 个空格作为缩进。 + + ```javascript + // bad + function () { + ∙∙∙∙var name; + } + + // bad + function () { + ∙var name; + } + + // good + function () { + ∙∙var name; + } + ``` + + - 在大括号前放一个空格。 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + ``` + + - 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + - 使用空格把运算符隔开。 + + ```javascript + // bad + var x=y+5; + + // good + var x = y + 5; + ``` + + - 在文件末尾插入一个空行。 + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function (global) { + // ...stuff... + })(this);↵ + ``` + + - 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + var leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + - 在块末和新语句前插入空行。 + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + var obj = { + foo: function () { + }, + bar: function () { + } + }; + return obj; + + // good + var obj = { + foo: function () { + }, + + bar: function () { + } + }; + + return obj; + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + +## 逗号 + + - 行首逗号: **不需要**。 + + ```javascript + // bad + var story = [ + once + , upon + , aTime + ]; + + // good + var story = [ + once, + upon, + aTime + ]; + + // bad + var hero = { + firstName: 'Bob' + , lastName: 'Parr' + , heroName: 'Mr. Incredible' + , superPower: 'strength' + }; + + // good + var hero = { + firstName: 'Bob', + lastName: 'Parr', + heroName: 'Mr. Incredible', + superPower: 'strength' + }; + ``` + + - 额外的行末逗号:**不需要**。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 ([source](http://es5.github.io/#D)): + + > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. + + ```javascript + // bad + var hero = { + firstName: 'Kevin', + lastName: 'Flynn', + }; + + var heroes = [ + 'Batman', + 'Superman', + ]; + + // good + var hero = { + firstName: 'Kevin', + lastName: 'Flynn' + }; + + var heroes = [ + 'Batman', + 'Superman' + ]; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 分号 + + - **使用分号。** + + ```javascript + // bad + (function () { + var name = 'Skywalker' + return name + })() + + // good + (function () { + var name = 'Skywalker'; + return name; + })(); + + // good (防止函数在两个 IIFE 合并时被当成一个参数 + ;(function () { + var name = 'Skywalker'; + return name; + })(); + ``` + + [了解更多](http://stackoverflow.com/a/7365214/1712802). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 类型转换 + + - 在语句开始时执行类型转换。 + - 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + var totalScore = this.reviewScore + ''; + + // good + var totalScore = '' + this.reviewScore; + + // bad + var totalScore = '' + this.reviewScore + ' total score'; + + // good + var totalScore = this.reviewScore + ' total score'; + ``` + + - 使用 `parseInt` 转换数字时总是带上类型转换的基数。 + + ```javascript + var inputValue = '4'; + + // bad + var val = new Number(inputValue); + + // bad + var val = +inputValue; + + // bad + var val = inputValue >> 0; + + // bad + var val = parseInt(inputValue); + + // good + var val = Number(inputValue); + + // good + var val = parseInt(inputValue, 10); + ``` + + - 如果因为某些原因 `parseInt` 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * parseInt was the reason my code was slow. + * Bitshifting the String to coerce it to a + * Number made it a lot faster. + */ + var val = inputValue >> 0; + ``` + + - **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([source](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + - 布尔: + + ```javascript + var age = 0; + + // bad + var hasAge = new Boolean(age); + + // good + var hasAge = Boolean(age); + + // good + var hasAge = !!age; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 命名规则 + + - 避免单字母命名。命名应具备描述性。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - 使用驼峰式命名对象、函数和实例。 + + ```javascript + // bad + var OBJEcttsssss = {}; + var this_is_my_object = {}; + var o = {}; + function c() {} + + // good + var thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + - 使用帕斯卡式命名构造函数或类。 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + var bad = new user({ + name: 'nope' + }); + + // good + function User(options) { + this.name = options.name; + } + + var good = new User({ + name: 'yup' + }); + ``` + + - 不要使用下划线前/后缀。 + + > 为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + this._firstName = 'Panda'; + + // good + this.firstName = 'Panda'; + ``` + + - 不要保存 `this` 的引用。使用 Function#bind。 + + ```javascript + // bad + function () { + var self = this; + return function () { + console.log(self); + }; + } + + // bad + function () { + var that = this; + return function () { + console.log(that); + }; + } + + // bad + function () { + var _this = this; + return function () { + console.log(_this); + }; + } + + // good + function () { + return function () { + console.log(this); + }.bind(this); + } + ``` + + - 给函数命名。这在做堆栈轨迹时很有帮助。 + + ```javascript + // bad + var log = function (msg) { + console.log(msg); + }; + + // good + var log = function log(msg) { + console.log(msg); + }; + ``` + + - **注:** IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。 + + - 如果你的文件导出一个类,你的文件名应该与类名完全相同。 + ```javascript + // file contents + class CheckBox { + // ... + } + module.exports = CheckBox; + + // in some other file + // bad + var CheckBox = require('./checkBox'); + + // bad + var CheckBox = require('./check_box'); + + // good + var CheckBox = require('./CheckBox'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 存取器 + + - 属性的存取函数不是必须的。 + - 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - 创建 get() 和 set() 函数是可以的,但要保持一致。 + + ```javascript + function Jedi(options) { + options || (options = {}); + var lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + Jedi.prototype.set = function set(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function get(key) { + return this[key]; + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 构造函数 + + - 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型! + + ```javascript + function Jedi() { + console.log('new jedi'); + } + + // bad + Jedi.prototype = { + fight: function fight() { + console.log('fighting'); + }, + + block: function block() { + console.log('blocking'); + } + }; + + // good + Jedi.prototype.fight = function fight() { + console.log('fighting'); + }; + + Jedi.prototype.block = function block() { + console.log('blocking'); + }; + ``` + + - 方法可以返回 `this` 来实现方法链式使用。 + + ```javascript + // bad + Jedi.prototype.jump = function jump() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + }; + + var luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + Jedi.prototype.jump = function jump() { + this.jumping = true; + return this; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + return this; + }; + + var luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - 写一个自定义的 `toString()` 方法是可以的,但是确保它可以正常工作且不会产生副作用。 + + ```javascript + function Jedi(options) { + options || (options = {}); + this.name = options.name || 'no name'; + } + + Jedi.prototype.getName = function getName() { + return this.name; + }; + + Jedi.prototype.toString = function toString() { + return 'Jedi - ' + this.getName(); + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 事件 + + - 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```js + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function (e, listingId) { + // do something with listingId + }); + ``` + + 更好的写法: + + ```js + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function (e, data) { + // do something with data.listingId + }); + ``` + + **[⬆ 回到顶部](#table-of-contents)** + + +## 模块 + + - 模块应该以 `!` 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。[详细说明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) + - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。 + - 增加一个名为 `noConflict()` 的方法来设置导出的模块为前一个版本并返回它。 + - 永远在模块顶部声明 `'use strict';`。 + + ```javascript + // fancyInput/fancyInput.js + + !function (global) { + 'use strict'; + + var previousFancyInput = global.FancyInput; + + function FancyInput(options) { + this.options = options || {}; + } + + FancyInput.noConflict = function noConflict() { + global.FancyInput = previousFancyInput; + return FancyInput; + }; + + global.FancyInput = FancyInput; + }(this); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## jQuery + + - 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + ```javascript + // bad + var sidebar = $('.sidebar'); + + // good + var $sidebar = $('.sidebar'); + ``` + + - 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + var $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + - 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容表](http://kangax.github.com/es5-compat-table/). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 测试 + + - **Yup.** + + ```javascript + function () { + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + +**[⬆ 回到顶部](#table-of-contents)** + + +## 资源 + + +**推荐阅读** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**工具** + + - Code Style Linters + + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**其它风格指南** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + - [JavaScript Standard Style](https://github.com/feross/standard) + +**其它风格** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**进一步阅读** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**书籍** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke + - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson + +**博客** + + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + +**播客** + + - [JavaScript Jabber](http://devchat.tv/js-jabber/) + + +**[⬆ 回到顶部](#table-of-contents)** + +## 谁在使用 + + 这是一个使用本风格指南的组织列表。给我们发 pull request 或开一个 issue 让我们将你增加到列表上。 + + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Super**: [SuperJobs/javascript](https://github.com/SuperJobs/javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +## 翻译 + + 这份风格指南也提供了其它语言的版本: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript](https://github.com/sivan/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + +## JavaScript 风格指南说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## 与我们讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + +## 贡献者 + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 + +(The MIT License) + +Copyright (c) 2014 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 回到顶部](#table-of-contents)** + +# }; diff --git a/es5/zh-cn_v2.md b/es5/zh-cn_v2.md new file mode 100644 index 0000000000..4040528a99 --- /dev/null +++ b/es5/zh-cn_v2.md @@ -0,0 +1,1328 @@ +[原文: https://github.com/airbnb/javascript](https://github.com/airbnb/javascript) + +注:本人根据自己的开发习惯删除和修改了部分规范 + +# JavaScript规范 + +## 内容列表 + + 1. [类型](#types) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [条件表达式和等号](#conditionals) + 1. [块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-coercion) + 1. [命名约定](#naming-conventions) + 1. [存取器](#accessors) + 1. [构造器](#constructors) + 1. [事件](#events) + 1. [模块](#modules) + 1. [jQuery](#jquery) + 1. [ES5 兼容性](#es5) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [哪些人在使用](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript风格指南](#guide-guide) + 1. [贡献者](#contributors) + 1. [许可](#license) + +## 类型 + + - **原始值**: 相当于传值 + + + `string` + + `number` + + `boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1, + bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **复杂类型**: 相当于传引用 + + + `object` + + `array` + + `function` + + ```javascript + var foo = [1, 2], + bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + + **[[⬆]](#TOC)** + +## 对象 + + - 使用字面值创建对象 + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - 不要使用保留字 [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) 作为键 + + ```javascript + // bad + var superman = { + class: 'superhero', + default: { clark: 'kent' }, + private: true + }; + + // good + var superman = { + klass: 'superhero', + defaults: { clark: 'kent' }, + hidden: true + }; + ``` + **[[⬆]](#TOC)** + +## 数组 + + - 使用字面值创建数组 + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - 如果你不知道数组的长度,使用push + + ```javascript + var someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + - 当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + + ```javascript + var len = items.length, + itemsCopy = [], + i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + itemsCopy = items.slice(); + ``` + + - 使用slice将类数组的对象转成数组. + + ```javascript + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + + **[[⬆]](#TOC)** + + +## 字符串 + + - 对字符串使用单引号 `''` + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob " + this.lastName; + + // good + var fullName = 'Bob ' + this.lastName; + ``` + + - 超过80个字符的字符串应该使用字符串连接换行 + - 注: 如果过度使用,长字符串连接可能会对性能有影响. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that \ + was thrown because of Batman. \ + When you stop to think about \ + how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + + // good + var errorMessage = 'This is a super long error that ' + + 'was thrown because of Batman.' + + 'When you stop to think about ' + + 'how Batman had anything to do ' + + 'with this, you would get nowhere ' + + 'fast.'; + ``` + + - 编程时使用join而不是字符串连接来构建字符串,特别是IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items, + messages, + length, i; + + messages = [{ + state: 'success', + message: 'This one worked.' + },{ + state: 'success', + message: 'This one worked as well.' + },{ + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = ''; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + items[i] = messages[i].message; + } + + return ''; + } + ``` + + **[[⬆]](#TOC)** + + +## 函数 + + - 函数表达式: + + ```javascript + // 匿名函数表达式 + var anonymous = function() { + return true; + }; + + // 有名函数表达式 + var named = function named() { + return true; + }; + + // 立即调用函数表达式 + (function() { + console.log('Welcome to the Internet. Please follow me.'); + })(); + ``` + + - 绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同。 + - **注:** ECMA-262定义把`块`定义为一组语句,函数声明不是一个语句。[阅读ECMA-262对这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + if (currentUser) { + var test = function test() { + console.log('Yup.'); + }; + } + ``` + + - 绝对不要把参数命名为 `arguments`, 这将会逾越函数作用域内传过来的 `arguments` 对象. + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + **[[⬆]](#TOC)** + + +## 属性 + + - 当使用变量访问属性时使用中括号. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + + **[[⬆]](#TOC)** + + +## 变量 + + - 总是使用 `var` 来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。 + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - 使用一个 `var` 以及新行声明多个变量,缩进4个空格。 + + ```javascript + // bad + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + ``` + + - 最后再声明未赋值的变量,当你想引用之前已赋值变量的时候很有用。 + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i, items = getItems(), + dragonball, + goSportsTeam = true, + len; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball, + length, + i; + ``` + + - 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。 + + ```javascript + // bad + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function() { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad + function() { + var name = getName(); + + if (!arguments.length) { + return false; + } + + return true; + } + + // good + function() { + if (!arguments.length) { + return false; + } + + var name = getName(); + + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## 条件表达式和等号 + + - 适当使用 `===` 和 `!==` 以及 `==` 和 `!=`. + - 条件表达式的强制类型转换遵循以下规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0, -0, or NaN** 被计算为 **false** , 否则为 **true** + + **字符串** 如果是空字符串 `''` 则被计算为 **false**, 否则为 **true** + + ```javascript + if ([0]) { + // true + // An array is an object, objects evaluate to true + } + ``` + + - 使用快捷方式. + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - 阅读 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) 了解更多 + + **[[⬆]](#TOC)** + + +## + + - 给所有多行的块使用大括号 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function() { return false; } + + // good + function() { + return false; + } + ``` + + **[[⬆]](#TOC)** + + +## 注释 + + - 使用 `/** ... */` 进行多行注释,包括描述,指定类型以及参数值和返回值 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param tag + // @return element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param tag + * @return element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - 使用 `//` 进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行. + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + ``` + + - 如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 `FIXME` 或 `TODO` 帮助其他人迅速理解 + + ```javascript + function Calculator() { + + // FIXME: shouldn't use a global here + total = 0; + + return this; + } + ``` + + ```javascript + function Calculator() { + + // TODO: total should be configurable by an options param + this.total = 0; + + return this; + } + ``` + + **[[⬆]](#TOC)** + + +## 空白 + + - 将tab设为4个空格 + + ```javascript + // bad + function() { + ∙∙var name; + } + + // bad + function() { + ∙var name; + } + + // good + function() { + ∙∙∙∙var name; + } + ``` + - 大括号前放一个空格 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + ``` + + - 在做长方法链时使用缩进. + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + var leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .class('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + **[[⬆]](#TOC)** + +## 逗号 + + - 不要将逗号放前面 + + ```javascript + // bad + var once + , upon + , aTime; + + // good + var once, + upon, + aTime; + + // bad + var hero = { + firstName: 'Bob' + , lastName: 'Parr' + , heroName: 'Mr. Incredible' + , superPower: 'strength' + }; + + // good + var hero = { + firstName: 'Bob', + lastName: 'Parr', + heroName: 'Mr. Incredible', + superPower: 'strength' + }; + ``` + + - 不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。 + + ```javascript + // bad + var hero = { + firstName: 'Kevin', + lastName: 'Flynn', + }; + + var heroes = [ + 'Batman', + 'Superman', + ]; + + // good + var hero = { + firstName: 'Kevin', + lastName: 'Flynn' + }; + + var heroes = [ + 'Batman', + 'Superman' + ]; + ``` + + **[[⬆]](#TOC)** + + +## 分号 + + - 语句结束一定要加分号 + + ```javascript + // bad + (function() { + var name = 'Skywalker' + return name + })() + + // good + (function() { + var name = 'Skywalker'; + return name; + })(); + + // good + ;(function() { + var name = 'Skywalker'; + return name; + })(); + ``` + + **[[⬆]](#TOC)** + + +## 类型转换 + + - 在语句的开始执行类型转换. + - 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + var totalScore = this.reviewScore + ''; + + // good + var totalScore = '' + this.reviewScore; + + // bad + var totalScore = '' + this.reviewScore + ' total score'; + + // good + var totalScore = this.reviewScore + ' total score'; + ``` + + - 对数字使用 `parseInt` 并且总是带上类型转换的基数. + + ```javascript + var inputValue = '4'; + + // bad + var val = new Number(inputValue); + + // bad + var val = +inputValue; + + // bad + var val = inputValue >> 0; + + // bad + var val = parseInt(inputValue); + + // good + var val = Number(inputValue); + + // good + var val = parseInt(inputValue, 10); + + // good + /** + * parseInt was the reason my code was slow. + * Bitshifting the String to coerce it to a + * Number made it a lot faster. + */ + var val = inputValue >> 0; + ``` + + - 布尔值: + + ```javascript + var age = 0; + + // bad + var hasAge = new Boolean(age); + + // good + var hasAge = Boolean(age); + + // good + var hasAge = !!age; + ``` + + **[[⬆]](#TOC)** + + +## 命名约定 + + - 避免单个字符名,让你的变量名有描述意义。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - 当命名对象、函数和实例时使用驼峰命名规则 + + ```javascript + // bad + var OBJEcttsssss = {}; + var this_is_my_object = {}; + var this-is-my-object = {}; + function c() {}; + var u = new user({ + name: 'Bob Parr' + }); + + // good + var thisIsMyObject = {}; + function thisIsMyFunction() {}; + var user = new User({ + name: 'Bob Parr' + }); + ``` + + - 当命名构造函数或类时使用驼峰式大写 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + var bad = new user({ + name: 'nope' + }); + + // good + function User(options) { + this.name = options.name; + } + + var good = new User({ + name: 'yup' + }); + ``` + + - 命名私有属性时前面加个下划线 `_` + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + + // good + this._firstName = 'Panda'; + ``` + + - 当保存对 `this` 的引用时使用 `_this`. + + ```javascript + // bad + function() { + var self = this; + return function() { + console.log(self); + }; + } + + // bad + function() { + var that = this; + return function() { + console.log(that); + }; + } + + // good + function() { + var _this = this; + return function() { + console.log(_this); + }; + } + ``` + + **[[⬆]](#TOC)** + + +## 存取器 + + - 属性的存取器函数不是必需的 + - 如果你确实有存取器函数的话使用getVal() 和 setVal('hello') + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - 如果属性是布尔值,使用isVal() 或 hasVal() + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - 可以创建get()和set()函数,但是要保持一致 + + ```javascript + function Jedi(options) { + options || (options = {}); + var lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + Jedi.prototype.set = function(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function(key) { + return this[key]; + }; + ``` + + **[[⬆]](#TOC)** + + +## 构造器 + + - 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。 + + ```javascript + function Jedi() { + console.log('new jedi'); + } + + // bad + Jedi.prototype = { + fight: function fight() { + console.log('fighting'); + }, + + block: function block() { + console.log('blocking'); + } + }; + + // good + Jedi.prototype.fight = function fight() { + console.log('fighting'); + }; + + Jedi.prototype.block = function block() { + console.log('blocking'); + }; + ``` + + - 方法可以返回 `this` 帮助方法可链。 + + ```javascript + // bad + Jedi.prototype.jump = function() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + }; + + var luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20) // => undefined + + // good + Jedi.prototype.jump = function() { + this.jumping = true; + return this; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + return this; + }; + + var luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - 可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。 + + ```javascript + function Jedi(options) { + options || (options = {}); + this.name = options.name || 'no name'; + } + + Jedi.prototype.getName = function getName() { + return this.name; + }; + + Jedi.prototype.toString = function toString() { + return 'Jedi - ' + this.getName(); + }; + ``` + + **[[⬆]](#TOC)** + + +## 事件 + + - 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器 + + ```js + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function(e, listingId) { + // do something with listingId + }); + ``` + + 更好: + + ```js + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function(e, data) { + // do something with data.listingId + }); + ``` + + **[[⬆]](#TOC)** + + +## 模块 + + - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + - 总是在模块顶部声明 `'use strict';` + + ```javascript + // fancyInput/fancyInput.js + + !function(global) { + 'use strict'; + + var previousFancyInput = global.FancyInput; + + function FancyInput(options) { + this.options = options || {}; + } + + FancyInput.noConflict = function noConflict() { + global.FancyInput = previousFancyInput; + return FancyInput; + }; + + global.FancyInput = FancyInput; + }(this); + ``` + + **[[⬆]](#TOC)** + + +## jQuery + + - 缓存jQuery查询 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + var $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - 对DOM查询使用级联的 `$('.sidebar ul')` 或 `$('.sidebar ul')`,[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - 对有作用域的jQuery对象查询使用 `find` + + ```javascript + // bad + $('.sidebar', 'ul').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good (slower) + $sidebar.find('ul'); + + // good (faster) + $($sidebar[0]).find('ul'); + ``` + + **[[⬆]](#TOC)** + + +## ECMAScript 5兼容性 + + - 参考[Kangax](https://twitter.com/kangax/)的 ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) + + **[[⬆]](#TOC)** + + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + + **[[⬆]](#TOC)** + + +## 资源 + +**Read This** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**其它规范** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + +**其它风格** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) + +**阅读更多** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + +**书籍** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + +**博客** + + - [Adam Lu](http://adamlu.com/) + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + + **[[⬆]](#TOC)** + +## 哪些人在使用 + + 这是一些使用这个风格规范的组织,给我们发pull request或打开一个问题,我们会把你加到列表中。 + + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **Userify**: [userify/javascript](https://github.com/userify/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +## 翻译 + + 这个风格规范也有其它语言版本: + + - :de: **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - :jp: **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - :br: **Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - :cn: **Chinese**: [adamlu/javascript-style-guide](https://github.com/adamlu/javascript-style-guide) + +## JavaScript风格指南 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## 贡献者 + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 + +(The MIT License) + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[[⬆]](#TOC)** + +# }; diff --git a/es6_zh-cn.md b/es6_zh-cn.md new file mode 100644 index 0000000000..562b05a33f --- /dev/null +++ b/es6_zh-cn.md @@ -0,0 +1,2192 @@ +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Airbnb JavaScript Style Guide() { + +**用更合理的方式写 JavaScript** + +ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/zh-cn_v2.md)。 + +翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 + + +## 目录 + + 1. [类型](#types) + 1. [引用](#references) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [解构](#destructuring) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [箭头函数](#arrow-functions) + 1. [构造函数](#constructors) + 1. [模块](#modules) + 1. [Iterators & Generators ](#iterators-and-generators) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [代码块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [事件](#events) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [使用人群](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) + 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) + 1. [Contributors](#contributors) + 1. [License](#license) + + +## 类型 + + - [1.1](#1.1) **基本类型**: 直接存取基本类型。 + + + `string` 字符串 + + `number` 数值 + + `boolean` 布尔类型 + + `null` + + `undefined` + + ```javascript + const foo = 1; + let bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - [1.2](#1.2) **复制类型**: 通过引用的方式存取复杂类型。 + + + `object` 对象 + + `array` 数组 + + `function` 函数 + + ```javascript + const foo = [1, 2]; + const bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 引用 + + - [2.1](#2.1) 对所有的引用使用 `const` ;不要使用 `var`。 + + > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 + + ```javascript + // bad + var a = 1; + var b = 2; + + // good + const a = 1; + const b = 2; + ``` + + - [2.2](#2.2) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + + > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 + + ```javascript + // bad + var count = 1; + if (true) { + count += 1; + } + + // good, use the let. + let count = 1; + if (true) { + count += 1; + } + ``` + + - [2.3](#2.3) 注意 `let` 和 `const` 都是块级作用域。 + + ```javascript + // const 和 let 只存在于它们被定义的区块内。 + { + let a = 1; + const b = 1; + } + console.log(a); // ReferenceError + console.log(b); // ReferenceError + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 对象 + + - [3.1](#3.1) 使用字面值创建对象。 + + ```javascript + // bad + const item = new Object(); + + // good + const item = {}; + ``` + + - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 + + ```javascript + // bad + const superman = { + default: { clark: 'kent' }, + private: true, + }; + + // good + const superman = { + defaults: { clark: 'kent' }, + hidden: true, + }; + ``` + + - [3.3](#3.3) 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + const superman = { + class: 'alien', + }; + + // bad + const superman = { + klass: 'alien', + }; + + // good + const superman = { + type: 'alien', + }; + ``` + + + - [3.4](#3.4) 创建有动态属性名的对象时,使用可被计算的属性名称。 + + > 为什么?因为这样可以让你在一个地方定义所有的对象属性。 + + ```javascript + function getKey(k) { + return `a key named ${k}`; + } + + // bad + const obj = { + id: 5, + name: 'San Francisco', + }; + obj[getKey('enabled')] = true; + + // good + const obj = { + id: 5, + name: 'San Francisco', + [getKey('enabled')]: true, + }; + ``` + + + - [3.5](#3.5) 使用对象方法的简写。 + + ```javascript + // bad + const atom = { + value: 1, + + addValue: function (value) { + return atom.value + value; + }, + }; + + // good + const atom = { + value: 1, + + addValue(value) { + return atom.value + value; + }, + }; + ``` + + + - [3.6](#3.6) 使用对象属性值的简写。 + + > 为什么?因为这样更短更有描述性。 + + ```javascript + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + lukeSkywalker: lukeSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + }; + ``` + + - [3.7](#3.7) 在对象属性声明前把简写的属性分组。 + + > 为什么?因为这样能清楚地看出哪些属性使用了简写。 + + ```javascript + const anakinSkywalker = 'Anakin Skywalker'; + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + lukeSkywalker, + episodeThree: 3, + mayTheFourth: 4, + anakinSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + anakinSkywalker, + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + episodeThree: 3, + mayTheFourth: 4, + }; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 数组 + + - [4.1](#4.1) 使用字面值创建数组。 + + ```javascript + // bad + const items = new Array(); + + // good + const items = []; + ``` + + - [4.2](#4.2) 向数组添加元素时使用 Arrary#push 替代直接赋值。 + + ```javascript + const someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + + - [4.3](#4.3) 使用拓展运算符 `...` 复制数组。 + + ```javascript + // bad + const len = items.length; + const itemsCopy = []; + let i; + + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + const itemsCopy = [...items]; + + // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + // good + itemsCopy = items.slice(); + ``` + - [4.4](#4.4) 使用 Array#from 把一个类数组对象转换成数组。 + + ```javascript + const foo = document.querySelectorAll('.foo'); + const nodes = Array.from(foo); + + // ES5 中使用slice将类数组的对象转成数组. + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 解构 + + - [5.1](#5.1) 使用解构存取和使用多属性对象。 + + > 为什么?因为解构能减少临时引用属性。 + + ```javascript + // bad + function getFullName(user) { + const firstName = user.firstName; + const lastName = user.lastName; + + return `${firstName} ${lastName}`; + } + + // good + function getFullName(obj) { + const { firstName, lastName } = obj; + return `${firstName} ${lastName}`; + } + + // best + function getFullName({ firstName, lastName }) { + return `${firstName} ${lastName}`; + } + ``` + + - [5.2](#5.2) 对数组使用解构赋值。 + + ```javascript + const arr = [1, 2, 3, 4]; + + // bad + const first = arr[0]; + const second = arr[1]; + + // good + const [first, second] = arr; + ``` + + - [5.3](#5.3) 需要回传多个值时,使用对象解构,而不是数组解构。 + > 为什么?增加属性或者改变排序不会改变调用时的位置。 + + ```javascript + // bad + function processInput(input) { + // then a miracle occurs + return [left, right, top, bottom]; + } + + // 调用时需要考虑回调数据的顺序。 + const [left, __, top] = processInput(input); + + // good + function processInput(input) { + // then a miracle occurs + return { left, right, top, bottom }; + } + + // 调用时只选择需要的数据 + const { left, right } = processInput(input); + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## Strings + + - [6.1](#6.1) 字符串使用单引号 `''` 。 + + ```javascript + // bad + const name = "Capt. Janeway"; + + // good + const name = 'Capt. Janeway'; + ``` + + - [6.2](#6.2) 字符串超过 80 个字节应该使用字符串连接号换行。此处可以更多个字符,目前编辑界面越来越大了 + - [6.3](#6.3) 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + 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.'; + + // bad + 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.'; + + // good + 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.'; + ``` + + + - [6.4](#6.4) 程序化生成字符串时,使用模板字符串代替字符串连接。 + + > 为什么?模板字符串更为简洁,更具可读性。 + + ```javascript + // bad + function sayHi(name) { + return 'How are you, ' + name + '?'; + } + + // bad + function sayHi(name) { + return ['How are you, ', name, '?'].join(); + } + + // good + function sayHi(name) { + return `How are you, ${name}?`; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 函数 + + - [7.1](#7.1) 使用函数声明代替函数表达式。 + + > 为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得[箭头函数](#arrow-functions)可以取代函数表达式。 + + ```javascript + // bad 匿名函数表达式 + var anonymous = function() () { + }; + // bad 有名函数表达式 + var named = function named() { + }; + + // good + function foo() { + } + ``` + + - [7.2](#7.2) 函数表达式: + + ```javascript + // 立即调用的函数表达式 (IIFE) + (() => { + console.log('Welcome to the Internet. Please follow me.'); + })(); + ``` + + - [7.3](#7.3) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + - [7.4](#7.4) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + let test; + if (currentUser) { + test = () => { + console.log('Yup.'); + }; + } + ``` + + - [7.5](#7.5) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + + - [7.6](#7.6) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + + > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 + + ```javascript + // bad + function concatenateAll() { + const args = Array.prototype.slice.call(arguments); + return args.join(''); + } + + // good + function concatenateAll(...args) { + return args.join(''); + } + ``` + + + - [7.7](#7.7) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 + + ```javascript + // really bad + function handleThings(opts) { + // 不!我们不应该改变函数参数。 + // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 + // 但这样的写法会造成一些 Bugs。 + //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) + opts = opts || {}; + // ... + } + + // still bad + function handleThings(opts) { + if (opts === void 0) { + opts = {}; + } + // ... + } + + // good + function handleThings(opts = {}) { + // ? 对比以上写法区别是啥?参见 es6 + // ... + } + ``` + + - [7.8](#7.8) 直接给函数参数赋值时需要避免副作用。 + + > 为什么?因为这样的写法让人感到很困惑。 + + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 箭头函数 + + - [8.1](#8.1) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + + > 为什么?因为箭头函数创造了新的一个 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 + + > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 + + ```javascript + // bad + ;[1, 2, 3].map(function (x) { + return x * x; + }); + + // good + ;[1, 2, 3].map((x) => { + return x * x; + }); + ``` + + - [8.2](#8.2) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 + + > 为什么?语法糖。在链式调用中可读性很高。 + + > 为什么不?当你打算回传一个对象的时候。 + + ```javascript + // good + ;[1, 2, 3].map(x => x * x); + + // good + ;[1, 2, 3].reduce((total, n) => { + return total + n; + }, 0); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 构造器 + + - [9.1](#9.1) 总是使用 `class`。避免直接操作 `prototype` 。 + + > 为什么? 因为 `class` 语法更为简洁更易读。 + + ```javascript + // bad + function Queue(contents = []) { + this._queue = [...contents]; + } + Queue.prototype.pop = function() { + const value = this._queue[0]; + this._queue.splice(0, 1); + return value; + } + + + // good + class Queue { + constructor(contents = []) { + this._queue = [...contents]; + } + pop() { + const value = this._queue[0]; + this._queue.splice(0, 1); + return value; + } + } + ``` + + - [9.2](#9.2) 使用 `extends` 继承。 + + > 为什么?因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + + ```javascript + // bad + const inherits = require('inherits'); + function PeekableQueue(contents) { + Queue.apply(this, contents); + } + inherits(PeekableQueue, Queue); + PeekableQueue.prototype.peek = function() { + return this._queue[0]; + } + + // good + class PeekableQueue extends Queue { + peek() { + return this._queue[0]; + } + } + ``` + + - [9.3](#9.3) 方法可以返回 `this` 来帮助链式调用。 + + ```javascript + // bad + Jedi.prototype.jump = function() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + }; + + const luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + class Jedi { + jump() { + this.jumping = true; + return this; + } + + setHeight(height) { + this.height = height; + return this; + } + } + + const luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - [9.4](#9.4) 可以写一个自定义的 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 + + ```javascript + class Jedi { + constructor(options = {}) { + this.name = options.name || 'no name'; + } + + getName() { + return this.name; + } + + toString() { + return `Jedi - ${this.getName()}`; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 模块 + + - [10.1](#10.1) 总是使用模组 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + > 为什么?模块就是未来,让我们开始迈向未来吧。 + + **ES5中要注意** + + - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + - 总是在模块顶部声明 `'use strict';` + + **ES6要注意** + + - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + - 默认已开启 `'use strict';` + + ```javascript + // bad + const AirbnbStyleGuide = require('./AirbnbStyleGuide'); + module.exports = AirbnbStyleGuide.es6; + + // ok + import AirbnbStyleGuide from './AirbnbStyleGuide'; + export default AirbnbStyleGuide.es6; + + // best + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + + - [10.2](#10.2) 不要使用通配符 import。 + + > 为什么?这样能确保你只有一个默认 export。 + + ```javascript + // bad + import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + + // good + import AirbnbStyleGuide from './AirbnbStyleGuide'; + ``` + + - [10.3](#10.3) 不要从 import 中直接 export。 + + > 为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + ```javascript + // bad + // filename es6.js + export { es6 as default } from './airbnbStyleGuide'; + + // good + // filename es6.js + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Iterators and Generators + + - [11.1](#11.1) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-of`。 + + > 为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + + ```javascript + const numbers = [1, 2, 3, 4, 5]; + + // bad + let sum = 0; + for (let num of numbers) { + sum += num; + } + sum === 15; + + // good + let sum = 0; + numbers.forEach((num) => sum += num); + sum === 15; + + // best (use the functional force) + const sum = numbers.reduce((total, num) => total + num, 0); + sum === 15; + ``` + + - [11.2](#11.2) 现在还不要使用 generators。 + + > 为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + +**[⬆ 返回目录](#table-of-contents)** + + +## 属性 + + - [12.1](#12.1) 使用 `.` 来访问对象的属性。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + // bad + const isJedi = luke['jedi']; + + // good + const isJedi = luke.jedi; + ``` + + - [12.2](#12.2) 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + function getProp(prop) { + return luke[prop]; + } + + const isJedi = getProp('jedi'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 变量 + + - [13.1](#13.1) 一直使用 `const` 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。) + + ```javascript + // bad + superPower = new SuperPower(); + + // good + const superPower = new SuperPower(); + ``` + + - [13.2](#13.2) 使用 `const` 声明每一个变量。 + + > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + + ```javascript + // bad + const items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (compare to above, and try to spot the mistake) + const items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + const items = getItems(); + const goSportsTeam = true; + const dragonball = 'z'; + ``` + + - [13.3](#13.3) 将所有的 `const` 和 `let` 分组 + + > 为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。 + + ```javascript + // bad + let i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + let i; + const items = getItems(); + let dragonball; + const goSportsTeam = true; + let len; + + // good + const goSportsTeam = true; + const items = getItems(); + let dragonball; + let i; + let length; + ``` + + - [13.4](#13.4) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 + + > 为什么?`let` 和 `const` 是块级作用域而不是函数作用域。 + + ```javascript + // good + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + const name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // bad - unnecessary function call + function(hasName) { + const name = getName(); + + if (!hasName) { + return false; + } + + this.setFirstName(name); + + return true; + } + + // good + function(hasName) { + if (!hasName) { + return false; + } + + const name = getName(); + this.setFirstName(name); + + return true; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Hoisting + + - [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)相当重要。 + + ```javascript + // 我们知道这样运行不了 + // (假设 notDefined 不是全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 由于变量提升的原因, + // 在引用变量后再声明变量是可以运行的。 + // 注:变量的赋值 `true` 不会被提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 编译器会把函数声明提升到作用域的顶层, + // 这意味着我们的例子可以改写成这样: + function example() { + let declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + + // 使用 const 和 let + function example() { + console.log(declaredButNotAssigned); // => throws a ReferenceError + console.log(typeof declaredButNotAssigned); // => throws a ReferenceError + const declaredButNotAssigned = true; + } + ``` + + - [14.2](#14.2) 匿名函数表达式的变量名会被提升,但函数内容并不会。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function() { + console.log('anonymous function expression'); + }; + } + ``` + + - [14.3](#14.3) 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // the same is true when the function name + // is the same as the variable name. + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + - [14.4](#14.4) 函数声明的名称和函数体都会被提升。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 比较运算符 & 等号 + + - [15.1](#15.1) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. + - [15.2](#15.2) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **Objects 对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **Booleans 布尔值** 被计算为 **布尔的值** + + **Numbers 数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** + + **Strings 字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0] && []) { + // true + // An array is an object, objects evaluate to true + } + ``` + + - [15.3](#15.3) 使用简写。 + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - [15.4](#15.4) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 代码块 + + - [16.1](#16.1) 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function() { return false; } + + // good + function() { + return false; + } + ``` + + - [16.2](#16.2) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 注释 + + - [17.1](#17.1) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - [17.2](#17.2) 使用 `//` 作为单行注释,后放一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + const active = true; // is current tab + + // good + // is current tab + const active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + ``` + + - [17.3](#17.3) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - [17.4](#17.4) 使用 `// FIXME`: 标注问题。 + + ```javascript + class Calculator { + constructor() { + // FIXME: shouldn't use a global here + total = 0; + } + } + ``` + + - [17.5](#17.5) 使用 `// TODO`: 标注问题的解决方式。 + + ```javascript + class Calculator { + constructor() { + // TODO: total should be configurable by an options param + this.total = 0; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 空白 + + - [18.1](#18.1) 使用 2 个空格作为缩进。 + + ```javascript + // bad + function() { + ∙∙∙∙const name; + } + + // bad + function() { + ∙const name; + } + + // good + function() { + ∙∙const name; + } + ``` + + - [18.2](#18.2) 在花括号前放一个空格。 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + ``` + + - [18.3](#18.3) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + - [18.4](#18.4) 使用空格把运算符隔开。 + + ```javascript + // bad + const x=y+5; + + // good + const x = y + 5; + ``` + + - [18.5](#18.5) 在文件末尾插入一个空行。 + + ```javascript + // bad + (function(global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function(global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function(global) { + // ...stuff... + })(this);↵ + ``` + + - [18.5](#18.5) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + - [18.6](#18.6) 在块末和新语句前插入空行。 + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + const obj = { + foo() { + }, + bar() { + }, + }; + return obj; + + // good + const obj = { + foo() { + }, + + bar() { + }, + }; + + return obj; + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 逗号 + + - [19.1](#19.1) 行首逗号:**不需要**。 + + ```javascript + // bad + const story = [ + once + , upon + , aTime + ]; + + // good + const story = [ + once, + upon, + aTime, + ]; + + // bad + const hero = { + firstName: 'Ada' + , lastName: 'Lovelace' + , birthYear: 1815 + , superPower: 'computers' + }; + + // good + const hero = { + firstName: 'Ada', + lastName: 'Lovelace', + birthYear: 1815, + superPower: 'computers', + }; + ``` + + - [19.2](#19.2) 增加结尾的逗号: **需要**。 + + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 + + ```javascript + // bad - git diff without trailing comma + const hero = { + firstName: 'Florence', + - lastName: 'Nightingale' + + lastName: 'Nightingale', + + inventorOf: ['coxcomb graph', 'modern nursing'] + } + + // good - git diff with trailing comma + const hero = { + firstName: 'Florence', + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'], + } + + // bad + const hero = { + firstName: 'Dana', + lastName: 'Scully' + }; + + const heroes = [ + 'Batman', + 'Superman' + ]; + + // good + const hero = { + firstName: 'Dana', + lastName: 'Scully', + }; + + const heroes = [ + 'Batman', + 'Superman', + ]; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 分号 + + - [20.1](#20.1) **使用分号** + + ```javascript + // bad + (function() { + const name = 'Skywalker' + return name + })() + + // good + (() => { + const name = 'Skywalker'; + return name; + })(); + + // good (防止函数在两个 IIFE 合并时被当成一个参数) + ;(() => { + const name = 'Skywalker'; + return name; + })(); + ``` + + [Read more](http://stackoverflow.com/a/7365214/1712802). + +**[⬆ 返回目录](#table-of-contents)** + + +## 类型转换 + + - [21.1](#21.1) 在语句开始时执行类型转换。 + - [21.2](#21.2) 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + const totalScore = this.reviewScore + ''; + + // good + const totalScore = String(this.reviewScore); + ``` + + - [21.3](#21.3) 对数字使用 `parseInt` 转换,并带上类型转换的基数。 + + ```javascript + const inputValue = '4'; + + // bad + const val = new Number(inputValue); + + // bad + const val = +inputValue; + + // bad + const val = inputValue >> 0; + + // bad + const val = parseInt(inputValue); + + // good + const val = Number(inputValue); + + // good + const val = parseInt(inputValue, 10); + ``` + + - [21.4](#21.4) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * 使用 parseInt 导致我的程序变慢, + * 改成使用位操作转换数字快多了。 + */ + const val = inputValue >> 0; + ``` + + - [21.5](#21.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: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + - [21.6](#21.6) 布尔运算: + + ```javascript + const age = 0; + + // bad + const hasAge = new Boolean(age); + + // good + const hasAge = Boolean(age); + + // good + const hasAge = !!age; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 命名规则 + + - [22.1](#22.1) 避免单字母命名。命名应具备描述性。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - [22.2](#22.2) 使用驼峰式(Camel Case、小驼峰)命名变量、对象、函数和实例。 + + ```javascript + // bad + const OBJEcttsssss = {}; + const this_is_my_object = {}; + function c() {} + + // good + const thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + - [22.3](#22.3) 使用帕斯卡式(Pascal Case、大驼峰式)命名构造函数或类。 + + Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + const bad = new user({ + name: 'nope', + }); + + // good + class User { + constructor(options) { + this.name = options.name; + } + } + + const good = new User({ + name: 'yup', + }); + ``` + + - [22.4](#22.4) 使用下划线 `_` 开头命名私有属性。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + + // good + this._firstName = 'Panda'; + ``` + + - [22.5](#22.5) 别保存 `this` 的引用。使用箭头函数或 Function#bind。 + + ```javascript + // bad + function foo() { + const self = this; + return function() { + console.log(self); + }; + } + + // bad + function foo() { + const that = this; + return function() { + console.log(that); + }; + } + + // good + function foo() { + return () => { + console.log(this); + }; + } + ``` + + - [22.6](#22.6) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 + + ```javascript + // file contents + class CheckBox { + // ... + } + export default CheckBox; + + // in some other file + // bad + import CheckBox from './checkBox'; + + // bad + import CheckBox from './check_box'; + + // good + import CheckBox from './CheckBox'; + ``` + + - [22.7](#22.7) 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。 + + ```javascript + function makeStyleGuide() { + } + + export default makeStyleGuide; + ``` + + - [22.8](#22.8) 当你导出单例、函数库、空对象时使用帕斯卡式命名。 + + ```javascript + const AirbnbStyleGuide = { + es6: { + } + }; + + export default AirbnbStyleGuide; + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 存取器 + + - [23.1](#23.1) 属性的存取函数不是必须的。 + - [23.2](#23.2) 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - [23.3](#23.3) 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - [23.4](#23.4) 创建 `get()` 和 `set()` 函数是可以的,但要保持一致。 + + ```javascript + class Jedi { + constructor(options = {}) { + const lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + set(key, val) { + this[key] = val; + } + + get(key) { + return this[key]; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 事件 + + - [24.1](#24.1) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```javascript + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function(e, listingId) { + // do something with listingId + }); + ``` + + 更好的写法: + + ```javascript + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function(e, data) { + // do something with data.listingId + }); + ``` + + **[⬆ 返回目录](#table-of-contents)** + +## jQuery + + - [25.1](#25.1) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + ```javascript + // bad + const sidebar = $('.sidebar'); + + // good + const $sidebar = $('.sidebar'); + ``` + + - [25.2](#25.2) 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + const $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - [25.3](#25.3) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - [25.4](#25.4) 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + + // good (slower) + $sidebar.find('ul'); + + // good (faster) + $($sidebar[0]).find('ul'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + - [26.1](#26.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 6(ES 2015+)规范 + + - [27.1](#27.1) 以下是链接到 ES6 的各个特性的列表。 + +1. [Arrow Functions](#arrow-functions) +1. [Classes](#constructors) +1. [Object Shorthand](#es6-object-shorthand) +1. [Object Concise](#es6-object-concise) +1. [Object Computed Properties](#es6-computed-properties) +1. [Template Strings](#es6-template-literals) +1. [Destructuring](#destructuring) +1. [Default Parameters](#es6-default-parameters) +1. [Rest](#es6-rest) +1. [Array Spreads](#es6-array-spreads) +1. [Let and Const](#references) +1. [Iterators and Generators](#iterators-and-generators) +1. [Modules](#modules) + +**[⬆ 返回目录](#table-of-contents)** + + +## 测试 + + - [28.1](#28.1) **Yup.** + + ```javascript + function() { + return true; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + +**[⬆ 返回目录](#table-of-contents)** + + +## 资源 + +**Learning ES6** + + - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) + - [ExploringJS](http://exploringjs.com/) + - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) + - [Comprehensive Overview of ES6 Features](http://es6-features.org/) + +**Read This** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**Tools** + + - Code Style Linters + + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) + + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**Other Styleguides** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + +**Other Styles** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**阅读更多 Further Reading** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**Books** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke + +**Blogs** + + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + +**播客 Podcasts** + + - [JavaScript Jabber](http://devchat.tv/js-jabber/) + + +**[⬆ 返回目录](#table-of-contents)** + +## 使用人群 + + This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. + + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **Userify**: [userify/javascript](https://github.com/userify/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +**[⬆ 返回目录](#table-of-contents)** + +## 翻译 + + This style guide is also available in other languages: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [yuche/javascript](https://github.com/yuche/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + + +## JavaScript 编码规范说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + + +## 一起来讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + + + +## 贡献者 Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + + +## 许可 License + +(The MIT License) + +Copyright (c) 2014-2017 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 返回目录](#table-of-contents)** + + +## 修正案 Amendments + +We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts. + +# }; From 35647058a60bd416f5bfa0625134126ab288f61d Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Wed, 8 Feb 2017 19:13:12 +0800 Subject: [PATCH 05/33] =?UTF-8?q?es6=20=E4=B8=AD=E6=96=87=E7=89=88=20v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es6_zh-cn.md | 1759 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 1435 insertions(+), 324 deletions(-) diff --git a/es6_zh-cn.md b/es6_zh-cn.md index 562b05a33f..436f748832 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -2,11 +2,23 @@ # Airbnb JavaScript Style Guide() { -**用更合理的方式写 JavaScript** +**A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript** + +翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/zh-cn_v2.md)。 -翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Other Style Guides + - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) + - [React](react/) + - [CSS-in-JavaScript](css-in-javascript/) + - [CSS & Sass](https://github.com/airbnb/css) + - [CSS & Sass 中文版](https://github.com/webcoding/css-style-guide) + - [Ruby](https://github.com/airbnb/ruby) ## 目录 @@ -19,7 +31,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z 1. [字符串](#strings) 1. [函数](#functions) 1. [箭头函数](#arrow-functions) - 1. [构造函数](#constructors) + 1. [类 & 构造函数](#classes--constructors) 1. [模块](#modules) 1. [Iterators & Generators ](#iterators-and-generators) 1. [属性](#properties) @@ -37,7 +49,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z 1. [事件](#events) 1. [jQuery](#jquery) 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) - 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) + 1. [ECMAScript 6 (ES 2015+) 编码规范](#ecmascript-6-styles) 1. [测试](#testing) 1. [性能](#performance) 1. [资源](#resources) @@ -51,7 +63,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 类型 - - [1.1](#1.1) **基本类型**: 直接存取基本类型。 + + - [1.1](#types--primitives) **基本类型**: 直接存取基本类型。 + `string` 字符串 + `number` 数值 @@ -67,7 +80,9 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z console.log(foo, bar); // => 1, 9 ``` - - [1.2](#1.2) **复制类型**: 通过引用的方式存取复杂类型。 + + + - [1.2](#types--complex) **复杂类型**: 通过引用的方式存取复杂类型。 + `object` 对象 + `array` 数组 @@ -87,9 +102,11 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 引用 - - [2.1](#2.1) 对所有的引用使用 `const` ;不要使用 `var`。 + + - [2.1](#references--prefer-const) 对所有的引用使用 `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) - > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 + > 为什么? 这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 ```javascript // bad @@ -101,9 +118,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const b = 2; ``` - - [2.2](#2.2) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + + - [2.2](#references--disallow-var) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) + jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) - > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 + > 为什么? 因为 `let` 是块级作用域,而 `var` 是函数作用域。 ```javascript // bad @@ -119,7 +139,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [2.3](#2.3) 注意 `let` 和 `const` 都是块级作用域。 + + - [2.3](#references--block-scope) 注意 `let` 和 `const` 都是块级作用域。 ```javascript // const 和 let 只存在于它们被定义的区块内。 @@ -136,7 +157,9 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 对象 - - [3.1](#3.1) 使用字面值创建对象。 + + - [3.1](#objects--no-new) 使用字面值创建对象。 + eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) ```javascript // bad @@ -145,7 +168,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // good const item = {}; ``` - + **************** - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 ```javascript @@ -180,11 +203,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z type: 'alien', }; ``` + ************* - - - [3.4](#3.4) 创建有动态属性名的对象时,使用可被计算的属性名称。 + + - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 - > 为什么?因为这样可以让你在一个地方定义所有的对象属性。 + > 为什么? 因为这样可以让你在一个地方定义所有的对象属性。 ```javascript function getKey(k) { @@ -206,8 +230,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; ``` - - - [3.5](#3.5) 使用对象方法的简写。 + + - [3.3](#es6-object-shorthand) 使用对象方法的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) ```javascript // bad @@ -229,10 +255,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; ``` - - - [3.6](#3.6) 使用对象属性值的简写。 + + - [3.4](#es6-object-concise) 使用对象属性值的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) - > 为什么?因为这样更短更有描述性。 + > 为什么? 因为这样更短更有描述性。 ```javascript const lukeSkywalker = 'Luke Skywalker'; @@ -248,9 +276,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; ``` - - [3.7](#3.7) 在对象属性声明前把简写的属性分组。 + + - [3.5](#objects--grouped-shorthand) 在对象属性声明前把简写的属性分组。 - > 为什么?因为这样能清楚地看出哪些属性使用了简写。 + > 为什么? 因为这样能清楚地看出哪些属性使用了简写。 ```javascript const anakinSkywalker = 'Anakin Skywalker'; @@ -277,12 +306,77 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; ``` + + - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. + eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) + jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) + + > 为什么? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. + + ```javascript + // bad + const bad = { + 'foo': 3, + 'bar': 4, + 'data-blah': 5, + }; + + // good + const good = { + foo: 3, + bar: 4, + 'data-blah': 5, + }; + ``` + + + - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. + + > 为什么? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). + + ```javascript + // bad + console.log(object.hasOwnProperty(key)); + + // good + console.log(Object.prototype.hasOwnProperty.call(object, key)); + + // best + const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. + /* or */ + import has from 'has'; + … + console.log(has.call(object, key)); + ``` + + + - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted. + + ```javascript + // very bad + const original = { a: 1, b: 2 }; + const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ + delete copy.a; // so does this + + // bad + const original = { a: 1, b: 2 }; + const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } + + // good + const original = { a: 1, b: 2 }; + const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } + + const { a, ...noA } = copy; // noA => { b: 2, c: 3 } + ``` + **[⬆ 返回目录](#table-of-contents)** ## 数组 - - [4.1](#4.1) 使用字面值创建数组。 + + - [4.1](#arrays--literals) 使用字面值创建数组。 + eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) ```javascript // bad @@ -292,12 +386,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const items = []; ``` - - [4.2](#4.2) 向数组添加元素时使用 Arrary#push 替代直接赋值。 + + - [4.2](#arrays--push) 向数组添加元素时使用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 替代直接赋值。 ```javascript const someStack = []; - // bad someStack[someStack.length] = 'abracadabra'; @@ -305,8 +399,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z someStack.push('abracadabra'); ``` - - - [4.3](#4.3) 使用拓展运算符 `...` 复制数组。 + + - [4.3](#es6-array-spreads) 使用拓展运算符 `...` 复制数组。 ```javascript // bad @@ -322,30 +416,85 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const itemsCopy = [...items]; // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - // good + // old itemsCopy = items.slice(); ``` - - [4.4](#4.4) 使用 Array#from 把一个类数组对象转换成数组。 + + + - [4.4](#arrays--from) 使用 [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 把一个类数组对象转换成数组。 ```javascript const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo); // ES5 中使用slice将类数组的对象转成数组. + // old function trigger() { var args = Array.prototype.slice.call(arguments); ... } ``` + + - [4.5](#arrays--callback-return) 在回调函数中使用 return 语句。如果函数体只由一个单独的语句组成,可以省略 return 关键字 [8.2](#8.2). + eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) + + ```javascript + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map(x => x + 1); + + // bad + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + }); + + // good + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + return flatten; + }); + + // bad + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } else { + return false; + } + }); + + // good + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } + + return false; + }); + ``` + **[⬆ 返回目录](#table-of-contents)** ## 解构 - - [5.1](#5.1) 使用解构存取和使用多属性对象。 + + - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 + jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) - > 为什么?因为解构能减少临时引用属性。 + > 为什么? 因为解构能减少临时引用属性。 ```javascript // bad @@ -357,8 +506,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } // good - function getFullName(obj) { - const { firstName, lastName } = obj; + function getFullName(user) { + const { firstName, lastName } = user; return `${firstName} ${lastName}`; } @@ -368,7 +517,9 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [5.2](#5.2) 对数组使用解构赋值。 + + - [5.2](#destructuring--array) 对数组使用解构赋值。 + jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) ```javascript const arr = [1, 2, 3, 4]; @@ -381,8 +532,11 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const [first, second] = arr; ``` - - [5.3](#5.3) 需要回传多个值时,使用对象解构,而不是数组解构。 - > 为什么?增加属性或者改变排序不会改变调用时的位置。 + + - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 + jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) + + > 为什么? 增加属性或者改变排序不会改变调用时的位置。 ```javascript // bad @@ -401,7 +555,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } // 调用时只选择需要的数据 - const { left, right } = processInput(input); + const { left, top } = processInput(input); ``` @@ -410,39 +564,52 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## Strings - - [6.1](#6.1) 字符串使用单引号 `''` 。 + + - [6.1](#strings--quotes) 字符串使用单引号 `''` 。 + eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) + jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) ```javascript // bad const name = "Capt. Janeway"; + // bad - template literals should contain interpolation or newlines + const name = `Capt. Janeway`; + // good const name = 'Capt. Janeway'; ``` - - [6.2](#6.2) 字符串超过 80 个字节应该使用字符串连接号换行。此处可以更多个字符,目前编辑界面越来越大了 - - [6.3](#6.3) 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + - [6.2](#strings--line-length) 字符串超过 100 个字节应该使用字符串连接号换行。此处可以更多个字符200或300,目前编辑界面越来越大了 - ```javascript - // bad - 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.'; + > 为什么? 切断长字符串,可以更好编码和搜索。 + 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript // bad 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.'; - // good + // bad 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.'; + + // good + 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.'; ``` - - - [6.4](#6.4) 程序化生成字符串时,使用模板字符串代替字符串连接。 - > 为什么?模板字符串更为简洁,更具可读性。 + + - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 + 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) + + > 为什么? 模板字符串插值更为简洁,更具可读性。 ```javascript // bad @@ -455,45 +622,81 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z return ['How are you, ', name, '?'].join(); } + // bad + function sayHi(name) { + return `How are you, ${ name }?`; + } + // good function sayHi(name) { return `How are you, ${name}?`; } ``` + + - [6.4](#strings--eval) 在字符串中永不使用 `eval()`, 它会导致很多漏洞。 + + + - [6.5](#strings--escaping) 字符串中不要使用不必要的转义。 + eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) + + > 为什么? 反斜杠会降低可读性,应该在必要时才去使用它。 + + ```javascript + // bad + const foo = '\'this\' \i\s \"quoted\"'; + + // good + const foo = '\'this\' is "quoted"'; + const foo = `'this' is "quoted"`; + ``` + **[⬆ 返回目录](#table-of-contents)** ## 函数 - - [7.1](#7.1) 使用函数声明代替函数表达式。 + - [7.1](#7.1) 使用函数表达式,而不是函数声明。 + eslint: [`func-style`](http://eslint.org/docs/rules/func-style) + jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) - > 为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得[箭头函数](#arrow-functions)可以取代函数表达式。 + > 为什么? 函数声明会把整个函数提升(hoisted),这导致非常容易在定义以前就被引用,这会降低可读性以及维护性(而函数表达式只会把函数的引用变量名提升)。如果发现一个函数定义非常大或复杂,会干扰其他逻辑的理解,此时也许是时候把它提取成独立模块了。 + > 另外不要忘记给匿名表达式命名,匿名函数会使错误堆栈中跟踪问题更加困难。 + > 函数提升规则,使得[箭头函数](#arrow-functions)可以取代函数表达式。 ```javascript // bad 匿名函数表达式 var anonymous = function() () { }; - // bad 有名函数表达式 - var named = function named() { - }; - // good + // bad function foo() { } + + // good + const foo = function named() { + }; ``` - - [7.2](#7.2) 函数表达式: + + - [7.2](#functions--iife) 用括号包裹 立即调用函数表达式(IIFE)。 + eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) + jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) ```javascript // 立即调用的函数表达式 (IIFE) + // 可使用箭头函数 (() => { console.log('Welcome to the Internet. Please follow me.'); - })(); + }()); ``` - - [7.3](#7.3) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 - - [7.4](#7.4) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + - [7.3](#functions--in-blocks) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) + + + - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 ```javascript // bad @@ -512,7 +715,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [7.5](#7.5) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 + + - [7.5](#functions--arguments-shadow) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 ```javascript // bad @@ -526,10 +730,11 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - - [7.6](#7.6) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + + - [7.6](#es6-rest) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) - > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 + > 为什么? 使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 ```javascript // bad @@ -544,8 +749,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - - [7.7](#7.7) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 + + - [7.7](#es6-default-parameters) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 ```javascript // really bad @@ -573,99 +778,310 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [7.8](#7.8) 直接给函数参数赋值时需要避免副作用。 + + - [7.8](#functions--default-side-effects) 函数参数设置默认值,要避免副作用。 + + > 为什么? 因为这样会让人感到很困惑。 + + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` + + + - [7.9](#functions--defaults-last) 总是把默认参数最后。 + + ```javascript + // bad + function handleThings(opts = {}, name) { + // ... + } + + // good + function handleThings(name, opts = {}) { + // ... + } + ``` + + + - [7.10](#functions--constructor) 永远不要使用构造函数创建新函数。 + eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) - > 为什么?因为这样的写法让人感到很困惑。 + > 为什么? 以这种方式创建一个函数,类似 eval(),会导致很多漏洞。 + + ```javascript + // bad + var add = new Function('a', 'b', 'return a + b'); + + // still bad + var subtract = Function('a', 'b', 'return a - b'); + ``` + + + - [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) + + > 为什么? 一致性好,当添加或移除名称时,不必添加或移除空间。 + + ```javascript + // bad + const f = function(){}; + const g = function (){}; + const h = function() {}; + + // good + const x = function () {}; + const y = function a() {}; + ``` - ```javascript - var b = 1; - // bad - function count(a = b++) { - console.log(a); - } - count(); // 1 - count(); // 2 - count(3); // 3 - count(); // 3 - ``` + + - [7.12](#functions--mutate-params) 不要使用变异参数。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + > 为什么? 操纵传入的参数,在原调用者上可能会导致不必要的变量副作用。 + + ```javascript + // bad + function f1(obj) { + obj.key = 1; + }; + + // good + function f2(obj) { + const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; + }; + ``` + + + - [7.13](#functions--reassign-params) 不要给形参重新赋值。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + + > 为什么? 这样做可能会导致意外行为,特别是操作 `arguments` 参数。这也会导致优化问题,特别是使用 V8 解析器. + + ```javascript + // bad + function f1(a) { + a = 1; + } + + function f2(a) { + if (!a) { a = 1; } + } + + // good + function f3(a) { + const b = a || 1; + } + + function f4(a = 1) { + } + ``` + + + - [7.14](#functions--spread-vs-apply) 使用拓展操作符 `...` 调用可变参函数。 + eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) + + > 为什么? 更简洁、并且也不用提供上下文作用域,而使用 `new` 和 `apply` 也没这个容易。 + + ```javascript + // bad + const x = [1, 2, 3, 4, 5]; + console.log.apply(console, x); + + // good + const x = [1, 2, 3, 4, 5]; + console.log(...x); + + // bad + new (Function.prototype.bind.apply(Date, [null, 2016, 08, 05])); + + // good + new Date(...[2016, 08, 05]); + ``` **[⬆ 返回目录](#table-of-contents)** ## 箭头函数 - - [8.1](#8.1) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + + - [8.1](#arrows--use-them) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + 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) - > 为什么?因为箭头函数创造了新的一个 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 + > 为什么? 因为箭头函数会创建一个你通常最想要的 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 - > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 + > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 ```javascript // bad - ;[1, 2, 3].map(function (x) { - return x * x; + [1, 2, 3].map(function (x) { + const y = x + 1; + return x * y; }); // good - ;[1, 2, 3].map((x) => { - return x * x; + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; }); ``` - - [8.2](#8.2) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 + + - [8.2](#arrows--implicit-return) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 - > 为什么?语法糖。在链式调用中可读性很高。 + > 为什么? 这是语法糖。在链式调用中可读性很高。 > 为什么不?当你打算回传一个对象的时候。 ```javascript + // bad + [1, 2, 3].map(number => { + const nextNumber = number + 1; + `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map(number => `A string containing the ${number}.`); + + // good + [1, 2, 3].map((number) => { + const nextNumber = number + 1; + return `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map((number, index) => ({ + [index]: number + })); + ``` + + + - [8.3](#arrows--paren-wrap) 如果表达式有多行,使用圆括号包裹,提高可读性。 + + > 为什么? 这样能更清楚的看到开始和结束位置。 + + ```js + // bad + ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + ); + + // good + ['get', 'post', 'put'].map(httpMethod => ( + Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + )); + ``` + + + - [8.4](#arrows--one-arg-parens) 如果函数只有一个参数就不要使用圆括号,否则总是用圆括号包裹参数。 + eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) + jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) + + > 为什么? 更少的视觉干扰 + + ```js + // bad + [1, 2, 3].map((x) => x * x); + // good - ;[1, 2, 3].map(x => x * x); + [1, 2, 3].map(x => x * x); // good - ;[1, 2, 3].reduce((total, n) => { + [1, 2, 3].reduce((total, n) => { return total + n; }, 0); + + // good + [1, 2, 3].map(number => ( + `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` + )); + + // bad + [1, 2, 3].map(x => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + + + - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) + eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) + + ```js + // bad + const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; + + // bad + const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; + + // good + const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); + + // good + const itemHeight = (item) => { + const { height, largeSize, smallSize } = item; + return height > 256 ? largeSize : smallSize; + }; ``` **[⬆ 返回目录](#table-of-contents)** - -## 构造器 + +## 类 & 构造器 - - [9.1](#9.1) 总是使用 `class`。避免直接操作 `prototype` 。 + + - [9.1](#constructors--use-class) 总是使用 `class`。避免直接操作 `prototype` 。 - > 为什么? 因为 `class` 语法更为简洁更易读。 + > 为什么? 因为 `class` 语法更为简洁更易读。 ```javascript // bad function Queue(contents = []) { - this._queue = [...contents]; + this.queue = [...contents]; } - Queue.prototype.pop = function() { - const value = this._queue[0]; - this._queue.splice(0, 1); + Queue.prototype.pop = function () { + const value = this.queue[0]; + this.queue.splice(0, 1); return value; - } + }; // good class Queue { constructor(contents = []) { - this._queue = [...contents]; + this.queue = [...contents]; } pop() { - const value = this._queue[0]; - this._queue.splice(0, 1); + const value = this.queue[0]; + this.queue.splice(0, 1); return value; } } ``` - - [9.2](#9.2) 使用 `extends` 继承。 + + - [9.2](#constructors--extends) 使用 `extends` 继承。 - > 为什么?因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 ```javascript // bad @@ -674,28 +1090,29 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z Queue.apply(this, contents); } inherits(PeekableQueue, Queue); - PeekableQueue.prototype.peek = function() { - return this._queue[0]; + PeekableQueue.prototype.peek = function () { + return this.queue[0]; } // good class PeekableQueue extends Queue { peek() { - return this._queue[0]; + return this.queue[0]; } } ``` - - [9.3](#9.3) 方法可以返回 `this` 来帮助链式调用。 + + - [9.3](#constructors--chaining) 方法可以返回 `this` 以便于链式调用。 ```javascript // bad - Jedi.prototype.jump = function() { + Jedi.prototype.jump = function () { this.jumping = true; return true; }; - Jedi.prototype.setHeight = function(height) { + Jedi.prototype.setHeight = function (height) { this.height = height; }; @@ -723,7 +1140,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ``` - - [9.4](#9.4) 可以写一个自定义的 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 + + - [9.4](#constructors--tostring) 可以自定义 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 ```javascript class Jedi { @@ -741,77 +1159,247 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` + + - [9.5](#constructors--no-useless) 类有默认构造函数,书写一个空的构造函数是没必要的。 + eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) + + ```javascript + // bad + class Jedi { + constructor() {} + + getName() { + return this.name; + } + } + + // bad + class Rey extends Jedi { + constructor(...args) { + super(...args); + } + } + + // good + class Rey extends Jedi { + constructor(...args) { + super(...args); + this.name = 'Rey'; + } + } + ``` + + + - [9.6](#classes--no-duplicate-members) 避免类成员重复。 + eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) + + > 为什么? 重复的类成员默认会使用最后一个——这肯定有一个错误存在。 + + ```javascript + // bad + class Foo { + bar() { return 1; } + bar() { return 2; } + } + + // good + class Foo { + bar() { return 1; } + } + + // good + class Foo { + bar() { return 2; } + } + ``` + **[⬆ 返回目录](#table-of-contents)** ## 模块 - - [10.1](#10.1) 总是使用模组 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + - [10.1](#modules--use-them) 总是使用模块 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + > 为什么? 模块就是未来,让我们开始迈向未来吧。 + > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) + + ```javascript + // bad + const AirbnbStyleGuide = require('./AirbnbStyleGuide'); + module.exports = AirbnbStyleGuide.es6; + + // ok + import AirbnbStyleGuide from './AirbnbStyleGuide'; + export default AirbnbStyleGuide.es6; + + // best + import { es6 } from './AirbnbStyleGuide'; + export default es6; + + // ES5中要注意 + // old + // - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + // - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + // - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + // - 总是在模块顶部声明 `'use strict';` + ``` + + + - [10.2](#modules--no-wildcard) 不要使用通配符 import。 + + > 为什么? 这样能确保你只有一个默认 export。 + + ```javascript + // bad + import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + + // good + import AirbnbStyleGuide from './AirbnbStyleGuide'; + ``` + + + - [10.3](#modules--no-export-from-import) 不要从 import 中直接 export。 + + > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + ```javascript + // bad + // filename es6.js + export { es6 as default } from './AirbnbStyleGuide'; + + // good + // filename es6.js + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + + + - [10.4](#modules--no-duplicate-imports) 一个模块只要一个 `import` 。 + eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) + + > 为什么? 同一模块多个 `import` 导入,会使代码更难维护。 + + ```javascript + // bad + import foo from 'foo'; + // … some other imports … // + import { named1, named2 } from 'foo'; + + // good + import foo, { named1, named2 } from 'foo'; + + // good + import foo, { + named1, + named2, + } from 'foo'; + ``` + + + - [10.5](#modules--no-mutable-exports) 禁止 `export` 可写值输出,使用常量(Functions/Classes 暂时例外)。 + eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) + + > 为什么? 避免全局使用时出现问题,除了某些特定情况下确实要这样用。一般来说,`export` 应该输出常量。 + + ```javascript + // bad + var count = 3; + export { count } + + // bad + let foo = 3; + export { foo } + + // good + const foo = 3; + export { foo } - > 为什么?模块就是未来,让我们开始迈向未来吧。 + // valid + export const count = 1 + export function getCount () {} + export class Counter {} + ``` - **ES5中要注意** + + - [10.6](#modules--prefer-default-export) 单出口模块,更倾向设置为默认出口。 +eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) - - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 - - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 - - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 - - 总是在模块顶部声明 `'use strict';` + ```javascript + // bad + export function foo() {} - **ES6要注意** + // good + export default function foo() {} + ``` - - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 - - 默认已开启 `'use strict';` + + - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 +eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) + + > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 ```javascript // bad - const AirbnbStyleGuide = require('./AirbnbStyleGuide'); - module.exports = AirbnbStyleGuide.es6; + import foo from 'foo'; + foo.init(); - // ok - import AirbnbStyleGuide from './AirbnbStyleGuide'; - export default AirbnbStyleGuide.es6; + import bar from 'bar'; - // best - import { es6 } from './AirbnbStyleGuide'; - export default es6; + // good + import foo from 'foo'; + import bar from 'bar'; + + foo.init(); ``` - - [10.2](#10.2) 不要使用通配符 import。 + + - [10.8](#modules--multiline-imports-over-newlines) 多 `import` 入口,代码书写格式应与 `array` 和 `object` 保持一致。 - > 为什么?这样能确保你只有一个默认 export。 + > 为什么? 大括号代码块、缩进以及逗号都保持风格一致 ```javascript // bad - import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good - import AirbnbStyleGuide from './AirbnbStyleGuide'; + import { + longNameA, + longNameB, + longNameC, + longNameD, + longNameE, + } from 'path'; ``` - - [10.3](#10.3) 不要从 import 中直接 export。 - - > 为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 +eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) + + > 为什么? 加载器语法要在 `webpack.config.js` 中使用 ```javascript // bad - // filename es6.js - export { es6 as default } from './airbnbStyleGuide'; + import fooSass from 'css!sass!foo.scss'; + import barCss from 'style!css!bar.css'; // good - // filename es6.js - import { es6 } from './AirbnbStyleGuide'; - export default es6; + import fooSass from 'foo.scss'; + import barCss from 'bar.css'; ``` **[⬆ 返回目录](#table-of-contents)** -## Iterators and Generators +## Iterators and Generators 迭代器 & 生成器 - - [11.1](#11.1) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-of`。 + + - [11.1](#iterators--nope) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-in` or `for-of`。 + eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](http://eslint.org/docs/rules/no-restricted-syntax) - > 为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + > 为什么? 这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + > 使用迭代器 `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... 遍历数组,使用 `Object.keys()` / `Object.values()` / `Object.entries()` 遍历对象。 ```javascript const numbers = [1, 2, 3, 4, 5]; @@ -824,7 +1412,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // good let sum = 0; - numbers.forEach((num) => sum += num); + numbers.forEach(num => sum += num); sum === 15; // best (use the functional force) @@ -832,16 +1420,65 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z sum === 15; ``` - - [11.2](#11.2) 现在还不要使用 generators。 + + - [11.2](#generators--nope) 现在还不要使用生成器 generators。 + + > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + + + - [11.3](#generators--spacing) 如果你必须使用generators 或无视[generators 忠告](#generators--nope),那么请务必确保函数签名(function*)是适当的间距。 + eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) + + > 为什么? `function` and `*` 是同一个关键词的不同组成部分—— `*` 不是 `function` 修饰符, `function*` 是一种特定结构, 不同于 `function`。 + + ```js + // bad + function * foo() { + } + + const bar = function * () { + } + + const baz = function *() { + } + + const quux = function*() { + } + + function*foo() { + } + + function *foo() { + } + + // very bad + function + * + foo() { + } + + const wat = function + * + () { + } + + // good + function* foo() { + } - > 为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + const foo = function* () { + } + ``` **[⬆ 返回目录](#table-of-contents)** ## 属性 - - [12.1](#12.1) 使用 `.` 来访问对象的属性。 + + - [12.1](#properties--dot) 使用 `.` 符号来访问对象的属性。 + eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) + jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) ```javascript const luke = { @@ -856,7 +1493,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const isJedi = luke.jedi; ``` - - [12.2](#12.2) 当通过变量访问属性时使用中括号 `[]`。 + + - [12.2](#properties--bracket) 当通过变量访问属性时使用中括号 `[]`。 ```javascript const luke = { @@ -876,7 +1514,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 变量 - - [13.1](#13.1) 一直使用 `const` 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。) + + - [13.1](#variables--const) 总是使用 `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) ```javascript // bad @@ -886,9 +1527,11 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const superPower = new SuperPower(); ``` - - [13.2](#13.2) 使用 `const` 声明每一个变量。 + + - [13.2](#variables--one-const) 使用 `const` 声明每一个变量。 + eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) - > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + > 为什么? 增加新变量将更容易,而且你永远不用再担心调换错 `;` or `,` ,并且 diff 工具下更少干扰。同时在 debugger 时,可以单步调试而不会一次跳过他们所有变量。 ```javascript // bad @@ -908,9 +1551,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const dragonball = 'z'; ``` - - [13.3](#13.3) 将所有的 `const` 和 `let` 分组 + + - [13.3](#variables--const-let-group) 将所有的 `const` 和 `let` 分组 - > 为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。 + > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 ```javascript // bad @@ -933,51 +1577,111 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z let length; ``` - - [13.4](#13.4) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 + + - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 - > 为什么?`let` 和 `const` 是块级作用域而不是函数作用域。 + > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 ```javascript - // good - function() { - test(); - console.log('doing stuff..'); - - //..other stuff.. - + // bad - unnecessary function call + function checkName(hasName) { const name = getName(); + if (hasName === 'test') { + return false; + } + if (name === 'test') { + this.setName(''); return false; } return name; } - // bad - unnecessary function call - function(hasName) { + // good + function checkName(hasName) { + if (hasName === 'test') { + return false; + } + const name = getName(); - if (!hasName) { + if (name === 'test') { + this.setName(''); return false; } - this.setFirstName(name); - - return true; + return name; } + ``` + + + - [13.5](#variables--no-chain-assignment) 不使用链接变量赋值。 + + > 为什么? 链接变量赋值会创建隐式全局变量。 + + ```javascript + // bad + (function example() { + // JavaScript interprets this as + // let a = ( b = ( c = 1 ) ); + // The let keyword only applies to variable a; variables b and c become + // global variables. + let a = b = c = 1; + }()); + + console.log(a); // undefined + console.log(b); // 1 + console.log(c); // 1 // good - function(hasName) { - if (!hasName) { - return false; + (function example() { + let a = 1; + let b = a; + let c = a; + }()); + + console.log(a); // undefined + console.log(b); // undefined + console.log(c); // undefined + + // the same applies for `const` + ``` + + + - [13.6](#variables--unary-increment-decrement) 便面使用一元递增和递减操作 (++, --) 。 + eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) + + > 为什么? 一元递增和递减语句默认在自动分号插入处理下会有错误问题。而且它还会出现 `num += 1` 替代 `num ++` 的情况。不允许使用它们,也可以代码更健壮。 + + ```javascript + // bad + + let array = [1, 2, 3]; + let num = 1; + num ++; + -- num; + + let sum = 0; + let truthyCount = 0; + for(let i = 0; i < array.length; i++){ + let value = array[i]; + sum += value; + if (value) { + truthyCount++; + } } - const name = getName(); - this.setFirstName(name); + // good - return true; - } + let array = [1, 2, 3]; + let num = 1; + num += 1; + num -= 1; + + const sum = array.reduce((a, b) => a + b, 0); + const truthyCount = array.filter(Boolean).length; ``` **[⬆ 返回目录](#table-of-contents)** @@ -985,18 +1689,18 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## Hoisting - - [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)相当重要。 + + - [14.1](#hoisting--about) `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)相当重要。 ```javascript - // 我们知道这样运行不了 - // (假设 notDefined 不是全局变量) + // 我们知道这样运行不了(假设 notDefined 不是全局变量) function example() { console.log(notDefined); // => throws a ReferenceError } // 由于变量提升的原因, // 在引用变量后再声明变量是可以运行的。 - // 注:变量的赋值 `true` 不会被提升。 + // **注意:** 变量赋值 `true` 的操作不会被提升。 function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; @@ -1018,7 +1722,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [14.2](#14.2) 匿名函数表达式的变量名会被提升,但函数内容并不会。 + + - [14.2](#hoisting--anon-expressions) 匿名函数表达式的变量名会被提升,但函数体并不会。 ```javascript function example() { @@ -1026,13 +1731,14 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z anonymous(); // => TypeError anonymous is not a function - var anonymous = function() { + var anonymous = function () { console.log('anonymous function expression'); }; } ``` - - [14.3](#14.3) 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。 + + - [14.3](#hoisting--named-expresions) 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。 ```javascript function example() { @@ -1060,7 +1766,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [14.4](#14.4) 函数声明的名称和函数体都会被提升。 + + - [14.4](#hoisting--declarations) 函数声明的名称和函数体都会被提升。 ```javascript function example() { @@ -1072,6 +1779,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` + - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/). + - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 **[⬆ 返回目录](#table-of-contents)** @@ -1079,8 +1788,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 比较运算符 & 等号 - - [15.1](#15.1) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. - - [15.2](#15.2) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + - [15.1](#comparison--eqeqeq) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=` 。 + eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html) + + + - [15.2](#comparison--if) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + **Objects 对象** 被计算为 **true** + **Undefined** 被计算为 **false** @@ -1092,42 +1805,144 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ```javascript if ([0] && []) { // true - // An array is an object, objects evaluate to true + // an array (even an empty one) is an object, objects will evaluate to true } ``` - - [15.3](#15.3) 使用简写。 + + - [15.3](#comparison--shortcuts) 使用简写。 ```javascript // bad - if (name !== '') { + if (isValid === true) { // ...stuff... } // good - if (name) { + if (isValid) { // ...stuff... } // bad - if (collection.length > 0) { + if (name) { // ...stuff... } // good + if (name !== '') { + // ...stuff... + } + + // bad if (collection.length) { // ...stuff... } + + // good + if (collection.length > 0) { + // 为什么没用简写??? + // ...stuff... + } + ``` + + + - [15.4](#comparison--moreinfo) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + + + - [15.5](#comparison--switch-blocks) `case` 以及 `default` 情况下使用括号会创建块级作用域 (e.g. `let`, `const`, `function`, and `class`). + + > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 + + eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). + + ```javascript + // ??? + // bad + switch (foo) { + case 1: + let x = 1; + break; + case 2: + const y = 2; + break; + case 3: + function f() {} + break; + default: + class C {} + } + + // good + switch (foo) { + case 1: { + let x = 1; + break; + } + case 2: { + const y = 2; + break; + } + case 3: { + function f() {} + break; + } + case 4: + bar(); + break; + default: { + class C {} + } + } + ``` + + + - [15.6](#comparison--nested-ternaries) 别嵌套使用三元表达式,要单个使用。 + + eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). + + ```javascript + // bad + const foo = maybe1 > maybe2 + ? "bar" + : value1 > value2 ? "baz" : null; + + // better + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 + ? 'bar' + : maybeNull; + + // best + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 ? 'bar' : maybeNull; ``` - - [15.4](#15.4) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + + - [15.7](#comparison--unneeded-ternary) 避免不必要使用三元表达式 + + eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). + + ```javascript + // bad + const foo = a ? a : b; + const bar = c ? true : false; + const baz = c ? false : true; + + // good + const foo = a || b; + const bar = !!c; + const baz = !c; + ``` **[⬆ 返回目录](#table-of-contents)** ## 代码块 - - [16.1](#16.1) 使用大括号包裹所有的多行代码块。 + + - [16.1](#blocks--braces) 使用大括号包裹所有的多行代码块。 ```javascript // bad @@ -1143,15 +1958,16 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } // bad - function() { return false; } + function foo() { return false; } // good - function() { + function bar() { return false; } ``` - - [16.2](#16.2) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + - [16.2](#blocks--cuddled-elses) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 ```javascript // bad @@ -1178,7 +1994,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 注释 - - [17.1](#17.1) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + - [17.1](#comments--multiline) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 ```javascript // bad @@ -1197,7 +2014,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // good /** * make() returns a new element - * based on the passed in tag name + * based on the passed-in tag name * * @param {String} tag * @return {Element} element @@ -1210,7 +2027,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [17.2](#17.2) 使用 `//` 作为单行注释,后放一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + - [17.2](#comments--singleline) 单行注释使用 `//`,后紧跟一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 ```javascript // bad @@ -1238,26 +2056,41 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z return type; } + + // also good + function getType() { + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } ``` - - [17.3](#17.3) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - [17.3](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 - - [17.4](#17.4) 使用 `// FIXME`: 标注问题。 + + - [17.4](#comments--fixme) 使用 `// FIXME`: 标注问题。 ```javascript - class Calculator { + class Calculator extends Abacus { constructor() { + super(); + // FIXME: shouldn't use a global here total = 0; } } ``` - - [17.5](#17.5) 使用 `// TODO`: 标注问题的解决方式。 + + - [17.5](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 ```javascript - class Calculator { + class Calculator extends Abacus { constructor() { + super(); + // TODO: total should be configurable by an options param this.total = 0; } @@ -1269,26 +2102,32 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 空白 - - [18.1](#18.1) 使用 2 个空格作为缩进。 + + - [18.1](#whitespace--spaces) 使用 2 个空格作为缩进。 + eslint: [`indent`](http://eslint.org/docs/rules/indent.html) + jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) ```javascript // bad - function() { + function foo() { ∙∙∙∙const name; } // bad - function() { + function bar() { ∙const name; } // good - function() { + function baz() { ∙∙const name; } ``` - - [18.2](#18.2) 在花括号前放一个空格。 + + - [18.2](#whitespace--before-blocks) 在花括号前放一个空格。 + eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) + jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) ```javascript // bad @@ -1314,7 +2153,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }); ``` - - [18.3](#18.3) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + - [18.3](#whitespace--around-keywords) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) + jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) ```javascript // bad @@ -1338,7 +2180,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [18.4](#18.4) 使用空格把运算符隔开。 + + - [18.4](#whitespace--infix-ops) 使用空格把运算符隔开。 + 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) ```javascript // bad @@ -1348,18 +2193,20 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const x = y + 5; ``` - - [18.5](#18.5) 在文件末尾插入一个空行。 + + - [18.5](#whitespace--newline-at-end) 在文件末尾插入一个空行。 + eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) ```javascript // bad - (function(global) { + (function (global) { // ...stuff... })(this); ``` ```javascript // bad - (function(global) { + (function (global) { // ...stuff... })(this);↵ ↵ @@ -1367,12 +2214,14 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ```javascript // good - (function(global) { + (function (global) { // ...stuff... })(this);↵ ``` - - [18.5](#18.5) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 + + - [18.6](#whitespace--chains) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 + 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) ```javascript // bad @@ -1395,7 +2244,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z .updateCount(); // bad - const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) + const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led); @@ -1409,9 +2258,14 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z .append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led); + + // good + const leds = stage.selectAll('.led').data(data); ``` - - [18.6](#18.6) 在块末和新语句前插入空行。 + + - [18.7](#whitespace--after-blocks) 在块末和新语句前插入空行。 + jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) ```javascript // bad @@ -1446,15 +2300,149 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; return obj; + + // bad + const arr = [ + function foo() { + }, + function bar() { + }, + ]; + return arr; + + // good + const arr = [ + function foo() { + }, + + function bar() { + }, + ]; + + return arr; + ``` + + + - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) + + ```javascript + // bad + function bar() { + + console.log(foo); + + } + + // also bad + if (baz) { + + console.log(qux); + } else { + console.log(foo); + + } + + // good + function bar() { + console.log(foo); + } + + // good + if (baz) { + console.log(qux); + } else { + console.log(foo); + } + ``` + + + - [18.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) + + ```javascript + // bad + function bar( foo ) { + return foo; + } + + // good + function bar(foo) { + return foo; + } + + // bad + if ( foo ) { + console.log(foo); + } + + // good + if (foo) { + console.log(foo); + } + ``` + + + - [18.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) + + ```javascript + // bad + const foo = [ 1, 2, 3 ]; + console.log(foo[ 0 ]); + + // good + const foo = [1, 2, 3]; + console.log(foo[0]); + ``` + + + - [18.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) + + ```javascript + // bad + const foo = {clark: 'kent'}; + + // good + const foo = { clark: 'kent' }; ``` + + - [18.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) + + > Why? This ensures readability and maintainability. + + ```javascript + // bad + const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; + + // bad + $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); + + // good + const foo = jsonData + && jsonData.foo + && jsonData.foo.bar + && jsonData.foo.bar.baz + && jsonData.foo.bar.baz.quux + && jsonData.foo.bar.baz.quux.xyzzy; + + // good + $.ajax({ + method: 'POST', + url: 'https://airbnb.com/', + data: { name: 'John' }, + }) + .done(() => console.log('Congratulations!')) + .fail(() => console.log('You have failed this city.')); + ``` **[⬆ 返回目录](#table-of-contents)** ## 逗号 - - [19.1](#19.1) 行首逗号:**不需要**。 + + - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 + eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) + jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) ```javascript // bad @@ -1488,9 +2476,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }; ``` - - [19.2](#19.2) 增加结尾的逗号: **需要**。 + + - [19.2](#commas--dangling) 增加结尾的逗号: **需要**。 + eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) + jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) - > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 ```javascript // bad - git diff without trailing comma @@ -1498,15 +2489,15 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', - + inventorOf: ['coxcomb graph', 'modern nursing'] - } + + inventorOf: ['coxcomb chart', 'modern nursing'] + }; // good - git diff with trailing comma const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], - } + }; // bad const hero = { @@ -1536,49 +2527,60 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 分号 - - [20.1](#20.1) **使用分号** + + - [20.1](#semicolons--required) **使用分号** + eslint: [`semi`](http://eslint.org/docs/rules/semi.html) + jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) ```javascript // bad - (function() { + (function () { const name = 'Skywalker' return name })() // good - (() => { + (function () { const name = 'Skywalker'; return name; - })(); + }()); // good (防止函数在两个 IIFE 合并时被当成一个参数) ;(() => { const name = 'Skywalker'; return name; - })(); + }()); ``` - [Read more](http://stackoverflow.com/a/7365214/1712802). + [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). **[⬆ 返回目录](#table-of-contents)** ## 类型转换 - - [21.1](#21.1) 在语句开始时执行类型转换。 - - [21.2](#21.2) 字符串: + + - [21.1](#coercion--explicit) 在语句开始时执行类型转换。 + + + - [21.2](#coercion--strings) 字符串: ```javascript - // => this.reviewScore = 9; + // => this.reviewScore = 9; + + // bad + const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() // bad - const totalScore = this.reviewScore + ''; + const totalScore = this.reviewScore.toString(); // isn't guaranteed to return a string // good const totalScore = String(this.reviewScore); ``` - - [21.3](#21.3) 对数字使用 `parseInt` 转换,并带上类型转换的基数。 + + - [21.3](#coercion--numbers) 数字:对数字使用 `parseInt` 转换,并带上类型转换的基数。 + eslint: [`radix`](http://eslint.org/docs/rules/radix) ```javascript const inputValue = '4'; @@ -1602,7 +2604,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const val = parseInt(inputValue, 10); ``` - - [21.4](#21.4) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + - [21.4](#coercion--comment-deviations) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 ```javascript // good @@ -1613,7 +2616,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z const val = inputValue >> 0; ``` - - [21.5](#21.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: + + - [21.5](#coercion--bitwise) **注意:** 小心使用位操作运算符。数字会被当成 [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: ```javascript 2147483647 >> 0 //=> 2147483647 @@ -1621,7 +2625,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z 2147483649 >> 0 //=> -2147483647 ``` - - [21.6](#21.6) 布尔运算: + + - [21.6](#coercion--booleans) 布尔运算: ```javascript const age = 0; @@ -1632,7 +2637,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // good const hasAge = Boolean(age); - // good + // best const hasAge = !!age; ``` @@ -1641,7 +2646,9 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 命名规则 - - [22.1](#22.1) 避免单字母命名。命名应具备描述性。 + + - [22.1](#naming--descriptive) 避免单字母命名。命名应具备描述性。 + eslint: [`id-length`](http://eslint.org/docs/rules/id-length) ```javascript // bad @@ -1655,7 +2662,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [22.2](#22.2) 使用驼峰式(Camel Case、小驼峰)命名变量、对象、函数和实例。 + + - [22.2](#naming--camelCase) 使用驼峰式(camelCase、小驼峰)命名变量、对象、函数和实例。 + eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) + jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) ```javascript // bad @@ -1668,7 +2678,10 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z function thisIsMyFunction() {} ``` - - [22.3](#22.3) 使用帕斯卡式(Pascal Case、大驼峰式)命名构造函数或类。 + + - [22.3](#naming--PascalCase) 使用帕斯卡式(PascalCase、大驼峰式)命名构造函数或类。 + eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) + jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 @@ -1694,24 +2707,32 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z }); ``` - - [22.4](#22.4) 使用下划线 `_` 开头命名私有属性。 + + - [22.4](#naming--leading-underscore) 不要使用尾随或前导下划线来命名私有属性。 + eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) + jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) + + > 为什么? JavaScript没有在属性或方法方面的隐私权的概念。虽然一个领先的下划线是一个共同的公约,意思是“私人”,事实上,这些属性是完全公开的,因此,是你的公共API合同的一部分。这个约定可能会导致开发人员错误地认为一个变化不算是中断,或者不需要测试。TL;DR:如果你想要的东西是“私人的”,它不应该可见。 ```javascript // bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; + this._firstName = 'Panda'; // good - this._firstName = 'Panda'; + this.firstName = 'Panda'; ``` - - [22.5](#22.5) 别保存 `this` 的引用。使用箭头函数或 Function#bind。 + + - [22.5](#naming--self-this) 别保存 `this` 的引用。应使用箭头函数或函数绑定 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)。 + jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) ```javascript // bad function foo() { const self = this; - return function() { + return function () { console.log(self); }; } @@ -1719,7 +2740,7 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // bad function foo() { const that = this; - return function() { + return function () { console.log(that); }; } @@ -1732,27 +2753,44 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [22.6](#22.6) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 + + - [22.6](#naming--filename-matches-export) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 ```javascript - // file contents + // file 1 contents class CheckBox { // ... } export default CheckBox; + // file 2 contents + export default function fortyTwo() { return 42; } + + // file 3 contents + export default function insideDirectory() {} + // in some other file // bad - import CheckBox from './checkBox'; + import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename + import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export + import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // bad - import CheckBox from './check_box'; + import CheckBox from './check_box'; // PascalCase import/export, snake_case filename + import forty_two from './forty_two'; // snake_case import/filename, camelCase export + import inside_directory from './inside_directory'; // snake_case import, camelCase export + import index from './inside_directory/index'; // requiring the index file explicitly + import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // good - import CheckBox from './CheckBox'; + import CheckBox from './CheckBox'; // PascalCase export/import/filename + import fortyTwo from './fortyTwo'; // camelCase export/import/filename + import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" + // ^ supports both insideDirectory.js and insideDirectory/index.js ``` - - [22.7](#22.7) 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。 + + - [22.7](#naming--camelCase-default-export) 当你导出默认的函数时使用驼峰式命名camelCase。你的文件名必须和函数名完全保持一致。 ```javascript function makeStyleGuide() { @@ -1761,7 +2799,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z export default makeStyleGuide; ``` - - [22.8](#22.8) 当你导出单例、函数库、空对象时使用帕斯卡式命名。 + + - [22.8](#naming--PascalCase-singleton) 当你导出构造函数、类、单例、函数库、空对象时使用帕斯卡式命名PascalCase。 ```javascript const AirbnbStyleGuide = { @@ -1772,30 +2811,43 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z export default AirbnbStyleGuide; ``` - **[⬆ 返回目录](#table-of-contents)** ## 存取器 - - [23.1](#23.1) 属性的存取函数不是必须的。 - - [23.2](#23.2) 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + - [23.1](#accessors--not-required) 属性的存取函数不是必须的。 + + + - [23.2](#accessors--no-getters-setters) 不要使用 getters/setters 有可能引起副作用,很难测试、维护、调试。如果你需要存取函数时使用 `getVal()` 和 `setVal(value)`。 ```javascript // bad - dragon.age(); - - // good - dragon.getAge(); + class Dragon { + get age() { + // ... + } - // bad - dragon.age(25); + set age(value) { + // ... + } + } // good - dragon.setAge(25); + class Dragon { + getAge() { + // ... + } + + setAge(value) { + // ... + } + } ``` - - [23.3](#23.3) 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + - [23.3](#accessors--boolean-prefix) 如果属性或方法是布尔值,使用 `isVal()` 或 `hasVal()` 判断。 ```javascript // bad @@ -1809,7 +2861,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [23.4](#23.4) 创建 `get()` 和 `set()` 函数是可以的,但要保持一致。 + + - [23.4](#accessors--consistent) 可以创建 `get()` 和 `set()` 函数,但要保持一致。 ```javascript class Jedi { @@ -1833,7 +2886,8 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## 事件 - - [24.1](#24.1) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + - [24.1](#events--hash) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: ```javascript // bad @@ -1841,20 +2895,20 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ... - $(this).on('listingUpdated', function(e, listingId) { + $(this).on('listingUpdated', (e, listingId) => { // do something with listingId }); ``` - 更好的写法: + prefer 更好的写法: ```javascript // good - $(this).trigger('listingUpdated', { listingId : listing.id }); + $(this).trigger('listingUpdated', { listingId: listing.id }); ... - $(this).on('listingUpdated', function(e, data) { + $(this).on('listingUpdated', (e, data) => { // do something with data.listingId }); ``` @@ -1863,7 +2917,9 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## jQuery - - [25.1](#25.1) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + - [25.1](#jquery--dollar-prefix) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) ```javascript // bad @@ -1871,9 +2927,13 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z // good const $sidebar = $('.sidebar'); + + // good + const $sidebarBtn = $('.sidebar-btn'); ``` - - [25.2](#25.2) 缓存 jQuery 查询。 + + - [25.2](#jquery--cache) 缓存 jQuery 查询。 ```javascript // bad @@ -1900,8 +2960,12 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z } ``` - - [25.3](#25.3) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - - [25.4](#25.4) 对有作用域的 jQuery 对象查询使用 `find`。 + + - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 + [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + + + - [25.4](#jquery--find) 对有作用域的 jQuery 对象查询使用 `find`。 ```javascript // bad @@ -1931,17 +2995,19 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z ## ECMAScript 5 兼容性 - - [26.1](#26.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). + + - [26.1](#es5-compat--kangax) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). **[⬆ 返回目录](#table-of-contents)** ## ECMAScript 6(ES 2015+)规范 - - [27.1](#27.1) 以下是链接到 ES6 的各个特性的列表。 + + - [27.1](#es6-styles) 以下是链接到 ES6 的各个特性的列表。 1. [Arrow Functions](#arrow-functions) -1. [Classes](#constructors) +1. [Classes](#classes--constructors) 1. [Object Shorthand](#es6-object-shorthand) 1. [Object Concise](#es6-object-concise) 1. [Object Computed Properties](#es6-computed-properties) @@ -1954,31 +3020,48 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z 1. [Iterators and Generators](#iterators-and-generators) 1. [Modules](#modules) + + - [27.2](#tc39-proposals) Do not use [TC39 proposals](https://github.com/tc39/proposals) that have not reached stage 3. + + > Why? [They are not finalized](https://tc39.github.io/process-document/), and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet. + **[⬆ 返回目录](#table-of-contents)** ## 测试 - - [28.1](#28.1) **Yup.** + + - [28.1](#testing--yup) **Yup.** ```javascript - function() { + function foo() { return true; } ``` + + - [28.2](#testing--for-real) **No, but seriously**: + + - Whichever testing framework you use, you should be writing tests! + - Strive to write many small pure functions, and minimize where mutations occur. + - Be cautious about stubs and mocks - they can make your tests more brittle. + - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. + - 100% test coverage is a good goal to strive for, even if it's not always practical to reach it. + - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future. + **[⬆ 返回目录](#table-of-contents)** ## 性能 - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](http://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](https://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](https://jsperf.com/ya-string-concat) + - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) - Loading... **[⬆ 返回目录](#table-of-contents)** @@ -1995,31 +3078,32 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z **Read This** - - [Annotated ECMAScript 5.1](http://es5.github.com/) + - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) + - old [Annotated ECMAScript 5.1](http://es5.github.com/) **Tools** - Code Style Linters + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) + + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) -**Other Styleguides** +**Other Style Guides** - - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) + - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) **Other Styles** - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Popular JavaScript Coding Conventions on GitHub](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman -**阅读更多 Further Reading** +**Further Reading** - - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban @@ -2027,73 +3111,93 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z **Books** - - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke + - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson **Blogs** - - [DailyJS](http://dailyjs.com/) - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) + - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](https://bocoup.com/weblog) - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) + - [NCZOnline](https://www.nczonline.net/) - [Perfection Kills](http://perfectionkills.com/) - [Ben Alman](http://benalman.com/) - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) + - [nettuts](http://code.tutsplus.com/?s=javascript) **播客 Podcasts** - - [JavaScript Jabber](http://devchat.tv/js-jabber/) + - [JavaScript Air](https://javascriptair.com/) + - [JavaScript Jabber](https://devchat.tv/js-jabber/) **[⬆ 返回目录](#table-of-contents)** ## 使用人群 - This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. + This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. + - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Ascribe**: [ascribe/javascript](https://github.com/ascribe/javascript) - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript) - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) + - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) + - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) + - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) + - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **DoSomething**: [DoSomething/eslint-config](https://github.com/DoSomething/eslint-config) - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript) - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript) + - **EvozonJs**: [evozonjs/javascript](https://github.com/evozonjs/javascript) - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) - - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) + - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) + - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) + - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md) - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **KickorStick**: [kickorstick/javascript](https://github.com/kickorstick/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) + - **Lonely Planet**: [lonelyplanet/javascript](https://github.com/lonelyplanet/javascript) + - **M2GEN**: [M2GEN/javascript](https://github.com/M2GEN/javascript) - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - **Muber**: [muber/javascript](https://github.com/muber/javascript) @@ -2101,19 +3205,27 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) + - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Springload**: [springload/javascript](https://github.com/springload/javascript) + - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) + - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) - **Target**: [target/javascript](https://github.com/target/javascript) - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **The Nerdery**: [thenerdery/javascript-standards](https://github.com/thenerdery/javascript-standards) - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) - - **Userify**: [userify/javascript](https://github.com/userify/javascript) - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript) - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) @@ -2127,17 +3239,18 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [yuche/javascript](https://github.com/yuche/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javascript-style-guide](https://github.com/mitsuruog/javascript-style-guide) - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [giangpii/javascript-style-guide](https://github.com/giangpii/javascript-style-guide) ## JavaScript 编码规范说明 @@ -2149,13 +3262,11 @@ ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/z - Find us on [gitter](https://gitter.im/airbnb/javascript). - ## 贡献者 Contributors - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - ## 许可 License From f4a6980fd52d12f291e95c80a4777827f7f38881 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Thu, 9 Feb 2017 16:40:37 +0800 Subject: [PATCH 06/33] =?UTF-8?q?=20ES6=20=E4=BB=A3=E7=A0=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es6_zh-cn.md | 635 +++- .../es5_zh-cn_v1.md | 0 .../es5_zh-cn_v2.md | 0 javascript-style-guide/es6_zh-cn_v2.md | 3303 +++++++++++++++++ 4 files changed, 3751 insertions(+), 187 deletions(-) rename es5/zh-cn_v1.md => javascript-style-guide/es5_zh-cn_v1.md (100%) rename es5/zh-cn_v2.md => javascript-style-guide/es5_zh-cn_v2.md (100%) create mode 100644 javascript-style-guide/es6_zh-cn_v2.md diff --git a/es6_zh-cn.md b/es6_zh-cn.md index 436f748832..606518e1cc 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -2,17 +2,28 @@ # Airbnb JavaScript Style Guide() { -**A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript** +*A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript* 翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 -ES5 的编码规范请查看[版本一](./es5/zh-cn_v1.md),[版本二](./es5/zh-cn_v2.md)。 +NOTE: 本文对[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 略有调整具体如下, + +- 尾逗号单行不要有,多行必须有。修改规则 `comma-dangle: ['error', 'never']` 中 never 改为 only-multiline,注意不是 always-multiline。 +- 行尾不要分号。调整后,代码更干净、整洁,但要注意 (, [, + , -, or ` 开头的语句,可能在“自动分号插入”机制(ASI)下会有问题。
    + 为什么? 参看[是否要分号的讨论](https://github.com/feross/standard/blob/master/RULES.md#helpful-reading) +- 本文档有大量对 ES6 新特性规则要求,此外的书写要求兼容 [ES5 代码规范](http://standardjs.com/rules.html)。 + +该文档保留了部分还属于 ES5 范畴的注意内容,由 old: ES5 标记。 + +ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v2.md)。 +ES6 的编码规范请查看下文(最新版),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es6_zh-cn_v2.md) [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Other Style Guides + - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) - [React](react/) - [CSS-in-JavaScript](css-in-javascript/) @@ -20,6 +31,7 @@ Other Style Guides - [CSS & Sass 中文版](https://github.com/webcoding/css-style-guide) - [Ruby](https://github.com/airbnb/ruby) + ## 目录 @@ -33,7 +45,7 @@ Other Style Guides 1. [箭头函数](#arrow-functions) 1. [类 & 构造函数](#classes--constructors) 1. [模块](#modules) - 1. [Iterators & Generators ](#iterators-and-generators) + 1. [Iterators and Generators](#iterators-and-generators) 1. [属性](#properties) 1. [变量](#variables) 1. [提升](#hoisting) @@ -49,7 +61,7 @@ Other Style Guides 1. [事件](#events) 1. [jQuery](#jquery) 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) - 1. [ECMAScript 6 (ES 2015+) 编码规范](#ecmascript-6-styles) + 1. [ECMAScript 6+ (ES 2015+) 编码规范](#ecmascript-6-styles) 1. [测试](#testing) 1. [性能](#performance) 1. [资源](#resources) @@ -60,6 +72,7 @@ Other Style Guides 1. [Contributors](#contributors) 1. [License](#license) + ## 类型 @@ -99,6 +112,7 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## 引用 @@ -154,6 +168,7 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## 对象 @@ -168,43 +183,7 @@ Other Style Guides // good const item = {}; ``` - **************** - - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 - - ```javascript - // bad - const superman = { - default: { clark: 'kent' }, - private: true, - }; - - // good - const superman = { - defaults: { clark: 'kent' }, - hidden: true, - }; - ``` - - - [3.3](#3.3) 使用同义词替换需要使用的保留字。 - - ```javascript - // bad - const superman = { - class: 'alien', - }; - - // bad - const superman = { - klass: 'alien', - }; - - // good - const superman = { - type: 'alien', - }; - ``` - ************* - + - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 @@ -288,7 +267,7 @@ Other Style Guides // bad const obj = { episodeOne: 1, - twoJedisWalkIntoACantina: 2, + twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, @@ -300,14 +279,14 @@ Other Style Guides lukeSkywalker, anakinSkywalker, episodeOne: 1, - twoJedisWalkIntoACantina: 2, + twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, }; ``` - - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. + - [3.6](#objects--quoted-props) 只用引号包裹属性名称是无效标识符的那些。 eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) @@ -330,9 +309,9 @@ Other Style Guides ``` - - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. + - [3.7](#objects--prototype-builtins) 不要直接调用 `Object.prototype` 的方法,如 `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf` - > 为什么? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). + > 为什么? 这些方法可能会有问题,比如 `{ hasOwnProperty: false }` 的情况 或者 可能是 null object (`Object.create(null)`). ```javascript // bad @@ -345,12 +324,14 @@ Other Style Guides const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. /* or */ import has from 'has'; - … + // ... console.log(has.call(object, key)); ``` - - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted. + - [3.8](#objects--rest-spread) 浅拷贝使用对象扩展符(rest operator)更好,相对于方法 [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 而言。 + + ???: 可以深拷贝么? ```javascript // very bad @@ -369,8 +350,46 @@ Other Style Guides const { a, ...noA } = copy; // noA => { b: 2, c: 3 } ``` + - old: ES5 [3.11](#3.11) 如果你的代码在浏览器环境下执行,不要使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 + + NOTE: 这是 ES5 的要求,在浏览器端(目前情况下)仍要注意遵守此要求。 + + ```javascript + // bad + const superman = { + default: { clark: 'kent' }, + private: true, + }; + + // good + const superman = { + defaults: { clark: 'kent' }, + hidden: true, + }; + ``` + + - old: ES5 [3.12](#3.12) 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + const superman = { + class: 'alien', + }; + + // bad + const superman = { + klass: 'alien', + }; + + // good + const superman = { + type: 'alien', + }; + ``` + **[⬆ 返回目录](#table-of-contents)** + ## 数组 @@ -408,15 +427,15 @@ Other Style Guides const itemsCopy = []; let i; - for (i = 0; i < len; i++) { + for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; - // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - // old + // old: ES5 + // 当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) itemsCopy = items.slice(); ``` @@ -427,8 +446,8 @@ Other Style Guides const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo); - // ES5 中使用slice将类数组的对象转成数组. - // old + // old: ES5 + // 使用slice将类数组的对象转成数组. function trigger() { var args = Array.prototype.slice.call(arguments); ... @@ -487,6 +506,7 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## 解构 @@ -561,6 +581,7 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## Strings @@ -603,7 +624,6 @@ Other Style Guides 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.'; ``` - - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) @@ -648,15 +668,17 @@ Other Style Guides // good const foo = '\'this\' is "quoted"'; - const foo = `'this' is "quoted"`; + const foo = `my name is '${name}'`; ``` **[⬆ 返回目录](#table-of-contents)** + ## 函数 - - [7.1](#7.1) 使用函数表达式,而不是函数声明。 + + - [7.1](#functions--declarations) 使用函数表达式,而不是函数声明。 eslint: [`func-style`](http://eslint.org/docs/rules/func-style) jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) @@ -665,16 +687,19 @@ Other Style Guides > 函数提升规则,使得[箭头函数](#arrow-functions)可以取代函数表达式。 ```javascript - // bad 匿名函数表达式 - var anonymous = function() () { - }; - // bad function foo() { + // ... } + // bad + const foo = function () { + // ... + }; + // good - const foo = function named() { + const foo = function bar() { + // ... }; ``` @@ -683,10 +708,13 @@ Other Style Guides eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) + > 为什么? 立即调用表达式(IIFE) 要使用圆括号包裹成一个独立的单元,这样表达更清晰。NOTE: 在模块的世界,你已经不再需要 IIFE 了。 + ```javascript // 立即调用的函数表达式 (IIFE) // 可使用箭头函数 - (() => { + // (() => { + (function () { console.log('Welcome to the Internet. Please follow me.'); }()); ``` @@ -696,7 +724,7 @@ Other Style Guides eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) - - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + - [7.4](#functions--note-on-blocks) NOTE: ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 ```javascript // bad @@ -720,13 +748,13 @@ Other Style Guides ```javascript // bad - function nope(name, options, arguments) { - // ...stuff... + function foo(name, options, arguments) { + // ... } // good - function yup(name, options, args) { - // ...stuff... + function foo(name, options, args) { + // ... } ``` @@ -773,7 +801,7 @@ Other Style Guides // good function handleThings(opts = {}) { - // ? 对比以上写法区别是啥?参见 es6 + // ? 对比以上写法核心区别是啥? // ... } ``` @@ -851,12 +879,12 @@ Other Style Guides // bad function f1(obj) { obj.key = 1; - }; + } // good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; - }; + } ``` @@ -869,18 +897,22 @@ Other Style Guides // bad function f1(a) { a = 1; + // ... } function f2(a) { if (!a) { a = 1; } + // ... } // good function f3(a) { const b = a || 1; + // ... } function f4(a = 1) { + // ... } ``` @@ -900,14 +932,48 @@ Other Style Guides console.log(...x); // bad - new (Function.prototype.bind.apply(Date, [null, 2016, 08, 05])); + new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good - new Date(...[2016, 08, 05]); + new Date(...[2016, 8, 5]); + ``` + + + - [7.15](#functions--signature-invocation-indentation) 多参函数声明或调用,书写规则: 每项独占一行,保持缩进, 且后面跟个逗号。 + + ```javascript + // bad + function foo(bar, + baz, + quux) { + // ... + } + + // good + function foo( + bar, + baz, + quux, + ) { + // ... + } + + // bad + console.log(foo, + bar, + baz); + + // good + console.log( + foo, + bar, + baz, + ); ``` **[⬆ 返回目录](#table-of-contents)** + ## 箭头函数 @@ -937,9 +1003,9 @@ Other Style Guides - [8.2](#arrows--implicit-return) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 - > 为什么? 这是语法糖。在链式调用中可读性很高。 + > 为什么? 这是语法糖。在链式调用中可读性很高。 - > 为什么不?当你打算回传一个对象的时候。 + > 为什么不?当你打算回传一个对象的时候。 ```javascript // bad @@ -959,7 +1025,7 @@ Other Style Guides // good [1, 2, 3].map((number, index) => ({ - [index]: number + [index]: number, })); ``` @@ -968,11 +1034,11 @@ Other Style Guides > 为什么? 这样能更清楚的看到开始和结束位置。 - ```js + ```javascript // bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, - httpMethod + httpMethod, ) ); @@ -980,19 +1046,19 @@ Other Style Guides ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, - httpMethod + httpMethod, ) )); ``` - - [8.4](#arrows--one-arg-parens) 如果函数只有一个参数就不要使用圆括号,否则总是用圆括号包裹参数。 + - [8.4](#arrows--one-arg-parens) 如果函数需要一个参数就省略掉圆括号,否则总是用圆括号包裹参数,这样更清晰。NOTE: 总是使用圆括号也是可以接受的,但要设置在 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) > 为什么? 更少的视觉干扰 - ```js + ```javascript // bad [1, 2, 3].map((x) => x * x); @@ -1026,7 +1092,7 @@ Other Style Guides - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) - ```js + ```javascript // bad const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; @@ -1045,6 +1111,7 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## 类 & 构造器 @@ -1081,7 +1148,7 @@ Other Style Guides - [9.2](#constructors--extends) 使用 `extends` 继承。 - > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 ```javascript // bad @@ -1092,7 +1159,7 @@ Other Style Guides inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; - } + }; // good class PeekableQueue extends Queue { @@ -1215,14 +1282,16 @@ Other Style Guides **[⬆ 返回目录](#table-of-contents)** + ## 模块 - [10.1](#modules--use-them) 总是使用模块 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 - > 为什么? 模块就是未来,让我们开始迈向未来吧。 - > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) + > 为什么? 模块就是未来,让我们开始迈向未来吧。 + + > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) ```javascript // bad @@ -1237,8 +1306,7 @@ Other Style Guides import { es6 } from './AirbnbStyleGuide'; export default es6; - // ES5中要注意 - // old + // old: ES5 要注意 // - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 // - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 // - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 @@ -1248,7 +1316,7 @@ Other Style Guides - [10.2](#modules--no-wildcard) 不要使用通配符 import。 - > 为什么? 这样能确保你只有一个默认 export。 + > 为什么? 这样能确保你只有一个默认 export。 ```javascript // bad @@ -1261,7 +1329,7 @@ Other Style Guides - [10.3](#modules--no-export-from-import) 不要从 import 中直接 export。 - > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 ```javascript // bad @@ -1309,21 +1377,21 @@ Other Style Guides // bad let foo = 3; - export { foo } + export { foo }; // good const foo = 3; - export { foo } + export { foo }; // valid - export const count = 1 - export function getCount () {} - export class Counter {} + export const count = 1; + export function getCount () {}; + export class Counter {}; ``` - [10.6](#modules--prefer-default-export) 单出口模块,更倾向设置为默认出口。 -eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) + eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) ```javascript // bad @@ -1335,7 +1403,7 @@ eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plu - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 -eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) + eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 @@ -1374,7 +1442,7 @@ eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-impo - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 -eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) + eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) > 为什么? 加载器语法要在 `webpack.config.js` 中使用 @@ -1390,6 +1458,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## Iterators and Generators 迭代器 & 生成器 @@ -1418,12 +1487,25 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; + + // bad + const increasedByOne = []; + for (let i = 0; i < numbers.length; i++) { + increasedByOne.push(numbers[i] + 1); + } + + // good + const increasedByOne = []; + numbers.forEach(num => increasedByOne.push(num + 1)); + + // best (keeping it functional) + const increasedByOne = numbers.map(num => num + 1); ``` - [11.2](#generators--nope) 现在还不要使用生成器 generators。 - > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) - [11.3](#generators--spacing) 如果你必须使用generators 或无视[generators 忠告](#generators--nope),那么请务必确保函数签名(function*)是适当的间距。 @@ -1431,47 +1513,65 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- > 为什么? `function` and `*` 是同一个关键词的不同组成部分—— `*` 不是 `function` 修饰符, `function*` 是一种特定结构, 不同于 `function`。 - ```js + ```javascript // bad function * foo() { + // ... } + // bad const bar = function * () { - } + // ... + }; + // bad const baz = function *() { - } + // ... + }; + // bad const quux = function*() { - } + // ... + }; + // bad function*foo() { + // ... } + // bad function *foo() { + // ... } // very bad function * foo() { + // ... } + // very bad const wat = function * () { - } + // ... + }; // good function* foo() { + // ... } + // good const foo = function* () { - } + // ... + }; ``` **[⬆ 返回目录](#table-of-contents)** + ## 属性 @@ -1511,6 +1611,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 变量 @@ -1529,7 +1630,8 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - [13.2](#variables--one-const) 使用 `const` 声明每一个变量。 - eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) + eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) + jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) > 为什么? 增加新变量将更容易,而且你永远不用再担心调换错 `;` or `,` ,并且 diff 工具下更少干扰。同时在 debugger 时,可以单步调试而不会一次跳过他们所有变量。 @@ -1554,7 +1656,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - [13.3](#variables--const-let-group) 将所有的 `const` 和 `let` 分组 - > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 + > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 ```javascript // bad @@ -1580,7 +1682,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 - > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 + > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 ```javascript // bad - unnecessary function call @@ -1650,7 +1752,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [13.6](#variables--unary-increment-decrement) 便面使用一元递增和递减操作 (++, --) 。 + - [13.6](#variables--unary-increment-decrement) 避免使用一元递增和递减操作 (++, --) 。 eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) > 为什么? 一元递增和递减语句默认在自动分号插入处理下会有错误问题。而且它还会出现 `num += 1` 替代 `num ++` 的情况。不允许使用它们,也可以代码更健壮。 @@ -1658,14 +1760,14 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript // bad - let array = [1, 2, 3]; + const array = [1, 2, 3]; let num = 1; - num ++; - -- num; + num++; + --num; let sum = 0; let truthyCount = 0; - for(let i = 0; i < array.length; i++){ + for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { @@ -1675,7 +1777,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // good - let array = [1, 2, 3]; + const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; @@ -1686,6 +1788,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## Hoisting @@ -1700,7 +1803,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // 由于变量提升的原因, // 在引用变量后再声明变量是可以运行的。 - // **注意:** 变量赋值 `true` 的操作不会被提升。 + // NOTE: 变量赋值 `true` 的操作不会被提升。 function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; @@ -1762,7 +1865,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- var named = function named() { console.log('named'); - } + }; } ``` @@ -1785,6 +1888,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 比较运算符 & 等号 @@ -1810,38 +1914,38 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [15.3](#comparison--shortcuts) 使用简写。 + - [15.3](#comparison--shortcuts) 布尔值使用简写,明确使用数字和字符串比较除外。 ```javascript // bad if (isValid === true) { - // ...stuff... + // ... } // good if (isValid) { - // ...stuff... + // ... } // bad if (name) { - // ...stuff... + // ... } // good if (name !== '') { - // ...stuff... + // ... } // bad if (collection.length) { - // ...stuff... + // ... } // good if (collection.length > 0) { - // 为什么没用简写??? - // ...stuff... + // 为什么用简写是 bad ??? + // ... } ``` @@ -1851,7 +1955,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - [15.5](#comparison--switch-blocks) `case` 以及 `default` 情况下使用括号会创建块级作用域 (e.g. `let`, `const`, `function`, and `class`). - > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 + > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). @@ -1866,7 +1970,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- const y = 2; break; case 3: - function f() {} + function f() { + // ... + } break; default: class C {} @@ -1883,7 +1989,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- break; } case 3: { - function f() {} + function f() { + // ... + } break; } case 4: @@ -1896,7 +2004,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [15.6](#comparison--nested-ternaries) 别嵌套使用三元表达式,要单个使用。 + - [15.6](#comparison--nested-ternaries) 不要嵌套使用三元表达式,要单个使用。 eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). @@ -1920,7 +2028,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [15.7](#comparison--unneeded-ternary) 避免不必要使用三元表达式 + - [15.7](#comparison--unneeded-ternary) 避免使用不必要的三元表达式 eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). @@ -1938,6 +2046,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 代码块 @@ -1988,9 +2097,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- } ``` - **[⬆ 返回目录](#table-of-contents)** + ## 注释 @@ -2006,7 +2115,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // @return {Element} element function make(tag) { - // ...stuff... + // ... return element; } @@ -2021,7 +2130,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- */ function make(tag) { - // ...stuff... + // ... return element; } @@ -2042,7 +2151,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- function getType() { console.log('fetching type...'); // set the default type to 'no type' - const type = this._type || 'no type'; + const type = this.type || 'no type'; return type; } @@ -2052,7 +2161,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- console.log('fetching type...'); // set the default type to 'no type' - const type = this._type || 'no type'; + const type = this.type || 'no type'; return type; } @@ -2060,17 +2169,54 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // also good function getType() { // set the default type to 'no type' - const type = this._type || 'no type'; + const type = this.type || 'no type'; return type; } ``` - - - [17.3](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + - [17.3](#comments--spaces) 注释以空格开始,这样更容易阅读。 + eslint: [`spaced-comment`](http://eslint.org/docs/rules/spaced-comment) + + ```javascript + // bad + //is current tab + const active = true; + + // good + // is current tab + const active = true; + + // bad + /** + *make() returns a new element + *based on the passed-in tag name + */ + function make(tag) { + + // ... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed-in tag name + */ + function make(tag) { + + // ... + + return element; + } + ``` + + + - [17.4](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 - - - [17.4](#comments--fixme) 使用 `// FIXME`: 标注问题。 + + - [17.5](#comments--fixme) 使用 `// FIXME`: 标注问题。 ```javascript class Calculator extends Abacus { @@ -2084,7 +2230,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [17.5](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 + - [17.6](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 ```javascript class Calculator extends Abacus { @@ -2099,6 +2245,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 空白 @@ -2110,22 +2257,22 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript // bad function foo() { - ∙∙∙∙const name; + ∙∙∙∙let name; } // bad function bar() { - ∙const name; + ∙let name; } // good function baz() { - ∙∙const name; + ∙∙let name; } ``` - - [18.2](#whitespace--before-blocks) 在花括号前放一个空格。 + - [18.2](#whitespace--before-blocks) 在大括号前放一个空格。 eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) @@ -2199,24 +2346,24 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript // bad - (function (global) { - // ...stuff... - })(this); + import { es6 } from './AirbnbStyleGuide'; + // ... + export default es6; ``` ```javascript // bad - (function (global) { - // ...stuff... - })(this);↵ + import { es6 } from './AirbnbStyleGuide'; + // ... + export default es6;↵ ↵ ``` ```javascript // good - (function (global) { - // ...stuff... - })(this);↵ + import { es6 } from './AirbnbStyleGuide'; + // ... + export default es6;↵ ``` @@ -2246,7 +2393,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // bad const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good @@ -2256,7 +2403,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good @@ -2323,7 +2470,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) + - [18.8](#whitespace--padded-blocks) 不要使用空白行紧邻块作用域填充空间。 + eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) + jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) ```javascript // bad @@ -2356,7 +2505,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [18.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) + - [18.9](#whitespace--in-parens) 不要在圆括号(parentheses)内侧两边填充空格,如函数调用、条件判断等。 + eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) + jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) ```javascript // bad @@ -2381,7 +2532,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [18.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) + - [18.10](#whitespace--in-brackets) 不要在中括号(brackets)内侧左右两边填充空格。 + eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) + jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) ```javascript // bad @@ -2394,7 +2547,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [18.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) + - [18.11](#whitespace--in-braces) 在大括号(curly braces)内侧两边添加空格。 + eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) + jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) ```javascript // bad @@ -2405,9 +2560,11 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [18.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) + - [18.12](#whitespace--max-len) 避免一行代码过长,超过100个字符(包括空格)。Note: 参看 [strings--line-length](#strings--line-length),过长字符应该分行处理。 + eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) + jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) - > Why? This ensures readability and maintainability. + > 为什么? 为了确保可读性、可维护性。注:作为一个良好的习惯,虽然显示器界面越来越大了,但代码为了通用性维护,如 shell 内也可以修改维护,那么有必要处理过长代码段。 ```javascript // bad @@ -2436,10 +2593,11 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 逗号 - + - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) @@ -2481,7 +2639,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) - > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 ```javascript // bad - git diff without trailing comma @@ -2498,7 +2656,9 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], }; + ``` + ```javascript // bad const hero = { firstName: 'Dana', @@ -2520,10 +2680,62 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- 'Batman', 'Superman', ]; + + // bad + function createHero( + firstName, + lastName, + inventorOf + ) { + // does nothing + } + + // good + function createHero( + firstName, + lastName, + inventorOf, + ) { + // does nothing + } + + // good (note that a comma must not appear after a "rest" element) + // NOTE: rest 元素后不能出现逗号 + function createHero( + firstName, + lastName, + inventorOf, + ...heroArgs + ) { + // does nothing + } + + // bad + createHero( + firstName, + lastName, + inventorOf + ); + + // good + createHero( + firstName, + lastName, + inventorOf, + ); + + // good (note that a comma must not appear after a "rest" element) + createHero( + firstName, + lastName, + inventorOf, + ...heroArgs + ); ``` **[⬆ 返回目录](#table-of-contents)** + ## 分号 @@ -2545,25 +2757,27 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- return name; }()); - // good (防止函数在两个 IIFE 合并时被当成一个参数) - ;(() => { + // good (防止函数在两个 IIFE 串联时其中一个被错误当成另一个的参数处理) + // NOTE: 箭头函数参数省略,使用圆括号包裹处理 + ;((() => { const name = 'Skywalker'; return name; - }()); + })()); ``` [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). **[⬆ 返回目录](#table-of-contents)** + ## 类型转换 - - [21.1](#coercion--explicit) 在语句开始时执行类型转换。 + - [21.1](#coercion--explicit) 在语句开头执行类型转换。 - - [21.2](#coercion--strings) 字符串: + - [21.2](#coercion--strings) 处理字符串: ```javascript // => this.reviewScore = 9; @@ -2579,7 +2793,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [21.3](#coercion--numbers) 数字:对数字使用 `parseInt` 转换,并带上类型转换的基数。 + - [21.3](#coercion--numbers) 处理数字:使用 `Number` 转换数字类型,而使用 `parseInt` 转换时,总是带着类型转换的基数。 eslint: [`radix`](http://eslint.org/docs/rules/radix) ```javascript @@ -2617,16 +2831,16 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [21.5](#coercion--bitwise) **注意:** 小心使用位操作运算符。数字会被当成 [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: + - [21.5](#coercion--bitwise) NOTE: 小心使用位操作运算符。数字会被当成 [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: ```javascript - 2147483647 >> 0 //=> 2147483647 - 2147483648 >> 0 //=> -2147483648 - 2147483649 >> 0 //=> -2147483647 + 2147483647 >> 0; // => 2147483647 + 2147483648 >> 0; // => -2147483648 + 2147483649 >> 0; // => -2147483647 ``` - - [21.6](#coercion--booleans) 布尔运算: + - [21.6](#coercion--booleans) 处理布尔值: ```javascript const age = 0; @@ -2643,6 +2857,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 命名规则 @@ -2653,12 +2868,12 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript // bad function q() { - // ...stuff... + // ... } // good function query() { - // ..stuff.. + // ... } ``` @@ -2794,6 +3009,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript function makeStyleGuide() { + // ... } export default makeStyleGuide; @@ -2805,14 +3021,46 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ```javascript const AirbnbStyleGuide = { es6: { - } + }, }; export default AirbnbStyleGuide; ``` + + - [22.9](#naming--Acronyms-and-Initialisms) 缩写词要么全大写,要么全小写。 + + > 为什么? 名字的目的是提升可读性,而不是给电脑算法的。 + + ```javascript + // bad + import SmsContainer from './containers/SmsContainer'; + + // bad + const HttpRequests = [ + // ... + ]; + + // good + import SMSContainer from './containers/SMSContainer'; + + // good + const HTTPRequests = [ + // ... + ]; + + // best + import TextMessageContainer from './containers/TextMessageContainer'; + + // best + const Requests = [ + // ... + ]; + ``` + **[⬆ 返回目录](#table-of-contents)** + ## 存取器 @@ -2847,7 +3095,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- ``` - - [23.3](#accessors--boolean-prefix) 如果属性或方法是布尔值,使用 `isVal()` 或 `hasVal()` 判断。 + - [23.3](#accessors--boolean-prefix) 如果属性/方法是布尔值,使用 `isVal()` 或 `hasVal()` 来处理。 ```javascript // bad @@ -2883,6 +3131,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 事件 @@ -2893,7 +3142,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // bad $(this).trigger('listingUpdated', listing.id); - ... + // ... $(this).on('listingUpdated', (e, listingId) => { // do something with listingId @@ -2906,14 +3155,15 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- // good $(this).trigger('listingUpdated', { listingId: listing.id }); - ... + // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingId }); ``` - **[⬆ 返回目录](#table-of-contents)** +**[⬆ 返回目录](#table-of-contents)** + ## jQuery @@ -2940,10 +3190,10 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- function setSidebar() { $('.sidebar').hide(); - // ...stuff... + // ... $('.sidebar').css({ - 'background-color': 'pink' + 'background-color': 'pink', }); } @@ -2952,10 +3202,10 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- const $sidebar = $('.sidebar'); $sidebar.hide(); - // ...stuff... + // ... $sidebar.css({ - 'background-color': 'pink' + 'background-color': 'pink', }); } ``` @@ -2992,6 +3242,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## ECMAScript 5 兼容性 @@ -3027,11 +3278,12 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + -## 测试 +## 自动化测试 - - [28.1](#testing--yup) **Yup.** + - [28.1](#testing--yup) 需要吗?**Yup.** ```javascript function foo() { @@ -3051,6 +3303,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 性能 @@ -3066,6 +3319,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 资源 @@ -3149,10 +3403,12 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 使用人群 This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. + - **3blades**: [3Blades/javascript](https://github.com/3blades/javascript) - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) @@ -3161,9 +3417,11 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - **Ascribe**: [ascribe/javascript](https://github.com/ascribe/javascript) - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript) + - **BashPros**: [BashPros/javascript](https://github.com/BashPros/javascript) - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) + - **Bonhomme**: [bonhommeparis/javascript](https://github.com/bonhommeparis/javascript) - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) @@ -3204,12 +3462,13 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Nulogy**: [nulogy/javascript](https://github.com/nulogy/javascript) - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) + - **React**: [facebook.github.io/react/contributing/how-to-contribute.html#style-guide](https://facebook.github.io/react/contributing/how-to-contribute.html#style-guide) - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) @@ -3218,6 +3477,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) + - **SwoopApp**: [swoopapp/javascript](https://github.com/swoopapp/javascript) - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) - **Target**: [target/javascript](https://github.com/target/javascript) @@ -3232,6 +3492,7 @@ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint- **[⬆ 返回目录](#table-of-contents)** + ## 翻译 This style guide is also available in other languages: diff --git a/es5/zh-cn_v1.md b/javascript-style-guide/es5_zh-cn_v1.md similarity index 100% rename from es5/zh-cn_v1.md rename to javascript-style-guide/es5_zh-cn_v1.md diff --git a/es5/zh-cn_v2.md b/javascript-style-guide/es5_zh-cn_v2.md similarity index 100% rename from es5/zh-cn_v2.md rename to javascript-style-guide/es5_zh-cn_v2.md diff --git a/javascript-style-guide/es6_zh-cn_v2.md b/javascript-style-guide/es6_zh-cn_v2.md new file mode 100644 index 0000000000..875b2f07ed --- /dev/null +++ b/javascript-style-guide/es6_zh-cn_v2.md @@ -0,0 +1,3303 @@ +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Airbnb JavaScript Style Guide() { + +**A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript** + +翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 + +ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v2.md)。 + +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Other Style Guides + - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) + - [React](react/) + - [CSS-in-JavaScript](css-in-javascript/) + - [CSS & Sass](https://github.com/airbnb/css) + - [CSS & Sass 中文版](https://github.com/webcoding/css-style-guide) + - [Ruby](https://github.com/airbnb/ruby) + + +## 目录 + + 1. [类型](#types) + 1. [引用](#references) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [解构](#destructuring) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [箭头函数](#arrow-functions) + 1. [类 & 构造函数](#classes--constructors) + 1. [模块](#modules) + 1. [Iterators & Generators ](#iterators-and-generators) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [代码块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [事件](#events) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [ECMAScript 6 (ES 2015+) 编码规范](#ecmascript-6-styles) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [使用人群](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) + 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) + 1. [Contributors](#contributors) + 1. [License](#license) + + +## 类型 + + + - [1.1](#types--primitives) **基本类型**: 直接存取基本类型。 + + + `string` 字符串 + + `number` 数值 + + `boolean` 布尔类型 + + `null` + + `undefined` + + ```javascript + const foo = 1; + let bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + + + - [1.2](#types--complex) **复杂类型**: 通过引用的方式存取复杂类型。 + + + `object` 对象 + + `array` 数组 + + `function` 函数 + + ```javascript + const foo = [1, 2]; + const bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 引用 + + + - [2.1](#references--prefer-const) 对所有的引用使用 `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) + + > 为什么? 这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 + + ```javascript + // bad + var a = 1; + var b = 2; + + // good + const a = 1; + const b = 2; + ``` + + + - [2.2](#references--disallow-var) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) + jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) + + > 为什么? 因为 `let` 是块级作用域,而 `var` 是函数作用域。 + + ```javascript + // bad + var count = 1; + if (true) { + count += 1; + } + + // good, use the let. + let count = 1; + if (true) { + count += 1; + } + ``` + + + - [2.3](#references--block-scope) 注意 `let` 和 `const` 都是块级作用域。 + + ```javascript + // const 和 let 只存在于它们被定义的区块内。 + { + let a = 1; + const b = 1; + } + console.log(a); // ReferenceError + console.log(b); // ReferenceError + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 对象 + + + - [3.1](#objects--no-new) 使用字面值创建对象。 + eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) + + ```javascript + // bad + const item = new Object(); + + // good + const item = {}; + ``` + **************** + - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 + + ```javascript + // bad + const superman = { + default: { clark: 'kent' }, + private: true, + }; + + // good + const superman = { + defaults: { clark: 'kent' }, + hidden: true, + }; + ``` + + - [3.3](#3.3) 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + const superman = { + class: 'alien', + }; + + // bad + const superman = { + klass: 'alien', + }; + + // good + const superman = { + type: 'alien', + }; + ``` + ************* + + + - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 + + > 为什么? 因为这样可以让你在一个地方定义所有的对象属性。 + + ```javascript + function getKey(k) { + return `a key named ${k}`; + } + + // bad + const obj = { + id: 5, + name: 'San Francisco', + }; + obj[getKey('enabled')] = true; + + // good + const obj = { + id: 5, + name: 'San Francisco', + [getKey('enabled')]: true, + }; + ``` + + + - [3.3](#es6-object-shorthand) 使用对象方法的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + + ```javascript + // bad + const atom = { + value: 1, + + addValue: function (value) { + return atom.value + value; + }, + }; + + // good + const atom = { + value: 1, + + addValue(value) { + return atom.value + value; + }, + }; + ``` + + + - [3.4](#es6-object-concise) 使用对象属性值的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + + > 为什么? 因为这样更短更有描述性。 + + ```javascript + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + lukeSkywalker: lukeSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + }; + ``` + + + - [3.5](#objects--grouped-shorthand) 在对象属性声明前把简写的属性分组。 + + > 为什么? 因为这样能清楚地看出哪些属性使用了简写。 + + ```javascript + const anakinSkywalker = 'Anakin Skywalker'; + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + lukeSkywalker, + episodeThree: 3, + mayTheFourth: 4, + anakinSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + anakinSkywalker, + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + episodeThree: 3, + mayTheFourth: 4, + }; + ``` + + + - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. + eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) + jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) + + > 为什么? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. + + ```javascript + // bad + const bad = { + 'foo': 3, + 'bar': 4, + 'data-blah': 5, + }; + + // good + const good = { + foo: 3, + bar: 4, + 'data-blah': 5, + }; + ``` + + + - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. + + > 为什么? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). + + ```javascript + // bad + console.log(object.hasOwnProperty(key)); + + // good + console.log(Object.prototype.hasOwnProperty.call(object, key)); + + // best + const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. + /* or */ + import has from 'has'; + … + console.log(has.call(object, key)); + ``` + + + - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted. + + ```javascript + // very bad + const original = { a: 1, b: 2 }; + const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ + delete copy.a; // so does this + + // bad + const original = { a: 1, b: 2 }; + const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } + + // good + const original = { a: 1, b: 2 }; + const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } + + const { a, ...noA } = copy; // noA => { b: 2, c: 3 } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 数组 + + + - [4.1](#arrays--literals) 使用字面值创建数组。 + eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) + + ```javascript + // bad + const items = new Array(); + + // good + const items = []; + ``` + + + - [4.2](#arrays--push) 向数组添加元素时使用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 替代直接赋值。 + + ```javascript + const someStack = []; + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + + - [4.3](#es6-array-spreads) 使用拓展运算符 `...` 复制数组。 + + ```javascript + // bad + const len = items.length; + const itemsCopy = []; + let i; + + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + const itemsCopy = [...items]; + + // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + // old + itemsCopy = items.slice(); + ``` + + + - [4.4](#arrays--from) 使用 [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 把一个类数组对象转换成数组。 + + ```javascript + const foo = document.querySelectorAll('.foo'); + const nodes = Array.from(foo); + + // ES5 中使用slice将类数组的对象转成数组. + // old + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + + + - [4.5](#arrays--callback-return) 在回调函数中使用 return 语句。如果函数体只由一个单独的语句组成,可以省略 return 关键字 [8.2](#8.2). + eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) + + ```javascript + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map(x => x + 1); + + // bad + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + }); + + // good + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + return flatten; + }); + + // bad + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } else { + return false; + } + }); + + // good + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } + + return false; + }); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 解构 + + + - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 + jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) + + > 为什么? 因为解构能减少临时引用属性。 + + ```javascript + // bad + function getFullName(user) { + const firstName = user.firstName; + const lastName = user.lastName; + + return `${firstName} ${lastName}`; + } + + // good + function getFullName(user) { + const { firstName, lastName } = user; + return `${firstName} ${lastName}`; + } + + // best + function getFullName({ firstName, lastName }) { + return `${firstName} ${lastName}`; + } + ``` + + + - [5.2](#destructuring--array) 对数组使用解构赋值。 + jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) + + ```javascript + const arr = [1, 2, 3, 4]; + + // bad + const first = arr[0]; + const second = arr[1]; + + // good + const [first, second] = arr; + ``` + + + - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 + jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) + + > 为什么? 增加属性或者改变排序不会改变调用时的位置。 + + ```javascript + // bad + function processInput(input) { + // then a miracle occurs + return [left, right, top, bottom]; + } + + // 调用时需要考虑回调数据的顺序。 + const [left, __, top] = processInput(input); + + // good + function processInput(input) { + // then a miracle occurs + return { left, right, top, bottom }; + } + + // 调用时只选择需要的数据 + const { left, top } = processInput(input); + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## Strings + + + - [6.1](#strings--quotes) 字符串使用单引号 `''` 。 + eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) + jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) + + ```javascript + // bad + const name = "Capt. Janeway"; + + // bad - template literals should contain interpolation or newlines + const name = `Capt. Janeway`; + + // good + const name = 'Capt. Janeway'; + ``` + + + - [6.2](#strings--line-length) 字符串超过 100 个字节应该使用字符串连接号换行。此处可以更多个字符200或300,目前编辑界面越来越大了 + + > 为什么? 切断长字符串,可以更好编码和搜索。 + + 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + 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.'; + + // bad + 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.'; + + // good + 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.'; + ``` + + + + - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 + 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) + + > 为什么? 模板字符串插值更为简洁,更具可读性。 + + ```javascript + // bad + function sayHi(name) { + return 'How are you, ' + name + '?'; + } + + // bad + function sayHi(name) { + return ['How are you, ', name, '?'].join(); + } + + // bad + function sayHi(name) { + return `How are you, ${ name }?`; + } + + // good + function sayHi(name) { + return `How are you, ${name}?`; + } + ``` + + + - [6.4](#strings--eval) 在字符串中永不使用 `eval()`, 它会导致很多漏洞。 + + + - [6.5](#strings--escaping) 字符串中不要使用不必要的转义。 + eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) + + > 为什么? 反斜杠会降低可读性,应该在必要时才去使用它。 + + ```javascript + // bad + const foo = '\'this\' \i\s \"quoted\"'; + + // good + const foo = '\'this\' is "quoted"'; + const foo = `'this' is "quoted"`; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 函数 + + - [7.1](#7.1) 使用函数表达式,而不是函数声明。 + eslint: [`func-style`](http://eslint.org/docs/rules/func-style) + jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) + + > 为什么? 函数声明会把整个函数提升(hoisted),这导致非常容易在定义以前就被引用,这会降低可读性以及维护性(而函数表达式只会把函数的引用变量名提升)。如果发现一个函数定义非常大或复杂,会干扰其他逻辑的理解,此时也许是时候把它提取成独立模块了。 + > 另外不要忘记给匿名表达式命名,匿名函数会使错误堆栈中跟踪问题更加困难。 + > 函数提升规则,使得[箭头函数](#arrow-functions)可以取代函数表达式。 + + ```javascript + // bad 匿名函数表达式 + var anonymous = function() () { + }; + + // bad + function foo() { + } + + // good + const foo = function named() { + }; + ``` + + + - [7.2](#functions--iife) 用括号包裹 立即调用函数表达式(IIFE)。 + eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) + jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) + + ```javascript + // 立即调用的函数表达式 (IIFE) + // 可使用箭头函数 + (() => { + console.log('Welcome to the Internet. Please follow me.'); + }()); + ``` + + + - [7.3](#functions--in-blocks) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) + + + - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + let test; + if (currentUser) { + test = () => { + console.log('Yup.'); + }; + } + ``` + + + - [7.5](#functions--arguments-shadow) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + + - [7.6](#es6-rest) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) + + > 为什么? 使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 + + ```javascript + // bad + function concatenateAll() { + const args = Array.prototype.slice.call(arguments); + return args.join(''); + } + + // good + function concatenateAll(...args) { + return args.join(''); + } + ``` + + + - [7.7](#es6-default-parameters) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 + + ```javascript + // really bad + function handleThings(opts) { + // 不!我们不应该改变函数参数。 + // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 + // 但这样的写法会造成一些 Bugs。 + //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) + opts = opts || {}; + // ... + } + + // still bad + function handleThings(opts) { + if (opts === void 0) { + opts = {}; + } + // ... + } + + // good + function handleThings(opts = {}) { + // ? 对比以上写法区别是啥?参见 es6 + // ... + } + ``` + + + - [7.8](#functions--default-side-effects) 函数参数设置默认值,要避免副作用。 + + > 为什么? 因为这样会让人感到很困惑。 + + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` + + + - [7.9](#functions--defaults-last) 总是把默认参数最后。 + + ```javascript + // bad + function handleThings(opts = {}, name) { + // ... + } + + // good + function handleThings(name, opts = {}) { + // ... + } + ``` + + + - [7.10](#functions--constructor) 永远不要使用构造函数创建新函数。 + eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) + + > 为什么? 以这种方式创建一个函数,类似 eval(),会导致很多漏洞。 + + ```javascript + // bad + var add = new Function('a', 'b', 'return a + b'); + + // still bad + var subtract = Function('a', 'b', 'return a - b'); + ``` + + + - [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) + + > 为什么? 一致性好,当添加或移除名称时,不必添加或移除空间。 + + ```javascript + // bad + const f = function(){}; + const g = function (){}; + const h = function() {}; + + // good + const x = function () {}; + const y = function a() {}; + ``` + + + - [7.12](#functions--mutate-params) 不要使用变异参数。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + + > 为什么? 操纵传入的参数,在原调用者上可能会导致不必要的变量副作用。 + + ```javascript + // bad + function f1(obj) { + obj.key = 1; + }; + + // good + function f2(obj) { + const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; + }; + ``` + + + - [7.13](#functions--reassign-params) 不要给形参重新赋值。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + + > 为什么? 这样做可能会导致意外行为,特别是操作 `arguments` 参数。这也会导致优化问题,特别是使用 V8 解析器. + + ```javascript + // bad + function f1(a) { + a = 1; + } + + function f2(a) { + if (!a) { a = 1; } + } + + // good + function f3(a) { + const b = a || 1; + } + + function f4(a = 1) { + } + ``` + + + - [7.14](#functions--spread-vs-apply) 使用拓展操作符 `...` 调用可变参函数。 + eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) + + > 为什么? 更简洁、并且也不用提供上下文作用域,而使用 `new` 和 `apply` 也没这个容易。 + + ```javascript + // bad + const x = [1, 2, 3, 4, 5]; + console.log.apply(console, x); + + // good + const x = [1, 2, 3, 4, 5]; + console.log(...x); + + // bad + new (Function.prototype.bind.apply(Date, [null, 2016, 08, 05])); + + // good + new Date(...[2016, 08, 05]); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 箭头函数 + + + - [8.1](#arrows--use-them) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + 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) + + > 为什么? 因为箭头函数会创建一个你通常最想要的 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 + + > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 + + ```javascript + // bad + [1, 2, 3].map(function (x) { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + + + - [8.2](#arrows--implicit-return) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 + + > 为什么? 这是语法糖。在链式调用中可读性很高。 + + > 为什么不?当你打算回传一个对象的时候。 + + ```javascript + // bad + [1, 2, 3].map(number => { + const nextNumber = number + 1; + `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map(number => `A string containing the ${number}.`); + + // good + [1, 2, 3].map((number) => { + const nextNumber = number + 1; + return `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map((number, index) => ({ + [index]: number + })); + ``` + + + - [8.3](#arrows--paren-wrap) 如果表达式有多行,使用圆括号包裹,提高可读性。 + + > 为什么? 这样能更清楚的看到开始和结束位置。 + + ```js + // bad + ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + ); + + // good + ['get', 'post', 'put'].map(httpMethod => ( + Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + )); + ``` + + + - [8.4](#arrows--one-arg-parens) 如果函数只有一个参数就不要使用圆括号,否则总是用圆括号包裹参数。 + eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) + jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) + + > 为什么? 更少的视觉干扰 + + ```js + // bad + [1, 2, 3].map((x) => x * x); + + // good + [1, 2, 3].map(x => x * x); + + // good + [1, 2, 3].reduce((total, n) => { + return total + n; + }, 0); + + // good + [1, 2, 3].map(number => ( + `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` + )); + + // bad + [1, 2, 3].map(x => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + + + - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) + eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) + + ```js + // bad + const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; + + // bad + const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; + + // good + const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); + + // good + const itemHeight = (item) => { + const { height, largeSize, smallSize } = item; + return height > 256 ? largeSize : smallSize; + }; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 类 & 构造器 + + + - [9.1](#constructors--use-class) 总是使用 `class`。避免直接操作 `prototype` 。 + + > 为什么? 因为 `class` 语法更为简洁更易读。 + + ```javascript + // bad + function Queue(contents = []) { + this.queue = [...contents]; + } + Queue.prototype.pop = function () { + const value = this.queue[0]; + this.queue.splice(0, 1); + return value; + }; + + + // good + class Queue { + constructor(contents = []) { + this.queue = [...contents]; + } + pop() { + const value = this.queue[0]; + this.queue.splice(0, 1); + return value; + } + } + ``` + + + - [9.2](#constructors--extends) 使用 `extends` 继承。 + + > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + + ```javascript + // bad + const inherits = require('inherits'); + function PeekableQueue(contents) { + Queue.apply(this, contents); + } + inherits(PeekableQueue, Queue); + PeekableQueue.prototype.peek = function () { + return this.queue[0]; + } + + // good + class PeekableQueue extends Queue { + peek() { + return this.queue[0]; + } + } + ``` + + + - [9.3](#constructors--chaining) 方法可以返回 `this` 以便于链式调用。 + + ```javascript + // bad + Jedi.prototype.jump = function () { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function (height) { + this.height = height; + }; + + const luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + class Jedi { + jump() { + this.jumping = true; + return this; + } + + setHeight(height) { + this.height = height; + return this; + } + } + + const luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + + - [9.4](#constructors--tostring) 可以自定义 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 + + ```javascript + class Jedi { + constructor(options = {}) { + this.name = options.name || 'no name'; + } + + getName() { + return this.name; + } + + toString() { + return `Jedi - ${this.getName()}`; + } + } + ``` + + + - [9.5](#constructors--no-useless) 类有默认构造函数,书写一个空的构造函数是没必要的。 + eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) + + ```javascript + // bad + class Jedi { + constructor() {} + + getName() { + return this.name; + } + } + + // bad + class Rey extends Jedi { + constructor(...args) { + super(...args); + } + } + + // good + class Rey extends Jedi { + constructor(...args) { + super(...args); + this.name = 'Rey'; + } + } + ``` + + + - [9.6](#classes--no-duplicate-members) 避免类成员重复。 + eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) + + > 为什么? 重复的类成员默认会使用最后一个——这肯定有一个错误存在。 + + ```javascript + // bad + class Foo { + bar() { return 1; } + bar() { return 2; } + } + + // good + class Foo { + bar() { return 1; } + } + + // good + class Foo { + bar() { return 2; } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 模块 + + + - [10.1](#modules--use-them) 总是使用模块 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + > 为什么? 模块就是未来,让我们开始迈向未来吧。 + > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) + + ```javascript + // bad + const AirbnbStyleGuide = require('./AirbnbStyleGuide'); + module.exports = AirbnbStyleGuide.es6; + + // ok + import AirbnbStyleGuide from './AirbnbStyleGuide'; + export default AirbnbStyleGuide.es6; + + // best + import { es6 } from './AirbnbStyleGuide'; + export default es6; + + // ES5中要注意 + // old + // - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + // - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + // - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + // - 总是在模块顶部声明 `'use strict';` + ``` + + + - [10.2](#modules--no-wildcard) 不要使用通配符 import。 + + > 为什么? 这样能确保你只有一个默认 export。 + + ```javascript + // bad + import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + + // good + import AirbnbStyleGuide from './AirbnbStyleGuide'; + ``` + + + - [10.3](#modules--no-export-from-import) 不要从 import 中直接 export。 + + > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + ```javascript + // bad + // filename es6.js + export { es6 as default } from './AirbnbStyleGuide'; + + // good + // filename es6.js + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + + + - [10.4](#modules--no-duplicate-imports) 一个模块只要一个 `import` 。 + eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) + + > 为什么? 同一模块多个 `import` 导入,会使代码更难维护。 + + ```javascript + // bad + import foo from 'foo'; + // … some other imports … // + import { named1, named2 } from 'foo'; + + // good + import foo, { named1, named2 } from 'foo'; + + // good + import foo, { + named1, + named2, + } from 'foo'; + ``` + + + - [10.5](#modules--no-mutable-exports) 禁止 `export` 可写值输出,使用常量(Functions/Classes 暂时例外)。 + eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) + + > 为什么? 避免全局使用时出现问题,除了某些特定情况下确实要这样用。一般来说,`export` 应该输出常量。 + + ```javascript + // bad + var count = 3; + export { count } + + // bad + let foo = 3; + export { foo } + + // good + const foo = 3; + export { foo } + + // valid + export const count = 1 + export function getCount () {} + export class Counter {} + ``` + + + - [10.6](#modules--prefer-default-export) 单出口模块,更倾向设置为默认出口。 +eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) + + ```javascript + // bad + export function foo() {} + + // good + export default function foo() {} + ``` + + + - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 +eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) + + > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 + + ```javascript + // bad + import foo from 'foo'; + foo.init(); + + import bar from 'bar'; + + // good + import foo from 'foo'; + import bar from 'bar'; + + foo.init(); + ``` + + + - [10.8](#modules--multiline-imports-over-newlines) 多 `import` 入口,代码书写格式应与 `array` 和 `object` 保持一致。 + + > 为什么? 大括号代码块、缩进以及逗号都保持风格一致 + + ```javascript + // bad + import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; + + // good + import { + longNameA, + longNameB, + longNameC, + longNameD, + longNameE, + } from 'path'; + ``` + + + - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 +eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) + + > 为什么? 加载器语法要在 `webpack.config.js` 中使用 + + ```javascript + // bad + import fooSass from 'css!sass!foo.scss'; + import barCss from 'style!css!bar.css'; + + // good + import fooSass from 'foo.scss'; + import barCss from 'bar.css'; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Iterators and Generators 迭代器 & 生成器 + + + - [11.1](#iterators--nope) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-in` or `for-of`。 + eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](http://eslint.org/docs/rules/no-restricted-syntax) + + > 为什么? 这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + + > 使用迭代器 `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... 遍历数组,使用 `Object.keys()` / `Object.values()` / `Object.entries()` 遍历对象。 + ```javascript + const numbers = [1, 2, 3, 4, 5]; + + // bad + let sum = 0; + for (let num of numbers) { + sum += num; + } + sum === 15; + + // good + let sum = 0; + numbers.forEach(num => sum += num); + sum === 15; + + // best (use the functional force) + const sum = numbers.reduce((total, num) => total + num, 0); + sum === 15; + ``` + + + - [11.2](#generators--nope) 现在还不要使用生成器 generators。 + + > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + + + - [11.3](#generators--spacing) 如果你必须使用generators 或无视[generators 忠告](#generators--nope),那么请务必确保函数签名(function*)是适当的间距。 + eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) + + > 为什么? `function` and `*` 是同一个关键词的不同组成部分—— `*` 不是 `function` 修饰符, `function*` 是一种特定结构, 不同于 `function`。 + + ```js + // bad + function * foo() { + } + + const bar = function * () { + } + + const baz = function *() { + } + + const quux = function*() { + } + + function*foo() { + } + + function *foo() { + } + + // very bad + function + * + foo() { + } + + const wat = function + * + () { + } + + // good + function* foo() { + } + + const foo = function* () { + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 属性 + + + - [12.1](#properties--dot) 使用 `.` 符号来访问对象的属性。 + eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) + jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + // bad + const isJedi = luke['jedi']; + + // good + const isJedi = luke.jedi; + ``` + + + - [12.2](#properties--bracket) 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + function getProp(prop) { + return luke[prop]; + } + + const isJedi = getProp('jedi'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 变量 + + + - [13.1](#variables--const) 总是使用 `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) + + ```javascript + // bad + superPower = new SuperPower(); + + // good + const superPower = new SuperPower(); + ``` + + + - [13.2](#variables--one-const) 使用 `const` 声明每一个变量。 + eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) + + > 为什么? 增加新变量将更容易,而且你永远不用再担心调换错 `;` or `,` ,并且 diff 工具下更少干扰。同时在 debugger 时,可以单步调试而不会一次跳过他们所有变量。 + + ```javascript + // bad + const items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (compare to above, and try to spot the mistake) + const items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + const items = getItems(); + const goSportsTeam = true; + const dragonball = 'z'; + ``` + + + - [13.3](#variables--const-let-group) 将所有的 `const` 和 `let` 分组 + + > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 + + ```javascript + // bad + let i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + let i; + const items = getItems(); + let dragonball; + const goSportsTeam = true; + let len; + + // good + const goSportsTeam = true; + const items = getItems(); + let dragonball; + let i; + let length; + ``` + + + - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 + + > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 + + ```javascript + // bad - unnecessary function call + function checkName(hasName) { + const name = getName(); + + if (hasName === 'test') { + return false; + } + + if (name === 'test') { + this.setName(''); + return false; + } + + return name; + } + + // good + function checkName(hasName) { + if (hasName === 'test') { + return false; + } + + const name = getName(); + + if (name === 'test') { + this.setName(''); + return false; + } + + return name; + } + ``` + + + - [13.5](#variables--no-chain-assignment) 不使用链接变量赋值。 + + > 为什么? 链接变量赋值会创建隐式全局变量。 + + ```javascript + // bad + (function example() { + // JavaScript interprets this as + // let a = ( b = ( c = 1 ) ); + // The let keyword only applies to variable a; variables b and c become + // global variables. + let a = b = c = 1; + }()); + + console.log(a); // undefined + console.log(b); // 1 + console.log(c); // 1 + + // good + (function example() { + let a = 1; + let b = a; + let c = a; + }()); + + console.log(a); // undefined + console.log(b); // undefined + console.log(c); // undefined + + // the same applies for `const` + ``` + + + - [13.6](#variables--unary-increment-decrement) 便面使用一元递增和递减操作 (++, --) 。 + eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) + + > 为什么? 一元递增和递减语句默认在自动分号插入处理下会有错误问题。而且它还会出现 `num += 1` 替代 `num ++` 的情况。不允许使用它们,也可以代码更健壮。 + + ```javascript + // bad + + let array = [1, 2, 3]; + let num = 1; + num ++; + -- num; + + let sum = 0; + let truthyCount = 0; + for(let i = 0; i < array.length; i++){ + let value = array[i]; + sum += value; + if (value) { + truthyCount++; + } + } + + // good + + let array = [1, 2, 3]; + let num = 1; + num += 1; + num -= 1; + + const sum = array.reduce((a, b) => a + b, 0); + const truthyCount = array.filter(Boolean).length; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Hoisting + + + - [14.1](#hoisting--about) `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)相当重要。 + + ```javascript + // 我们知道这样运行不了(假设 notDefined 不是全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 由于变量提升的原因, + // 在引用变量后再声明变量是可以运行的。 + // **注意:** 变量赋值 `true` 的操作不会被提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 编译器会把函数声明提升到作用域的顶层, + // 这意味着我们的例子可以改写成这样: + function example() { + let declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + + // 使用 const 和 let + function example() { + console.log(declaredButNotAssigned); // => throws a ReferenceError + console.log(typeof declaredButNotAssigned); // => throws a ReferenceError + const declaredButNotAssigned = true; + } + ``` + + + - [14.2](#hoisting--anon-expressions) 匿名函数表达式的变量名会被提升,但函数体并不会。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function () { + console.log('anonymous function expression'); + }; + } + ``` + + + - [14.3](#hoisting--named-expresions) 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // the same is true when the function name + // is the same as the variable name. + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + + - [14.4](#hoisting--declarations) 函数声明的名称和函数体都会被提升。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/). + + - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 比较运算符 & 等号 + + + - [15.1](#comparison--eqeqeq) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=` 。 + eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html) + + + - [15.2](#comparison--if) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **Objects 对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **Booleans 布尔值** 被计算为 **布尔的值** + + **Numbers 数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** + + **Strings 字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0] && []) { + // true + // an array (even an empty one) is an object, objects will evaluate to true + } + ``` + + + - [15.3](#comparison--shortcuts) 使用简写。 + + ```javascript + // bad + if (isValid === true) { + // ...stuff... + } + + // good + if (isValid) { + // ...stuff... + } + + // bad + if (name) { + // ...stuff... + } + + // good + if (name !== '') { + // ...stuff... + } + + // bad + if (collection.length) { + // ...stuff... + } + + // good + if (collection.length > 0) { + // 为什么没用简写??? + // ...stuff... + } + ``` + + + - [15.4](#comparison--moreinfo) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + + + - [15.5](#comparison--switch-blocks) `case` 以及 `default` 情况下使用括号会创建块级作用域 (e.g. `let`, `const`, `function`, and `class`). + + > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 + + eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). + + ```javascript + // ??? + // bad + switch (foo) { + case 1: + let x = 1; + break; + case 2: + const y = 2; + break; + case 3: + function f() {} + break; + default: + class C {} + } + + // good + switch (foo) { + case 1: { + let x = 1; + break; + } + case 2: { + const y = 2; + break; + } + case 3: { + function f() {} + break; + } + case 4: + bar(); + break; + default: { + class C {} + } + } + ``` + + + - [15.6](#comparison--nested-ternaries) 别嵌套使用三元表达式,要单个使用。 + + eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). + + ```javascript + // bad + const foo = maybe1 > maybe2 + ? "bar" + : value1 > value2 ? "baz" : null; + + // better + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 + ? 'bar' + : maybeNull; + + // best + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 ? 'bar' : maybeNull; + ``` + + + - [15.7](#comparison--unneeded-ternary) 避免不必要使用三元表达式 + + eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). + + ```javascript + // bad + const foo = a ? a : b; + const bar = c ? true : false; + const baz = c ? false : true; + + // good + const foo = a || b; + const bar = !!c; + const baz = !c; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 代码块 + + + - [16.1](#blocks--braces) 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function foo() { return false; } + + // good + function bar() { + return false; + } + ``` + + + - [16.2](#blocks--cuddled-elses) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 注释 + + + - [17.1](#comments--multiline) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed-in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + + - [17.2](#comments--singleline) 单行注释使用 `//`,后紧跟一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + const active = true; // is current tab + + // good + // is current tab + const active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // also good + function getType() { + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + ``` + + + - [17.3](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + + - [17.4](#comments--fixme) 使用 `// FIXME`: 标注问题。 + + ```javascript + class Calculator extends Abacus { + constructor() { + super(); + + // FIXME: shouldn't use a global here + total = 0; + } + } + ``` + + + - [17.5](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 + + ```javascript + class Calculator extends Abacus { + constructor() { + super(); + + // TODO: total should be configurable by an options param + this.total = 0; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 空白 + + + - [18.1](#whitespace--spaces) 使用 2 个空格作为缩进。 + eslint: [`indent`](http://eslint.org/docs/rules/indent.html) + jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) + + ```javascript + // bad + function foo() { + ∙∙∙∙const name; + } + + // bad + function bar() { + ∙const name; + } + + // good + function baz() { + ∙∙const name; + } + ``` + + + - [18.2](#whitespace--before-blocks) 在花括号前放一个空格。 + eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) + jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + ``` + + + - [18.3](#whitespace--around-keywords) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) + jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + + - [18.4](#whitespace--infix-ops) 使用空格把运算符隔开。 + 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) + + ```javascript + // bad + const x=y+5; + + // good + const x = y + 5; + ``` + + + - [18.5](#whitespace--newline-at-end) 在文件末尾插入一个空行。 + eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function (global) { + // ...stuff... + })(this);↵ + ``` + + + - [18.6](#whitespace--chains) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 + 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) + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led').data(data); + ``` + + + - [18.7](#whitespace--after-blocks) 在块末和新语句前插入空行。 + jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + const obj = { + foo() { + }, + bar() { + }, + }; + return obj; + + // good + const obj = { + foo() { + }, + + bar() { + }, + }; + + return obj; + + // bad + const arr = [ + function foo() { + }, + function bar() { + }, + ]; + return arr; + + // good + const arr = [ + function foo() { + }, + + function bar() { + }, + ]; + + return arr; + ``` + + + - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) + + ```javascript + // bad + function bar() { + + console.log(foo); + + } + + // also bad + if (baz) { + + console.log(qux); + } else { + console.log(foo); + + } + + // good + function bar() { + console.log(foo); + } + + // good + if (baz) { + console.log(qux); + } else { + console.log(foo); + } + ``` + + + - [18.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) + + ```javascript + // bad + function bar( foo ) { + return foo; + } + + // good + function bar(foo) { + return foo; + } + + // bad + if ( foo ) { + console.log(foo); + } + + // good + if (foo) { + console.log(foo); + } + ``` + + + - [18.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) + + ```javascript + // bad + const foo = [ 1, 2, 3 ]; + console.log(foo[ 0 ]); + + // good + const foo = [1, 2, 3]; + console.log(foo[0]); + ``` + + + - [18.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) + + ```javascript + // bad + const foo = {clark: 'kent'}; + + // good + const foo = { clark: 'kent' }; + ``` + + + - [18.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) + + > Why? This ensures readability and maintainability. + + ```javascript + // bad + const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; + + // bad + $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); + + // good + const foo = jsonData + && jsonData.foo + && jsonData.foo.bar + && jsonData.foo.bar.baz + && jsonData.foo.bar.baz.quux + && jsonData.foo.bar.baz.quux.xyzzy; + + // good + $.ajax({ + method: 'POST', + url: 'https://airbnb.com/', + data: { name: 'John' }, + }) + .done(() => console.log('Congratulations!')) + .fail(() => console.log('You have failed this city.')); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 逗号 + + + - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 + eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) + jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) + + ```javascript + // bad + const story = [ + once + , upon + , aTime + ]; + + // good + const story = [ + once, + upon, + aTime, + ]; + + // bad + const hero = { + firstName: 'Ada' + , lastName: 'Lovelace' + , birthYear: 1815 + , superPower: 'computers' + }; + + // good + const hero = { + firstName: 'Ada', + lastName: 'Lovelace', + birthYear: 1815, + superPower: 'computers', + }; + ``` + + + - [19.2](#commas--dangling) 增加结尾的逗号: **需要**。 + eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) + jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) + + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 + + ```javascript + // bad - git diff without trailing comma + const hero = { + firstName: 'Florence', + - lastName: 'Nightingale' + + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'] + }; + + // good - git diff with trailing comma + const hero = { + firstName: 'Florence', + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'], + }; + + // bad + const hero = { + firstName: 'Dana', + lastName: 'Scully' + }; + + const heroes = [ + 'Batman', + 'Superman' + ]; + + // good + const hero = { + firstName: 'Dana', + lastName: 'Scully', + }; + + const heroes = [ + 'Batman', + 'Superman', + ]; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 分号 + + + - [20.1](#semicolons--required) **使用分号** + eslint: [`semi`](http://eslint.org/docs/rules/semi.html) + jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) + + ```javascript + // bad + (function () { + const name = 'Skywalker' + return name + })() + + // good + (function () { + const name = 'Skywalker'; + return name; + }()); + + // good (防止函数在两个 IIFE 合并时被当成一个参数) + ;(() => { + const name = 'Skywalker'; + return name; + }()); + ``` + + [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). + +**[⬆ 返回目录](#table-of-contents)** + + +## 类型转换 + + + - [21.1](#coercion--explicit) 在语句开始时执行类型转换。 + + + - [21.2](#coercion--strings) 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() + + // bad + const totalScore = this.reviewScore.toString(); // isn't guaranteed to return a string + + // good + const totalScore = String(this.reviewScore); + ``` + + + - [21.3](#coercion--numbers) 数字:对数字使用 `parseInt` 转换,并带上类型转换的基数。 + eslint: [`radix`](http://eslint.org/docs/rules/radix) + + ```javascript + const inputValue = '4'; + + // bad + const val = new Number(inputValue); + + // bad + const val = +inputValue; + + // bad + const val = inputValue >> 0; + + // bad + const val = parseInt(inputValue); + + // good + const val = Number(inputValue); + + // good + const val = parseInt(inputValue, 10); + ``` + + + - [21.4](#coercion--comment-deviations) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * 使用 parseInt 导致我的程序变慢, + * 改成使用位操作转换数字快多了。 + */ + const val = inputValue >> 0; + ``` + + + - [21.5](#coercion--bitwise) **注意:** 小心使用位操作运算符。数字会被当成 [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: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + + - [21.6](#coercion--booleans) 布尔运算: + + ```javascript + const age = 0; + + // bad + const hasAge = new Boolean(age); + + // good + const hasAge = Boolean(age); + + // best + const hasAge = !!age; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 命名规则 + + + - [22.1](#naming--descriptive) 避免单字母命名。命名应具备描述性。 + eslint: [`id-length`](http://eslint.org/docs/rules/id-length) + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + + - [22.2](#naming--camelCase) 使用驼峰式(camelCase、小驼峰)命名变量、对象、函数和实例。 + eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) + jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) + + ```javascript + // bad + const OBJEcttsssss = {}; + const this_is_my_object = {}; + function c() {} + + // good + const thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + + - [22.3](#naming--PascalCase) 使用帕斯卡式(PascalCase、大驼峰式)命名构造函数或类。 + eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) + jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) + + Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + const bad = new user({ + name: 'nope', + }); + + // good + class User { + constructor(options) { + this.name = options.name; + } + } + + const good = new User({ + name: 'yup', + }); + ``` + + + - [22.4](#naming--leading-underscore) 不要使用尾随或前导下划线来命名私有属性。 + eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) + jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) + + > 为什么? JavaScript没有在属性或方法方面的隐私权的概念。虽然一个领先的下划线是一个共同的公约,意思是“私人”,事实上,这些属性是完全公开的,因此,是你的公共API合同的一部分。这个约定可能会导致开发人员错误地认为一个变化不算是中断,或者不需要测试。TL;DR:如果你想要的东西是“私人的”,它不应该可见。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + this._firstName = 'Panda'; + + // good + this.firstName = 'Panda'; + ``` + + + - [22.5](#naming--self-this) 别保存 `this` 的引用。应使用箭头函数或函数绑定 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)。 + jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) + + ```javascript + // bad + function foo() { + const self = this; + return function () { + console.log(self); + }; + } + + // bad + function foo() { + const that = this; + return function () { + console.log(that); + }; + } + + // good + function foo() { + return () => { + console.log(this); + }; + } + ``` + + + - [22.6](#naming--filename-matches-export) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 + + ```javascript + // file 1 contents + class CheckBox { + // ... + } + export default CheckBox; + + // file 2 contents + export default function fortyTwo() { return 42; } + + // file 3 contents + export default function insideDirectory() {} + + // in some other file + // bad + import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename + import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export + import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export + + // bad + import CheckBox from './check_box'; // PascalCase import/export, snake_case filename + import forty_two from './forty_two'; // snake_case import/filename, camelCase export + import inside_directory from './inside_directory'; // snake_case import, camelCase export + import index from './inside_directory/index'; // requiring the index file explicitly + import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly + + // good + import CheckBox from './CheckBox'; // PascalCase export/import/filename + import fortyTwo from './fortyTwo'; // camelCase export/import/filename + import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" + // ^ supports both insideDirectory.js and insideDirectory/index.js + ``` + + + - [22.7](#naming--camelCase-default-export) 当你导出默认的函数时使用驼峰式命名camelCase。你的文件名必须和函数名完全保持一致。 + + ```javascript + function makeStyleGuide() { + } + + export default makeStyleGuide; + ``` + + + - [22.8](#naming--PascalCase-singleton) 当你导出构造函数、类、单例、函数库、空对象时使用帕斯卡式命名PascalCase。 + + ```javascript + const AirbnbStyleGuide = { + es6: { + } + }; + + export default AirbnbStyleGuide; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 存取器 + + + - [23.1](#accessors--not-required) 属性的存取函数不是必须的。 + + + - [23.2](#accessors--no-getters-setters) 不要使用 getters/setters 有可能引起副作用,很难测试、维护、调试。如果你需要存取函数时使用 `getVal()` 和 `setVal(value)`。 + + ```javascript + // bad + class Dragon { + get age() { + // ... + } + + set age(value) { + // ... + } + } + + // good + class Dragon { + getAge() { + // ... + } + + setAge(value) { + // ... + } + } + ``` + + + - [23.3](#accessors--boolean-prefix) 如果属性或方法是布尔值,使用 `isVal()` 或 `hasVal()` 判断。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + + - [23.4](#accessors--consistent) 可以创建 `get()` 和 `set()` 函数,但要保持一致。 + + ```javascript + class Jedi { + constructor(options = {}) { + const lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + set(key, val) { + this[key] = val; + } + + get(key) { + return this[key]; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 事件 + + + - [24.1](#events--hash) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```javascript + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', (e, listingId) => { + // do something with listingId + }); + ``` + + prefer 更好的写法: + + ```javascript + // good + $(this).trigger('listingUpdated', { listingId: listing.id }); + + ... + + $(this).on('listingUpdated', (e, data) => { + // do something with data.listingId + }); + ``` + + **[⬆ 返回目录](#table-of-contents)** + +## jQuery + + + - [25.1](#jquery--dollar-prefix) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) + + ```javascript + // bad + const sidebar = $('.sidebar'); + + // good + const $sidebar = $('.sidebar'); + + // good + const $sidebarBtn = $('.sidebar-btn'); + ``` + + + - [25.2](#jquery--cache) 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + const $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + + - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 + [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + + + - [25.4](#jquery--find) 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + + // good (slower) + $sidebar.find('ul'); + + // good (faster) + $($sidebar[0]).find('ul'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + + - [26.1](#es5-compat--kangax) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 6(ES 2015+)规范 + + + - [27.1](#es6-styles) 以下是链接到 ES6 的各个特性的列表。 + +1. [Arrow Functions](#arrow-functions) +1. [Classes](#classes--constructors) +1. [Object Shorthand](#es6-object-shorthand) +1. [Object Concise](#es6-object-concise) +1. [Object Computed Properties](#es6-computed-properties) +1. [Template Strings](#es6-template-literals) +1. [Destructuring](#destructuring) +1. [Default Parameters](#es6-default-parameters) +1. [Rest](#es6-rest) +1. [Array Spreads](#es6-array-spreads) +1. [Let and Const](#references) +1. [Iterators and Generators](#iterators-and-generators) +1. [Modules](#modules) + + + - [27.2](#tc39-proposals) Do not use [TC39 proposals](https://github.com/tc39/proposals) that have not reached stage 3. + + > Why? [They are not finalized](https://tc39.github.io/process-document/), and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet. + +**[⬆ 返回目录](#table-of-contents)** + + +## 测试 + + + - [28.1](#testing--yup) **Yup.** + + ```javascript + function foo() { + return true; + } + ``` + + + - [28.2](#testing--for-real) **No, but seriously**: + + - Whichever testing framework you use, you should be writing tests! + - Strive to write many small pure functions, and minimize where mutations occur. + - Be cautious about stubs and mocks - they can make your tests more brittle. + - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. + - 100% test coverage is a good goal to strive for, even if it's not always practical to reach it. + - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future. + +**[⬆ 返回目录](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](https://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](https://jsperf.com/ya-string-concat) + - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) + - Loading... + +**[⬆ 返回目录](#table-of-contents)** + + +## 资源 + +**Learning ES6** + + - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) + - [ExploringJS](http://exploringjs.com/) + - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) + - [Comprehensive Overview of ES6 Features](http://es6-features.org/) + +**Read This** + + - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) + - old [Annotated ECMAScript 5.1](http://es5.github.com/) + +**Tools** + + - Code Style Linters + + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) + + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**Other Style Guides** + + - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) + - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) + +**Other Styles** + + - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on GitHub](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**Further Reading** + + - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**Books** + + - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke + - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson + +**Blogs** + + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](https://bocoup.com/weblog) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](https://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://code.tutsplus.com/?s=javascript) + +**播客 Podcasts** + + - [JavaScript Air](https://javascriptair.com/) + - [JavaScript Jabber](https://devchat.tv/js-jabber/) + + +**[⬆ 返回目录](#table-of-contents)** + +## 使用人群 + + This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. + + - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Ascribe**: [ascribe/javascript](https://github.com/ascribe/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) + - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) + - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) + - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) + - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **DoSomething**: [DoSomething/eslint-config](https://github.com/DoSomething/eslint-config) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript) + - **EvozonJs**: [evozonjs/javascript](https://github.com/evozonjs/javascript) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) + - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) + - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) + - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **KickorStick**: [kickorstick/javascript](https://github.com/kickorstick/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) + - **Lonely Planet**: [lonelyplanet/javascript](https://github.com/lonelyplanet/javascript) + - **M2GEN**: [M2GEN/javascript](https://github.com/M2GEN/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) + - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **Springload**: [springload/javascript](https://github.com/springload/javascript) + - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) + - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **The Nerdery**: [thenerdery/javascript-standards](https://github.com/thenerdery/javascript-standards) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +**[⬆ 返回目录](#table-of-contents)** + +## 翻译 + + This style guide is also available in other languages: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javascript-style-guide](https://github.com/mitsuruog/javascript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [giangpii/javascript-style-guide](https://github.com/giangpii/javascript-style-guide) + + +## JavaScript 编码规范说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + + +## 一起来讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + + +## 贡献者 Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 License + +(The MIT License) + +Copyright (c) 2014-2017 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 返回目录](#table-of-contents)** + + +## 修正案 Amendments + +We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts. + +# }; From 7ff1c1f2473ab56a7b7ed906e09f095a68f72f86 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Thu, 9 Feb 2017 17:12:44 +0800 Subject: [PATCH 07/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es6_zh-cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/es6_zh-cn.md b/es6_zh-cn.md index 606518e1cc..deac10dbc1 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -117,7 +117,7 @@ Other Style Guides ## 引用 - - [2.1](#references--prefer-const) 对所有的引用使用 `const` ;不要使用 `var`。 + - [2.1](#references--prefer-const) 对所有的引用使用 `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) > 为什么? 这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 @@ -290,7 +290,7 @@ Other Style Guides eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) - > 为什么? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. + > 为什么? 通常我认为这样更容易阅读,这样可以使用语法高亮并且许多 js 引擎也更容易优化代码。 ```javascript // bad @@ -311,7 +311,7 @@ Other Style Guides - [3.7](#objects--prototype-builtins) 不要直接调用 `Object.prototype` 的方法,如 `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf` - > 为什么? 这些方法可能会有问题,比如 `{ hasOwnProperty: false }` 的情况 或者 可能是 null object (`Object.create(null)`). + > 为什么? 这些方法可能会有问题,比如 `{ hasOwnProperty: false }` 的情况 或者 可能是 null object (`Object.create(null)`)。 ```javascript // bad From b006fb636b9b359ef30215f0c44fb7fcb59a7da4 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Thu, 9 Feb 2017 17:24:49 +0800 Subject: [PATCH 08/33] =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=86=99=E6=88=90?= =?UTF-8?q?=E5=A4=9A=E8=A1=8C=E6=97=B6=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es6_zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es6_zh-cn.md b/es6_zh-cn.md index deac10dbc1..90b720b9a2 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -939,7 +939,7 @@ Other Style Guides ``` - - [7.15](#functions--signature-invocation-indentation) 多参函数声明或调用,书写规则: 每项独占一行,保持缩进, 且后面跟个逗号。 + - [7.15](#functions--signature-invocation-indentation) 函数声明或调用参数写成多行时,书写规则: 每项独占一行,保持缩进,且后面跟个逗号。 ```javascript // bad From 6c76699725aceec53cf179f1ed4d28dcaa71938e Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Thu, 9 Feb 2017 20:56:16 +0800 Subject: [PATCH 09/33] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E6=B3=95?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintignore | 3 ++ .eslintrc | 43 +++++++++++++++++++++ es6_zh-cn.md | 103 +++++++++++++++++++++++++++++--------------------- test.js | 93 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 43 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 test.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..2641c16f85 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules/* +**/vendor/*.js +dist/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..1c2d9ff5e7 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,43 @@ + +# JS 书写规范 +# http://eslint.org +# http://standardjs.com/rules.html +# +# @dependencies 安装到全局,就不用每个项目单独安装了 +# standard: npm install -g eslint-config-standard eslint-plugin-standard eslint-plugin-promise +# airbnb: npm install -g eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y eslint-plugin-airbnb + +# NOTE: 在新项目中使用 error 级别,在老项目中使用 warning 级别 + +{ + root: true, + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, + globals: { + "Vue": false, + }, + + "plugins": [ + "html", + ], + # https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style + # https://github.com/feross/eslint-config-standard/blob/master/eslintrc.json + # extends: 'standard', + # ES6 推荐规范 + extends: 'airbnb', + # extends: 'defaults/configurations/airbnb/es6', + + # add your custom rules here + rules: { + # 行尾分号,默认配置always,要求在行末加上分号,standard 配置强制不带 + semi: ['error', "never"], + # 多行模式必须带逗号,单行模式不能带逗号 + 'comma-dangle': ['error', 'always-multiline'], + # 禁止使用 console debugger + 'no-console': 1, + 'no-debugger': 1, + }, +} diff --git a/es6_zh-cn.md b/es6_zh-cn.md index 90b720b9a2..17d06e3066 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -8,16 +8,33 @@ NOTE: 本文对[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 略有调整具体如下, -- 尾逗号单行不要有,多行必须有。修改规则 `comma-dangle: ['error', 'never']` 中 never 改为 only-multiline,注意不是 always-multiline。 +- 尾逗号单行不要有,多行必须有。修改规则 `comma-dangle: ['error', 'never']` 中 never 改为 always-multiline。 - 行尾不要分号。调整后,代码更干净、整洁,但要注意 (, [, + , -, or ` 开头的语句,可能在“自动分号插入”机制(ASI)下会有问题。
    为什么? 参看[是否要分号的讨论](https://github.com/feross/standard/blob/master/RULES.md#helpful-reading) -- 本文档有大量对 ES6 新特性规则要求,此外的书写要求兼容 [ES5 代码规范](http://standardjs.com/rules.html)。 +- 本文档有大量对 ES6 新特性规则要求,此外的书写要求兼容 [ES5 代码规范](http://standardjs.com/rules.html)。以下是一些空白相关的部分 + - 不要行尾空格 + - 文档底部保留一个空行 + - 空白行不要字符 + - 最多可以连续使用两个空白行 该文档保留了部分还属于 ES5 范畴的注意内容,由 old: ES5 标记。 ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v2.md)。 ES6 的编码规范请查看下文(最新版),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es6_zh-cn_v2.md) +## Usage + +``` +# 安装依赖到全局,就不用每个项目单独安装了 +# standard: npm install -g eslint-config-standard eslint-plugin-standard eslint-plugin-promise +# airbnb: npm install -g eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y eslint-plugin-airbnb +# 推荐使用 airbnb,目前稍有改动,具体详见[.eslintrc](./.eslintrc) + +# 测试配置效果,可以参看文档用例,使用 test.js 集中测试,可以保留用例 +``` + +NOTE: 使用规范最好的方式,就是配置好,直接用,然后各种问题就出来了 + [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -74,7 +91,7 @@ Other Style Guides -## 类型 +## 类型 - [1.1](#types--primitives) **基本类型**: 直接存取基本类型。 @@ -134,7 +151,7 @@ Other Style Guides - [2.2](#references--disallow-var) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 - eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) + eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) > 为什么? 因为 `let` 是块级作用域,而 `var` 是函数作用域。 @@ -183,7 +200,7 @@ Other Style Guides // good const item = {}; ``` - + - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 @@ -211,7 +228,7 @@ Other Style Guides - [3.3](#es6-object-shorthand) 使用对象方法的简写。 - eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) ```javascript @@ -236,7 +253,7 @@ Other Style Guides - [3.4](#es6-object-concise) 使用对象属性值的简写。 - eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) > 为什么? 因为这样更短更有描述性。 @@ -287,7 +304,7 @@ Other Style Guides - [3.6](#objects--quoted-props) 只用引号包裹属性名称是无效标识符的那些。 - eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) + eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) > 为什么? 通常我认为这样更容易阅读,这样可以使用语法高亮并且许多 js 引擎也更容易优化代码。 @@ -511,7 +528,7 @@ Other Style Guides ## 解构 - - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 + - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) > 为什么? 因为解构能减少临时引用属性。 @@ -538,7 +555,7 @@ Other Style Guides ``` - - [5.2](#destructuring--array) 对数组使用解构赋值。 + - [5.2](#destructuring--array) 对数组使用解构赋值。 jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) ```javascript @@ -553,9 +570,9 @@ Other Style Guides ``` - - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 + - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) - + > 为什么? 增加属性或者改变排序不会改变调用时的位置。 ```javascript @@ -587,7 +604,7 @@ Other Style Guides - [6.1](#strings--quotes) 字符串使用单引号 `''` 。 - eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) + eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) ```javascript @@ -626,7 +643,7 @@ Other Style Guides - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 - eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) + 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) > 为什么? 模板字符串插值更为简洁,更具可读性。 @@ -679,7 +696,7 @@ Other Style Guides - [7.1](#functions--declarations) 使用函数表达式,而不是函数声明。 - eslint: [`func-style`](http://eslint.org/docs/rules/func-style) + eslint: [`func-style`](http://eslint.org/docs/rules/func-style) jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) > 为什么? 函数声明会把整个函数提升(hoisted),这导致非常容易在定义以前就被引用,这会降低可读性以及维护性(而函数表达式只会把函数的引用变量名提升)。如果发现一个函数定义非常大或复杂,会干扰其他逻辑的理解,此时也许是时候把它提取成独立模块了。 @@ -705,7 +722,7 @@ Other Style Guides - [7.2](#functions--iife) 用括号包裹 立即调用函数表达式(IIFE)。 - eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) + eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) > 为什么? 立即调用表达式(IIFE) 要使用圆括号包裹成一个独立的单元,这样表达更清晰。NOTE: 在模块的世界,你已经不再需要 IIFE 了。 @@ -979,7 +996,7 @@ Other Style Guides - [8.1](#arrows--use-them) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 - eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) + 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) > 为什么? 因为箭头函数会创建一个你通常最想要的 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 @@ -1052,8 +1069,8 @@ Other Style Guides ``` - - [8.4](#arrows--one-arg-parens) 如果函数需要一个参数就省略掉圆括号,否则总是用圆括号包裹参数,这样更清晰。NOTE: 总是使用圆括号也是可以接受的,但要设置在 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) + - [8.4](#arrows--one-arg-parens) 如果函数需要一个参数就省略掉圆括号,否则总是用圆括号包裹参数,这样更清晰。NOTE: 总是使用圆括号也是可以接受的,但要设置在 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) > 为什么? 更少的视觉干扰 @@ -1089,7 +1106,7 @@ Other Style Guides ``` - - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) + - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) ```javascript @@ -1404,7 +1421,7 @@ Other Style Guides - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) - + > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 ```javascript @@ -1443,7 +1460,7 @@ Other Style Guides - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) - + > 为什么? 加载器语法要在 `webpack.config.js` 中使用 ```javascript @@ -1577,7 +1594,7 @@ Other Style Guides - [12.1](#properties--dot) 使用 `.` 符号来访问对象的属性。 - eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) + eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) ```javascript @@ -1683,7 +1700,7 @@ Other Style Guides - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 - + ```javascript // bad - unnecessary function call function checkName(hasName) { @@ -1717,7 +1734,7 @@ Other Style Guides return name; } ``` - + - [13.5](#variables--no-chain-assignment) 不使用链接变量赋值。 @@ -2251,7 +2268,7 @@ Other Style Guides - [18.1](#whitespace--spaces) 使用 2 个空格作为缩进。 - eslint: [`indent`](http://eslint.org/docs/rules/indent.html) + eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) ```javascript @@ -2273,7 +2290,7 @@ Other Style Guides - [18.2](#whitespace--before-blocks) 在大括号前放一个空格。 - eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) + eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) ```javascript @@ -2302,7 +2319,7 @@ Other Style Guides - [18.3](#whitespace--around-keywords) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 - eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) + eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) ```javascript @@ -2329,7 +2346,7 @@ Other Style Guides - [18.4](#whitespace--infix-ops) 使用空格把运算符隔开。 - eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) + 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) ```javascript @@ -2405,7 +2422,7 @@ Other Style Guides .append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); - + // good const leds = stage.selectAll('.led').data(data); ``` @@ -2471,7 +2488,7 @@ Other Style Guides - [18.8](#whitespace--padded-blocks) 不要使用空白行紧邻块作用域填充空间。 - eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) + eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) ```javascript @@ -2506,7 +2523,7 @@ Other Style Guides - [18.9](#whitespace--in-parens) 不要在圆括号(parentheses)内侧两边填充空格,如函数调用、条件判断等。 - eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) + eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) ```javascript @@ -2533,7 +2550,7 @@ Other Style Guides - [18.10](#whitespace--in-brackets) 不要在中括号(brackets)内侧左右两边填充空格。 - eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) + eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) ```javascript @@ -2548,7 +2565,7 @@ Other Style Guides - [18.11](#whitespace--in-braces) 在大括号(curly braces)内侧两边添加空格。 - eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) + eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) ```javascript @@ -2561,7 +2578,7 @@ Other Style Guides - [18.12](#whitespace--max-len) 避免一行代码过长,超过100个字符(包括空格)。Note: 参看 [strings--line-length](#strings--line-length),过长字符应该分行处理。 - eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) + eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) > 为什么? 为了确保可读性、可维护性。注:作为一个良好的习惯,虽然显示器界面越来越大了,但代码为了通用性维护,如 shell 内也可以修改维护,那么有必要处理过长代码段。 @@ -2599,7 +2616,7 @@ Other Style Guides - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 - eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) + eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) ```javascript @@ -2636,7 +2653,7 @@ Other Style Guides - [19.2](#commas--dangling) 增加结尾的逗号: **需要**。 - eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) + eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 @@ -2879,7 +2896,7 @@ Other Style Guides - [22.2](#naming--camelCase) 使用驼峰式(camelCase、小驼峰)命名变量、对象、函数和实例。 - eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) + eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) ```javascript @@ -2895,7 +2912,7 @@ Other Style Guides - [22.3](#naming--PascalCase) 使用帕斯卡式(PascalCase、大驼峰式)命名构造函数或类。 - eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) + eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 @@ -2924,7 +2941,7 @@ Other Style Guides - [22.4](#naming--leading-underscore) 不要使用尾随或前导下划线来命名私有属性。 - eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) + eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) > 为什么? JavaScript没有在属性或方法方面的隐私权的概念。虽然一个领先的下划线是一个共同的公约,意思是“私人”,事实上,这些属性是完全公开的,因此,是你的公共API合同的一部分。这个约定可能会导致开发人员错误地认为一个变化不算是中断,或者不需要测试。TL;DR:如果你想要的东西是“私人的”,它不应该可见。 @@ -3211,9 +3228,9 @@ Other Style Guides ``` - - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 + - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - + - [25.4](#jquery--find) 对有作用域的 jQuery 对象查询使用 `find`。 @@ -3293,7 +3310,7 @@ Other Style Guides - [28.2](#testing--for-real) **No, but seriously**: - + - Whichever testing framework you use, you should be writing tests! - Strive to write many small pure functions, and minimize where mutations occur. - Be cautious about stubs and mocks - they can make your tests more brittle. diff --git a/test.js b/test.js new file mode 100644 index 0000000000..9a342551dd --- /dev/null +++ b/test.js @@ -0,0 +1,93 @@ + +// ES6 代码规范练习场 + +// 为便于测试,关闭部分规则检测 +/* eslint no-unused-vars: 0 */ + +// [类型](#types) +// 使用 let const 替代 var +var type11 = 123 +var type2 = 'hello' + +const type3 = 123 +let type4 = 123 +// type4 = 456 +const type5 = 'hello' + +const type121 = [1, 2] +const type122 = type121 +type122[0] = 9 + +console.log(type122[0], type122[0]) + + +// [引用](#references) + + +// [对象](#objects) + + +// [数组](#arrays) + + +// [解构](#destructuring) + + +// [字符串](#strings) + + +// [函数](#functions) + + +// [箭头函数](#arrow-functions) + + +// [类 & 构造函数](#classes--constructors) + + +// [模块](#modules) + + +// [Iterators and Generators](#iterators-and-generators) + + +// [属性](#properties) + + +// [变量](#variables) + + +// [提升](#hoisting) + + +// [比较运算符 & 等号](#comparison-operators--equality) + + +// [代码块](#blocks) + + +// [注释](#comments) + + +// [空白](#whitespace) + + +// [逗号](#commas) + + +// [分号](#semicolons) + + +// [类型转换](#type-casting--coercion) + + +// [命名规则](#naming-conventions) + + +// [存取器](#accessors) + + +// [事件](#events) + + +// [jQuery](#jquery) From fe6f77d59e7539efaf459bef0e90e519330f027d Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Fri, 10 Feb 2017 10:21:50 +0800 Subject: [PATCH 10/33] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=B0=83=E6=95=B4=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es6_zh-cn.md | 4 +- javascript-style-guide/es5_zh-cn_v1.md | 1740 ------------- javascript-style-guide/es5_zh-cn_v2.md | 1328 ---------- javascript-style-guide/es6_zh-cn_v2.md | 3303 ------------------------ 4 files changed, 2 insertions(+), 6373 deletions(-) delete mode 100644 javascript-style-guide/es5_zh-cn_v1.md delete mode 100644 javascript-style-guide/es5_zh-cn_v2.md delete mode 100644 javascript-style-guide/es6_zh-cn_v2.md diff --git a/es6_zh-cn.md b/es6_zh-cn.md index 17d06e3066..5ec25dd23e 100644 --- a/es6_zh-cn.md +++ b/es6_zh-cn.md @@ -19,8 +19,8 @@ NOTE: 本文对[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascr 该文档保留了部分还属于 ES5 范畴的注意内容,由 old: ES5 标记。 -ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v2.md)。 -ES6 的编码规范请查看下文(最新版),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es6_zh-cn_v2.md) +ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v2.md),[版本三: 推荐](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v3.md)。 +ES6 的编码规范请查看下文(最新版),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es6_zh-cn_v2.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es6_zh-cn_v2.md),[版本三](#table-of-contents) ## Usage diff --git a/javascript-style-guide/es5_zh-cn_v1.md b/javascript-style-guide/es5_zh-cn_v1.md deleted file mode 100644 index 2a29c76a45..0000000000 --- a/javascript-style-guide/es5_zh-cn_v1.md +++ /dev/null @@ -1,1740 +0,0 @@ -# Airbnb JavaScript Style Guide() { - -*用更合理的方式写 JavaScript* - -## 目录 - - 1. [类型](#types) - 1. [对象](#objects) - 1. [数组](#arrays) - 1. [字符串](#strings) - 1. [函数](#functions) - 1. [属性](#properties) - 1. [变量](#variables) - 1. [提升](#hoisting) - 1. [比较运算符 & 等号](#comparison-operators--equality) - 1. [块](#blocks) - 1. [注释](#comments) - 1. [空白](#whitespace) - 1. [逗号](#commas) - 1. [分号](#semicolons) - 1. [类型转化](#type-casting--coercion) - 1. [命名规则](#naming-conventions) - 1. [存取器](#accessors) - 1. [构造函数](#constructors) - 1. [事件](#events) - 1. [模块](#modules) - 1. [jQuery](#jquery) - 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) - 1. [测试](#testing) - 1. [性能](#performance) - 1. [资源](#resources) - 1. [谁在使用](#in-the-wild) - 1. [翻译](#translation) - 1. [JavaScript 风格指南说明](#the-javascript-style-guide-guide) - 1. [与我们讨论 JavaScript](#chat-with-us-about-javascript) - 1. [贡献者](#contributors) - 1. [许可](#license) - -## 类型 - - - **原始值**: 存取直接作用于它自身。 - - + `string` - + `number` - + `boolean` - + `null` - + `undefined` - - ```javascript - var foo = 1; - var bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - **复杂类型**: 存取时作用于它自身值的引用。 - - + `object` - + `array` - + `function` - - ```javascript - var foo = [1, 2]; - var bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - -**[⬆ 回到顶部](#table-of-contents)** - -## 对象 - - - 使用直接量创建对象。 - - ```javascript - // bad - var item = new Object(); - - // good - var item = {}; - ``` - - - 不要使用[保留字](http://es5.github.io/#x7.6.1)作为键名,它们在 IE8 下不工作。[更多信息](https://github.com/airbnb/javascript/issues/61)。 - - ```javascript - // bad - var superman = { - default: { clark: 'kent' }, - private: true - }; - - // good - var superman = { - defaults: { clark: 'kent' }, - hidden: true - }; - ``` - - - 使用同义词替换需要使用的保留字。 - - ```javascript - // bad - var superman = { - class: 'alien' - }; - - // bad - var superman = { - klass: 'alien' - }; - - // good - var superman = { - type: 'alien' - }; - ``` - -**[⬆ 回到顶部](#table-of-contents)** - -## 数组 - - - 使用直接量创建数组。 - - ```javascript - // bad - var items = new Array(); - - // good - var items = []; - ``` - - - 向数组增加元素时使用 Array#push 来替代直接赋值。 - - ```javascript - var someStack = []; - - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - 当你需要拷贝数组时,使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - - ```javascript - var len = items.length; - var itemsCopy = []; - var i; - - // bad - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // good - itemsCopy = items.slice(); - ``` - - - 使用 Array#slice 将类数组对象转换成数组。 - - ```javascript - function trigger() { - var args = Array.prototype.slice.call(arguments); - ... - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 字符串 - - - 使用单引号 `''` 包裹字符串。 - - ```javascript - // bad - var name = "Bob Parr"; - - // good - var name = 'Bob Parr'; - - // bad - var fullName = "Bob " + this.lastName; - - // good - var fullName = 'Bob ' + this.lastName; - ``` - - - 超过 100 个字符的字符串应该使用连接符写成多行。 - - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[jsPerf](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40). - - ```javascript - // bad - var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; - - // bad - var errorMessage = 'This is a super long error that was thrown because \ - of Batman. When you stop to think about how Batman had anything to do \ - with this, you would get nowhere \ - fast.'; - - // good - var errorMessage = 'This is a super long error that was thrown because ' + - 'of Batman. When you stop to think about how Batman had anything to do ' + - 'with this, you would get nowhere fast.'; - ``` - - - 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:[jsPerf](http://jsperf.com/string-vs-array-concat/2). - - ```javascript - var items; - var messages; - var length; - var i; - - messages = [{ - state: 'success', - message: 'This one worked.' - }, { - state: 'success', - message: 'This one worked as well.' - }, { - state: 'error', - message: 'This one did not work.' - }]; - - length = messages.length; - - // bad - function inbox(messages) { - items = '
      '; - - for (i = 0; i < length; i++) { - items += '
    • ' + messages[i].message + '
    • '; - } - - return items + '
    '; - } - - // good - function inbox(messages) { - items = []; - - for (i = 0; i < length; i++) { - // use direct assignment in this case because we're micro-optimizing. - items[i] = '
  • ' + messages[i].message + '
  • '; - } - - return '
      ' + items.join('') + '
    '; - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 函数 - - - 函数表达式: - - ```javascript - // 匿名函数表达式 - var anonymous = function() { - return true; - }; - - // 命名函数表达式 - var named = function named() { - return true; - }; - - // 立即调用的函数表达式(IIFE) - (function () { - console.log('Welcome to the Internet. Please follow me.'); - }()); - ``` - - - 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 - - **注:** ECMA-262 把 `块` 定义为一组语句。函数声明不是语句。[阅读对 ECMA-262 这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - var test; - if (currentUser) { - test = function test() { - console.log('Yup.'); - }; - } - ``` - - - 永远不要把参数命名为 `arguments`。这将取代函数作用域内的 `arguments` 对象。 - - ```javascript - // bad - function nope(name, options, arguments) { - // ...stuff... - } - - // good - function yup(name, options, args) { - // ...stuff... - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - - -## 属性 - - - 使用 `.` 来访问对象的属性。 - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - // bad - var isJedi = luke['jedi']; - - // good - var isJedi = luke.jedi; - ``` - - - 当通过变量访问属性时使用中括号 `[]`。 - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - function getProp(prop) { - return luke[prop]; - } - - var isJedi = getProp('jedi'); - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 变量 - - - 总是使用 `var` 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。 - - ```javascript - // bad - superPower = new SuperPower(); - - // good - var superPower = new SuperPower(); - ``` - - - 使用 `var` 声明每一个变量。 - 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 - - ```javascript - // bad - var items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - - // bad - // (跟上面的代码比较一下,看看哪里错了) - var items = getItems(), - goSportsTeam = true; - dragonball = 'z'; - - // good - var items = getItems(); - var goSportsTeam = true; - var dragonball = 'z'; - ``` - - - 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。 - - ```javascript - // bad - var i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - var i; - var items = getItems(); - var dragonball; - var goSportsTeam = true; - var len; - - // good - var items = getItems(); - var goSportsTeam = true; - var dragonball; - var length; - var i; - ``` - - - 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。 - - ```javascript - // bad - function () { - test(); - console.log('doing stuff..'); - - //..other stuff.. - - var name = getName(); - - if (name === 'test') { - return false; - } - - return name; - } - - // good - function () { - var name = getName(); - - test(); - console.log('doing stuff..'); - - //..other stuff.. - - if (name === 'test') { - return false; - } - - return name; - } - - // bad - 不必要的函数调用 - function () { - var name = getName(); - - if (!arguments.length) { - return false; - } - - this.setFirstName(name); - - return true; - } - - // good - function () { - var name; - - if (!arguments.length) { - return false; - } - - name = getName(); - this.setFirstName(name); - - return true; - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 提升 - - - 变量声明会提升至作用域顶部,但赋值不会。 - - ```javascript - // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量) - function example() { - console.log(notDefined); // => throws a ReferenceError - } - - // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。 - // 注:变量赋值为 `true` 不会提升。 - function example() { - console.log(declaredButNotAssigned); // => undefined - var declaredButNotAssigned = true; - } - - // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: - function example() { - var declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - ``` - - - 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。 - - ```javascript - function example() { - console.log(anonymous); // => undefined - - anonymous(); // => TypeError anonymous is not a function - - var anonymous = function () { - console.log('anonymous function expression'); - }; - } - ``` - - - 命名函数表达式会提升变量名,但不会提升函数名或函数体。 - - ```javascript - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - superPower(); // => ReferenceError superPower is not defined - - var named = function superPower() { - console.log('Flying'); - }; - } - - // 当函数名跟变量名一样时,表现也是如此。 - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - var named = function named() { - console.log('named'); - } - } - ``` - - - 函数声明提升它们的名字和函数体。 - - ```javascript - function example() { - superPower(); // => Flying - - function superPower() { - console.log('Flying'); - } - } - ``` - - - 了解更多信息在 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). - -**[⬆ 回到顶部](#table-of-contents)** - - - -## 比较运算符 & 等号 - - - 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. - - 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: - - + **对象** 被计算为 **true** - + **Undefined** 被计算为 **false** - + **Null** 被计算为 **false** - + **布尔值** 被计算为 **布尔的值** - + **数字** 如果是 **+0、-0 或 NaN** 被计算为 **false**,否则为 **true** - + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** - - ```javascript - if ([0]) { - // true - // 一个数组就是一个对象,对象被计算为 true - } - ``` - - - 使用快捷方式。 - - ```javascript - // bad - if (name !== '') { - // ...stuff... - } - - // good - if (name) { - // ...stuff... - } - - // bad - if (collection.length > 0) { - // ...stuff... - } - - // good - if (collection.length) { - // ...stuff... - } - ``` - - - 了解更多信息在 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. - -**[⬆ 回到顶部](#table-of-contents)** - - -## - - - 使用大括号包裹所有的多行代码块。 - - ```javascript - // bad - if (test) - return false; - - // good - if (test) return false; - - // good - if (test) { - return false; - } - - // bad - function () { return false; } - - // good - function () { - return false; - } - ``` - - - 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 - - ```javascript - // bad - if (test) { - thing1(); - thing2(); - } - else { - thing3(); - } - - // good - if (test) { - thing1(); - thing2(); - } else { - thing3(); - } - ``` - - -**[⬆ 回到顶部](#table-of-contents)** - - -## 注释 - - - 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 - - ```javascript - // bad - // make() returns a new element - // based on the passed in tag name - // - // @param {String} tag - // @return {Element} element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the passed in tag name - * - * @param {String} tag - * @return {Element} element - */ - function make(tag) { - - // ...stuff... - - return element; - } - ``` - - - 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 - - ```javascript - // bad - var active = true; // is current tab - - // good - // is current tab - var active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - var type = this.type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - var type = this.type || 'no type'; - - return type; - } - ``` - - - 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 - - - 使用 `// FIXME:` 标注问题。 - - ```javascript - function Calculator() { - - // FIXME: shouldn't use a global here - total = 0; - - return this; - } - ``` - - - 使用 `// TODO:` 标注问题的解决方式。 - - ```javascript - function Calculator() { - - // TODO: total should be configurable by an options param - this.total = 0; - - return this; - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 空白 - - - 使用 2 个空格作为缩进。 - - ```javascript - // bad - function () { - ∙∙∙∙var name; - } - - // bad - function () { - ∙var name; - } - - // good - function () { - ∙∙var name; - } - ``` - - - 在大括号前放一个空格。 - - ```javascript - // bad - function test(){ - console.log('test'); - } - - // good - function test() { - console.log('test'); - } - - // bad - dog.set('attr',{ - age: '1 year', - breed: 'Bernese Mountain Dog' - }); - - // good - dog.set('attr', { - age: '1 year', - breed: 'Bernese Mountain Dog' - }); - ``` - - - 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 - - ```javascript - // bad - if(isJedi) { - fight (); - } - - // good - if (isJedi) { - fight(); - } - - // bad - function fight () { - console.log ('Swooosh!'); - } - - // good - function fight() { - console.log('Swooosh!'); - } - ``` - - - 使用空格把运算符隔开。 - - ```javascript - // bad - var x=y+5; - - // good - var x = y + 5; - ``` - - - 在文件末尾插入一个空行。 - - ```javascript - // bad - (function (global) { - // ...stuff... - })(this); - ``` - - ```javascript - // bad - (function (global) { - // ...stuff... - })(this);↵ - ↵ - ``` - - ```javascript - // good - (function (global) { - // ...stuff... - })(this);↵ - ``` - - - 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 - - ```javascript - // bad - $('#items').find('.selected').highlight().end().find('.open').updateCount(); - - // bad - $('#items'). - find('.selected'). - highlight(). - end(). - find('.open'). - updateCount(); - - // good - $('#items') - .find('.selected') - .highlight() - .end() - .find('.open') - .updateCount(); - - // bad - var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) - .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - var leds = stage.selectAll('.led') - .data(data) - .enter().append('svg:svg') - .classed('led', true) - .attr('width', (radius + margin) * 2) - .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - ``` - - - 在块末和新语句前插入空行。 - - ```javascript - // bad - if (foo) { - return bar; - } - return baz; - - // good - if (foo) { - return bar; - } - - return baz; - - // bad - var obj = { - foo: function () { - }, - bar: function () { - } - }; - return obj; - - // good - var obj = { - foo: function () { - }, - - bar: function () { - } - }; - - return obj; - ``` - - -**[⬆ 回到顶部](#table-of-contents)** - -## 逗号 - - - 行首逗号: **不需要**。 - - ```javascript - // bad - var story = [ - once - , upon - , aTime - ]; - - // good - var story = [ - once, - upon, - aTime - ]; - - // bad - var hero = { - firstName: 'Bob' - , lastName: 'Parr' - , heroName: 'Mr. Incredible' - , superPower: 'strength' - }; - - // good - var hero = { - firstName: 'Bob', - lastName: 'Parr', - heroName: 'Mr. Incredible', - superPower: 'strength' - }; - ``` - - - 额外的行末逗号:**不需要**。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 ([source](http://es5.github.io/#D)): - - > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. - - ```javascript - // bad - var hero = { - firstName: 'Kevin', - lastName: 'Flynn', - }; - - var heroes = [ - 'Batman', - 'Superman', - ]; - - // good - var hero = { - firstName: 'Kevin', - lastName: 'Flynn' - }; - - var heroes = [ - 'Batman', - 'Superman' - ]; - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 分号 - - - **使用分号。** - - ```javascript - // bad - (function () { - var name = 'Skywalker' - return name - })() - - // good - (function () { - var name = 'Skywalker'; - return name; - })(); - - // good (防止函数在两个 IIFE 合并时被当成一个参数 - ;(function () { - var name = 'Skywalker'; - return name; - })(); - ``` - - [了解更多](http://stackoverflow.com/a/7365214/1712802). - -**[⬆ 回到顶部](#table-of-contents)** - - -## 类型转换 - - - 在语句开始时执行类型转换。 - - 字符串: - - ```javascript - // => this.reviewScore = 9; - - // bad - var totalScore = this.reviewScore + ''; - - // good - var totalScore = '' + this.reviewScore; - - // bad - var totalScore = '' + this.reviewScore + ' total score'; - - // good - var totalScore = this.reviewScore + ' total score'; - ``` - - - 使用 `parseInt` 转换数字时总是带上类型转换的基数。 - - ```javascript - var inputValue = '4'; - - // bad - var val = new Number(inputValue); - - // bad - var val = +inputValue; - - // bad - var val = inputValue >> 0; - - // bad - var val = parseInt(inputValue); - - // good - var val = Number(inputValue); - - // good - var val = parseInt(inputValue, 10); - ``` - - - 如果因为某些原因 `parseInt` 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 - - ```javascript - // good - /** - * parseInt was the reason my code was slow. - * Bitshifting the String to coerce it to a - * Number made it a lot faster. - */ - var val = inputValue >> 0; - ``` - - - **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([source](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: - - ```javascript - 2147483647 >> 0 //=> 2147483647 - 2147483648 >> 0 //=> -2147483648 - 2147483649 >> 0 //=> -2147483647 - ``` - - - 布尔: - - ```javascript - var age = 0; - - // bad - var hasAge = new Boolean(age); - - // good - var hasAge = Boolean(age); - - // good - var hasAge = !!age; - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 命名规则 - - - 避免单字母命名。命名应具备描述性。 - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - 使用驼峰式命名对象、函数和实例。 - - ```javascript - // bad - var OBJEcttsssss = {}; - var this_is_my_object = {}; - var o = {}; - function c() {} - - // good - var thisIsMyObject = {}; - function thisIsMyFunction() {} - ``` - - - 使用帕斯卡式命名构造函数或类。 - - ```javascript - // bad - function user(options) { - this.name = options.name; - } - - var bad = new user({ - name: 'nope' - }); - - // good - function User(options) { - this.name = options.name; - } - - var good = new User({ - name: 'yup' - }); - ``` - - - 不要使用下划线前/后缀。 - - > 为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。 - - ```javascript - // bad - this.__firstName__ = 'Panda'; - this.firstName_ = 'Panda'; - this._firstName = 'Panda'; - - // good - this.firstName = 'Panda'; - ``` - - - 不要保存 `this` 的引用。使用 Function#bind。 - - ```javascript - // bad - function () { - var self = this; - return function () { - console.log(self); - }; - } - - // bad - function () { - var that = this; - return function () { - console.log(that); - }; - } - - // bad - function () { - var _this = this; - return function () { - console.log(_this); - }; - } - - // good - function () { - return function () { - console.log(this); - }.bind(this); - } - ``` - - - 给函数命名。这在做堆栈轨迹时很有帮助。 - - ```javascript - // bad - var log = function (msg) { - console.log(msg); - }; - - // good - var log = function log(msg) { - console.log(msg); - }; - ``` - - - **注:** IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。 - - - 如果你的文件导出一个类,你的文件名应该与类名完全相同。 - ```javascript - // file contents - class CheckBox { - // ... - } - module.exports = CheckBox; - - // in some other file - // bad - var CheckBox = require('./checkBox'); - - // bad - var CheckBox = require('./check_box'); - - // good - var CheckBox = require('./CheckBox'); - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 存取器 - - - 属性的存取函数不是必须的。 - - 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 - - ```javascript - // bad - dragon.age(); - - // good - dragon.getAge(); - - // bad - dragon.age(25); - - // good - dragon.setAge(25); - ``` - - - 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 - - ```javascript - // bad - if (!dragon.age()) { - return false; - } - - // good - if (!dragon.hasAge()) { - return false; - } - ``` - - - 创建 get() 和 set() 函数是可以的,但要保持一致。 - - ```javascript - function Jedi(options) { - options || (options = {}); - var lightsaber = options.lightsaber || 'blue'; - this.set('lightsaber', lightsaber); - } - - Jedi.prototype.set = function set(key, val) { - this[key] = val; - }; - - Jedi.prototype.get = function get(key) { - return this[key]; - }; - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 构造函数 - - - 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型! - - ```javascript - function Jedi() { - console.log('new jedi'); - } - - // bad - Jedi.prototype = { - fight: function fight() { - console.log('fighting'); - }, - - block: function block() { - console.log('blocking'); - } - }; - - // good - Jedi.prototype.fight = function fight() { - console.log('fighting'); - }; - - Jedi.prototype.block = function block() { - console.log('blocking'); - }; - ``` - - - 方法可以返回 `this` 来实现方法链式使用。 - - ```javascript - // bad - Jedi.prototype.jump = function jump() { - this.jumping = true; - return true; - }; - - Jedi.prototype.setHeight = function setHeight(height) { - this.height = height; - }; - - var luke = new Jedi(); - luke.jump(); // => true - luke.setHeight(20); // => undefined - - // good - Jedi.prototype.jump = function jump() { - this.jumping = true; - return this; - }; - - Jedi.prototype.setHeight = function setHeight(height) { - this.height = height; - return this; - }; - - var luke = new Jedi(); - - luke.jump() - .setHeight(20); - ``` - - - - 写一个自定义的 `toString()` 方法是可以的,但是确保它可以正常工作且不会产生副作用。 - - ```javascript - function Jedi(options) { - options || (options = {}); - this.name = options.name || 'no name'; - } - - Jedi.prototype.getName = function getName() { - return this.name; - }; - - Jedi.prototype.toString = function toString() { - return 'Jedi - ' + this.getName(); - }; - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 事件 - - - 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: - - ```js - // bad - $(this).trigger('listingUpdated', listing.id); - - ... - - $(this).on('listingUpdated', function (e, listingId) { - // do something with listingId - }); - ``` - - 更好的写法: - - ```js - // good - $(this).trigger('listingUpdated', { listingId : listing.id }); - - ... - - $(this).on('listingUpdated', function (e, data) { - // do something with data.listingId - }); - ``` - - **[⬆ 回到顶部](#table-of-contents)** - - -## 模块 - - - 模块应该以 `!` 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。[详细说明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) - - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。 - - 增加一个名为 `noConflict()` 的方法来设置导出的模块为前一个版本并返回它。 - - 永远在模块顶部声明 `'use strict';`。 - - ```javascript - // fancyInput/fancyInput.js - - !function (global) { - 'use strict'; - - var previousFancyInput = global.FancyInput; - - function FancyInput(options) { - this.options = options || {}; - } - - FancyInput.noConflict = function noConflict() { - global.FancyInput = previousFancyInput; - return FancyInput; - }; - - global.FancyInput = FancyInput; - }(this); - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## jQuery - - - 使用 `$` 作为存储 jQuery 对象的变量名前缀。 - - ```javascript - // bad - var sidebar = $('.sidebar'); - - // good - var $sidebar = $('.sidebar'); - ``` - - - 缓存 jQuery 查询。 - - ```javascript - // bad - function setSidebar() { - $('.sidebar').hide(); - - // ...stuff... - - $('.sidebar').css({ - 'background-color': 'pink' - }); - } - - // good - function setSidebar() { - var $sidebar = $('.sidebar'); - $sidebar.hide(); - - // ...stuff... - - $sidebar.css({ - 'background-color': 'pink' - }); - } - ``` - - - 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - - 对有作用域的 jQuery 对象查询使用 `find`。 - - ```javascript - // bad - $('ul', '.sidebar').hide(); - - // bad - $('.sidebar').find('ul').hide(); - - // good - $('.sidebar ul').hide(); - - // good - $('.sidebar > ul').hide(); - - // good - $sidebar.find('ul').hide(); - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## ECMAScript 5 兼容性 - - - 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容表](http://kangax.github.com/es5-compat-table/). - -**[⬆ 回到顶部](#table-of-contents)** - - -## 测试 - - - **Yup.** - - ```javascript - function () { - return true; - } - ``` - -**[⬆ 回到顶部](#table-of-contents)** - - -## 性能 - - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](http://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](http://jsperf.com/ya-string-concat) - - Loading... - -**[⬆ 回到顶部](#table-of-contents)** - - -## 资源 - - -**推荐阅读** - - - [Annotated ECMAScript 5.1](http://es5.github.com/) - -**工具** - - - Code Style Linters - + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) - + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) - -**其它风格指南** - - - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) - - [JavaScript Standard Style](https://github.com/feross/standard) - -**其它风格** - - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen - - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman - -**进一步阅读** - - - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban - - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock - -**书籍** - - - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov - - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke - - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson - -**博客** - - - [DailyJS](http://dailyjs.com/) - - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) - - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) - - [Perfection Kills](http://perfectionkills.com/) - - [Ben Alman](http://benalman.com/) - - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) - -**播客** - - - [JavaScript Jabber](http://devchat.tv/js-jabber/) - - -**[⬆ 回到顶部](#table-of-contents)** - -## 谁在使用 - - 这是一个使用本风格指南的组织列表。给我们发 pull request 或开一个 issue 让我们将你增加到列表上。 - - - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) - - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) - - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) - - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) - - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) - - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) - - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) - - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) - - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) - - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - - **Muber**: [muber/javascript](https://github.com/muber/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) - - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript) - - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) - - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) - - **Super**: [SuperJobs/javascript](https://github.com/SuperJobs/javascript) - - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) - - **Target**: [target/javascript](https://github.com/target/javascript) - - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) - - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) - - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) - - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) - -## 翻译 - - 这份风格指南也提供了其它语言的版本: - - - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript](https://github.com/sivan/javascript) - - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) - -## JavaScript 风格指南说明 - - - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - -## 与我们讨论 JavaScript - - - Find us on [gitter](https://gitter.im/airbnb/javascript). - -## 贡献者 - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## 许可 - -(The MIT License) - -Copyright (c) 2014 Airbnb - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[⬆ 回到顶部](#table-of-contents)** - -# }; diff --git a/javascript-style-guide/es5_zh-cn_v2.md b/javascript-style-guide/es5_zh-cn_v2.md deleted file mode 100644 index 4040528a99..0000000000 --- a/javascript-style-guide/es5_zh-cn_v2.md +++ /dev/null @@ -1,1328 +0,0 @@ -[原文: https://github.com/airbnb/javascript](https://github.com/airbnb/javascript) - -注:本人根据自己的开发习惯删除和修改了部分规范 - -# JavaScript规范 - -## 内容列表 - - 1. [类型](#types) - 1. [对象](#objects) - 1. [数组](#arrays) - 1. [字符串](#strings) - 1. [函数](#functions) - 1. [属性](#properties) - 1. [变量](#variables) - 1. [条件表达式和等号](#conditionals) - 1. [块](#blocks) - 1. [注释](#comments) - 1. [空白](#whitespace) - 1. [逗号](#commas) - 1. [分号](#semicolons) - 1. [类型转换](#type-coercion) - 1. [命名约定](#naming-conventions) - 1. [存取器](#accessors) - 1. [构造器](#constructors) - 1. [事件](#events) - 1. [模块](#modules) - 1. [jQuery](#jquery) - 1. [ES5 兼容性](#es5) - 1. [性能](#performance) - 1. [资源](#resources) - 1. [哪些人在使用](#in-the-wild) - 1. [翻译](#translation) - 1. [JavaScript风格指南](#guide-guide) - 1. [贡献者](#contributors) - 1. [许可](#license) - -## 类型 - - - **原始值**: 相当于传值 - - + `string` - + `number` - + `boolean` - + `null` - + `undefined` - - ```javascript - var foo = 1, - bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - **复杂类型**: 相当于传引用 - - + `object` - + `array` - + `function` - - ```javascript - var foo = [1, 2], - bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - - **[[⬆]](#TOC)** - -## 对象 - - - 使用字面值创建对象 - - ```javascript - // bad - var item = new Object(); - - // good - var item = {}; - ``` - - - 不要使用保留字 [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) 作为键 - - ```javascript - // bad - var superman = { - class: 'superhero', - default: { clark: 'kent' }, - private: true - }; - - // good - var superman = { - klass: 'superhero', - defaults: { clark: 'kent' }, - hidden: true - }; - ``` - **[[⬆]](#TOC)** - -## 数组 - - - 使用字面值创建数组 - - ```javascript - // bad - var items = new Array(); - - // good - var items = []; - ``` - - - 如果你不知道数组的长度,使用push - - ```javascript - var someStack = []; - - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - 当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - - ```javascript - var len = items.length, - itemsCopy = [], - i; - - // bad - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // good - itemsCopy = items.slice(); - ``` - - - 使用slice将类数组的对象转成数组. - - ```javascript - function trigger() { - var args = Array.prototype.slice.call(arguments); - ... - } - ``` - - **[[⬆]](#TOC)** - - -## 字符串 - - - 对字符串使用单引号 `''` - - ```javascript - // bad - var name = "Bob Parr"; - - // good - var name = 'Bob Parr'; - - // bad - var fullName = "Bob " + this.lastName; - - // good - var fullName = 'Bob ' + this.lastName; - ``` - - - 超过80个字符的字符串应该使用字符串连接换行 - - 注: 如果过度使用,长字符串连接可能会对性能有影响. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) - - ```javascript - // bad - var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; - - // bad - var errorMessage = 'This is a super long error that \ - was thrown because of Batman. \ - When you stop to think about \ - how Batman had anything to do \ - with this, you would get nowhere \ - fast.'; - - - // good - var errorMessage = 'This is a super long error that ' + - 'was thrown because of Batman.' + - 'When you stop to think about ' + - 'how Batman had anything to do ' + - 'with this, you would get nowhere ' + - 'fast.'; - ``` - - - 编程时使用join而不是字符串连接来构建字符串,特别是IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). - - ```javascript - var items, - messages, - length, i; - - messages = [{ - state: 'success', - message: 'This one worked.' - },{ - state: 'success', - message: 'This one worked as well.' - },{ - state: 'error', - message: 'This one did not work.' - }]; - - length = messages.length; - - // bad - function inbox(messages) { - items = '
      '; - - for (i = 0; i < length; i++) { - items += '
    • ' + messages[i].message + '
    • '; - } - - return items + '
    '; - } - - // good - function inbox(messages) { - items = []; - - for (i = 0; i < length; i++) { - items[i] = messages[i].message; - } - - return '
    • ' + items.join('
    • ') + '
    '; - } - ``` - - **[[⬆]](#TOC)** - - -## 函数 - - - 函数表达式: - - ```javascript - // 匿名函数表达式 - var anonymous = function() { - return true; - }; - - // 有名函数表达式 - var named = function named() { - return true; - }; - - // 立即调用函数表达式 - (function() { - console.log('Welcome to the Internet. Please follow me.'); - })(); - ``` - - - 绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同。 - - **注:** ECMA-262定义把`块`定义为一组语句,函数声明不是一个语句。[阅读ECMA-262对这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - if (currentUser) { - var test = function test() { - console.log('Yup.'); - }; - } - ``` - - - 绝对不要把参数命名为 `arguments`, 这将会逾越函数作用域内传过来的 `arguments` 对象. - - ```javascript - // bad - function nope(name, options, arguments) { - // ...stuff... - } - - // good - function yup(name, options, args) { - // ...stuff... - } - ``` - - **[[⬆]](#TOC)** - - -## 属性 - - - 当使用变量访问属性时使用中括号. - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - function getProp(prop) { - return luke[prop]; - } - - var isJedi = getProp('jedi'); - ``` - - **[[⬆]](#TOC)** - - -## 变量 - - - 总是使用 `var` 来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。 - - ```javascript - // bad - superPower = new SuperPower(); - - // good - var superPower = new SuperPower(); - ``` - - - 使用一个 `var` 以及新行声明多个变量,缩进4个空格。 - - ```javascript - // bad - var items = getItems(); - var goSportsTeam = true; - var dragonball = 'z'; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - ``` - - - 最后再声明未赋值的变量,当你想引用之前已赋值变量的时候很有用。 - - ```javascript - // bad - var i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - var i, items = getItems(), - dragonball, - goSportsTeam = true, - len; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball, - length, - i; - ``` - - - 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。 - - ```javascript - // bad - function() { - test(); - console.log('doing stuff..'); - - //..other stuff.. - - var name = getName(); - - if (name === 'test') { - return false; - } - - return name; - } - - // good - function() { - var name = getName(); - - test(); - console.log('doing stuff..'); - - //..other stuff.. - - if (name === 'test') { - return false; - } - - return name; - } - - // bad - function() { - var name = getName(); - - if (!arguments.length) { - return false; - } - - return true; - } - - // good - function() { - if (!arguments.length) { - return false; - } - - var name = getName(); - - return true; - } - ``` - - **[[⬆]](#TOC)** - - -## 条件表达式和等号 - - - 适当使用 `===` 和 `!==` 以及 `==` 和 `!=`. - - 条件表达式的强制类型转换遵循以下规则: - - + **对象** 被计算为 **true** - + **Undefined** 被计算为 **false** - + **Null** 被计算为 **false** - + **布尔值** 被计算为 **布尔的值** - + **数字** 如果是 **+0, -0, or NaN** 被计算为 **false** , 否则为 **true** - + **字符串** 如果是空字符串 `''` 则被计算为 **false**, 否则为 **true** - - ```javascript - if ([0]) { - // true - // An array is an object, objects evaluate to true - } - ``` - - - 使用快捷方式. - - ```javascript - // bad - if (name !== '') { - // ...stuff... - } - - // good - if (name) { - // ...stuff... - } - - // bad - if (collection.length > 0) { - // ...stuff... - } - - // good - if (collection.length) { - // ...stuff... - } - ``` - - - 阅读 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) 了解更多 - - **[[⬆]](#TOC)** - - -## - - - 给所有多行的块使用大括号 - - ```javascript - // bad - if (test) - return false; - - // good - if (test) return false; - - // good - if (test) { - return false; - } - - // bad - function() { return false; } - - // good - function() { - return false; - } - ``` - - **[[⬆]](#TOC)** - - -## 注释 - - - 使用 `/** ... */` 进行多行注释,包括描述,指定类型以及参数值和返回值 - - ```javascript - // bad - // make() returns a new element - // based on the passed in tag name - // - // @param tag - // @return element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the passed in tag name - * - * @param tag - * @return element - */ - function make(tag) { - - // ...stuff... - - return element; - } - ``` - - - 使用 `//` 进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行. - - ```javascript - // bad - var active = true; // is current tab - - // good - // is current tab - var active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - ``` - - - 如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 `FIXME` 或 `TODO` 帮助其他人迅速理解 - - ```javascript - function Calculator() { - - // FIXME: shouldn't use a global here - total = 0; - - return this; - } - ``` - - ```javascript - function Calculator() { - - // TODO: total should be configurable by an options param - this.total = 0; - - return this; - } - ``` - - **[[⬆]](#TOC)** - - -## 空白 - - - 将tab设为4个空格 - - ```javascript - // bad - function() { - ∙∙var name; - } - - // bad - function() { - ∙var name; - } - - // good - function() { - ∙∙∙∙var name; - } - ``` - - 大括号前放一个空格 - - ```javascript - // bad - function test(){ - console.log('test'); - } - - // good - function test() { - console.log('test'); - } - - // bad - dog.set('attr',{ - age: '1 year', - breed: 'Bernese Mountain Dog' - }); - - // good - dog.set('attr', { - age: '1 year', - breed: 'Bernese Mountain Dog' - }); - ``` - - - 在做长方法链时使用缩进. - - ```javascript - // bad - $('#items').find('.selected').highlight().end().find('.open').updateCount(); - - // good - $('#items') - .find('.selected') - .highlight() - .end() - .find('.open') - .updateCount(); - - // bad - var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) - .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - var leds = stage.selectAll('.led') - .data(data) - .enter().append('svg:svg') - .class('led', true) - .attr('width', (radius + margin) * 2) - .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - ``` - - **[[⬆]](#TOC)** - -## 逗号 - - - 不要将逗号放前面 - - ```javascript - // bad - var once - , upon - , aTime; - - // good - var once, - upon, - aTime; - - // bad - var hero = { - firstName: 'Bob' - , lastName: 'Parr' - , heroName: 'Mr. Incredible' - , superPower: 'strength' - }; - - // good - var hero = { - firstName: 'Bob', - lastName: 'Parr', - heroName: 'Mr. Incredible', - superPower: 'strength' - }; - ``` - - - 不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。 - - ```javascript - // bad - var hero = { - firstName: 'Kevin', - lastName: 'Flynn', - }; - - var heroes = [ - 'Batman', - 'Superman', - ]; - - // good - var hero = { - firstName: 'Kevin', - lastName: 'Flynn' - }; - - var heroes = [ - 'Batman', - 'Superman' - ]; - ``` - - **[[⬆]](#TOC)** - - -## 分号 - - - 语句结束一定要加分号 - - ```javascript - // bad - (function() { - var name = 'Skywalker' - return name - })() - - // good - (function() { - var name = 'Skywalker'; - return name; - })(); - - // good - ;(function() { - var name = 'Skywalker'; - return name; - })(); - ``` - - **[[⬆]](#TOC)** - - -## 类型转换 - - - 在语句的开始执行类型转换. - - 字符串: - - ```javascript - // => this.reviewScore = 9; - - // bad - var totalScore = this.reviewScore + ''; - - // good - var totalScore = '' + this.reviewScore; - - // bad - var totalScore = '' + this.reviewScore + ' total score'; - - // good - var totalScore = this.reviewScore + ' total score'; - ``` - - - 对数字使用 `parseInt` 并且总是带上类型转换的基数. - - ```javascript - var inputValue = '4'; - - // bad - var val = new Number(inputValue); - - // bad - var val = +inputValue; - - // bad - var val = inputValue >> 0; - - // bad - var val = parseInt(inputValue); - - // good - var val = Number(inputValue); - - // good - var val = parseInt(inputValue, 10); - - // good - /** - * parseInt was the reason my code was slow. - * Bitshifting the String to coerce it to a - * Number made it a lot faster. - */ - var val = inputValue >> 0; - ``` - - - 布尔值: - - ```javascript - var age = 0; - - // bad - var hasAge = new Boolean(age); - - // good - var hasAge = Boolean(age); - - // good - var hasAge = !!age; - ``` - - **[[⬆]](#TOC)** - - -## 命名约定 - - - 避免单个字符名,让你的变量名有描述意义。 - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - 当命名对象、函数和实例时使用驼峰命名规则 - - ```javascript - // bad - var OBJEcttsssss = {}; - var this_is_my_object = {}; - var this-is-my-object = {}; - function c() {}; - var u = new user({ - name: 'Bob Parr' - }); - - // good - var thisIsMyObject = {}; - function thisIsMyFunction() {}; - var user = new User({ - name: 'Bob Parr' - }); - ``` - - - 当命名构造函数或类时使用驼峰式大写 - - ```javascript - // bad - function user(options) { - this.name = options.name; - } - - var bad = new user({ - name: 'nope' - }); - - // good - function User(options) { - this.name = options.name; - } - - var good = new User({ - name: 'yup' - }); - ``` - - - 命名私有属性时前面加个下划线 `_` - - ```javascript - // bad - this.__firstName__ = 'Panda'; - this.firstName_ = 'Panda'; - - // good - this._firstName = 'Panda'; - ``` - - - 当保存对 `this` 的引用时使用 `_this`. - - ```javascript - // bad - function() { - var self = this; - return function() { - console.log(self); - }; - } - - // bad - function() { - var that = this; - return function() { - console.log(that); - }; - } - - // good - function() { - var _this = this; - return function() { - console.log(_this); - }; - } - ``` - - **[[⬆]](#TOC)** - - -## 存取器 - - - 属性的存取器函数不是必需的 - - 如果你确实有存取器函数的话使用getVal() 和 setVal('hello') - - ```javascript - // bad - dragon.age(); - - // good - dragon.getAge(); - - // bad - dragon.age(25); - - // good - dragon.setAge(25); - ``` - - - 如果属性是布尔值,使用isVal() 或 hasVal() - - ```javascript - // bad - if (!dragon.age()) { - return false; - } - - // good - if (!dragon.hasAge()) { - return false; - } - ``` - - - 可以创建get()和set()函数,但是要保持一致 - - ```javascript - function Jedi(options) { - options || (options = {}); - var lightsaber = options.lightsaber || 'blue'; - this.set('lightsaber', lightsaber); - } - - Jedi.prototype.set = function(key, val) { - this[key] = val; - }; - - Jedi.prototype.get = function(key) { - return this[key]; - }; - ``` - - **[[⬆]](#TOC)** - - -## 构造器 - - - 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。 - - ```javascript - function Jedi() { - console.log('new jedi'); - } - - // bad - Jedi.prototype = { - fight: function fight() { - console.log('fighting'); - }, - - block: function block() { - console.log('blocking'); - } - }; - - // good - Jedi.prototype.fight = function fight() { - console.log('fighting'); - }; - - Jedi.prototype.block = function block() { - console.log('blocking'); - }; - ``` - - - 方法可以返回 `this` 帮助方法可链。 - - ```javascript - // bad - Jedi.prototype.jump = function() { - this.jumping = true; - return true; - }; - - Jedi.prototype.setHeight = function(height) { - this.height = height; - }; - - var luke = new Jedi(); - luke.jump(); // => true - luke.setHeight(20) // => undefined - - // good - Jedi.prototype.jump = function() { - this.jumping = true; - return this; - }; - - Jedi.prototype.setHeight = function(height) { - this.height = height; - return this; - }; - - var luke = new Jedi(); - - luke.jump() - .setHeight(20); - ``` - - - - 可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。 - - ```javascript - function Jedi(options) { - options || (options = {}); - this.name = options.name || 'no name'; - } - - Jedi.prototype.getName = function getName() { - return this.name; - }; - - Jedi.prototype.toString = function toString() { - return 'Jedi - ' + this.getName(); - }; - ``` - - **[[⬆]](#TOC)** - - -## 事件 - - - 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器 - - ```js - // bad - $(this).trigger('listingUpdated', listing.id); - - ... - - $(this).on('listingUpdated', function(e, listingId) { - // do something with listingId - }); - ``` - - 更好: - - ```js - // good - $(this).trigger('listingUpdated', { listingId : listing.id }); - - ... - - $(this).on('listingUpdated', function(e, data) { - // do something with data.listingId - }); - ``` - - **[[⬆]](#TOC)** - - -## 模块 - - - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 - - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 - - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 - - 总是在模块顶部声明 `'use strict';` - - ```javascript - // fancyInput/fancyInput.js - - !function(global) { - 'use strict'; - - var previousFancyInput = global.FancyInput; - - function FancyInput(options) { - this.options = options || {}; - } - - FancyInput.noConflict = function noConflict() { - global.FancyInput = previousFancyInput; - return FancyInput; - }; - - global.FancyInput = FancyInput; - }(this); - ``` - - **[[⬆]](#TOC)** - - -## jQuery - - - 缓存jQuery查询 - - ```javascript - // bad - function setSidebar() { - $('.sidebar').hide(); - - // ...stuff... - - $('.sidebar').css({ - 'background-color': 'pink' - }); - } - - // good - function setSidebar() { - var $sidebar = $('.sidebar'); - $sidebar.hide(); - - // ...stuff... - - $sidebar.css({ - 'background-color': 'pink' - }); - } - ``` - - - 对DOM查询使用级联的 `$('.sidebar ul')` 或 `$('.sidebar ul')`,[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - - 对有作用域的jQuery对象查询使用 `find` - - ```javascript - // bad - $('.sidebar', 'ul').hide(); - - // bad - $('.sidebar').find('ul').hide(); - - // good - $('.sidebar ul').hide(); - - // good - $('.sidebar > ul').hide(); - - // good (slower) - $sidebar.find('ul'); - - // good (faster) - $($sidebar[0]).find('ul'); - ``` - - **[[⬆]](#TOC)** - - -## ECMAScript 5兼容性 - - - 参考[Kangax](https://twitter.com/kangax/)的 ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) - - **[[⬆]](#TOC)** - - - -## 性能 - - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](http://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](http://jsperf.com/ya-string-concat) - - Loading... - - **[[⬆]](#TOC)** - - -## 资源 - -**Read This** - - - [Annotated ECMAScript 5.1](http://es5.github.com/) - -**其它规范** - - - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) - -**其它风格** - - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen - - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - -**阅读更多** - - - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - -**书籍** - - - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - -**博客** - - - [Adam Lu](http://adamlu.com/) - - [DailyJS](http://dailyjs.com/) - - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) - - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) - - [Perfection Kills](http://perfectionkills.com/) - - [Ben Alman](http://benalman.com/) - - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) - - **[[⬆]](#TOC)** - -## 哪些人在使用 - - 这是一些使用这个风格规范的组织,给我们发pull request或打开一个问题,我们会把你加到列表中。 - - - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) - - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) - - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **Userify**: [userify/javascript](https://github.com/userify/javascript) - - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) - -## 翻译 - - 这个风格规范也有其它语言版本: - - - :de: **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - - :jp: **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) - - :br: **Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - - :cn: **Chinese**: [adamlu/javascript-style-guide](https://github.com/adamlu/javascript-style-guide) - -## JavaScript风格指南 - - - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - -## 贡献者 - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## 许可 - -(The MIT License) - -Copyright (c) 2012 Airbnb - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[[⬆]](#TOC)** - -# }; diff --git a/javascript-style-guide/es6_zh-cn_v2.md b/javascript-style-guide/es6_zh-cn_v2.md deleted file mode 100644 index 875b2f07ed..0000000000 --- a/javascript-style-guide/es6_zh-cn_v2.md +++ /dev/null @@ -1,3303 +0,0 @@ -[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -# Airbnb JavaScript Style Guide() { - -**A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript** - -翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 - -ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/javascript-style-guide/es5_zh-cn_v2.md)。 - -[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) -[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) -[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -Other Style Guides - - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) - - [React](react/) - - [CSS-in-JavaScript](css-in-javascript/) - - [CSS & Sass](https://github.com/airbnb/css) - - [CSS & Sass 中文版](https://github.com/webcoding/css-style-guide) - - [Ruby](https://github.com/airbnb/ruby) - - -## 目录 - - 1. [类型](#types) - 1. [引用](#references) - 1. [对象](#objects) - 1. [数组](#arrays) - 1. [解构](#destructuring) - 1. [字符串](#strings) - 1. [函数](#functions) - 1. [箭头函数](#arrow-functions) - 1. [类 & 构造函数](#classes--constructors) - 1. [模块](#modules) - 1. [Iterators & Generators ](#iterators-and-generators) - 1. [属性](#properties) - 1. [变量](#variables) - 1. [提升](#hoisting) - 1. [比较运算符 & 等号](#comparison-operators--equality) - 1. [代码块](#blocks) - 1. [注释](#comments) - 1. [空白](#whitespace) - 1. [逗号](#commas) - 1. [分号](#semicolons) - 1. [类型转换](#type-casting--coercion) - 1. [命名规则](#naming-conventions) - 1. [存取器](#accessors) - 1. [事件](#events) - 1. [jQuery](#jquery) - 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) - 1. [ECMAScript 6 (ES 2015+) 编码规范](#ecmascript-6-styles) - 1. [测试](#testing) - 1. [性能](#performance) - 1. [资源](#resources) - 1. [使用人群](#in-the-wild) - 1. [翻译](#translation) - 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) - 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) - 1. [Contributors](#contributors) - 1. [License](#license) - - -## 类型 - - - - [1.1](#types--primitives) **基本类型**: 直接存取基本类型。 - - + `string` 字符串 - + `number` 数值 - + `boolean` 布尔类型 - + `null` - + `undefined` - - ```javascript - const foo = 1; - let bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - - - [1.2](#types--complex) **复杂类型**: 通过引用的方式存取复杂类型。 - - + `object` 对象 - + `array` 数组 - + `function` 函数 - - ```javascript - const foo = [1, 2]; - const bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 引用 - - - - [2.1](#references--prefer-const) 对所有的引用使用 `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) - - > 为什么? 这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 - - ```javascript - // bad - var a = 1; - var b = 2; - - // good - const a = 1; - const b = 2; - ``` - - - - [2.2](#references--disallow-var) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 - eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) - jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) - - > 为什么? 因为 `let` 是块级作用域,而 `var` 是函数作用域。 - - ```javascript - // bad - var count = 1; - if (true) { - count += 1; - } - - // good, use the let. - let count = 1; - if (true) { - count += 1; - } - ``` - - - - [2.3](#references--block-scope) 注意 `let` 和 `const` 都是块级作用域。 - - ```javascript - // const 和 let 只存在于它们被定义的区块内。 - { - let a = 1; - const b = 1; - } - console.log(a); // ReferenceError - console.log(b); // ReferenceError - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 对象 - - - - [3.1](#objects--no-new) 使用字面值创建对象。 - eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) - - ```javascript - // bad - const item = new Object(); - - // good - const item = {}; - ``` - **************** - - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 - - ```javascript - // bad - const superman = { - default: { clark: 'kent' }, - private: true, - }; - - // good - const superman = { - defaults: { clark: 'kent' }, - hidden: true, - }; - ``` - - - [3.3](#3.3) 使用同义词替换需要使用的保留字。 - - ```javascript - // bad - const superman = { - class: 'alien', - }; - - // bad - const superman = { - klass: 'alien', - }; - - // good - const superman = { - type: 'alien', - }; - ``` - ************* - - - - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 - - > 为什么? 因为这样可以让你在一个地方定义所有的对象属性。 - - ```javascript - function getKey(k) { - return `a key named ${k}`; - } - - // bad - const obj = { - id: 5, - name: 'San Francisco', - }; - obj[getKey('enabled')] = true; - - // good - const obj = { - id: 5, - name: 'San Francisco', - [getKey('enabled')]: true, - }; - ``` - - - - [3.3](#es6-object-shorthand) 使用对象方法的简写。 - eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) - jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) - - ```javascript - // bad - const atom = { - value: 1, - - addValue: function (value) { - return atom.value + value; - }, - }; - - // good - const atom = { - value: 1, - - addValue(value) { - return atom.value + value; - }, - }; - ``` - - - - [3.4](#es6-object-concise) 使用对象属性值的简写。 - eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) - jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) - - > 为什么? 因为这样更短更有描述性。 - - ```javascript - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - lukeSkywalker: lukeSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - }; - ``` - - - - [3.5](#objects--grouped-shorthand) 在对象属性声明前把简写的属性分组。 - - > 为什么? 因为这样能清楚地看出哪些属性使用了简写。 - - ```javascript - const anakinSkywalker = 'Anakin Skywalker'; - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - episodeOne: 1, - twoJedisWalkIntoACantina: 2, - lukeSkywalker, - episodeThree: 3, - mayTheFourth: 4, - anakinSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - anakinSkywalker, - episodeOne: 1, - twoJedisWalkIntoACantina: 2, - episodeThree: 3, - mayTheFourth: 4, - }; - ``` - - - - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. - eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) - jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) - - > 为什么? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. - - ```javascript - // bad - const bad = { - 'foo': 3, - 'bar': 4, - 'data-blah': 5, - }; - - // good - const good = { - foo: 3, - bar: 4, - 'data-blah': 5, - }; - ``` - - - - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. - - > 为什么? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). - - ```javascript - // bad - console.log(object.hasOwnProperty(key)); - - // good - console.log(Object.prototype.hasOwnProperty.call(object, key)); - - // best - const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. - /* or */ - import has from 'has'; - … - console.log(has.call(object, key)); - ``` - - - - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted. - - ```javascript - // very bad - const original = { a: 1, b: 2 }; - const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ - delete copy.a; // so does this - - // bad - const original = { a: 1, b: 2 }; - const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } - - // good - const original = { a: 1, b: 2 }; - const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } - - const { a, ...noA } = copy; // noA => { b: 2, c: 3 } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 数组 - - - - [4.1](#arrays--literals) 使用字面值创建数组。 - eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) - - ```javascript - // bad - const items = new Array(); - - // good - const items = []; - ``` - - - - [4.2](#arrays--push) 向数组添加元素时使用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 替代直接赋值。 - - ```javascript - const someStack = []; - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - - [4.3](#es6-array-spreads) 使用拓展运算符 `...` 复制数组。 - - ```javascript - // bad - const len = items.length; - const itemsCopy = []; - let i; - - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // good - const itemsCopy = [...items]; - - // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - // old - itemsCopy = items.slice(); - ``` - - - - [4.4](#arrays--from) 使用 [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 把一个类数组对象转换成数组。 - - ```javascript - const foo = document.querySelectorAll('.foo'); - const nodes = Array.from(foo); - - // ES5 中使用slice将类数组的对象转成数组. - // old - function trigger() { - var args = Array.prototype.slice.call(arguments); - ... - } - ``` - - - - [4.5](#arrays--callback-return) 在回调函数中使用 return 语句。如果函数体只由一个单独的语句组成,可以省略 return 关键字 [8.2](#8.2). - eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) - - ```javascript - // good - [1, 2, 3].map((x) => { - const y = x + 1; - return x * y; - }); - - // good - [1, 2, 3].map(x => x + 1); - - // bad - const flat = {}; - [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { - const flatten = memo.concat(item); - flat[index] = flatten; - }); - - // good - const flat = {}; - [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { - const flatten = memo.concat(item); - flat[index] = flatten; - return flatten; - }); - - // bad - inbox.filter((msg) => { - const { subject, author } = msg; - if (subject === 'Mockingbird') { - return author === 'Harper Lee'; - } else { - return false; - } - }); - - // good - inbox.filter((msg) => { - const { subject, author } = msg; - if (subject === 'Mockingbird') { - return author === 'Harper Lee'; - } - - return false; - }); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 解构 - - - - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 - jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) - - > 为什么? 因为解构能减少临时引用属性。 - - ```javascript - // bad - function getFullName(user) { - const firstName = user.firstName; - const lastName = user.lastName; - - return `${firstName} ${lastName}`; - } - - // good - function getFullName(user) { - const { firstName, lastName } = user; - return `${firstName} ${lastName}`; - } - - // best - function getFullName({ firstName, lastName }) { - return `${firstName} ${lastName}`; - } - ``` - - - - [5.2](#destructuring--array) 对数组使用解构赋值。 - jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) - - ```javascript - const arr = [1, 2, 3, 4]; - - // bad - const first = arr[0]; - const second = arr[1]; - - // good - const [first, second] = arr; - ``` - - - - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 - jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) - - > 为什么? 增加属性或者改变排序不会改变调用时的位置。 - - ```javascript - // bad - function processInput(input) { - // then a miracle occurs - return [left, right, top, bottom]; - } - - // 调用时需要考虑回调数据的顺序。 - const [left, __, top] = processInput(input); - - // good - function processInput(input) { - // then a miracle occurs - return { left, right, top, bottom }; - } - - // 调用时只选择需要的数据 - const { left, top } = processInput(input); - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## Strings - - - - [6.1](#strings--quotes) 字符串使用单引号 `''` 。 - eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) - jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) - - ```javascript - // bad - const name = "Capt. Janeway"; - - // bad - template literals should contain interpolation or newlines - const name = `Capt. Janeway`; - - // good - const name = 'Capt. Janeway'; - ``` - - - - [6.2](#strings--line-length) 字符串超过 100 个字节应该使用字符串连接号换行。此处可以更多个字符200或300,目前编辑界面越来越大了 - - > 为什么? 切断长字符串,可以更好编码和搜索。 - - 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). - - ```javascript - // bad - 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.'; - - // bad - 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.'; - - // good - 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.'; - ``` - - - - - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 - 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) - - > 为什么? 模板字符串插值更为简洁,更具可读性。 - - ```javascript - // bad - function sayHi(name) { - return 'How are you, ' + name + '?'; - } - - // bad - function sayHi(name) { - return ['How are you, ', name, '?'].join(); - } - - // bad - function sayHi(name) { - return `How are you, ${ name }?`; - } - - // good - function sayHi(name) { - return `How are you, ${name}?`; - } - ``` - - - - [6.4](#strings--eval) 在字符串中永不使用 `eval()`, 它会导致很多漏洞。 - - - - [6.5](#strings--escaping) 字符串中不要使用不必要的转义。 - eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) - - > 为什么? 反斜杠会降低可读性,应该在必要时才去使用它。 - - ```javascript - // bad - const foo = '\'this\' \i\s \"quoted\"'; - - // good - const foo = '\'this\' is "quoted"'; - const foo = `'this' is "quoted"`; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 函数 - - - [7.1](#7.1) 使用函数表达式,而不是函数声明。 - eslint: [`func-style`](http://eslint.org/docs/rules/func-style) - jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) - - > 为什么? 函数声明会把整个函数提升(hoisted),这导致非常容易在定义以前就被引用,这会降低可读性以及维护性(而函数表达式只会把函数的引用变量名提升)。如果发现一个函数定义非常大或复杂,会干扰其他逻辑的理解,此时也许是时候把它提取成独立模块了。 - > 另外不要忘记给匿名表达式命名,匿名函数会使错误堆栈中跟踪问题更加困难。 - > 函数提升规则,使得[箭头函数](#arrow-functions)可以取代函数表达式。 - - ```javascript - // bad 匿名函数表达式 - var anonymous = function() () { - }; - - // bad - function foo() { - } - - // good - const foo = function named() { - }; - ``` - - - - [7.2](#functions--iife) 用括号包裹 立即调用函数表达式(IIFE)。 - eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) - jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) - - ```javascript - // 立即调用的函数表达式 (IIFE) - // 可使用箭头函数 - (() => { - console.log('Welcome to the Internet. Please follow me.'); - }()); - ``` - - - - [7.3](#functions--in-blocks) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 - eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) - - - - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - let test; - if (currentUser) { - test = () => { - console.log('Yup.'); - }; - } - ``` - - - - [7.5](#functions--arguments-shadow) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 - - ```javascript - // bad - function nope(name, options, arguments) { - // ...stuff... - } - - // good - function yup(name, options, args) { - // ...stuff... - } - ``` - - - - [7.6](#es6-rest) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 - eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) - - > 为什么? 使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 - - ```javascript - // bad - function concatenateAll() { - const args = Array.prototype.slice.call(arguments); - return args.join(''); - } - - // good - function concatenateAll(...args) { - return args.join(''); - } - ``` - - - - [7.7](#es6-default-parameters) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 - - ```javascript - // really bad - function handleThings(opts) { - // 不!我们不应该改变函数参数。 - // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 - // 但这样的写法会造成一些 Bugs。 - //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) - opts = opts || {}; - // ... - } - - // still bad - function handleThings(opts) { - if (opts === void 0) { - opts = {}; - } - // ... - } - - // good - function handleThings(opts = {}) { - // ? 对比以上写法区别是啥?参见 es6 - // ... - } - ``` - - - - [7.8](#functions--default-side-effects) 函数参数设置默认值,要避免副作用。 - - > 为什么? 因为这样会让人感到很困惑。 - - ```javascript - var b = 1; - // bad - function count(a = b++) { - console.log(a); - } - count(); // 1 - count(); // 2 - count(3); // 3 - count(); // 3 - ``` - - - - [7.9](#functions--defaults-last) 总是把默认参数最后。 - - ```javascript - // bad - function handleThings(opts = {}, name) { - // ... - } - - // good - function handleThings(name, opts = {}) { - // ... - } - ``` - - - - [7.10](#functions--constructor) 永远不要使用构造函数创建新函数。 - eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) - - > 为什么? 以这种方式创建一个函数,类似 eval(),会导致很多漏洞。 - - ```javascript - // bad - var add = new Function('a', 'b', 'return a + b'); - - // still bad - var subtract = Function('a', 'b', 'return a - b'); - ``` - - - - [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) - - > 为什么? 一致性好,当添加或移除名称时,不必添加或移除空间。 - - ```javascript - // bad - const f = function(){}; - const g = function (){}; - const h = function() {}; - - // good - const x = function () {}; - const y = function a() {}; - ``` - - - - [7.12](#functions--mutate-params) 不要使用变异参数。 - eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) - - > 为什么? 操纵传入的参数,在原调用者上可能会导致不必要的变量副作用。 - - ```javascript - // bad - function f1(obj) { - obj.key = 1; - }; - - // good - function f2(obj) { - const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; - }; - ``` - - - - [7.13](#functions--reassign-params) 不要给形参重新赋值。 - eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) - - > 为什么? 这样做可能会导致意外行为,特别是操作 `arguments` 参数。这也会导致优化问题,特别是使用 V8 解析器. - - ```javascript - // bad - function f1(a) { - a = 1; - } - - function f2(a) { - if (!a) { a = 1; } - } - - // good - function f3(a) { - const b = a || 1; - } - - function f4(a = 1) { - } - ``` - - - - [7.14](#functions--spread-vs-apply) 使用拓展操作符 `...` 调用可变参函数。 - eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) - - > 为什么? 更简洁、并且也不用提供上下文作用域,而使用 `new` 和 `apply` 也没这个容易。 - - ```javascript - // bad - const x = [1, 2, 3, 4, 5]; - console.log.apply(console, x); - - // good - const x = [1, 2, 3, 4, 5]; - console.log(...x); - - // bad - new (Function.prototype.bind.apply(Date, [null, 2016, 08, 05])); - - // good - new Date(...[2016, 08, 05]); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 箭头函数 - - - - [8.1](#arrows--use-them) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 - 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) - - > 为什么? 因为箭头函数会创建一个你通常最想要的 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 - - > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 - - ```javascript - // bad - [1, 2, 3].map(function (x) { - const y = x + 1; - return x * y; - }); - - // good - [1, 2, 3].map((x) => { - const y = x + 1; - return x * y; - }); - ``` - - - - [8.2](#arrows--implicit-return) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 - - > 为什么? 这是语法糖。在链式调用中可读性很高。 - - > 为什么不?当你打算回传一个对象的时候。 - - ```javascript - // bad - [1, 2, 3].map(number => { - const nextNumber = number + 1; - `A string containing the ${nextNumber}.`; - }); - - // good - [1, 2, 3].map(number => `A string containing the ${number}.`); - - // good - [1, 2, 3].map((number) => { - const nextNumber = number + 1; - return `A string containing the ${nextNumber}.`; - }); - - // good - [1, 2, 3].map((number, index) => ({ - [index]: number - })); - ``` - - - - [8.3](#arrows--paren-wrap) 如果表达式有多行,使用圆括号包裹,提高可读性。 - - > 为什么? 这样能更清楚的看到开始和结束位置。 - - ```js - // bad - ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( - httpMagicObjectWithAVeryLongName, - httpMethod - ) - ); - - // good - ['get', 'post', 'put'].map(httpMethod => ( - Object.prototype.hasOwnProperty.call( - httpMagicObjectWithAVeryLongName, - httpMethod - ) - )); - ``` - - - - [8.4](#arrows--one-arg-parens) 如果函数只有一个参数就不要使用圆括号,否则总是用圆括号包裹参数。 - eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) - jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) - - > 为什么? 更少的视觉干扰 - - ```js - // bad - [1, 2, 3].map((x) => x * x); - - // good - [1, 2, 3].map(x => x * x); - - // good - [1, 2, 3].reduce((total, n) => { - return total + n; - }, 0); - - // good - [1, 2, 3].map(number => ( - `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` - )); - - // bad - [1, 2, 3].map(x => { - const y = x + 1; - return x * y; - }); - - // good - [1, 2, 3].map((x) => { - const y = x + 1; - return x * y; - }); - ``` - - - - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) - eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) - - ```js - // bad - const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; - - // bad - const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; - - // good - const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); - - // good - const itemHeight = (item) => { - const { height, largeSize, smallSize } = item; - return height > 256 ? largeSize : smallSize; - }; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 类 & 构造器 - - - - [9.1](#constructors--use-class) 总是使用 `class`。避免直接操作 `prototype` 。 - - > 为什么? 因为 `class` 语法更为简洁更易读。 - - ```javascript - // bad - function Queue(contents = []) { - this.queue = [...contents]; - } - Queue.prototype.pop = function () { - const value = this.queue[0]; - this.queue.splice(0, 1); - return value; - }; - - - // good - class Queue { - constructor(contents = []) { - this.queue = [...contents]; - } - pop() { - const value = this.queue[0]; - this.queue.splice(0, 1); - return value; - } - } - ``` - - - - [9.2](#constructors--extends) 使用 `extends` 继承。 - - > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 - - ```javascript - // bad - const inherits = require('inherits'); - function PeekableQueue(contents) { - Queue.apply(this, contents); - } - inherits(PeekableQueue, Queue); - PeekableQueue.prototype.peek = function () { - return this.queue[0]; - } - - // good - class PeekableQueue extends Queue { - peek() { - return this.queue[0]; - } - } - ``` - - - - [9.3](#constructors--chaining) 方法可以返回 `this` 以便于链式调用。 - - ```javascript - // bad - Jedi.prototype.jump = function () { - this.jumping = true; - return true; - }; - - Jedi.prototype.setHeight = function (height) { - this.height = height; - }; - - const luke = new Jedi(); - luke.jump(); // => true - luke.setHeight(20); // => undefined - - // good - class Jedi { - jump() { - this.jumping = true; - return this; - } - - setHeight(height) { - this.height = height; - return this; - } - } - - const luke = new Jedi(); - - luke.jump() - .setHeight(20); - ``` - - - - - [9.4](#constructors--tostring) 可以自定义 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 - - ```javascript - class Jedi { - constructor(options = {}) { - this.name = options.name || 'no name'; - } - - getName() { - return this.name; - } - - toString() { - return `Jedi - ${this.getName()}`; - } - } - ``` - - - - [9.5](#constructors--no-useless) 类有默认构造函数,书写一个空的构造函数是没必要的。 - eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) - - ```javascript - // bad - class Jedi { - constructor() {} - - getName() { - return this.name; - } - } - - // bad - class Rey extends Jedi { - constructor(...args) { - super(...args); - } - } - - // good - class Rey extends Jedi { - constructor(...args) { - super(...args); - this.name = 'Rey'; - } - } - ``` - - - - [9.6](#classes--no-duplicate-members) 避免类成员重复。 - eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) - - > 为什么? 重复的类成员默认会使用最后一个——这肯定有一个错误存在。 - - ```javascript - // bad - class Foo { - bar() { return 1; } - bar() { return 2; } - } - - // good - class Foo { - bar() { return 1; } - } - - // good - class Foo { - bar() { return 2; } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 模块 - - - - [10.1](#modules--use-them) 总是使用模块 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 - - > 为什么? 模块就是未来,让我们开始迈向未来吧。 - > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) - - ```javascript - // bad - const AirbnbStyleGuide = require('./AirbnbStyleGuide'); - module.exports = AirbnbStyleGuide.es6; - - // ok - import AirbnbStyleGuide from './AirbnbStyleGuide'; - export default AirbnbStyleGuide.es6; - - // best - import { es6 } from './AirbnbStyleGuide'; - export default es6; - - // ES5中要注意 - // old - // - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 - // - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 - // - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 - // - 总是在模块顶部声明 `'use strict';` - ``` - - - - [10.2](#modules--no-wildcard) 不要使用通配符 import。 - - > 为什么? 这样能确保你只有一个默认 export。 - - ```javascript - // bad - import * as AirbnbStyleGuide from './AirbnbStyleGuide'; - - // good - import AirbnbStyleGuide from './AirbnbStyleGuide'; - ``` - - - - [10.3](#modules--no-export-from-import) 不要从 import 中直接 export。 - - > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 - - ```javascript - // bad - // filename es6.js - export { es6 as default } from './AirbnbStyleGuide'; - - // good - // filename es6.js - import { es6 } from './AirbnbStyleGuide'; - export default es6; - ``` - - - - [10.4](#modules--no-duplicate-imports) 一个模块只要一个 `import` 。 - eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) - - > 为什么? 同一模块多个 `import` 导入,会使代码更难维护。 - - ```javascript - // bad - import foo from 'foo'; - // … some other imports … // - import { named1, named2 } from 'foo'; - - // good - import foo, { named1, named2 } from 'foo'; - - // good - import foo, { - named1, - named2, - } from 'foo'; - ``` - - - - [10.5](#modules--no-mutable-exports) 禁止 `export` 可写值输出,使用常量(Functions/Classes 暂时例外)。 - eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) - - > 为什么? 避免全局使用时出现问题,除了某些特定情况下确实要这样用。一般来说,`export` 应该输出常量。 - - ```javascript - // bad - var count = 3; - export { count } - - // bad - let foo = 3; - export { foo } - - // good - const foo = 3; - export { foo } - - // valid - export const count = 1 - export function getCount () {} - export class Counter {} - ``` - - - - [10.6](#modules--prefer-default-export) 单出口模块,更倾向设置为默认出口。 -eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) - - ```javascript - // bad - export function foo() {} - - // good - export default function foo() {} - ``` - - - - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 -eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) - - > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 - - ```javascript - // bad - import foo from 'foo'; - foo.init(); - - import bar from 'bar'; - - // good - import foo from 'foo'; - import bar from 'bar'; - - foo.init(); - ``` - - - - [10.8](#modules--multiline-imports-over-newlines) 多 `import` 入口,代码书写格式应与 `array` 和 `object` 保持一致。 - - > 为什么? 大括号代码块、缩进以及逗号都保持风格一致 - - ```javascript - // bad - import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; - - // good - import { - longNameA, - longNameB, - longNameC, - longNameD, - longNameE, - } from 'path'; - ``` - - - - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 -eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) - - > 为什么? 加载器语法要在 `webpack.config.js` 中使用 - - ```javascript - // bad - import fooSass from 'css!sass!foo.scss'; - import barCss from 'style!css!bar.css'; - - // good - import fooSass from 'foo.scss'; - import barCss from 'bar.css'; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## Iterators and Generators 迭代器 & 生成器 - - - - [11.1](#iterators--nope) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-in` or `for-of`。 - eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](http://eslint.org/docs/rules/no-restricted-syntax) - - > 为什么? 这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 - - > 使用迭代器 `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... 遍历数组,使用 `Object.keys()` / `Object.values()` / `Object.entries()` 遍历对象。 - ```javascript - const numbers = [1, 2, 3, 4, 5]; - - // bad - let sum = 0; - for (let num of numbers) { - sum += num; - } - sum === 15; - - // good - let sum = 0; - numbers.forEach(num => sum += num); - sum === 15; - - // best (use the functional force) - const sum = numbers.reduce((total, num) => total + num, 0); - sum === 15; - ``` - - - - [11.2](#generators--nope) 现在还不要使用生成器 generators。 - - > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) - - - - [11.3](#generators--spacing) 如果你必须使用generators 或无视[generators 忠告](#generators--nope),那么请务必确保函数签名(function*)是适当的间距。 - eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) - - > 为什么? `function` and `*` 是同一个关键词的不同组成部分—— `*` 不是 `function` 修饰符, `function*` 是一种特定结构, 不同于 `function`。 - - ```js - // bad - function * foo() { - } - - const bar = function * () { - } - - const baz = function *() { - } - - const quux = function*() { - } - - function*foo() { - } - - function *foo() { - } - - // very bad - function - * - foo() { - } - - const wat = function - * - () { - } - - // good - function* foo() { - } - - const foo = function* () { - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 属性 - - - - [12.1](#properties--dot) 使用 `.` 符号来访问对象的属性。 - eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) - jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - // bad - const isJedi = luke['jedi']; - - // good - const isJedi = luke.jedi; - ``` - - - - [12.2](#properties--bracket) 当通过变量访问属性时使用中括号 `[]`。 - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - function getProp(prop) { - return luke[prop]; - } - - const isJedi = getProp('jedi'); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 变量 - - - - [13.1](#variables--const) 总是使用 `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) - - ```javascript - // bad - superPower = new SuperPower(); - - // good - const superPower = new SuperPower(); - ``` - - - - [13.2](#variables--one-const) 使用 `const` 声明每一个变量。 - eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) - - > 为什么? 增加新变量将更容易,而且你永远不用再担心调换错 `;` or `,` ,并且 diff 工具下更少干扰。同时在 debugger 时,可以单步调试而不会一次跳过他们所有变量。 - - ```javascript - // bad - const items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - - // bad - // (compare to above, and try to spot the mistake) - const items = getItems(), - goSportsTeam = true; - dragonball = 'z'; - - // good - const items = getItems(); - const goSportsTeam = true; - const dragonball = 'z'; - ``` - - - - [13.3](#variables--const-let-group) 将所有的 `const` 和 `let` 分组 - - > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 - - ```javascript - // bad - let i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - let i; - const items = getItems(); - let dragonball; - const goSportsTeam = true; - let len; - - // good - const goSportsTeam = true; - const items = getItems(); - let dragonball; - let i; - let length; - ``` - - - - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 - - > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 - - ```javascript - // bad - unnecessary function call - function checkName(hasName) { - const name = getName(); - - if (hasName === 'test') { - return false; - } - - if (name === 'test') { - this.setName(''); - return false; - } - - return name; - } - - // good - function checkName(hasName) { - if (hasName === 'test') { - return false; - } - - const name = getName(); - - if (name === 'test') { - this.setName(''); - return false; - } - - return name; - } - ``` - - - - [13.5](#variables--no-chain-assignment) 不使用链接变量赋值。 - - > 为什么? 链接变量赋值会创建隐式全局变量。 - - ```javascript - // bad - (function example() { - // JavaScript interprets this as - // let a = ( b = ( c = 1 ) ); - // The let keyword only applies to variable a; variables b and c become - // global variables. - let a = b = c = 1; - }()); - - console.log(a); // undefined - console.log(b); // 1 - console.log(c); // 1 - - // good - (function example() { - let a = 1; - let b = a; - let c = a; - }()); - - console.log(a); // undefined - console.log(b); // undefined - console.log(c); // undefined - - // the same applies for `const` - ``` - - - - [13.6](#variables--unary-increment-decrement) 便面使用一元递增和递减操作 (++, --) 。 - eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) - - > 为什么? 一元递增和递减语句默认在自动分号插入处理下会有错误问题。而且它还会出现 `num += 1` 替代 `num ++` 的情况。不允许使用它们,也可以代码更健壮。 - - ```javascript - // bad - - let array = [1, 2, 3]; - let num = 1; - num ++; - -- num; - - let sum = 0; - let truthyCount = 0; - for(let i = 0; i < array.length; i++){ - let value = array[i]; - sum += value; - if (value) { - truthyCount++; - } - } - - // good - - let array = [1, 2, 3]; - let num = 1; - num += 1; - num -= 1; - - const sum = array.reduce((a, b) => a + b, 0); - const truthyCount = array.filter(Boolean).length; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## Hoisting - - - - [14.1](#hoisting--about) `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)相当重要。 - - ```javascript - // 我们知道这样运行不了(假设 notDefined 不是全局变量) - function example() { - console.log(notDefined); // => throws a ReferenceError - } - - // 由于变量提升的原因, - // 在引用变量后再声明变量是可以运行的。 - // **注意:** 变量赋值 `true` 的操作不会被提升。 - function example() { - console.log(declaredButNotAssigned); // => undefined - var declaredButNotAssigned = true; - } - - // 编译器会把函数声明提升到作用域的顶层, - // 这意味着我们的例子可以改写成这样: - function example() { - let declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - - // 使用 const 和 let - function example() { - console.log(declaredButNotAssigned); // => throws a ReferenceError - console.log(typeof declaredButNotAssigned); // => throws a ReferenceError - const declaredButNotAssigned = true; - } - ``` - - - - [14.2](#hoisting--anon-expressions) 匿名函数表达式的变量名会被提升,但函数体并不会。 - - ```javascript - function example() { - console.log(anonymous); // => undefined - - anonymous(); // => TypeError anonymous is not a function - - var anonymous = function () { - console.log('anonymous function expression'); - }; - } - ``` - - - - [14.3](#hoisting--named-expresions) 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。 - - ```javascript - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - superPower(); // => ReferenceError superPower is not defined - - var named = function superPower() { - console.log('Flying'); - }; - } - - // the same is true when the function name - // is the same as the variable name. - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - var named = function named() { - console.log('named'); - } - } - ``` - - - - [14.4](#hoisting--declarations) 函数声明的名称和函数体都会被提升。 - - ```javascript - function example() { - superPower(); // => Flying - - function superPower() { - console.log('Flying'); - } - } - ``` - - - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/). - - - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 - -**[⬆ 返回目录](#table-of-contents)** - - -## 比较运算符 & 等号 - - - - [15.1](#comparison--eqeqeq) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=` 。 - eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html) - - - - [15.2](#comparison--if) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: - - + **Objects 对象** 被计算为 **true** - + **Undefined** 被计算为 **false** - + **Null** 被计算为 **false** - + **Booleans 布尔值** 被计算为 **布尔的值** - + **Numbers 数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** - + **Strings 字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** - - ```javascript - if ([0] && []) { - // true - // an array (even an empty one) is an object, objects will evaluate to true - } - ``` - - - - [15.3](#comparison--shortcuts) 使用简写。 - - ```javascript - // bad - if (isValid === true) { - // ...stuff... - } - - // good - if (isValid) { - // ...stuff... - } - - // bad - if (name) { - // ...stuff... - } - - // good - if (name !== '') { - // ...stuff... - } - - // bad - if (collection.length) { - // ...stuff... - } - - // good - if (collection.length > 0) { - // 为什么没用简写??? - // ...stuff... - } - ``` - - - - [15.4](#comparison--moreinfo) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 - - - - [15.5](#comparison--switch-blocks) `case` 以及 `default` 情况下使用括号会创建块级作用域 (e.g. `let`, `const`, `function`, and `class`). - - > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 - - eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). - - ```javascript - // ??? - // bad - switch (foo) { - case 1: - let x = 1; - break; - case 2: - const y = 2; - break; - case 3: - function f() {} - break; - default: - class C {} - } - - // good - switch (foo) { - case 1: { - let x = 1; - break; - } - case 2: { - const y = 2; - break; - } - case 3: { - function f() {} - break; - } - case 4: - bar(); - break; - default: { - class C {} - } - } - ``` - - - - [15.6](#comparison--nested-ternaries) 别嵌套使用三元表达式,要单个使用。 - - eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). - - ```javascript - // bad - const foo = maybe1 > maybe2 - ? "bar" - : value1 > value2 ? "baz" : null; - - // better - const maybeNull = value1 > value2 ? 'baz' : null; - - const foo = maybe1 > maybe2 - ? 'bar' - : maybeNull; - - // best - const maybeNull = value1 > value2 ? 'baz' : null; - - const foo = maybe1 > maybe2 ? 'bar' : maybeNull; - ``` - - - - [15.7](#comparison--unneeded-ternary) 避免不必要使用三元表达式 - - eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). - - ```javascript - // bad - const foo = a ? a : b; - const bar = c ? true : false; - const baz = c ? false : true; - - // good - const foo = a || b; - const bar = !!c; - const baz = !c; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 代码块 - - - - [16.1](#blocks--braces) 使用大括号包裹所有的多行代码块。 - - ```javascript - // bad - if (test) - return false; - - // good - if (test) return false; - - // good - if (test) { - return false; - } - - // bad - function foo() { return false; } - - // good - function bar() { - return false; - } - ``` - - - - [16.2](#blocks--cuddled-elses) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 - - ```javascript - // bad - if (test) { - thing1(); - thing2(); - } - else { - thing3(); - } - - // good - if (test) { - thing1(); - thing2(); - } else { - thing3(); - } - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## 注释 - - - - [17.1](#comments--multiline) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 - - ```javascript - // bad - // make() returns a new element - // based on the passed in tag name - // - // @param {String} tag - // @return {Element} element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the passed-in tag name - * - * @param {String} tag - * @return {Element} element - */ - function make(tag) { - - // ...stuff... - - return element; - } - ``` - - - - [17.2](#comments--singleline) 单行注释使用 `//`,后紧跟一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 - - ```javascript - // bad - const active = true; // is current tab - - // good - // is current tab - const active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - - // also good - function getType() { - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - ``` - - - - [17.3](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 - - - - [17.4](#comments--fixme) 使用 `// FIXME`: 标注问题。 - - ```javascript - class Calculator extends Abacus { - constructor() { - super(); - - // FIXME: shouldn't use a global here - total = 0; - } - } - ``` - - - - [17.5](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 - - ```javascript - class Calculator extends Abacus { - constructor() { - super(); - - // TODO: total should be configurable by an options param - this.total = 0; - } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 空白 - - - - [18.1](#whitespace--spaces) 使用 2 个空格作为缩进。 - eslint: [`indent`](http://eslint.org/docs/rules/indent.html) - jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) - - ```javascript - // bad - function foo() { - ∙∙∙∙const name; - } - - // bad - function bar() { - ∙const name; - } - - // good - function baz() { - ∙∙const name; - } - ``` - - - - [18.2](#whitespace--before-blocks) 在花括号前放一个空格。 - eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) - jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) - - ```javascript - // bad - function test(){ - console.log('test'); - } - - // good - function test() { - console.log('test'); - } - - // bad - dog.set('attr',{ - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - - // good - dog.set('attr', { - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - ``` - - - - [18.3](#whitespace--around-keywords) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 - eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) - jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) - - ```javascript - // bad - if(isJedi) { - fight (); - } - - // good - if (isJedi) { - fight(); - } - - // bad - function fight () { - console.log ('Swooosh!'); - } - - // good - function fight() { - console.log('Swooosh!'); - } - ``` - - - - [18.4](#whitespace--infix-ops) 使用空格把运算符隔开。 - 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) - - ```javascript - // bad - const x=y+5; - - // good - const x = y + 5; - ``` - - - - [18.5](#whitespace--newline-at-end) 在文件末尾插入一个空行。 - eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) - - ```javascript - // bad - (function (global) { - // ...stuff... - })(this); - ``` - - ```javascript - // bad - (function (global) { - // ...stuff... - })(this);↵ - ↵ - ``` - - ```javascript - // good - (function (global) { - // ...stuff... - })(this);↵ - ``` - - - - [18.6](#whitespace--chains) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 - 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) - - ```javascript - // bad - $('#items').find('.selected').highlight().end().find('.open').updateCount(); - - // bad - $('#items'). - find('.selected'). - highlight(). - end(). - find('.open'). - updateCount(); - - // good - $('#items') - .find('.selected') - .highlight() - .end() - .find('.open') - .updateCount(); - - // bad - const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) - .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - const leds = stage.selectAll('.led') - .data(data) - .enter().append('svg:svg') - .classed('led', true) - .attr('width', (radius + margin) * 2) - .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - const leds = stage.selectAll('.led').data(data); - ``` - - - - [18.7](#whitespace--after-blocks) 在块末和新语句前插入空行。 - jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) - - ```javascript - // bad - if (foo) { - return bar; - } - return baz; - - // good - if (foo) { - return bar; - } - - return baz; - - // bad - const obj = { - foo() { - }, - bar() { - }, - }; - return obj; - - // good - const obj = { - foo() { - }, - - bar() { - }, - }; - - return obj; - - // bad - const arr = [ - function foo() { - }, - function bar() { - }, - ]; - return arr; - - // good - const arr = [ - function foo() { - }, - - function bar() { - }, - ]; - - return arr; - ``` - - - - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) - - ```javascript - // bad - function bar() { - - console.log(foo); - - } - - // also bad - if (baz) { - - console.log(qux); - } else { - console.log(foo); - - } - - // good - function bar() { - console.log(foo); - } - - // good - if (baz) { - console.log(qux); - } else { - console.log(foo); - } - ``` - - - - [18.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) - - ```javascript - // bad - function bar( foo ) { - return foo; - } - - // good - function bar(foo) { - return foo; - } - - // bad - if ( foo ) { - console.log(foo); - } - - // good - if (foo) { - console.log(foo); - } - ``` - - - - [18.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) - - ```javascript - // bad - const foo = [ 1, 2, 3 ]; - console.log(foo[ 0 ]); - - // good - const foo = [1, 2, 3]; - console.log(foo[0]); - ``` - - - - [18.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) - - ```javascript - // bad - const foo = {clark: 'kent'}; - - // good - const foo = { clark: 'kent' }; - ``` - - - - [18.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) - - > Why? This ensures readability and maintainability. - - ```javascript - // bad - const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; - - // bad - $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); - - // good - const foo = jsonData - && jsonData.foo - && jsonData.foo.bar - && jsonData.foo.bar.baz - && jsonData.foo.bar.baz.quux - && jsonData.foo.bar.baz.quux.xyzzy; - - // good - $.ajax({ - method: 'POST', - url: 'https://airbnb.com/', - data: { name: 'John' }, - }) - .done(() => console.log('Congratulations!')) - .fail(() => console.log('You have failed this city.')); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 逗号 - - - - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 - eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) - jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) - - ```javascript - // bad - const story = [ - once - , upon - , aTime - ]; - - // good - const story = [ - once, - upon, - aTime, - ]; - - // bad - const hero = { - firstName: 'Ada' - , lastName: 'Lovelace' - , birthYear: 1815 - , superPower: 'computers' - }; - - // good - const hero = { - firstName: 'Ada', - lastName: 'Lovelace', - birthYear: 1815, - superPower: 'computers', - }; - ``` - - - - [19.2](#commas--dangling) 增加结尾的逗号: **需要**。 - eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) - jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) - - > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 - - ```javascript - // bad - git diff without trailing comma - const hero = { - firstName: 'Florence', - - lastName: 'Nightingale' - + lastName: 'Nightingale', - + inventorOf: ['coxcomb chart', 'modern nursing'] - }; - - // good - git diff with trailing comma - const hero = { - firstName: 'Florence', - lastName: 'Nightingale', - + inventorOf: ['coxcomb chart', 'modern nursing'], - }; - - // bad - const hero = { - firstName: 'Dana', - lastName: 'Scully' - }; - - const heroes = [ - 'Batman', - 'Superman' - ]; - - // good - const hero = { - firstName: 'Dana', - lastName: 'Scully', - }; - - const heroes = [ - 'Batman', - 'Superman', - ]; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 分号 - - - - [20.1](#semicolons--required) **使用分号** - eslint: [`semi`](http://eslint.org/docs/rules/semi.html) - jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) - - ```javascript - // bad - (function () { - const name = 'Skywalker' - return name - })() - - // good - (function () { - const name = 'Skywalker'; - return name; - }()); - - // good (防止函数在两个 IIFE 合并时被当成一个参数) - ;(() => { - const name = 'Skywalker'; - return name; - }()); - ``` - - [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). - -**[⬆ 返回目录](#table-of-contents)** - - -## 类型转换 - - - - [21.1](#coercion--explicit) 在语句开始时执行类型转换。 - - - - [21.2](#coercion--strings) 字符串: - - ```javascript - // => this.reviewScore = 9; - - // bad - const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() - - // bad - const totalScore = this.reviewScore.toString(); // isn't guaranteed to return a string - - // good - const totalScore = String(this.reviewScore); - ``` - - - - [21.3](#coercion--numbers) 数字:对数字使用 `parseInt` 转换,并带上类型转换的基数。 - eslint: [`radix`](http://eslint.org/docs/rules/radix) - - ```javascript - const inputValue = '4'; - - // bad - const val = new Number(inputValue); - - // bad - const val = +inputValue; - - // bad - const val = inputValue >> 0; - - // bad - const val = parseInt(inputValue); - - // good - const val = Number(inputValue); - - // good - const val = parseInt(inputValue, 10); - ``` - - - - [21.4](#coercion--comment-deviations) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 - - ```javascript - // good - /** - * 使用 parseInt 导致我的程序变慢, - * 改成使用位操作转换数字快多了。 - */ - const val = inputValue >> 0; - ``` - - - - [21.5](#coercion--bitwise) **注意:** 小心使用位操作运算符。数字会被当成 [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: - - ```javascript - 2147483647 >> 0 //=> 2147483647 - 2147483648 >> 0 //=> -2147483648 - 2147483649 >> 0 //=> -2147483647 - ``` - - - - [21.6](#coercion--booleans) 布尔运算: - - ```javascript - const age = 0; - - // bad - const hasAge = new Boolean(age); - - // good - const hasAge = Boolean(age); - - // best - const hasAge = !!age; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 命名规则 - - - - [22.1](#naming--descriptive) 避免单字母命名。命名应具备描述性。 - eslint: [`id-length`](http://eslint.org/docs/rules/id-length) - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - - [22.2](#naming--camelCase) 使用驼峰式(camelCase、小驼峰)命名变量、对象、函数和实例。 - eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) - jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) - - ```javascript - // bad - const OBJEcttsssss = {}; - const this_is_my_object = {}; - function c() {} - - // good - const thisIsMyObject = {}; - function thisIsMyFunction() {} - ``` - - - - [22.3](#naming--PascalCase) 使用帕斯卡式(PascalCase、大驼峰式)命名构造函数或类。 - eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) - jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) - - Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 - - ```javascript - // bad - function user(options) { - this.name = options.name; - } - - const bad = new user({ - name: 'nope', - }); - - // good - class User { - constructor(options) { - this.name = options.name; - } - } - - const good = new User({ - name: 'yup', - }); - ``` - - - - [22.4](#naming--leading-underscore) 不要使用尾随或前导下划线来命名私有属性。 - eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) - jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) - - > 为什么? JavaScript没有在属性或方法方面的隐私权的概念。虽然一个领先的下划线是一个共同的公约,意思是“私人”,事实上,这些属性是完全公开的,因此,是你的公共API合同的一部分。这个约定可能会导致开发人员错误地认为一个变化不算是中断,或者不需要测试。TL;DR:如果你想要的东西是“私人的”,它不应该可见。 - - ```javascript - // bad - this.__firstName__ = 'Panda'; - this.firstName_ = 'Panda'; - this._firstName = 'Panda'; - - // good - this.firstName = 'Panda'; - ``` - - - - [22.5](#naming--self-this) 别保存 `this` 的引用。应使用箭头函数或函数绑定 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)。 - jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) - - ```javascript - // bad - function foo() { - const self = this; - return function () { - console.log(self); - }; - } - - // bad - function foo() { - const that = this; - return function () { - console.log(that); - }; - } - - // good - function foo() { - return () => { - console.log(this); - }; - } - ``` - - - - [22.6](#naming--filename-matches-export) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 - - ```javascript - // file 1 contents - class CheckBox { - // ... - } - export default CheckBox; - - // file 2 contents - export default function fortyTwo() { return 42; } - - // file 3 contents - export default function insideDirectory() {} - - // in some other file - // bad - import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename - import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export - import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export - - // bad - import CheckBox from './check_box'; // PascalCase import/export, snake_case filename - import forty_two from './forty_two'; // snake_case import/filename, camelCase export - import inside_directory from './inside_directory'; // snake_case import, camelCase export - import index from './inside_directory/index'; // requiring the index file explicitly - import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly - - // good - import CheckBox from './CheckBox'; // PascalCase export/import/filename - import fortyTwo from './fortyTwo'; // camelCase export/import/filename - import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" - // ^ supports both insideDirectory.js and insideDirectory/index.js - ``` - - - - [22.7](#naming--camelCase-default-export) 当你导出默认的函数时使用驼峰式命名camelCase。你的文件名必须和函数名完全保持一致。 - - ```javascript - function makeStyleGuide() { - } - - export default makeStyleGuide; - ``` - - - - [22.8](#naming--PascalCase-singleton) 当你导出构造函数、类、单例、函数库、空对象时使用帕斯卡式命名PascalCase。 - - ```javascript - const AirbnbStyleGuide = { - es6: { - } - }; - - export default AirbnbStyleGuide; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 存取器 - - - - [23.1](#accessors--not-required) 属性的存取函数不是必须的。 - - - - [23.2](#accessors--no-getters-setters) 不要使用 getters/setters 有可能引起副作用,很难测试、维护、调试。如果你需要存取函数时使用 `getVal()` 和 `setVal(value)`。 - - ```javascript - // bad - class Dragon { - get age() { - // ... - } - - set age(value) { - // ... - } - } - - // good - class Dragon { - getAge() { - // ... - } - - setAge(value) { - // ... - } - } - ``` - - - - [23.3](#accessors--boolean-prefix) 如果属性或方法是布尔值,使用 `isVal()` 或 `hasVal()` 判断。 - - ```javascript - // bad - if (!dragon.age()) { - return false; - } - - // good - if (!dragon.hasAge()) { - return false; - } - ``` - - - - [23.4](#accessors--consistent) 可以创建 `get()` 和 `set()` 函数,但要保持一致。 - - ```javascript - class Jedi { - constructor(options = {}) { - const lightsaber = options.lightsaber || 'blue'; - this.set('lightsaber', lightsaber); - } - - set(key, val) { - this[key] = val; - } - - get(key) { - return this[key]; - } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 事件 - - - - [24.1](#events--hash) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: - - ```javascript - // bad - $(this).trigger('listingUpdated', listing.id); - - ... - - $(this).on('listingUpdated', (e, listingId) => { - // do something with listingId - }); - ``` - - prefer 更好的写法: - - ```javascript - // good - $(this).trigger('listingUpdated', { listingId: listing.id }); - - ... - - $(this).on('listingUpdated', (e, data) => { - // do something with data.listingId - }); - ``` - - **[⬆ 返回目录](#table-of-contents)** - -## jQuery - - - - [25.1](#jquery--dollar-prefix) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 - jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) - - ```javascript - // bad - const sidebar = $('.sidebar'); - - // good - const $sidebar = $('.sidebar'); - - // good - const $sidebarBtn = $('.sidebar-btn'); - ``` - - - - [25.2](#jquery--cache) 缓存 jQuery 查询。 - - ```javascript - // bad - function setSidebar() { - $('.sidebar').hide(); - - // ...stuff... - - $('.sidebar').css({ - 'background-color': 'pink' - }); - } - - // good - function setSidebar() { - const $sidebar = $('.sidebar'); - $sidebar.hide(); - - // ...stuff... - - $sidebar.css({ - 'background-color': 'pink' - }); - } - ``` - - - - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 - [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - - - - [25.4](#jquery--find) 对有作用域的 jQuery 对象查询使用 `find`。 - - ```javascript - // bad - $('ul', '.sidebar').hide(); - - // bad - $('.sidebar').find('ul').hide(); - - // good - $('.sidebar ul').hide(); - - // good - $('.sidebar > ul').hide(); - - // good - $sidebar.find('ul').hide(); - - // good (slower) - $sidebar.find('ul'); - - // good (faster) - $($sidebar[0]).find('ul'); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## ECMAScript 5 兼容性 - - - - [26.1](#es5-compat--kangax) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). - -**[⬆ 返回目录](#table-of-contents)** - - -## ECMAScript 6(ES 2015+)规范 - - - - [27.1](#es6-styles) 以下是链接到 ES6 的各个特性的列表。 - -1. [Arrow Functions](#arrow-functions) -1. [Classes](#classes--constructors) -1. [Object Shorthand](#es6-object-shorthand) -1. [Object Concise](#es6-object-concise) -1. [Object Computed Properties](#es6-computed-properties) -1. [Template Strings](#es6-template-literals) -1. [Destructuring](#destructuring) -1. [Default Parameters](#es6-default-parameters) -1. [Rest](#es6-rest) -1. [Array Spreads](#es6-array-spreads) -1. [Let and Const](#references) -1. [Iterators and Generators](#iterators-and-generators) -1. [Modules](#modules) - - - - [27.2](#tc39-proposals) Do not use [TC39 proposals](https://github.com/tc39/proposals) that have not reached stage 3. - - > Why? [They are not finalized](https://tc39.github.io/process-document/), and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet. - -**[⬆ 返回目录](#table-of-contents)** - - -## 测试 - - - - [28.1](#testing--yup) **Yup.** - - ```javascript - function foo() { - return true; - } - ``` - - - - [28.2](#testing--for-real) **No, but seriously**: - - - Whichever testing framework you use, you should be writing tests! - - Strive to write many small pure functions, and minimize where mutations occur. - - Be cautious about stubs and mocks - they can make your tests more brittle. - - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. - - 100% test coverage is a good goal to strive for, even if it's not always practical to reach it. - - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future. - -**[⬆ 返回目录](#table-of-contents)** - - -## 性能 - - - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](https://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](https://jsperf.com/ya-string-concat) - - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) - - Loading... - -**[⬆ 返回目录](#table-of-contents)** - - -## 资源 - -**Learning ES6** - - - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) - - [ExploringJS](http://exploringjs.com/) - - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) - - [Comprehensive Overview of ES6 Features](http://es6-features.org/) - -**Read This** - - - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) - - old [Annotated ECMAScript 5.1](http://es5.github.com/) - -**Tools** - - - Code Style Linters - + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) - + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) - -**Other Style Guides** - - - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) - - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) - -**Other Styles** - - - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen - - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - - [Popular JavaScript Coding Conventions on GitHub](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman - -**Further Reading** - - - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban - - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock - -**Books** - - - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov - - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke - - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson - -**Blogs** - - - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](https://bocoup.com/weblog) - - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](https://www.nczonline.net/) - - [Perfection Kills](http://perfectionkills.com/) - - [Ben Alman](http://benalman.com/) - - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://code.tutsplus.com/?s=javascript) - -**播客 Podcasts** - - - [JavaScript Air](https://javascriptair.com/) - - [JavaScript Jabber](https://devchat.tv/js-jabber/) - - -**[⬆ 返回目录](#table-of-contents)** - -## 使用人群 - - This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. - - - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) - - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) - - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) - - **Ascribe**: [ascribe/javascript](https://github.com/ascribe/javascript) - - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript) - - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) - - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) - - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) - - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) - - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) - - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) - - **DoSomething**: [DoSomething/eslint-config](https://github.com/DoSomething/eslint-config) - - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) - - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript) - - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) - - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript) - - **EvozonJs**: [evozonjs/javascript](https://github.com/evozonjs/javascript) - - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) - - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) - - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) - - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) - - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) - - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) - - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) - - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md) - - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **KickorStick**: [kickorstick/javascript](https://github.com/kickorstick/javascript) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) - - **Lonely Planet**: [lonelyplanet/javascript](https://github.com/lonelyplanet/javascript) - - **M2GEN**: [M2GEN/javascript](https://github.com/M2GEN/javascript) - - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) - - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - - **Muber**: [muber/javascript](https://github.com/muber/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) - - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) - - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) - - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) - - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **Springload**: [springload/javascript](https://github.com/springload/javascript) - - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) - - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) - - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) - - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) - - **Target**: [target/javascript](https://github.com/target/javascript) - - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - - **The Nerdery**: [thenerdery/javascript-standards](https://github.com/thenerdery/javascript-standards) - - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) - - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) - - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript) - - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) - - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) - -**[⬆ 返回目录](#table-of-contents)** - -## 翻译 - - This style guide is also available in other languages: - - - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) - - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javascript-style-guide](https://github.com/mitsuruog/javascript-style-guide) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) - - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [giangpii/javascript-style-guide](https://github.com/giangpii/javascript-style-guide) - - -## JavaScript 编码规范说明 - - - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - - -## 一起来讨论 JavaScript - - - Find us on [gitter](https://gitter.im/airbnb/javascript). - - -## 贡献者 Contributors - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## 许可 License - -(The MIT License) - -Copyright (c) 2014-2017 Airbnb - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[⬆ 返回目录](#table-of-contents)** - - -## 修正案 Amendments - -We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts. - -# }; From 6556bdd98fe8d2c10a004d4d7ccb8159eac1f794 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Fri, 10 Feb 2017 10:22:04 +0800 Subject: [PATCH 11/33] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=B0=83=E6=95=B4=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/es5_zh-cn_v1.md | 1743 ++++++++++++++++++++++ docs/es5_zh-cn_v2.md | 1331 +++++++++++++++++ docs/es5_zh-cn_v3.md | 1743 ++++++++++++++++++++++ docs/es6-zh-cn_v1.md | 2157 +++++++++++++++++++++++++++ docs/es6_zh-cn_v2.md | 3303 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 10277 insertions(+) create mode 100644 docs/es5_zh-cn_v1.md create mode 100644 docs/es5_zh-cn_v2.md create mode 100644 docs/es5_zh-cn_v3.md create mode 100644 docs/es6-zh-cn_v1.md create mode 100644 docs/es6_zh-cn_v2.md diff --git a/docs/es5_zh-cn_v1.md b/docs/es5_zh-cn_v1.md new file mode 100644 index 0000000000..30c5668807 --- /dev/null +++ b/docs/es5_zh-cn_v1.md @@ -0,0 +1,1743 @@ + +source: https://github.com/sivan/javascript-style-guide/blob/master/es5/README.md + +# Airbnb JavaScript Style Guide() { + +*用更合理的方式写 JavaScript* + +## 目录 + + 1. [类型](#types) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转化](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [构造函数](#constructors) + 1. [事件](#events) + 1. [模块](#modules) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [谁在使用](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 风格指南说明](#the-javascript-style-guide-guide) + 1. [与我们讨论 JavaScript](#chat-with-us-about-javascript) + 1. [贡献者](#contributors) + 1. [许可](#license) + +## 类型 + + - **原始值**: 存取直接作用于它自身。 + + + `string` + + `number` + + `boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1; + var bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **复杂类型**: 存取时作用于它自身值的引用。 + + + `object` + + `array` + + `function` + + ```javascript + var foo = [1, 2]; + var bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 对象 + + - 使用直接量创建对象。 + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - 不要使用[保留字](http://es5.github.io/#x7.6.1)作为键名,它们在 IE8 下不工作。[更多信息](https://github.com/airbnb/javascript/issues/61)。 + + ```javascript + // bad + var superman = { + default: { clark: 'kent' }, + private: true + }; + + // good + var superman = { + defaults: { clark: 'kent' }, + hidden: true + }; + ``` + + - 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + var superman = { + class: 'alien' + }; + + // bad + var superman = { + klass: 'alien' + }; + + // good + var superman = { + type: 'alien' + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 数组 + + - 使用直接量创建数组。 + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - 向数组增加元素时使用 Array#push 来替代直接赋值。 + + ```javascript + var someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + - 当你需要拷贝数组时,使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + + ```javascript + var len = items.length; + var itemsCopy = []; + var i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + itemsCopy = items.slice(); + ``` + + - 使用 Array#slice 将类数组对象转换成数组。 + + ```javascript + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 字符串 + + - 使用单引号 `''` 包裹字符串。 + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob " + this.lastName; + + // good + var fullName = 'Bob ' + this.lastName; + ``` + + - 超过 100 个字符的字符串应该使用连接符写成多行。 + - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[jsPerf](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that was thrown because \ + of Batman. When you stop to think about how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + // good + var errorMessage = 'This is a super long error that was thrown because ' + + 'of Batman. When you stop to think about how Batman had anything to do ' + + 'with this, you would get nowhere fast.'; + ``` + + - 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:[jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items; + var messages; + var length; + var i; + + messages = [{ + state: 'success', + message: 'This one worked.' + }, { + state: 'success', + message: 'This one worked as well.' + }, { + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = '
      '; + + for (i = 0; i < length; i++) { + items += '
    • ' + messages[i].message + '
    • '; + } + + return items + '
    '; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + // use direct assignment in this case because we're micro-optimizing. + items[i] = '
  • ' + messages[i].message + '
  • '; + } + + return '
      ' + items.join('') + '
    '; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 函数 + + - 函数表达式: + + ```javascript + // 匿名函数表达式 + var anonymous = function() { + return true; + }; + + // 命名函数表达式 + var named = function named() { + return true; + }; + + // 立即调用的函数表达式(IIFE) + (function () { + console.log('Welcome to the Internet. Please follow me.'); + }()); + ``` + + - 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + - **注:** ECMA-262 把 `块` 定义为一组语句。函数声明不是语句。[阅读对 ECMA-262 这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + var test; + if (currentUser) { + test = function test() { + console.log('Yup.'); + }; + } + ``` + + - 永远不要把参数命名为 `arguments`。这将取代函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 属性 + + - 使用 `.` 来访问对象的属性。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + // bad + var isJedi = luke['jedi']; + + // good + var isJedi = luke.jedi; + ``` + + - 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 变量 + + - 总是使用 `var` 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。 + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - 使用 `var` 声明每一个变量。 + 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + + ```javascript + // bad + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (跟上面的代码比较一下,看看哪里错了) + var items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + ``` + + - 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。 + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i; + var items = getItems(); + var dragonball; + var goSportsTeam = true; + var len; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball; + var length; + var i; + ``` + + - 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。 + + ```javascript + // bad + function () { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function () { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad - 不必要的函数调用 + function () { + var name = getName(); + + if (!arguments.length) { + return false; + } + + this.setFirstName(name); + + return true; + } + + // good + function () { + var name; + + if (!arguments.length) { + return false; + } + + name = getName(); + this.setFirstName(name); + + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 提升 + + - 变量声明会提升至作用域顶部,但赋值不会。 + + ```javascript + // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。 + // 注:变量赋值为 `true` 不会提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: + function example() { + var declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + ``` + + - 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function () { + console.log('anonymous function expression'); + }; + } + ``` + + - 命名函数表达式会提升变量名,但不会提升函数名或函数体。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // 当函数名跟变量名一样时,表现也是如此。 + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + - 函数声明提升它们的名字和函数体。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - 了解更多信息在 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 比较运算符 & 等号 + + - 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. + - 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0、-0 或 NaN** 被计算为 **false**,否则为 **true** + + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0]) { + // true + // 一个数组就是一个对象,对象被计算为 true + } + ``` + + - 使用快捷方式。 + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - 了解更多信息在 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. + +**[⬆ 回到顶部](#table-of-contents)** + + +## + + - 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function () { return false; } + + // good + function () { + return false; + } + ``` + + - 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + + +## 注释 + + - 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + ``` + + - 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - 使用 `// FIXME:` 标注问题。 + + ```javascript + function Calculator() { + + // FIXME: shouldn't use a global here + total = 0; + + return this; + } + ``` + + - 使用 `// TODO:` 标注问题的解决方式。 + + ```javascript + function Calculator() { + + // TODO: total should be configurable by an options param + this.total = 0; + + return this; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 空白 + + - 使用 2 个空格作为缩进。 + + ```javascript + // bad + function () { + ∙∙∙∙var name; + } + + // bad + function () { + ∙var name; + } + + // good + function () { + ∙∙var name; + } + ``` + + - 在大括号前放一个空格。 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + ``` + + - 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + - 使用空格把运算符隔开。 + + ```javascript + // bad + var x=y+5; + + // good + var x = y + 5; + ``` + + - 在文件末尾插入一个空行。 + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function (global) { + // ...stuff... + })(this);↵ + ``` + + - 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + var leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + - 在块末和新语句前插入空行。 + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + var obj = { + foo: function () { + }, + bar: function () { + } + }; + return obj; + + // good + var obj = { + foo: function () { + }, + + bar: function () { + } + }; + + return obj; + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + +## 逗号 + + - 行首逗号: **不需要**。 + + ```javascript + // bad + var story = [ + once + , upon + , aTime + ]; + + // good + var story = [ + once, + upon, + aTime + ]; + + // bad + var hero = { + firstName: 'Bob' + , lastName: 'Parr' + , heroName: 'Mr. Incredible' + , superPower: 'strength' + }; + + // good + var hero = { + firstName: 'Bob', + lastName: 'Parr', + heroName: 'Mr. Incredible', + superPower: 'strength' + }; + ``` + + - 额外的行末逗号:**不需要**。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 ([source](http://es5.github.io/#D)): + + > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. + + ```javascript + // bad + var hero = { + firstName: 'Kevin', + lastName: 'Flynn', + }; + + var heroes = [ + 'Batman', + 'Superman', + ]; + + // good + var hero = { + firstName: 'Kevin', + lastName: 'Flynn' + }; + + var heroes = [ + 'Batman', + 'Superman' + ]; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 分号 + + - **使用分号。** + + ```javascript + // bad + (function () { + var name = 'Skywalker' + return name + })() + + // good + (function () { + var name = 'Skywalker'; + return name; + })(); + + // good (防止函数在两个 IIFE 合并时被当成一个参数 + ;(function () { + var name = 'Skywalker'; + return name; + })(); + ``` + + [了解更多](http://stackoverflow.com/a/7365214/1712802). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 类型转换 + + - 在语句开始时执行类型转换。 + - 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + var totalScore = this.reviewScore + ''; + + // good + var totalScore = '' + this.reviewScore; + + // bad + var totalScore = '' + this.reviewScore + ' total score'; + + // good + var totalScore = this.reviewScore + ' total score'; + ``` + + - 使用 `parseInt` 转换数字时总是带上类型转换的基数。 + + ```javascript + var inputValue = '4'; + + // bad + var val = new Number(inputValue); + + // bad + var val = +inputValue; + + // bad + var val = inputValue >> 0; + + // bad + var val = parseInt(inputValue); + + // good + var val = Number(inputValue); + + // good + var val = parseInt(inputValue, 10); + ``` + + - 如果因为某些原因 `parseInt` 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * parseInt was the reason my code was slow. + * Bitshifting the String to coerce it to a + * Number made it a lot faster. + */ + var val = inputValue >> 0; + ``` + + - **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([source](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + - 布尔: + + ```javascript + var age = 0; + + // bad + var hasAge = new Boolean(age); + + // good + var hasAge = Boolean(age); + + // good + var hasAge = !!age; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 命名规则 + + - 避免单字母命名。命名应具备描述性。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - 使用驼峰式命名对象、函数和实例。 + + ```javascript + // bad + var OBJEcttsssss = {}; + var this_is_my_object = {}; + var o = {}; + function c() {} + + // good + var thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + - 使用帕斯卡式命名构造函数或类。 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + var bad = new user({ + name: 'nope' + }); + + // good + function User(options) { + this.name = options.name; + } + + var good = new User({ + name: 'yup' + }); + ``` + + - 不要使用下划线前/后缀。 + + > 为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + this._firstName = 'Panda'; + + // good + this.firstName = 'Panda'; + ``` + + - 不要保存 `this` 的引用。使用 Function#bind。 + + ```javascript + // bad + function () { + var self = this; + return function () { + console.log(self); + }; + } + + // bad + function () { + var that = this; + return function () { + console.log(that); + }; + } + + // bad + function () { + var _this = this; + return function () { + console.log(_this); + }; + } + + // good + function () { + return function () { + console.log(this); + }.bind(this); + } + ``` + + - 给函数命名。这在做堆栈轨迹时很有帮助。 + + ```javascript + // bad + var log = function (msg) { + console.log(msg); + }; + + // good + var log = function log(msg) { + console.log(msg); + }; + ``` + + - **注:** IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。 + + - 如果你的文件导出一个类,你的文件名应该与类名完全相同。 + ```javascript + // file contents + class CheckBox { + // ... + } + module.exports = CheckBox; + + // in some other file + // bad + var CheckBox = require('./checkBox'); + + // bad + var CheckBox = require('./check_box'); + + // good + var CheckBox = require('./CheckBox'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 存取器 + + - 属性的存取函数不是必须的。 + - 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - 创建 get() 和 set() 函数是可以的,但要保持一致。 + + ```javascript + function Jedi(options) { + options || (options = {}); + var lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + Jedi.prototype.set = function set(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function get(key) { + return this[key]; + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 构造函数 + + - 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型! + + ```javascript + function Jedi() { + console.log('new jedi'); + } + + // bad + Jedi.prototype = { + fight: function fight() { + console.log('fighting'); + }, + + block: function block() { + console.log('blocking'); + } + }; + + // good + Jedi.prototype.fight = function fight() { + console.log('fighting'); + }; + + Jedi.prototype.block = function block() { + console.log('blocking'); + }; + ``` + + - 方法可以返回 `this` 来实现方法链式使用。 + + ```javascript + // bad + Jedi.prototype.jump = function jump() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + }; + + var luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + Jedi.prototype.jump = function jump() { + this.jumping = true; + return this; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + return this; + }; + + var luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - 写一个自定义的 `toString()` 方法是可以的,但是确保它可以正常工作且不会产生副作用。 + + ```javascript + function Jedi(options) { + options || (options = {}); + this.name = options.name || 'no name'; + } + + Jedi.prototype.getName = function getName() { + return this.name; + }; + + Jedi.prototype.toString = function toString() { + return 'Jedi - ' + this.getName(); + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 事件 + + - 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```js + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function (e, listingId) { + // do something with listingId + }); + ``` + + 更好的写法: + + ```js + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function (e, data) { + // do something with data.listingId + }); + ``` + + **[⬆ 回到顶部](#table-of-contents)** + + +## 模块 + + - 模块应该以 `!` 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。[详细说明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) + - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。 + - 增加一个名为 `noConflict()` 的方法来设置导出的模块为前一个版本并返回它。 + - 永远在模块顶部声明 `'use strict';`。 + + ```javascript + // fancyInput/fancyInput.js + + !function (global) { + 'use strict'; + + var previousFancyInput = global.FancyInput; + + function FancyInput(options) { + this.options = options || {}; + } + + FancyInput.noConflict = function noConflict() { + global.FancyInput = previousFancyInput; + return FancyInput; + }; + + global.FancyInput = FancyInput; + }(this); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## jQuery + + - 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + ```javascript + // bad + var sidebar = $('.sidebar'); + + // good + var $sidebar = $('.sidebar'); + ``` + + - 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + var $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + - 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容表](http://kangax.github.com/es5-compat-table/). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 测试 + + - **Yup.** + + ```javascript + function () { + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + +**[⬆ 回到顶部](#table-of-contents)** + + +## 资源 + + +**推荐阅读** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**工具** + + - Code Style Linters + + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**其它风格指南** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + - [JavaScript Standard Style](https://github.com/feross/standard) + +**其它风格** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**进一步阅读** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**书籍** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke + - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson + +**博客** + + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + +**播客** + + - [JavaScript Jabber](http://devchat.tv/js-jabber/) + + +**[⬆ 回到顶部](#table-of-contents)** + +## 谁在使用 + + 这是一个使用本风格指南的组织列表。给我们发 pull request 或开一个 issue 让我们将你增加到列表上。 + + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Super**: [SuperJobs/javascript](https://github.com/SuperJobs/javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +## 翻译 + + 这份风格指南也提供了其它语言的版本: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript](https://github.com/sivan/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + +## JavaScript 风格指南说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## 与我们讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + +## 贡献者 + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 + +(The MIT License) + +Copyright (c) 2014 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 回到顶部](#table-of-contents)** + +# }; diff --git a/docs/es5_zh-cn_v2.md b/docs/es5_zh-cn_v2.md new file mode 100644 index 0000000000..3ebaff4d03 --- /dev/null +++ b/docs/es5_zh-cn_v2.md @@ -0,0 +1,1331 @@ + +source: https://github.com/adamlu/javascript-style-guide + +[原文: https://github.com/airbnb/javascript](https://github.com/airbnb/javascript) + +注:本人根据自己的开发习惯删除和修改了部分规范 + +# JavaScript规范 + +## 内容列表 + + 1. [类型](#types) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [条件表达式和等号](#conditionals) + 1. [块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-coercion) + 1. [命名约定](#naming-conventions) + 1. [存取器](#accessors) + 1. [构造器](#constructors) + 1. [事件](#events) + 1. [模块](#modules) + 1. [jQuery](#jquery) + 1. [ES5 兼容性](#es5) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [哪些人在使用](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript风格指南](#guide-guide) + 1. [贡献者](#contributors) + 1. [许可](#license) + +## 类型 + + - **原始值**: 相当于传值 + + + `string` + + `number` + + `boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1, + bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **复杂类型**: 相当于传引用 + + + `object` + + `array` + + `function` + + ```javascript + var foo = [1, 2], + bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + + **[[⬆]](#TOC)** + +## 对象 + + - 使用字面值创建对象 + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - 不要使用保留字 [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) 作为键 + + ```javascript + // bad + var superman = { + class: 'superhero', + default: { clark: 'kent' }, + private: true + }; + + // good + var superman = { + klass: 'superhero', + defaults: { clark: 'kent' }, + hidden: true + }; + ``` + **[[⬆]](#TOC)** + +## 数组 + + - 使用字面值创建数组 + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - 如果你不知道数组的长度,使用push + + ```javascript + var someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + - 当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + + ```javascript + var len = items.length, + itemsCopy = [], + i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + itemsCopy = items.slice(); + ``` + + - 使用slice将类数组的对象转成数组. + + ```javascript + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + + **[[⬆]](#TOC)** + + +## 字符串 + + - 对字符串使用单引号 `''` + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob " + this.lastName; + + // good + var fullName = 'Bob ' + this.lastName; + ``` + + - 超过80个字符的字符串应该使用字符串连接换行 + - 注: 如果过度使用,长字符串连接可能会对性能有影响. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that \ + was thrown because of Batman. \ + When you stop to think about \ + how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + + // good + var errorMessage = 'This is a super long error that ' + + 'was thrown because of Batman.' + + 'When you stop to think about ' + + 'how Batman had anything to do ' + + 'with this, you would get nowhere ' + + 'fast.'; + ``` + + - 编程时使用join而不是字符串连接来构建字符串,特别是IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items, + messages, + length, i; + + messages = [{ + state: 'success', + message: 'This one worked.' + },{ + state: 'success', + message: 'This one worked as well.' + },{ + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = '
      '; + + for (i = 0; i < length; i++) { + items += '
    • ' + messages[i].message + '
    • '; + } + + return items + '
    '; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + items[i] = messages[i].message; + } + + return '
    • ' + items.join('
    • ') + '
    '; + } + ``` + + **[[⬆]](#TOC)** + + +## 函数 + + - 函数表达式: + + ```javascript + // 匿名函数表达式 + var anonymous = function() { + return true; + }; + + // 有名函数表达式 + var named = function named() { + return true; + }; + + // 立即调用函数表达式 + (function() { + console.log('Welcome to the Internet. Please follow me.'); + })(); + ``` + + - 绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同。 + - **注:** ECMA-262定义把`块`定义为一组语句,函数声明不是一个语句。[阅读ECMA-262对这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + if (currentUser) { + var test = function test() { + console.log('Yup.'); + }; + } + ``` + + - 绝对不要把参数命名为 `arguments`, 这将会逾越函数作用域内传过来的 `arguments` 对象. + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + **[[⬆]](#TOC)** + + +## 属性 + + - 当使用变量访问属性时使用中括号. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + + **[[⬆]](#TOC)** + + +## 变量 + + - 总是使用 `var` 来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。 + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - 使用一个 `var` 以及新行声明多个变量,缩进4个空格。 + + ```javascript + // bad + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + ``` + + - 最后再声明未赋值的变量,当你想引用之前已赋值变量的时候很有用。 + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i, items = getItems(), + dragonball, + goSportsTeam = true, + len; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball, + length, + i; + ``` + + - 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。 + + ```javascript + // bad + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function() { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad + function() { + var name = getName(); + + if (!arguments.length) { + return false; + } + + return true; + } + + // good + function() { + if (!arguments.length) { + return false; + } + + var name = getName(); + + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## 条件表达式和等号 + + - 适当使用 `===` 和 `!==` 以及 `==` 和 `!=`. + - 条件表达式的强制类型转换遵循以下规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0, -0, or NaN** 被计算为 **false** , 否则为 **true** + + **字符串** 如果是空字符串 `''` 则被计算为 **false**, 否则为 **true** + + ```javascript + if ([0]) { + // true + // An array is an object, objects evaluate to true + } + ``` + + - 使用快捷方式. + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - 阅读 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) 了解更多 + + **[[⬆]](#TOC)** + + +## + + - 给所有多行的块使用大括号 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function() { return false; } + + // good + function() { + return false; + } + ``` + + **[[⬆]](#TOC)** + + +## 注释 + + - 使用 `/** ... */` 进行多行注释,包括描述,指定类型以及参数值和返回值 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param tag + // @return element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param tag + * @return element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - 使用 `//` 进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行. + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + ``` + + - 如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 `FIXME` 或 `TODO` 帮助其他人迅速理解 + + ```javascript + function Calculator() { + + // FIXME: shouldn't use a global here + total = 0; + + return this; + } + ``` + + ```javascript + function Calculator() { + + // TODO: total should be configurable by an options param + this.total = 0; + + return this; + } + ``` + + **[[⬆]](#TOC)** + + +## 空白 + + - 将tab设为4个空格 + + ```javascript + // bad + function() { + ∙∙var name; + } + + // bad + function() { + ∙var name; + } + + // good + function() { + ∙∙∙∙var name; + } + ``` + - 大括号前放一个空格 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + ``` + + - 在做长方法链时使用缩进. + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + var leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .class('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + **[[⬆]](#TOC)** + +## 逗号 + + - 不要将逗号放前面 + + ```javascript + // bad + var once + , upon + , aTime; + + // good + var once, + upon, + aTime; + + // bad + var hero = { + firstName: 'Bob' + , lastName: 'Parr' + , heroName: 'Mr. Incredible' + , superPower: 'strength' + }; + + // good + var hero = { + firstName: 'Bob', + lastName: 'Parr', + heroName: 'Mr. Incredible', + superPower: 'strength' + }; + ``` + + - 不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。 + + ```javascript + // bad + var hero = { + firstName: 'Kevin', + lastName: 'Flynn', + }; + + var heroes = [ + 'Batman', + 'Superman', + ]; + + // good + var hero = { + firstName: 'Kevin', + lastName: 'Flynn' + }; + + var heroes = [ + 'Batman', + 'Superman' + ]; + ``` + + **[[⬆]](#TOC)** + + +## 分号 + + - 语句结束一定要加分号 + + ```javascript + // bad + (function() { + var name = 'Skywalker' + return name + })() + + // good + (function() { + var name = 'Skywalker'; + return name; + })(); + + // good + ;(function() { + var name = 'Skywalker'; + return name; + })(); + ``` + + **[[⬆]](#TOC)** + + +## 类型转换 + + - 在语句的开始执行类型转换. + - 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + var totalScore = this.reviewScore + ''; + + // good + var totalScore = '' + this.reviewScore; + + // bad + var totalScore = '' + this.reviewScore + ' total score'; + + // good + var totalScore = this.reviewScore + ' total score'; + ``` + + - 对数字使用 `parseInt` 并且总是带上类型转换的基数. + + ```javascript + var inputValue = '4'; + + // bad + var val = new Number(inputValue); + + // bad + var val = +inputValue; + + // bad + var val = inputValue >> 0; + + // bad + var val = parseInt(inputValue); + + // good + var val = Number(inputValue); + + // good + var val = parseInt(inputValue, 10); + + // good + /** + * parseInt was the reason my code was slow. + * Bitshifting the String to coerce it to a + * Number made it a lot faster. + */ + var val = inputValue >> 0; + ``` + + - 布尔值: + + ```javascript + var age = 0; + + // bad + var hasAge = new Boolean(age); + + // good + var hasAge = Boolean(age); + + // good + var hasAge = !!age; + ``` + + **[[⬆]](#TOC)** + + +## 命名约定 + + - 避免单个字符名,让你的变量名有描述意义。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - 当命名对象、函数和实例时使用驼峰命名规则 + + ```javascript + // bad + var OBJEcttsssss = {}; + var this_is_my_object = {}; + var this-is-my-object = {}; + function c() {}; + var u = new user({ + name: 'Bob Parr' + }); + + // good + var thisIsMyObject = {}; + function thisIsMyFunction() {}; + var user = new User({ + name: 'Bob Parr' + }); + ``` + + - 当命名构造函数或类时使用驼峰式大写 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + var bad = new user({ + name: 'nope' + }); + + // good + function User(options) { + this.name = options.name; + } + + var good = new User({ + name: 'yup' + }); + ``` + + - 命名私有属性时前面加个下划线 `_` + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + + // good + this._firstName = 'Panda'; + ``` + + - 当保存对 `this` 的引用时使用 `_this`. + + ```javascript + // bad + function() { + var self = this; + return function() { + console.log(self); + }; + } + + // bad + function() { + var that = this; + return function() { + console.log(that); + }; + } + + // good + function() { + var _this = this; + return function() { + console.log(_this); + }; + } + ``` + + **[[⬆]](#TOC)** + + +## 存取器 + + - 属性的存取器函数不是必需的 + - 如果你确实有存取器函数的话使用getVal() 和 setVal('hello') + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - 如果属性是布尔值,使用isVal() 或 hasVal() + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - 可以创建get()和set()函数,但是要保持一致 + + ```javascript + function Jedi(options) { + options || (options = {}); + var lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + Jedi.prototype.set = function(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function(key) { + return this[key]; + }; + ``` + + **[[⬆]](#TOC)** + + +## 构造器 + + - 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。 + + ```javascript + function Jedi() { + console.log('new jedi'); + } + + // bad + Jedi.prototype = { + fight: function fight() { + console.log('fighting'); + }, + + block: function block() { + console.log('blocking'); + } + }; + + // good + Jedi.prototype.fight = function fight() { + console.log('fighting'); + }; + + Jedi.prototype.block = function block() { + console.log('blocking'); + }; + ``` + + - 方法可以返回 `this` 帮助方法可链。 + + ```javascript + // bad + Jedi.prototype.jump = function() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + }; + + var luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20) // => undefined + + // good + Jedi.prototype.jump = function() { + this.jumping = true; + return this; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + return this; + }; + + var luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - 可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。 + + ```javascript + function Jedi(options) { + options || (options = {}); + this.name = options.name || 'no name'; + } + + Jedi.prototype.getName = function getName() { + return this.name; + }; + + Jedi.prototype.toString = function toString() { + return 'Jedi - ' + this.getName(); + }; + ``` + + **[[⬆]](#TOC)** + + +## 事件 + + - 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器 + + ```js + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function(e, listingId) { + // do something with listingId + }); + ``` + + 更好: + + ```js + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function(e, data) { + // do something with data.listingId + }); + ``` + + **[[⬆]](#TOC)** + + +## 模块 + + - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + - 总是在模块顶部声明 `'use strict';` + + ```javascript + // fancyInput/fancyInput.js + + !function(global) { + 'use strict'; + + var previousFancyInput = global.FancyInput; + + function FancyInput(options) { + this.options = options || {}; + } + + FancyInput.noConflict = function noConflict() { + global.FancyInput = previousFancyInput; + return FancyInput; + }; + + global.FancyInput = FancyInput; + }(this); + ``` + + **[[⬆]](#TOC)** + + +## jQuery + + - 缓存jQuery查询 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + var $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - 对DOM查询使用级联的 `$('.sidebar ul')` 或 `$('.sidebar ul')`,[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - 对有作用域的jQuery对象查询使用 `find` + + ```javascript + // bad + $('.sidebar', 'ul').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good (slower) + $sidebar.find('ul'); + + // good (faster) + $($sidebar[0]).find('ul'); + ``` + + **[[⬆]](#TOC)** + + +## ECMAScript 5兼容性 + + - 参考[Kangax](https://twitter.com/kangax/)的 ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) + + **[[⬆]](#TOC)** + + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + + **[[⬆]](#TOC)** + + +## 资源 + +**Read This** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**其它规范** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + +**其它风格** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) + +**阅读更多** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + +**书籍** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + +**博客** + + - [Adam Lu](http://adamlu.com/) + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + + **[[⬆]](#TOC)** + +## 哪些人在使用 + + 这是一些使用这个风格规范的组织,给我们发pull request或打开一个问题,我们会把你加到列表中。 + + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **Userify**: [userify/javascript](https://github.com/userify/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +## 翻译 + + 这个风格规范也有其它语言版本: + + - :de: **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - :jp: **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - :br: **Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - :cn: **Chinese**: [adamlu/javascript-style-guide](https://github.com/adamlu/javascript-style-guide) + +## JavaScript风格指南 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## 贡献者 + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 + +(The MIT License) + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[[⬆]](#TOC)** + +# }; diff --git a/docs/es5_zh-cn_v3.md b/docs/es5_zh-cn_v3.md new file mode 100644 index 0000000000..441c334544 --- /dev/null +++ b/docs/es5_zh-cn_v3.md @@ -0,0 +1,1743 @@ + +source: https://github.com/sivan/javascript-style-guide/tree/master/es5 + +# Airbnb JavaScript Style Guide() { + +*用更合理的方式写 JavaScript* + +## 目录 + + 1. [类型](#types) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转化](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [构造函数](#constructors) + 1. [事件](#events) + 1. [模块](#modules) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [谁在使用](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 风格指南说明](#the-javascript-style-guide-guide) + 1. [与我们讨论 JavaScript](#chat-with-us-about-javascript) + 1. [贡献者](#contributors) + 1. [许可](#license) + +## 类型 + + - **原始值**: 存取直接作用于它自身。 + + + `string` + + `number` + + `boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1; + var bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **复杂类型**: 存取时作用于它自身值的引用。 + + + `object` + + `array` + + `function` + + ```javascript + var foo = [1, 2]; + var bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 对象 + + - 使用直接量创建对象。 + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - 不要使用[保留字](http://es5.github.io/#x7.6.1)作为键名,它们在 IE8 下不工作。[更多信息](https://github.com/airbnb/javascript/issues/61)。 + + ```javascript + // bad + var superman = { + default: { clark: 'kent' }, + private: true + }; + + // good + var superman = { + defaults: { clark: 'kent' }, + hidden: true + }; + ``` + + - 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + var superman = { + class: 'alien' + }; + + // bad + var superman = { + klass: 'alien' + }; + + // good + var superman = { + type: 'alien' + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + +## 数组 + + - 使用直接量创建数组。 + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - 向数组增加元素时使用 Array#push 来替代直接赋值。 + + ```javascript + var someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + - 当你需要拷贝数组时,使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + + ```javascript + var len = items.length; + var itemsCopy = []; + var i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + itemsCopy = items.slice(); + ``` + + - 使用 Array#slice 将类数组对象转换成数组。 + + ```javascript + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 字符串 + + - 使用单引号 `''` 包裹字符串。 + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob " + this.lastName; + + // good + var fullName = 'Bob ' + this.lastName; + ``` + + - 超过 100 个字符的字符串应该使用连接符写成多行。 + - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[jsPerf](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that was thrown because \ + of Batman. When you stop to think about how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + // good + var errorMessage = 'This is a super long error that was thrown because ' + + 'of Batman. When you stop to think about how Batman had anything to do ' + + 'with this, you would get nowhere fast.'; + ``` + + - 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:[jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items; + var messages; + var length; + var i; + + messages = [{ + state: 'success', + message: 'This one worked.' + }, { + state: 'success', + message: 'This one worked as well.' + }, { + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = '
      '; + + for (i = 0; i < length; i++) { + items += '
    • ' + messages[i].message + '
    • '; + } + + return items + '
    '; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + // use direct assignment in this case because we're micro-optimizing. + items[i] = '
  • ' + messages[i].message + '
  • '; + } + + return '
      ' + items.join('') + '
    '; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 函数 + + - 函数表达式: + + ```javascript + // 匿名函数表达式 + var anonymous = function() { + return true; + }; + + // 命名函数表达式 + var named = function named() { + return true; + }; + + // 立即调用的函数表达式(IIFE) + (function () { + console.log('Welcome to the Internet. Please follow me.'); + }()); + ``` + + - 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + - **注:** ECMA-262 把 `块` 定义为一组语句。函数声明不是语句。[阅读对 ECMA-262 这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + var test; + if (currentUser) { + test = function test() { + console.log('Yup.'); + }; + } + ``` + + - 永远不要把参数命名为 `arguments`。这将取代函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 属性 + + - 使用 `.` 来访问对象的属性。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + // bad + var isJedi = luke['jedi']; + + // good + var isJedi = luke.jedi; + ``` + + - 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 变量 + + - 总是使用 `var` 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。 + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - 使用 `var` 声明每一个变量。 + 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + + ```javascript + // bad + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (跟上面的代码比较一下,看看哪里错了) + var items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + ``` + + - 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。 + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i; + var items = getItems(); + var dragonball; + var goSportsTeam = true; + var len; + + // good + var items = getItems(); + var goSportsTeam = true; + var dragonball; + var length; + var i; + ``` + + - 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。 + + ```javascript + // bad + function () { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function () { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad - 不必要的函数调用 + function () { + var name = getName(); + + if (!arguments.length) { + return false; + } + + this.setFirstName(name); + + return true; + } + + // good + function () { + var name; + + if (!arguments.length) { + return false; + } + + name = getName(); + this.setFirstName(name); + + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 提升 + + - 变量声明会提升至作用域顶部,但赋值不会。 + + ```javascript + // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。 + // 注:变量赋值为 `true` 不会提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: + function example() { + var declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + ``` + + - 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function () { + console.log('anonymous function expression'); + }; + } + ``` + + - 命名函数表达式会提升变量名,但不会提升函数名或函数体。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // 当函数名跟变量名一样时,表现也是如此。 + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + - 函数声明提升它们的名字和函数体。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - 了解更多信息在 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). + +**[⬆ 回到顶部](#table-of-contents)** + + + +## 比较运算符 & 等号 + + - 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. + - 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0、-0 或 NaN** 被计算为 **false**,否则为 **true** + + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0]) { + // true + // 一个数组就是一个对象,对象被计算为 true + } + ``` + + - 使用快捷方式。 + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - 了解更多信息在 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. + +**[⬆ 回到顶部](#table-of-contents)** + + +## + + - 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function () { return false; } + + // good + function () { + return false; + } + ``` + + - 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + + +## 注释 + + - 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this.type || 'no type'; + + return type; + } + ``` + + - 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - 使用 `// FIXME:` 标注问题。 + + ```javascript + function Calculator() { + + // FIXME: shouldn't use a global here + total = 0; + + return this; + } + ``` + + - 使用 `// TODO:` 标注问题的解决方式。 + + ```javascript + function Calculator() { + + // TODO: total should be configurable by an options param + this.total = 0; + + return this; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 空白 + + - 使用 2 个空格作为缩进。 + + ```javascript + // bad + function () { + ∙∙∙∙var name; + } + + // bad + function () { + ∙var name; + } + + // good + function () { + ∙∙var name; + } + ``` + + - 在大括号前放一个空格。 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog' + }); + ``` + + - 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + - 使用空格把运算符隔开。 + + ```javascript + // bad + var x=y+5; + + // good + var x = y + 5; + ``` + + - 在文件末尾插入一个空行。 + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function (global) { + // ...stuff... + })(this);↵ + ``` + + - 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + var leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + - 在块末和新语句前插入空行。 + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + var obj = { + foo: function () { + }, + bar: function () { + } + }; + return obj; + + // good + var obj = { + foo: function () { + }, + + bar: function () { + } + }; + + return obj; + ``` + + +**[⬆ 回到顶部](#table-of-contents)** + +## 逗号 + + - 行首逗号: **不需要**。 + + ```javascript + // bad + var story = [ + once + , upon + , aTime + ]; + + // good + var story = [ + once, + upon, + aTime + ]; + + // bad + var hero = { + firstName: 'Bob' + , lastName: 'Parr' + , heroName: 'Mr. Incredible' + , superPower: 'strength' + }; + + // good + var hero = { + firstName: 'Bob', + lastName: 'Parr', + heroName: 'Mr. Incredible', + superPower: 'strength' + }; + ``` + + - 额外的行末逗号:**不需要**。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 ([source](http://es5.github.io/#D)): + + > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. + + ```javascript + // bad + var hero = { + firstName: 'Kevin', + lastName: 'Flynn', + }; + + var heroes = [ + 'Batman', + 'Superman', + ]; + + // good + var hero = { + firstName: 'Kevin', + lastName: 'Flynn' + }; + + var heroes = [ + 'Batman', + 'Superman' + ]; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 分号 + + - **使用分号。** + + ```javascript + // bad + (function () { + var name = 'Skywalker' + return name + })() + + // good + (function () { + var name = 'Skywalker'; + return name; + })(); + + // good (防止函数在两个 IIFE 合并时被当成一个参数 + ;(function () { + var name = 'Skywalker'; + return name; + })(); + ``` + + [了解更多](http://stackoverflow.com/a/7365214/1712802). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 类型转换 + + - 在语句开始时执行类型转换。 + - 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + var totalScore = this.reviewScore + ''; + + // good + var totalScore = '' + this.reviewScore; + + // bad + var totalScore = '' + this.reviewScore + ' total score'; + + // good + var totalScore = this.reviewScore + ' total score'; + ``` + + - 使用 `parseInt` 转换数字时总是带上类型转换的基数。 + + ```javascript + var inputValue = '4'; + + // bad + var val = new Number(inputValue); + + // bad + var val = +inputValue; + + // bad + var val = inputValue >> 0; + + // bad + var val = parseInt(inputValue); + + // good + var val = Number(inputValue); + + // good + var val = parseInt(inputValue, 10); + ``` + + - 如果因为某些原因 `parseInt` 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * parseInt was the reason my code was slow. + * Bitshifting the String to coerce it to a + * Number made it a lot faster. + */ + var val = inputValue >> 0; + ``` + + - **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([source](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + - 布尔: + + ```javascript + var age = 0; + + // bad + var hasAge = new Boolean(age); + + // good + var hasAge = Boolean(age); + + // good + var hasAge = !!age; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 命名规则 + + - 避免单字母命名。命名应具备描述性。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - 使用驼峰式命名对象、函数和实例。 + + ```javascript + // bad + var OBJEcttsssss = {}; + var this_is_my_object = {}; + var o = {}; + function c() {} + + // good + var thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + - 使用帕斯卡式命名构造函数或类。 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + var bad = new user({ + name: 'nope' + }); + + // good + function User(options) { + this.name = options.name; + } + + var good = new User({ + name: 'yup' + }); + ``` + + - 不要使用下划线前/后缀。 + + > 为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + this._firstName = 'Panda'; + + // good + this.firstName = 'Panda'; + ``` + + - 不要保存 `this` 的引用。使用 Function#bind。 + + ```javascript + // bad + function () { + var self = this; + return function () { + console.log(self); + }; + } + + // bad + function () { + var that = this; + return function () { + console.log(that); + }; + } + + // bad + function () { + var _this = this; + return function () { + console.log(_this); + }; + } + + // good + function () { + return function () { + console.log(this); + }.bind(this); + } + ``` + + - 给函数命名。这在做堆栈轨迹时很有帮助。 + + ```javascript + // bad + var log = function (msg) { + console.log(msg); + }; + + // good + var log = function log(msg) { + console.log(msg); + }; + ``` + + - **注:** IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。 + + - 如果你的文件导出一个类,你的文件名应该与类名完全相同。 + ```javascript + // file contents + class CheckBox { + // ... + } + module.exports = CheckBox; + + // in some other file + // bad + var CheckBox = require('./checkBox'); + + // bad + var CheckBox = require('./check_box'); + + // good + var CheckBox = require('./CheckBox'); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 存取器 + + - 属性的存取函数不是必须的。 + - 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - 创建 get() 和 set() 函数是可以的,但要保持一致。 + + ```javascript + function Jedi(options) { + options || (options = {}); + var lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + Jedi.prototype.set = function set(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function get(key) { + return this[key]; + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 构造函数 + + - 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型! + + ```javascript + function Jedi() { + console.log('new jedi'); + } + + // bad + Jedi.prototype = { + fight: function fight() { + console.log('fighting'); + }, + + block: function block() { + console.log('blocking'); + } + }; + + // good + Jedi.prototype.fight = function fight() { + console.log('fighting'); + }; + + Jedi.prototype.block = function block() { + console.log('blocking'); + }; + ``` + + - 方法可以返回 `this` 来实现方法链式使用。 + + ```javascript + // bad + Jedi.prototype.jump = function jump() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + }; + + var luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + Jedi.prototype.jump = function jump() { + this.jumping = true; + return this; + }; + + Jedi.prototype.setHeight = function setHeight(height) { + this.height = height; + return this; + }; + + var luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - 写一个自定义的 `toString()` 方法是可以的,但是确保它可以正常工作且不会产生副作用。 + + ```javascript + function Jedi(options) { + options || (options = {}); + this.name = options.name || 'no name'; + } + + Jedi.prototype.getName = function getName() { + return this.name; + }; + + Jedi.prototype.toString = function toString() { + return 'Jedi - ' + this.getName(); + }; + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 事件 + + - 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```js + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function (e, listingId) { + // do something with listingId + }); + ``` + + 更好的写法: + + ```js + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function (e, data) { + // do something with data.listingId + }); + ``` + + **[⬆ 回到顶部](#table-of-contents)** + + +## 模块 + + - 模块应该以 `!` 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。[详细说明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) + - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。 + - 增加一个名为 `noConflict()` 的方法来设置导出的模块为前一个版本并返回它。 + - 永远在模块顶部声明 `'use strict';`。 + + ```javascript + // fancyInput/fancyInput.js + + !function (global) { + 'use strict'; + + var previousFancyInput = global.FancyInput; + + function FancyInput(options) { + this.options = options || {}; + } + + FancyInput.noConflict = function noConflict() { + global.FancyInput = previousFancyInput; + return FancyInput; + }; + + global.FancyInput = FancyInput; + }(this); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## jQuery + + - 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + ```javascript + // bad + var sidebar = $('.sidebar'); + + // good + var $sidebar = $('.sidebar'); + ``` + + - 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + var $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + - 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容表](http://kangax.github.com/es5-compat-table/). + +**[⬆ 回到顶部](#table-of-contents)** + + +## 测试 + + - **Yup.** + + ```javascript + function () { + return true; + } + ``` + +**[⬆ 回到顶部](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + +**[⬆ 回到顶部](#table-of-contents)** + + +## 资源 + + +**推荐阅读** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**工具** + + - Code Style Linters + + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**其它风格指南** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + - [JavaScript Standard Style](https://github.com/feross/standard) + +**其它风格** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**进一步阅读** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**书籍** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke + - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson + +**博客** + + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + +**播客** + + - [JavaScript Jabber](http://devchat.tv/js-jabber/) + + +**[⬆ 回到顶部](#table-of-contents)** + +## 谁在使用 + + 这是一个使用本风格指南的组织列表。给我们发 pull request 或开一个 issue 让我们将你增加到列表上。 + + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Super**: [SuperJobs/javascript](https://github.com/SuperJobs/javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +## 翻译 + + 这份风格指南也提供了其它语言的版本: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript](https://github.com/sivan/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + +## JavaScript 风格指南说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## 与我们讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + +## 贡献者 + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 + +(The MIT License) + +Copyright (c) 2014 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 回到顶部](#table-of-contents)** + +# }; diff --git a/docs/es6-zh-cn_v1.md b/docs/es6-zh-cn_v1.md new file mode 100644 index 0000000000..9709f373da --- /dev/null +++ b/docs/es6-zh-cn_v1.md @@ -0,0 +1,2157 @@ + +source: https://github.com/yuche/javascript + +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Airbnb JavaScript Style Guide() { + +**用更合理的方式写 JavaScript** + +ES5 的编码规范请查看[版本一](https://github.com/sivan/javascript-style-guide/blob/master/es5/README.md),[版本二](https://github.com/adamlu/javascript-style-guide)。 + +翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 + + +## 目录 + + 1. [类型](#types) + 1. [引用](#references) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [解构](#destructuring) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [箭头函数](#arrow-functions) + 1. [构造函数](#constructors) + 1. [模块](#modules) + 1. [Iterators & Generators ](#iterators-and-generators) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [代码块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [事件](#events) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [使用人群](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) + 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) + 1. [Contributors](#contributors) + 1. [License](#license) + + +## 类型 + + - [1.1](#1.1) **基本类型**: 直接存取基本类型。 + + + `字符串` + + `数值` + + `布尔类型` + + `null` + + `undefined` + + ```javascript + const foo = 1; + let bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - [1.2](#1.2) **复制类型**: 通过引用的方式存取复杂类型。 + + + `对象` + + `数组` + + `函数` + + ```javascript + const foo = [1, 2]; + const bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 引用 + + - [2.1](#2.1) 对所有的引用使用 `const` ;不要使用 `var`。 + + > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 + + ```javascript + // bad + var a = 1; + var b = 2; + + // good + const a = 1; + const b = 2; + ``` + + - [2.2](#2.2) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + + > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 + + ```javascript + // bad + var count = 1; + if (true) { + count += 1; + } + + // good, use the let. + let count = 1; + if (true) { + count += 1; + } + ``` + + - [2.3](#2.3) 注意 `let` 和 `const` 都是块级作用域。 + + ```javascript + // const 和 let 只存在于它们被定义的区块内。 + { + let a = 1; + const b = 1; + } + console.log(a); // ReferenceError + console.log(b); // ReferenceError + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 对象 + + - [3.1](#3.1) 使用字面值创建对象。 + + ```javascript + // bad + const item = new Object(); + + // good + const item = {}; + ``` + + - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 + + ```javascript + // bad + const superman = { + default: { clark: 'kent' }, + private: true, + }; + + // good + const superman = { + defaults: { clark: 'kent' }, + hidden: true, + }; + ``` + + - [3.3](#3.3) 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + const superman = { + class: 'alien', + }; + + // bad + const superman = { + klass: 'alien', + }; + + // good + const superman = { + type: 'alien', + }; + ``` + + + - [3.4](#3.4) 创建有动态属性名的对象时,使用可被计算的属性名称。 + + > 为什么?因为这样可以让你在一个地方定义所有的对象属性。 + + ```javascript + function getKey(k) { + return `a key named ${k}`; + } + + // bad + const obj = { + id: 5, + name: 'San Francisco', + }; + obj[getKey('enabled')] = true; + + // good + const obj = { + id: 5, + name: 'San Francisco', + [getKey('enabled')]: true, + }; + ``` + + + - [3.5](#3.5) 使用对象方法的简写。 + + ```javascript + // bad + const atom = { + value: 1, + + addValue: function (value) { + return atom.value + value; + }, + }; + + // good + const atom = { + value: 1, + + addValue(value) { + return atom.value + value; + }, + }; + ``` + + + - [3.6](#3.6) 使用对象属性值的简写。 + + > 为什么?因为这样更短更有描述性。 + + ```javascript + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + lukeSkywalker: lukeSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + }; + ``` + + - [3.7](#3.7) 在对象属性声明前把简写的属性分组。 + + > 为什么?因为这样能清楚地看出哪些属性使用了简写。 + + ```javascript + const anakinSkywalker = 'Anakin Skywalker'; + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + lukeSkywalker, + episodeThree: 3, + mayTheFourth: 4, + anakinSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + anakinSkywalker, + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + episodeThree: 3, + mayTheFourth: 4, + }; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 数组 + + - [4.1](#4.1) 使用字面值创建数组。 + + ```javascript + // bad + const items = new Array(); + + // good + const items = []; + ``` + + - [4.2](#4.2) 向数组添加元素时使用 Arrary#push 替代直接赋值。 + + ```javascript + const someStack = []; + + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + + - [4.3](#4.3) 使用拓展运算符 `...` 复制数组。 + + ```javascript + // bad + const len = items.length; + const itemsCopy = []; + let i; + + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + const itemsCopy = [...items]; + ``` + - [4.4](#4.4) 使用 Array#from 把一个类数组对象转换成数组。 + + ```javascript + const foo = document.querySelectorAll('.foo'); + const nodes = Array.from(foo); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 解构 + + - [5.1](#5.1) 使用解构存取和使用多属性对象。 + + > 为什么?因为解构能减少临时引用属性。 + + ```javascript + // bad + function getFullName(user) { + const firstName = user.firstName; + const lastName = user.lastName; + + return `${firstName} ${lastName}`; + } + + // good + function getFullName(obj) { + const { firstName, lastName } = obj; + return `${firstName} ${lastName}`; + } + + // best + function getFullName({ firstName, lastName }) { + return `${firstName} ${lastName}`; + } + ``` + + - [5.2](#5.2) 对数组使用解构赋值。 + + ```javascript + const arr = [1, 2, 3, 4]; + + // bad + const first = arr[0]; + const second = arr[1]; + + // good + const [first, second] = arr; + ``` + + - [5.3](#5.3) 需要回传多个值时,使用对象解构,而不是数组解构。 + > 为什么?增加属性或者改变排序不会改变调用时的位置。 + + ```javascript + // bad + function processInput(input) { + // then a miracle occurs + return [left, right, top, bottom]; + } + + // 调用时需要考虑回调数据的顺序。 + const [left, __, top] = processInput(input); + + // good + function processInput(input) { + // then a miracle occurs + return { left, right, top, bottom }; + } + + // 调用时只选择需要的数据 + const { left, right } = processInput(input); + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## Strings + + - [6.1](#6.1) 字符串使用单引号 `''` 。 + + ```javascript + // bad + const name = "Capt. Janeway"; + + // good + const name = 'Capt. Janeway'; + ``` + + - [6.2](#6.2) 字符串超过 80 个字节应该使用字符串连接号换行。 + - [6.3](#6.3) 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + 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.'; + + // bad + 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.'; + + // good + 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.'; + ``` + + + - [6.4](#6.4) 程序化生成字符串时,使用模板字符串代替字符串连接。 + + > 为什么?模板字符串更为简洁,更具可读性。 + + ```javascript + // bad + function sayHi(name) { + return 'How are you, ' + name + '?'; + } + + // bad + function sayHi(name) { + return ['How are you, ', name, '?'].join(); + } + + // good + function sayHi(name) { + return `How are you, ${name}?`; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 函数 + + - [7.1](#7.1) 使用函数声明代替函数表达式。 + + > 为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得[箭头函数](#arrow-functions)可以取代函数表达式。 + + ```javascript + // bad + const foo = function () { + }; + + // good + function foo() { + } + ``` + + - [7.2](#7.2) 函数表达式: + + ```javascript + // 立即调用的函数表达式 (IIFE) + (() => { + console.log('Welcome to the Internet. Please follow me.'); + })(); + ``` + + - [7.3](#7.3) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + - [7.4](#7.4) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + let test; + if (currentUser) { + test = () => { + console.log('Yup.'); + }; + } + ``` + + - [7.5](#7.5) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + + - [7.6](#7.6) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + + > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 + + ```javascript + // bad + function concatenateAll() { + const args = Array.prototype.slice.call(arguments); + return args.join(''); + } + + // good + function concatenateAll(...args) { + return args.join(''); + } + ``` + + + - [7.7](#7.7) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 + + ```javascript + // really bad + function handleThings(opts) { + // 不!我们不应该改变函数参数。 + // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 + // 但这样的写法会造成一些 Bugs。 + //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) + opts = opts || {}; + // ... + } + + // still bad + function handleThings(opts) { + if (opts === void 0) { + opts = {}; + } + // ... + } + + // good + function handleThings(opts = {}) { + // ... + } + ``` + + - [7.8](#7.8) 直接给函数参数赋值时需要避免副作用。 + + > 为什么?因为这样的写法让人感到很困惑。 + + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 箭头函数 + + - [8.1](#8.1) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + + > 为什么?因为箭头函数创造了新的一个 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 + + > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 + + ```javascript + // bad + [1, 2, 3].map(function (x) { + return x * x; + }); + + // good + [1, 2, 3].map((x) => { + return x * x; + }); + ``` + + - [8.2](#8.2) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 + + > 为什么?语法糖。在链式调用中可读性很高。 + + > 为什么不?当你打算回传一个对象的时候。 + + ```javascript + // good + [1, 2, 3].map(x => x * x); + + // good + [1, 2, 3].reduce((total, n) => { + return total + n; + }, 0); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 构造器 + + - [9.1](#9.1) 总是使用 `class`。避免直接操作 `prototype` 。 + + > 为什么? 因为 `class` 语法更为简洁更易读。 + + ```javascript + // bad + function Queue(contents = []) { + this._queue = [...contents]; + } + Queue.prototype.pop = function() { + const value = this._queue[0]; + this._queue.splice(0, 1); + return value; + } + + + // good + class Queue { + constructor(contents = []) { + this._queue = [...contents]; + } + pop() { + const value = this._queue[0]; + this._queue.splice(0, 1); + return value; + } + } + ``` + + - [9.2](#9.2) 使用 `extends` 继承。 + + > 为什么?因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + + ```javascript + // bad + const inherits = require('inherits'); + function PeekableQueue(contents) { + Queue.apply(this, contents); + } + inherits(PeekableQueue, Queue); + PeekableQueue.prototype.peek = function() { + return this._queue[0]; + } + + // good + class PeekableQueue extends Queue { + peek() { + return this._queue[0]; + } + } + ``` + + - [9.3](#9.3) 方法可以返回 `this` 来帮助链式调用。 + + ```javascript + // bad + Jedi.prototype.jump = function() { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function(height) { + this.height = height; + }; + + const luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + class Jedi { + jump() { + this.jumping = true; + return this; + } + + setHeight(height) { + this.height = height; + return this; + } + } + + const luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + - [9.4](#9.4) 可以写一个自定义的 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 + + ```javascript + class Jedi { + constructor(options = {}) { + this.name = options.name || 'no name'; + } + + getName() { + return this.name; + } + + toString() { + return `Jedi - ${this.getName()}`; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 模块 + + - [10.1](#10.1) 总是使用模组 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + > 为什么?模块就是未来,让我们开始迈向未来吧。 + + ```javascript + // bad + const AirbnbStyleGuide = require('./AirbnbStyleGuide'); + module.exports = AirbnbStyleGuide.es6; + + // ok + import AirbnbStyleGuide from './AirbnbStyleGuide'; + export default AirbnbStyleGuide.es6; + + // best + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + + - [10.2](#10.2) 不要使用通配符 import。 + + > 为什么?这样能确保你只有一个默认 export。 + + ```javascript + // bad + import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + + // good + import AirbnbStyleGuide from './AirbnbStyleGuide'; + ``` + + - [10.3](#10.3) 不要从 import 中直接 export。 + + > 为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + ```javascript + // bad + // filename es6.js + export { es6 as default } from './airbnbStyleGuide'; + + // good + // filename es6.js + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Iterators and Generators + + - [11.1](#11.1) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-of`。 + + > 为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + + ```javascript + const numbers = [1, 2, 3, 4, 5]; + + // bad + let sum = 0; + for (let num of numbers) { + sum += num; + } + + sum === 15; + + // good + let sum = 0; + numbers.forEach((num) => sum += num); + sum === 15; + + // best (use the functional force) + const sum = numbers.reduce((total, num) => total + num, 0); + sum === 15; + ``` + + - [11.2](#11.2) 现在还不要使用 generators。 + + > 为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + +**[⬆ 返回目录](#table-of-contents)** + + +## 属性 + + - [12.1](#12.1) 使用 `.` 来访问对象的属性。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + // bad + const isJedi = luke['jedi']; + + // good + const isJedi = luke.jedi; + ``` + + - [12.2](#12.2) 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + function getProp(prop) { + return luke[prop]; + } + + const isJedi = getProp('jedi'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 变量 + + - [13.1](#13.1) 一直使用 `const` 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。) + + ```javascript + // bad + superPower = new SuperPower(); + + // good + const superPower = new SuperPower(); + ``` + + - [13.2](#13.2) 使用 `const` 声明每一个变量。 + + > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 + + ```javascript + // bad + const items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (compare to above, and try to spot the mistake) + const items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + const items = getItems(); + const goSportsTeam = true; + const dragonball = 'z'; + ``` + + - [13.3](#13.3) 将所有的 `const` 和 `let` 分组 + + > 为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。 + + ```javascript + // bad + let i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + let i; + const items = getItems(); + let dragonball; + const goSportsTeam = true; + let len; + + // good + const goSportsTeam = true; + const items = getItems(); + let dragonball; + let i; + let length; + ``` + + - [13.4](#13.4) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 + + > 为什么?`let` 和 `const` 是块级作用域而不是函数作用域。 + + ```javascript + // good + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + const name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // bad - unnecessary function call + function(hasName) { + const name = getName(); + + if (!hasName) { + return false; + } + + this.setFirstName(name); + + return true; + } + + // good + function(hasName) { + if (!hasName) { + return false; + } + + const name = getName(); + this.setFirstName(name); + + return true; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Hoisting + + - [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)相当重要。 + + ```javascript + // 我们知道这样运行不了 + // (假设 notDefined 不是全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 由于变量提升的原因, + // 在引用变量后再声明变量是可以运行的。 + // 注:变量的赋值 `true` 不会被提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 编译器会把函数声明提升到作用域的顶层, + // 这意味着我们的例子可以改写成这样: + function example() { + let declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + + // 使用 const 和 let + function example() { + console.log(declaredButNotAssigned); // => throws a ReferenceError + console.log(typeof declaredButNotAssigned); // => throws a ReferenceError + const declaredButNotAssigned = true; + } + ``` + + - [14.2](#14.2) 匿名函数表达式的变量名会被提升,但函数内容并不会。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function() { + console.log('anonymous function expression'); + }; + } + ``` + + - [14.3](#14.3) 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // the same is true when the function name + // is the same as the variable name. + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + - [14.4](#14.4) 函数声明的名称和函数体都会被提升。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 比较运算符 & 等号 + + - [15.1](#15.1) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. + - [15.2](#15.2) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **布尔值** 被计算为 **布尔的值** + + **数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** + + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0]) { + // true + // An array is an object, objects evaluate to true + } + ``` + + - [15.3](#15.3) 使用简写。 + + ```javascript + // bad + if (name !== '') { + // ...stuff... + } + + // good + if (name) { + // ...stuff... + } + + // bad + if (collection.length > 0) { + // ...stuff... + } + + // good + if (collection.length) { + // ...stuff... + } + ``` + + - [15.4](#15.4) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 代码块 + + - [16.1](#16.1) 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function() { return false; } + + // good + function() { + return false; + } + ``` + + - [16.2](#16.2) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 注释 + + - [17.1](#17.1) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + - [17.2](#17.2) 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + const active = true; // is current tab + + // good + // is current tab + const active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + ``` + + - [17.3](#17.3) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + - [17.4](#17.4) 使用 `// FIXME`: 标注问题。 + + ```javascript + class Calculator { + constructor() { + // FIXME: shouldn't use a global here + total = 0; + } + } + ``` + + - [17.5](#17.5) 使用 `// TODO`: 标注问题的解决方式。 + + ```javascript + class Calculator { + constructor() { + // TODO: total should be configurable by an options param + this.total = 0; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 空白 + + - [18.1](#18.1) 使用 2 个空格作为缩进。 + + ```javascript + // bad + function() { + ∙∙∙∙const name; + } + + // bad + function() { + ∙const name; + } + + // good + function() { + ∙∙const name; + } + ``` + + - [18.2](#18.2) 在花括号前放一个空格。 + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + ``` + + - [18.3](#18.3) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + - [18.4](#18.4) 使用空格把运算符隔开。 + + ```javascript + // bad + const x=y+5; + + // good + const x = y + 5; + ``` + + - [18.5](#18.5) 在文件末尾插入一个空行。 + + ```javascript + // bad + (function(global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function(global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function(global) { + // ...stuff... + })(this);↵ + ``` + + - [18.5](#18.5) 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + ``` + + - [18.6](#18.6) 在块末和新语句前插入空行。 + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + const obj = { + foo() { + }, + bar() { + }, + }; + return obj; + + // good + const obj = { + foo() { + }, + + bar() { + }, + }; + + return obj; + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 逗号 + + - [19.1](#19.1) 行首逗号:**不需要**。 + + ```javascript + // bad + const story = [ + once + , upon + , aTime + ]; + + // good + const story = [ + once, + upon, + aTime, + ]; + + // bad + const hero = { + firstName: 'Ada' + , lastName: 'Lovelace' + , birthYear: 1815 + , superPower: 'computers' + }; + + // good + const hero = { + firstName: 'Ada', + lastName: 'Lovelace', + birthYear: 1815, + superPower: 'computers', + }; + ``` + + - [19.2](#19.2) 增加结尾的逗号: **需要**。 + + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 + + ```javascript + // bad - git diff without trailing comma + const hero = { + firstName: 'Florence', + - lastName: 'Nightingale' + + lastName: 'Nightingale', + + inventorOf: ['coxcomb graph', 'modern nursing'] + } + + // good - git diff with trailing comma + const hero = { + firstName: 'Florence', + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'], + } + + // bad + const hero = { + firstName: 'Dana', + lastName: 'Scully' + }; + + const heroes = [ + 'Batman', + 'Superman' + ]; + + // good + const hero = { + firstName: 'Dana', + lastName: 'Scully', + }; + + const heroes = [ + 'Batman', + 'Superman', + ]; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 分号 + + - [20.1](#20.1) **使用分号** + + ```javascript + // bad + (function() { + const name = 'Skywalker' + return name + })() + + // good + (() => { + const name = 'Skywalker'; + return name; + })(); + + // good (防止函数在两个 IIFE 合并时被当成一个参数) + ;(() => { + const name = 'Skywalker'; + return name; + })(); + ``` + + [Read more](http://stackoverflow.com/a/7365214/1712802). + +**[⬆ 返回目录](#table-of-contents)** + + +## 类型转换 + + - [21.1](#21.1) 在语句开始时执行类型转换。 + - [21.2](#21.2) 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + const totalScore = this.reviewScore + ''; + + // good + const totalScore = String(this.reviewScore); + ``` + + - [21.3](#21.3) 对数字使用 `parseInt` 转换,并带上类型转换的基数。 + + ```javascript + const inputValue = '4'; + + // bad + const val = new Number(inputValue); + + // bad + const val = +inputValue; + + // bad + const val = inputValue >> 0; + + // bad + const val = parseInt(inputValue); + + // good + const val = Number(inputValue); + + // good + const val = parseInt(inputValue, 10); + ``` + + - [21.4](#21.4) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * 使用 parseInt 导致我的程序变慢, + * 改成使用位操作转换数字快多了。 + */ + const val = inputValue >> 0; + ``` + + - [21.5](#21.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: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + - [21.6](#21.6) 布尔: + + ```javascript + const age = 0; + + // bad + const hasAge = new Boolean(age); + + // good + const hasAge = Boolean(age); + + // good + const hasAge = !!age; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 命名规则 + + - [22.1](#22.1) 避免单字母命名。命名应具备描述性。 + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - [22.2](#22.2) 使用驼峰式命名对象、函数和实例。 + + ```javascript + // bad + const OBJEcttsssss = {}; + const this_is_my_object = {}; + function c() {} + + // good + const thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + - [22.3](#22.3) 使用帕斯卡式命名构造函数或类。 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + const bad = new user({ + name: 'nope', + }); + + // good + class User { + constructor(options) { + this.name = options.name; + } + } + + const good = new User({ + name: 'yup', + }); + ``` + + - [22.4](#22.4) 使用下划线 `_` 开头命名私有属性。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + + // good + this._firstName = 'Panda'; + ``` + + - [22.5](#22.5) 别保存 `this` 的引用。使用箭头函数或 Function#bind。 + + ```javascript + // bad + function foo() { + const self = this; + return function() { + console.log(self); + }; + } + + // bad + function foo() { + const that = this; + return function() { + console.log(that); + }; + } + + // good + function foo() { + return () => { + console.log(this); + }; + } + ``` + + - [22.6](#22.6) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 + + ```javascript + // file contents + class CheckBox { + // ... + } + export default CheckBox; + + // in some other file + // bad + import CheckBox from './checkBox'; + + // bad + import CheckBox from './check_box'; + + // good + import CheckBox from './CheckBox'; + ``` + + - [22.7](#22.7) 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。 + + ```javascript + function makeStyleGuide() { + } + + export default makeStyleGuide; + ``` + + - [22.8](#22.8) 当你导出单例、函数库、空对象时使用帕斯卡式命名。 + + ```javascript + const AirbnbStyleGuide = { + es6: { + } + }; + + export default AirbnbStyleGuide; + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 存取器 + + - [23.1](#23.1) 属性的存取函数不是必须的。 + - [23.2](#23.2) 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 + + ```javascript + // bad + dragon.age(); + + // good + dragon.getAge(); + + // bad + dragon.age(25); + + // good + dragon.setAge(25); + ``` + + - [23.3](#23.3) 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + - [23.4](#23.4) 创建 `get()` 和 `set()` 函数是可以的,但要保持一致。 + + ```javascript + class Jedi { + constructor(options = {}) { + const lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + set(key, val) { + this[key] = val; + } + + get(key) { + return this[key]; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 事件 + + - [24.1](#24.1) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```javascript + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', function(e, listingId) { + // do something with listingId + }); + ``` + + 更好的写法: + + ```javascript + // good + $(this).trigger('listingUpdated', { listingId : listing.id }); + + ... + + $(this).on('listingUpdated', function(e, data) { + // do something with data.listingId + }); + ``` + + **[⬆ 返回目录](#table-of-contents)** + + +## jQuery + + - [25.1](#25.1) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + + ```javascript + // bad + const sidebar = $('.sidebar'); + + // good + const $sidebar = $('.sidebar'); + ``` + + - [25.2](#25.2) 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + const $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + - [25.3](#25.3) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + - [25.4](#25.4) 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + - [26.1](#26.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 6 规范 + + - [27.1](#27.1) 以下是链接到 ES6 的各个特性的列表。 + +1. [Arrow Functions](#arrow-functions) +1. [Classes](#constructors) +1. [Object Shorthand](#es6-object-shorthand) +1. [Object Concise](#es6-object-concise) +1. [Object Computed Properties](#es6-computed-properties) +1. [Template Strings](#es6-template-literals) +1. [Destructuring](#destructuring) +1. [Default Parameters](#es6-default-parameters) +1. [Rest](#es6-rest) +1. [Array Spreads](#es6-array-spreads) +1. [Let and Const](#references) +1. [Iterators and Generators](#iterators-and-generators) +1. [Modules](#modules) + +**[⬆ 返回目录](#table-of-contents)** + + +## 测试 + + - [28.1](#28.1) **Yup.** + + ```javascript + function() { + return true; + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](http://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](http://jsperf.com/ya-string-concat) + - Loading... + +**[⬆ 返回目录](#table-of-contents)** + + +## 资源 + +**Learning ES6** + + - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) + - [ExploringJS](http://exploringjs.com/) + - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) + - [Comprehensive Overview of ES6 Features](http://es6-features.org/) + +**Read This** + + - [Annotated ECMAScript 5.1](http://es5.github.com/) + +**Tools** + + - Code Style Linters + + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) + + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**Other Styleguides** + + - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) + - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + +**Other Styles** + + - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**Further Reading** + + - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**Books** + + - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke + +**Blogs** + + - [DailyJS](http://dailyjs.com/) + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](http://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://net.tutsplus.com/?s=javascript) + +**Podcasts** + + - [JavaScript Jabber](http://devchat.tv/js-jabber/) + + +**[⬆ 返回目录](#table-of-contents)** + + +## 使用人群 + + This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. + + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **Userify**: [userify/javascript](https://github.com/userify/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +**[⬆ 返回目录](#table-of-contents)** + + +## 翻译 + + This style guide is also available in other languages: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [yuche/javascript](https://github.com/yuche/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + + +## JavaScript 编码规范说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + + +## 一起来讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + +## Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## License + +(The MIT License) + +Copyright (c) 2014 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 返回目录](#table-of-contents)** + +# }; diff --git a/docs/es6_zh-cn_v2.md b/docs/es6_zh-cn_v2.md new file mode 100644 index 0000000000..63be40ff86 --- /dev/null +++ b/docs/es6_zh-cn_v2.md @@ -0,0 +1,3303 @@ +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Airbnb JavaScript Style Guide() { + +**A mostly reasonable approach to JavaScript——用更合理的方式写 JavaScript** + +翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 + +ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v2.md)。 + +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) +[![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Other Style Guides + - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) + - [React](react/) + - [CSS-in-JavaScript](css-in-javascript/) + - [CSS & Sass](https://github.com/airbnb/css) + - [CSS & Sass 中文版](https://github.com/webcoding/css-style-guide) + - [Ruby](https://github.com/airbnb/ruby) + + +## 目录 + + 1. [类型](#types) + 1. [引用](#references) + 1. [对象](#objects) + 1. [数组](#arrays) + 1. [解构](#destructuring) + 1. [字符串](#strings) + 1. [函数](#functions) + 1. [箭头函数](#arrow-functions) + 1. [类 & 构造函数](#classes--constructors) + 1. [模块](#modules) + 1. [Iterators & Generators ](#iterators-and-generators) + 1. [属性](#properties) + 1. [变量](#variables) + 1. [提升](#hoisting) + 1. [比较运算符 & 等号](#comparison-operators--equality) + 1. [代码块](#blocks) + 1. [注释](#comments) + 1. [空白](#whitespace) + 1. [逗号](#commas) + 1. [分号](#semicolons) + 1. [类型转换](#type-casting--coercion) + 1. [命名规则](#naming-conventions) + 1. [存取器](#accessors) + 1. [事件](#events) + 1. [jQuery](#jquery) + 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) + 1. [ECMAScript 6 (ES 2015+) 编码规范](#ecmascript-6-styles) + 1. [测试](#testing) + 1. [性能](#performance) + 1. [资源](#resources) + 1. [使用人群](#in-the-wild) + 1. [翻译](#translation) + 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) + 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) + 1. [Contributors](#contributors) + 1. [License](#license) + + +## 类型 + + + - [1.1](#types--primitives) **基本类型**: 直接存取基本类型。 + + + `string` 字符串 + + `number` 数值 + + `boolean` 布尔类型 + + `null` + + `undefined` + + ```javascript + const foo = 1; + let bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + + + - [1.2](#types--complex) **复杂类型**: 通过引用的方式存取复杂类型。 + + + `object` 对象 + + `array` 数组 + + `function` 函数 + + ```javascript + const foo = [1, 2]; + const bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 引用 + + + - [2.1](#references--prefer-const) 对所有的引用使用 `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) + + > 为什么? 这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 + + ```javascript + // bad + var a = 1; + var b = 2; + + // good + const a = 1; + const b = 2; + ``` + + + - [2.2](#references--disallow-var) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 + eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) + jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) + + > 为什么? 因为 `let` 是块级作用域,而 `var` 是函数作用域。 + + ```javascript + // bad + var count = 1; + if (true) { + count += 1; + } + + // good, use the let. + let count = 1; + if (true) { + count += 1; + } + ``` + + + - [2.3](#references--block-scope) 注意 `let` 和 `const` 都是块级作用域。 + + ```javascript + // const 和 let 只存在于它们被定义的区块内。 + { + let a = 1; + const b = 1; + } + console.log(a); // ReferenceError + console.log(b); // ReferenceError + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 对象 + + + - [3.1](#objects--no-new) 使用字面值创建对象。 + eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) + + ```javascript + // bad + const item = new Object(); + + // good + const item = {}; + ``` + **************** + - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 + + ```javascript + // bad + const superman = { + default: { clark: 'kent' }, + private: true, + }; + + // good + const superman = { + defaults: { clark: 'kent' }, + hidden: true, + }; + ``` + + - [3.3](#3.3) 使用同义词替换需要使用的保留字。 + + ```javascript + // bad + const superman = { + class: 'alien', + }; + + // bad + const superman = { + klass: 'alien', + }; + + // good + const superman = { + type: 'alien', + }; + ``` + ************* + + + - [3.2](#es6-computed-properties) 创建有动态属性名的对象时,使用可被计算的属性名称。 + + > 为什么? 因为这样可以让你在一个地方定义所有的对象属性。 + + ```javascript + function getKey(k) { + return `a key named ${k}`; + } + + // bad + const obj = { + id: 5, + name: 'San Francisco', + }; + obj[getKey('enabled')] = true; + + // good + const obj = { + id: 5, + name: 'San Francisco', + [getKey('enabled')]: true, + }; + ``` + + + - [3.3](#es6-object-shorthand) 使用对象方法的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + + ```javascript + // bad + const atom = { + value: 1, + + addValue: function (value) { + return atom.value + value; + }, + }; + + // good + const atom = { + value: 1, + + addValue(value) { + return atom.value + value; + }, + }; + ``` + + + - [3.4](#es6-object-concise) 使用对象属性值的简写。 + eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) + jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + + > 为什么? 因为这样更短更有描述性。 + + ```javascript + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + lukeSkywalker: lukeSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + }; + ``` + + + - [3.5](#objects--grouped-shorthand) 在对象属性声明前把简写的属性分组。 + + > 为什么? 因为这样能清楚地看出哪些属性使用了简写。 + + ```javascript + const anakinSkywalker = 'Anakin Skywalker'; + const lukeSkywalker = 'Luke Skywalker'; + + // bad + const obj = { + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + lukeSkywalker, + episodeThree: 3, + mayTheFourth: 4, + anakinSkywalker, + }; + + // good + const obj = { + lukeSkywalker, + anakinSkywalker, + episodeOne: 1, + twoJedisWalkIntoACantina: 2, + episodeThree: 3, + mayTheFourth: 4, + }; + ``` + + + - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. + eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) + jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) + + > 为什么? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. + + ```javascript + // bad + const bad = { + 'foo': 3, + 'bar': 4, + 'data-blah': 5, + }; + + // good + const good = { + foo: 3, + bar: 4, + 'data-blah': 5, + }; + ``` + + + - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. + + > 为什么? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). + + ```javascript + // bad + console.log(object.hasOwnProperty(key)); + + // good + console.log(Object.prototype.hasOwnProperty.call(object, key)); + + // best + const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. + /* or */ + import has from 'has'; + … + console.log(has.call(object, key)); + ``` + + + - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted. + + ```javascript + // very bad + const original = { a: 1, b: 2 }; + const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ + delete copy.a; // so does this + + // bad + const original = { a: 1, b: 2 }; + const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } + + // good + const original = { a: 1, b: 2 }; + const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } + + const { a, ...noA } = copy; // noA => { b: 2, c: 3 } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 数组 + + + - [4.1](#arrays--literals) 使用字面值创建数组。 + eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) + + ```javascript + // bad + const items = new Array(); + + // good + const items = []; + ``` + + + - [4.2](#arrays--push) 向数组添加元素时使用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 替代直接赋值。 + + ```javascript + const someStack = []; + + // bad + someStack[someStack.length] = 'abracadabra'; + + // good + someStack.push('abracadabra'); + ``` + + + - [4.3](#es6-array-spreads) 使用拓展运算符 `...` 复制数组。 + + ```javascript + // bad + const len = items.length; + const itemsCopy = []; + let i; + + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + + // good + const itemsCopy = [...items]; + + // ES5 中,当你需要拷贝数组时使用slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) + // old + itemsCopy = items.slice(); + ``` + + + - [4.4](#arrays--from) 使用 [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 把一个类数组对象转换成数组。 + + ```javascript + const foo = document.querySelectorAll('.foo'); + const nodes = Array.from(foo); + + // ES5 中使用slice将类数组的对象转成数组. + // old + function trigger() { + var args = Array.prototype.slice.call(arguments); + ... + } + ``` + + + - [4.5](#arrays--callback-return) 在回调函数中使用 return 语句。如果函数体只由一个单独的语句组成,可以省略 return 关键字 [8.2](#8.2). + eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) + + ```javascript + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map(x => x + 1); + + // bad + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + }); + + // good + const flat = {}; + [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { + const flatten = memo.concat(item); + flat[index] = flatten; + return flatten; + }); + + // bad + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } else { + return false; + } + }); + + // good + inbox.filter((msg) => { + const { subject, author } = msg; + if (subject === 'Mockingbird') { + return author === 'Harper Lee'; + } + + return false; + }); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 解构 + + + - [5.1](#destructuring--object) 使用解构存取和使用多属性对象。 + jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) + + > 为什么? 因为解构能减少临时引用属性。 + + ```javascript + // bad + function getFullName(user) { + const firstName = user.firstName; + const lastName = user.lastName; + + return `${firstName} ${lastName}`; + } + + // good + function getFullName(user) { + const { firstName, lastName } = user; + return `${firstName} ${lastName}`; + } + + // best + function getFullName({ firstName, lastName }) { + return `${firstName} ${lastName}`; + } + ``` + + + - [5.2](#destructuring--array) 对数组使用解构赋值。 + jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) + + ```javascript + const arr = [1, 2, 3, 4]; + + // bad + const first = arr[0]; + const second = arr[1]; + + // good + const [first, second] = arr; + ``` + + + - [5.3](#destructuring--object-over-array) 需要回传多个值时,使用对象解构,而不是数组解构。 + jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) + + > 为什么? 增加属性或者改变排序不会改变调用时的位置。 + + ```javascript + // bad + function processInput(input) { + // then a miracle occurs + return [left, right, top, bottom]; + } + + // 调用时需要考虑回调数据的顺序。 + const [left, __, top] = processInput(input); + + // good + function processInput(input) { + // then a miracle occurs + return { left, right, top, bottom }; + } + + // 调用时只选择需要的数据 + const { left, top } = processInput(input); + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## Strings + + + - [6.1](#strings--quotes) 字符串使用单引号 `''` 。 + eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) + jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) + + ```javascript + // bad + const name = "Capt. Janeway"; + + // bad - template literals should contain interpolation or newlines + const name = `Capt. Janeway`; + + // good + const name = 'Capt. Janeway'; + ``` + + + - [6.2](#strings--line-length) 字符串超过 100 个字节应该使用字符串连接号换行。此处可以更多个字符200或300,目前编辑界面越来越大了 + + > 为什么? 切断长字符串,可以更好编码和搜索。 + + 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). + + ```javascript + // bad + 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.'; + + // bad + 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.'; + + // good + 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.'; + ``` + + + + - [6.3](#es6-template-literals) 程序化生成字符串时,使用模板字符串代替字符串连接。 + 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) + + > 为什么? 模板字符串插值更为简洁,更具可读性。 + + ```javascript + // bad + function sayHi(name) { + return 'How are you, ' + name + '?'; + } + + // bad + function sayHi(name) { + return ['How are you, ', name, '?'].join(); + } + + // bad + function sayHi(name) { + return `How are you, ${ name }?`; + } + + // good + function sayHi(name) { + return `How are you, ${name}?`; + } + ``` + + + - [6.4](#strings--eval) 在字符串中永不使用 `eval()`, 它会导致很多漏洞。 + + + - [6.5](#strings--escaping) 字符串中不要使用不必要的转义。 + eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) + + > 为什么? 反斜杠会降低可读性,应该在必要时才去使用它。 + + ```javascript + // bad + const foo = '\'this\' \i\s \"quoted\"'; + + // good + const foo = '\'this\' is "quoted"'; + const foo = `'this' is "quoted"`; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 函数 + + - [7.1](#7.1) 使用函数表达式,而不是函数声明。 + eslint: [`func-style`](http://eslint.org/docs/rules/func-style) + jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations) + + > 为什么? 函数声明会把整个函数提升(hoisted),这导致非常容易在定义以前就被引用,这会降低可读性以及维护性(而函数表达式只会把函数的引用变量名提升)。如果发现一个函数定义非常大或复杂,会干扰其他逻辑的理解,此时也许是时候把它提取成独立模块了。 + > 另外不要忘记给匿名表达式命名,匿名函数会使错误堆栈中跟踪问题更加困难。 + > 函数提升规则,使得[箭头函数](#arrow-functions)可以取代函数表达式。 + + ```javascript + // bad 匿名函数表达式 + var anonymous = function() () { + }; + + // bad + function foo() { + } + + // good + const foo = function named() { + }; + ``` + + + - [7.2](#functions--iife) 用括号包裹 立即调用函数表达式(IIFE)。 + eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) + jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) + + ```javascript + // 立即调用的函数表达式 (IIFE) + // 可使用箭头函数 + (() => { + console.log('Welcome to the Internet. Please follow me.'); + }()); + ``` + + + - [7.3](#functions--in-blocks) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 + eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) + + + - [7.4](#functions--note-on-blocks) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + let test; + if (currentUser) { + test = () => { + console.log('Yup.'); + }; + } + ``` + + + - [7.5](#functions--arguments-shadow) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 + + ```javascript + // bad + function nope(name, options, arguments) { + // ...stuff... + } + + // good + function yup(name, options, args) { + // ...stuff... + } + ``` + + + - [7.6](#es6-rest) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 + eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) + + > 为什么? 使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 + + ```javascript + // bad + function concatenateAll() { + const args = Array.prototype.slice.call(arguments); + return args.join(''); + } + + // good + function concatenateAll(...args) { + return args.join(''); + } + ``` + + + - [7.7](#es6-default-parameters) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 + + ```javascript + // really bad + function handleThings(opts) { + // 不!我们不应该改变函数参数。 + // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 + // 但这样的写法会造成一些 Bugs。 + //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) + opts = opts || {}; + // ... + } + + // still bad + function handleThings(opts) { + if (opts === void 0) { + opts = {}; + } + // ... + } + + // good + function handleThings(opts = {}) { + // ? 对比以上写法区别是啥?参见 es6 + // ... + } + ``` + + + - [7.8](#functions--default-side-effects) 函数参数设置默认值,要避免副作用。 + + > 为什么? 因为这样会让人感到很困惑。 + + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` + + + - [7.9](#functions--defaults-last) 总是把默认参数最后。 + + ```javascript + // bad + function handleThings(opts = {}, name) { + // ... + } + + // good + function handleThings(name, opts = {}) { + // ... + } + ``` + + + - [7.10](#functions--constructor) 永远不要使用构造函数创建新函数。 + eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) + + > 为什么? 以这种方式创建一个函数,类似 eval(),会导致很多漏洞。 + + ```javascript + // bad + var add = new Function('a', 'b', 'return a + b'); + + // still bad + var subtract = Function('a', 'b', 'return a - b'); + ``` + + + - [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) + + > 为什么? 一致性好,当添加或移除名称时,不必添加或移除空间。 + + ```javascript + // bad + const f = function(){}; + const g = function (){}; + const h = function() {}; + + // good + const x = function () {}; + const y = function a() {}; + ``` + + + - [7.12](#functions--mutate-params) 不要使用变异参数。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + + > 为什么? 操纵传入的参数,在原调用者上可能会导致不必要的变量副作用。 + + ```javascript + // bad + function f1(obj) { + obj.key = 1; + }; + + // good + function f2(obj) { + const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; + }; + ``` + + + - [7.13](#functions--reassign-params) 不要给形参重新赋值。 + eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + + > 为什么? 这样做可能会导致意外行为,特别是操作 `arguments` 参数。这也会导致优化问题,特别是使用 V8 解析器. + + ```javascript + // bad + function f1(a) { + a = 1; + } + + function f2(a) { + if (!a) { a = 1; } + } + + // good + function f3(a) { + const b = a || 1; + } + + function f4(a = 1) { + } + ``` + + + - [7.14](#functions--spread-vs-apply) 使用拓展操作符 `...` 调用可变参函数。 + eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) + + > 为什么? 更简洁、并且也不用提供上下文作用域,而使用 `new` 和 `apply` 也没这个容易。 + + ```javascript + // bad + const x = [1, 2, 3, 4, 5]; + console.log.apply(console, x); + + // good + const x = [1, 2, 3, 4, 5]; + console.log(...x); + + // bad + new (Function.prototype.bind.apply(Date, [null, 2016, 08, 05])); + + // good + new Date(...[2016, 08, 05]); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 箭头函数 + + + - [8.1](#arrows--use-them) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 + 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) + + > 为什么? 因为箭头函数会创建一个你通常最想要的 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 + + > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 + + ```javascript + // bad + [1, 2, 3].map(function (x) { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + + + - [8.2](#arrows--implicit-return) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 + + > 为什么? 这是语法糖。在链式调用中可读性很高。 + + > 为什么不?当你打算回传一个对象的时候。 + + ```javascript + // bad + [1, 2, 3].map(number => { + const nextNumber = number + 1; + `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map(number => `A string containing the ${number}.`); + + // good + [1, 2, 3].map((number) => { + const nextNumber = number + 1; + return `A string containing the ${nextNumber}.`; + }); + + // good + [1, 2, 3].map((number, index) => ({ + [index]: number + })); + ``` + + + - [8.3](#arrows--paren-wrap) 如果表达式有多行,使用圆括号包裹,提高可读性。 + + > 为什么? 这样能更清楚的看到开始和结束位置。 + + ```js + // bad + ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + ); + + // good + ['get', 'post', 'put'].map(httpMethod => ( + Object.prototype.hasOwnProperty.call( + httpMagicObjectWithAVeryLongName, + httpMethod + ) + )); + ``` + + + - [8.4](#arrows--one-arg-parens) 如果函数只有一个参数就不要使用圆括号,否则总是用圆括号包裹参数。 + eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) + jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) + + > 为什么? 更少的视觉干扰 + + ```js + // bad + [1, 2, 3].map((x) => x * x); + + // good + [1, 2, 3].map(x => x * x); + + // good + [1, 2, 3].reduce((total, n) => { + return total + n; + }, 0); + + // good + [1, 2, 3].map(number => ( + `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` + )); + + // bad + [1, 2, 3].map(x => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + + + - [8.5](#arrows--confusing) 避免使用比较操作符 (`<=`, `>=`),会混淆 箭头函数语法 (`=>`) + eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) + + ```js + // bad + const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; + + // bad + const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; + + // good + const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); + + // good + const itemHeight = (item) => { + const { height, largeSize, smallSize } = item; + return height > 256 ? largeSize : smallSize; + }; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 类 & 构造器 + + + - [9.1](#constructors--use-class) 总是使用 `class`。避免直接操作 `prototype` 。 + + > 为什么? 因为 `class` 语法更为简洁更易读。 + + ```javascript + // bad + function Queue(contents = []) { + this.queue = [...contents]; + } + Queue.prototype.pop = function () { + const value = this.queue[0]; + this.queue.splice(0, 1); + return value; + }; + + + // good + class Queue { + constructor(contents = []) { + this.queue = [...contents]; + } + pop() { + const value = this.queue[0]; + this.queue.splice(0, 1); + return value; + } + } + ``` + + + - [9.2](#constructors--extends) 使用 `extends` 继承。 + + > 为什么? 因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 + + ```javascript + // bad + const inherits = require('inherits'); + function PeekableQueue(contents) { + Queue.apply(this, contents); + } + inherits(PeekableQueue, Queue); + PeekableQueue.prototype.peek = function () { + return this.queue[0]; + } + + // good + class PeekableQueue extends Queue { + peek() { + return this.queue[0]; + } + } + ``` + + + - [9.3](#constructors--chaining) 方法可以返回 `this` 以便于链式调用。 + + ```javascript + // bad + Jedi.prototype.jump = function () { + this.jumping = true; + return true; + }; + + Jedi.prototype.setHeight = function (height) { + this.height = height; + }; + + const luke = new Jedi(); + luke.jump(); // => true + luke.setHeight(20); // => undefined + + // good + class Jedi { + jump() { + this.jumping = true; + return this; + } + + setHeight(height) { + this.height = height; + return this; + } + } + + const luke = new Jedi(); + + luke.jump() + .setHeight(20); + ``` + + + + - [9.4](#constructors--tostring) 可以自定义 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 + + ```javascript + class Jedi { + constructor(options = {}) { + this.name = options.name || 'no name'; + } + + getName() { + return this.name; + } + + toString() { + return `Jedi - ${this.getName()}`; + } + } + ``` + + + - [9.5](#constructors--no-useless) 类有默认构造函数,书写一个空的构造函数是没必要的。 + eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) + + ```javascript + // bad + class Jedi { + constructor() {} + + getName() { + return this.name; + } + } + + // bad + class Rey extends Jedi { + constructor(...args) { + super(...args); + } + } + + // good + class Rey extends Jedi { + constructor(...args) { + super(...args); + this.name = 'Rey'; + } + } + ``` + + + - [9.6](#classes--no-duplicate-members) 避免类成员重复。 + eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) + + > 为什么? 重复的类成员默认会使用最后一个——这肯定有一个错误存在。 + + ```javascript + // bad + class Foo { + bar() { return 1; } + bar() { return 2; } + } + + // good + class Foo { + bar() { return 1; } + } + + // good + class Foo { + bar() { return 2; } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 模块 + + + - [10.1](#modules--use-them) 总是使用模块 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 + + > 为什么? 模块就是未来,让我们开始迈向未来吧。 + > NOTE: 模块命名?(这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致) + + ```javascript + // bad + const AirbnbStyleGuide = require('./AirbnbStyleGuide'); + module.exports = AirbnbStyleGuide.es6; + + // ok + import AirbnbStyleGuide from './AirbnbStyleGuide'; + export default AirbnbStyleGuide.es6; + + // best + import { es6 } from './AirbnbStyleGuide'; + export default es6; + + // ES5中要注意 + // old + // - 模块应该以 `!` 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 + // - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致 + // - 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它 + // - 总是在模块顶部声明 `'use strict';` + ``` + + + - [10.2](#modules--no-wildcard) 不要使用通配符 import。 + + > 为什么? 这样能确保你只有一个默认 export。 + + ```javascript + // bad + import * as AirbnbStyleGuide from './AirbnbStyleGuide'; + + // good + import AirbnbStyleGuide from './AirbnbStyleGuide'; + ``` + + + - [10.3](#modules--no-export-from-import) 不要从 import 中直接 export。 + + > 为什么? 虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 + + ```javascript + // bad + // filename es6.js + export { es6 as default } from './AirbnbStyleGuide'; + + // good + // filename es6.js + import { es6 } from './AirbnbStyleGuide'; + export default es6; + ``` + + + - [10.4](#modules--no-duplicate-imports) 一个模块只要一个 `import` 。 + eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) + + > 为什么? 同一模块多个 `import` 导入,会使代码更难维护。 + + ```javascript + // bad + import foo from 'foo'; + // … some other imports … // + import { named1, named2 } from 'foo'; + + // good + import foo, { named1, named2 } from 'foo'; + + // good + import foo, { + named1, + named2, + } from 'foo'; + ``` + + + - [10.5](#modules--no-mutable-exports) 禁止 `export` 可写值输出,使用常量(Functions/Classes 暂时例外)。 + eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) + + > 为什么? 避免全局使用时出现问题,除了某些特定情况下确实要这样用。一般来说,`export` 应该输出常量。 + + ```javascript + // bad + var count = 3; + export { count } + + // bad + let foo = 3; + export { foo } + + // good + const foo = 3; + export { foo } + + // valid + export const count = 1 + export function getCount () {} + export class Counter {} + ``` + + + - [10.6](#modules--prefer-default-export) 单出口模块,更倾向设置为默认出口。 +eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) + + ```javascript + // bad + export function foo() {} + + // good + export default function foo() {} + ``` + + + - [10.7](#modules--imports-first) 把所有的 `import` 语句放在其他语句上面 +eslint: [`import/imports-first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md) + + > 为什么? 把 `import` 提升, 保持将它们写在文件顶部是个好习惯。 + + ```javascript + // bad + import foo from 'foo'; + foo.init(); + + import bar from 'bar'; + + // good + import foo from 'foo'; + import bar from 'bar'; + + foo.init(); + ``` + + + - [10.8](#modules--multiline-imports-over-newlines) 多 `import` 入口,代码书写格式应与 `array` 和 `object` 保持一致。 + + > 为什么? 大括号代码块、缩进以及逗号都保持风格一致 + + ```javascript + // bad + import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; + + // good + import { + longNameA, + longNameB, + longNameC, + longNameD, + longNameE, + } from 'path'; + ``` + + + - [10.9](#modules--no-webpack-loader-syntax) 不允许在 `import` 语句里包含 webpack 加载器语法。 +eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) + + > 为什么? 加载器语法要在 `webpack.config.js` 中使用 + + ```javascript + // bad + import fooSass from 'css!sass!foo.scss'; + import barCss from 'style!css!bar.css'; + + // good + import fooSass from 'foo.scss'; + import barCss from 'bar.css'; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Iterators and Generators 迭代器 & 生成器 + + + - [11.1](#iterators--nope) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-in` or `for-of`。 + eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](http://eslint.org/docs/rules/no-restricted-syntax) + + > 为什么? 这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 + + > 使用迭代器 `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... 遍历数组,使用 `Object.keys()` / `Object.values()` / `Object.entries()` 遍历对象。 + ```javascript + const numbers = [1, 2, 3, 4, 5]; + + // bad + let sum = 0; + for (let num of numbers) { + sum += num; + } + sum === 15; + + // good + let sum = 0; + numbers.forEach(num => sum += num); + sum === 15; + + // best (use the functional force) + const sum = numbers.reduce((total, num) => total + num, 0); + sum === 15; + ``` + + + - [11.2](#generators--nope) 现在还不要使用生成器 generators。 + + > 为什么? 因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) + + + - [11.3](#generators--spacing) 如果你必须使用generators 或无视[generators 忠告](#generators--nope),那么请务必确保函数签名(function*)是适当的间距。 + eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) + + > 为什么? `function` and `*` 是同一个关键词的不同组成部分—— `*` 不是 `function` 修饰符, `function*` 是一种特定结构, 不同于 `function`。 + + ```js + // bad + function * foo() { + } + + const bar = function * () { + } + + const baz = function *() { + } + + const quux = function*() { + } + + function*foo() { + } + + function *foo() { + } + + // very bad + function + * + foo() { + } + + const wat = function + * + () { + } + + // good + function* foo() { + } + + const foo = function* () { + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 属性 + + + - [12.1](#properties--dot) 使用 `.` 符号来访问对象的属性。 + eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) + jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + // bad + const isJedi = luke['jedi']; + + // good + const isJedi = luke.jedi; + ``` + + + - [12.2](#properties--bracket) 当通过变量访问属性时使用中括号 `[]`。 + + ```javascript + const luke = { + jedi: true, + age: 28, + }; + + function getProp(prop) { + return luke[prop]; + } + + const isJedi = getProp('jedi'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 变量 + + + - [13.1](#variables--const) 总是使用 `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) + + ```javascript + // bad + superPower = new SuperPower(); + + // good + const superPower = new SuperPower(); + ``` + + + - [13.2](#variables--one-const) 使用 `const` 声明每一个变量。 + eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) + + > 为什么? 增加新变量将更容易,而且你永远不用再担心调换错 `;` or `,` ,并且 diff 工具下更少干扰。同时在 debugger 时,可以单步调试而不会一次跳过他们所有变量。 + + ```javascript + // bad + const items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + + // bad + // (compare to above, and try to spot the mistake) + const items = getItems(), + goSportsTeam = true; + dragonball = 'z'; + + // good + const items = getItems(); + const goSportsTeam = true; + const dragonball = 'z'; + ``` + + + - [13.3](#variables--const-let-group) 将所有的 `const` 和 `let` 分组 + + > 为什么? 当你需要把已赋值变量赋值给未赋值变量时非常有用。 + + ```javascript + // bad + let i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + let i; + const items = getItems(); + let dragonball; + const goSportsTeam = true; + let len; + + // good + const goSportsTeam = true; + const items = getItems(); + let dragonball; + let i; + let length; + ``` + + + - [13.4](#variables--define-where-used) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 + + > 为什么? `let` 和 `const` 是块级作用域而不是函数作用域。 + + ```javascript + // bad - unnecessary function call + function checkName(hasName) { + const name = getName(); + + if (hasName === 'test') { + return false; + } + + if (name === 'test') { + this.setName(''); + return false; + } + + return name; + } + + // good + function checkName(hasName) { + if (hasName === 'test') { + return false; + } + + const name = getName(); + + if (name === 'test') { + this.setName(''); + return false; + } + + return name; + } + ``` + + + - [13.5](#variables--no-chain-assignment) 不使用链接变量赋值。 + + > 为什么? 链接变量赋值会创建隐式全局变量。 + + ```javascript + // bad + (function example() { + // JavaScript interprets this as + // let a = ( b = ( c = 1 ) ); + // The let keyword only applies to variable a; variables b and c become + // global variables. + let a = b = c = 1; + }()); + + console.log(a); // undefined + console.log(b); // 1 + console.log(c); // 1 + + // good + (function example() { + let a = 1; + let b = a; + let c = a; + }()); + + console.log(a); // undefined + console.log(b); // undefined + console.log(c); // undefined + + // the same applies for `const` + ``` + + + - [13.6](#variables--unary-increment-decrement) 便面使用一元递增和递减操作 (++, --) 。 + eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) + + > 为什么? 一元递增和递减语句默认在自动分号插入处理下会有错误问题。而且它还会出现 `num += 1` 替代 `num ++` 的情况。不允许使用它们,也可以代码更健壮。 + + ```javascript + // bad + + let array = [1, 2, 3]; + let num = 1; + num ++; + -- num; + + let sum = 0; + let truthyCount = 0; + for(let i = 0; i < array.length; i++){ + let value = array[i]; + sum += value; + if (value) { + truthyCount++; + } + } + + // good + + let array = [1, 2, 3]; + let num = 1; + num += 1; + num -= 1; + + const sum = array.reduce((a, b) => a + b, 0); + const truthyCount = array.filter(Boolean).length; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## Hoisting + + + - [14.1](#hoisting--about) `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)相当重要。 + + ```javascript + // 我们知道这样运行不了(假设 notDefined 不是全局变量) + function example() { + console.log(notDefined); // => throws a ReferenceError + } + + // 由于变量提升的原因, + // 在引用变量后再声明变量是可以运行的。 + // **注意:** 变量赋值 `true` 的操作不会被提升。 + function example() { + console.log(declaredButNotAssigned); // => undefined + var declaredButNotAssigned = true; + } + + // 编译器会把函数声明提升到作用域的顶层, + // 这意味着我们的例子可以改写成这样: + function example() { + let declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + + // 使用 const 和 let + function example() { + console.log(declaredButNotAssigned); // => throws a ReferenceError + console.log(typeof declaredButNotAssigned); // => throws a ReferenceError + const declaredButNotAssigned = true; + } + ``` + + + - [14.2](#hoisting--anon-expressions) 匿名函数表达式的变量名会被提升,但函数体并不会。 + + ```javascript + function example() { + console.log(anonymous); // => undefined + + anonymous(); // => TypeError anonymous is not a function + + var anonymous = function () { + console.log('anonymous function expression'); + }; + } + ``` + + + - [14.3](#hoisting--named-expresions) 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。 + + ```javascript + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + superPower(); // => ReferenceError superPower is not defined + + var named = function superPower() { + console.log('Flying'); + }; + } + + // the same is true when the function name + // is the same as the variable name. + function example() { + console.log(named); // => undefined + + named(); // => TypeError named is not a function + + var named = function named() { + console.log('named'); + } + } + ``` + + + - [14.4](#hoisting--declarations) 函数声明的名称和函数体都会被提升。 + + ```javascript + function example() { + superPower(); // => Flying + + function superPower() { + console.log('Flying'); + } + } + ``` + + - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/). + + - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 + +**[⬆ 返回目录](#table-of-contents)** + + +## 比较运算符 & 等号 + + + - [15.1](#comparison--eqeqeq) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=` 。 + eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html) + + + - [15.2](#comparison--if) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: + + + **Objects 对象** 被计算为 **true** + + **Undefined** 被计算为 **false** + + **Null** 被计算为 **false** + + **Booleans 布尔值** 被计算为 **布尔的值** + + **Numbers 数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** + + **Strings 字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** + + ```javascript + if ([0] && []) { + // true + // an array (even an empty one) is an object, objects will evaluate to true + } + ``` + + + - [15.3](#comparison--shortcuts) 使用简写。 + + ```javascript + // bad + if (isValid === true) { + // ...stuff... + } + + // good + if (isValid) { + // ...stuff... + } + + // bad + if (name) { + // ...stuff... + } + + // good + if (name !== '') { + // ...stuff... + } + + // bad + if (collection.length) { + // ...stuff... + } + + // good + if (collection.length > 0) { + // 为什么没用简写??? + // ...stuff... + } + ``` + + + - [15.4](#comparison--moreinfo) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 + + + - [15.5](#comparison--switch-blocks) `case` 以及 `default` 情况下使用括号会创建块级作用域 (e.g. `let`, `const`, `function`, and `class`). + + > 为什么? 变量声明在整个 `switch` 代码块中是可用的,但只有在执行到 `case` 并赋值时才初始化。多 `case` 中重复定义会导致问题。 + + eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). + + ```javascript + // ??? + // bad + switch (foo) { + case 1: + let x = 1; + break; + case 2: + const y = 2; + break; + case 3: + function f() {} + break; + default: + class C {} + } + + // good + switch (foo) { + case 1: { + let x = 1; + break; + } + case 2: { + const y = 2; + break; + } + case 3: { + function f() {} + break; + } + case 4: + bar(); + break; + default: { + class C {} + } + } + ``` + + + - [15.6](#comparison--nested-ternaries) 别嵌套使用三元表达式,要单个使用。 + + eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). + + ```javascript + // bad + const foo = maybe1 > maybe2 + ? "bar" + : value1 > value2 ? "baz" : null; + + // better + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 + ? 'bar' + : maybeNull; + + // best + const maybeNull = value1 > value2 ? 'baz' : null; + + const foo = maybe1 > maybe2 ? 'bar' : maybeNull; + ``` + + + - [15.7](#comparison--unneeded-ternary) 避免不必要使用三元表达式 + + eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). + + ```javascript + // bad + const foo = a ? a : b; + const bar = c ? true : false; + const baz = c ? false : true; + + // good + const foo = a || b; + const bar = !!c; + const baz = !c; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 代码块 + + + - [16.1](#blocks--braces) 使用大括号包裹所有的多行代码块。 + + ```javascript + // bad + if (test) + return false; + + // good + if (test) return false; + + // good + if (test) { + return false; + } + + // bad + function foo() { return false; } + + // good + function bar() { + return false; + } + ``` + + + - [16.2](#blocks--cuddled-elses) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 + + ```javascript + // bad + if (test) { + thing1(); + thing2(); + } + else { + thing3(); + } + + // good + if (test) { + thing1(); + thing2(); + } else { + thing3(); + } + ``` + + +**[⬆ 返回目录](#table-of-contents)** + + +## 注释 + + + - [17.1](#comments--multiline) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 + + ```javascript + // bad + // make() returns a new element + // based on the passed in tag name + // + // @param {String} tag + // @return {Element} element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the passed-in tag name + * + * @param {String} tag + * @return {Element} element + */ + function make(tag) { + + // ...stuff... + + return element; + } + ``` + + + - [17.2](#comments--singleline) 单行注释使用 `//`,后紧跟一空格。在评论对象上面另起一行使用单行注释。在注释前插入空行。 + + ```javascript + // bad + const active = true; // is current tab + + // good + // is current tab + const active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + + // also good + function getType() { + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } + ``` + + + - [17.3](#comments--actionitems) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 + + + - [17.4](#comments--fixme) 使用 `// FIXME`: 标注问题。 + + ```javascript + class Calculator extends Abacus { + constructor() { + super(); + + // FIXME: shouldn't use a global here + total = 0; + } + } + ``` + + + - [17.5](#comments--todo) 使用 `// TODO`: 标注问题还需要解决。 + + ```javascript + class Calculator extends Abacus { + constructor() { + super(); + + // TODO: total should be configurable by an options param + this.total = 0; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 空白 + + + - [18.1](#whitespace--spaces) 使用 2 个空格作为缩进。 + eslint: [`indent`](http://eslint.org/docs/rules/indent.html) + jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) + + ```javascript + // bad + function foo() { + ∙∙∙∙const name; + } + + // bad + function bar() { + ∙const name; + } + + // good + function baz() { + ∙∙const name; + } + ``` + + + - [18.2](#whitespace--before-blocks) 在花括号前放一个空格。 + eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) + jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) + + ```javascript + // bad + function test(){ + console.log('test'); + } + + // good + function test() { + console.log('test'); + } + + // bad + dog.set('attr',{ + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + + // good + dog.set('attr', { + age: '1 year', + breed: 'Bernese Mountain Dog', + }); + ``` + + + - [18.3](#whitespace--around-keywords) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 + eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) + jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) + + ```javascript + // bad + if(isJedi) { + fight (); + } + + // good + if (isJedi) { + fight(); + } + + // bad + function fight () { + console.log ('Swooosh!'); + } + + // good + function fight() { + console.log('Swooosh!'); + } + ``` + + + - [18.4](#whitespace--infix-ops) 使用空格把运算符隔开。 + 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) + + ```javascript + // bad + const x=y+5; + + // good + const x = y + 5; + ``` + + + - [18.5](#whitespace--newline-at-end) 在文件末尾插入一个空行。 + eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this); + ``` + + ```javascript + // bad + (function (global) { + // ...stuff... + })(this);↵ + ↵ + ``` + + ```javascript + // good + (function (global) { + // ...stuff... + })(this);↵ + ``` + + + - [18.6](#whitespace--chains) 在使用长方法链时进行缩进。以 `.` 起行并缩进,强调这是方法调用而不是新语句。 + 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) + + ```javascript + // bad + $('#items').find('.selected').highlight().end().find('.open').updateCount(); + + // bad + $('#items'). + find('.selected'). + highlight(). + end(). + find('.open'). + updateCount(); + + // good + $('#items') + .find('.selected') + .highlight() + .end() + .find('.open') + .updateCount(); + + // bad + const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) + .attr('width', (radius + margin) * 2).append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led') + .data(data) + .enter().append('svg:svg') + .classed('led', true) + .attr('width', (radius + margin) * 2) + .append('svg:g') + .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') + .call(tron.led); + + // good + const leds = stage.selectAll('.led').data(data); + ``` + + + - [18.7](#whitespace--after-blocks) 在块末和新语句前插入空行。 + jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) + + ```javascript + // bad + if (foo) { + return bar; + } + return baz; + + // good + if (foo) { + return bar; + } + + return baz; + + // bad + const obj = { + foo() { + }, + bar() { + }, + }; + return obj; + + // good + const obj = { + foo() { + }, + + bar() { + }, + }; + + return obj; + + // bad + const arr = [ + function foo() { + }, + function bar() { + }, + ]; + return arr; + + // good + const arr = [ + function foo() { + }, + + function bar() { + }, + ]; + + return arr; + ``` + + + - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) + + ```javascript + // bad + function bar() { + + console.log(foo); + + } + + // also bad + if (baz) { + + console.log(qux); + } else { + console.log(foo); + + } + + // good + function bar() { + console.log(foo); + } + + // good + if (baz) { + console.log(qux); + } else { + console.log(foo); + } + ``` + + + - [18.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) + + ```javascript + // bad + function bar( foo ) { + return foo; + } + + // good + function bar(foo) { + return foo; + } + + // bad + if ( foo ) { + console.log(foo); + } + + // good + if (foo) { + console.log(foo); + } + ``` + + + - [18.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) + + ```javascript + // bad + const foo = [ 1, 2, 3 ]; + console.log(foo[ 0 ]); + + // good + const foo = [1, 2, 3]; + console.log(foo[0]); + ``` + + + - [18.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) + + ```javascript + // bad + const foo = {clark: 'kent'}; + + // good + const foo = { clark: 'kent' }; + ``` + + + - [18.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) + + > Why? This ensures readability and maintainability. + + ```javascript + // bad + const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; + + // bad + $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); + + // good + const foo = jsonData + && jsonData.foo + && jsonData.foo.bar + && jsonData.foo.bar.baz + && jsonData.foo.bar.baz.quux + && jsonData.foo.bar.baz.quux.xyzzy; + + // good + $.ajax({ + method: 'POST', + url: 'https://airbnb.com/', + data: { name: 'John' }, + }) + .done(() => console.log('Congratulations!')) + .fail(() => console.log('You have failed this city.')); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 逗号 + + + - [19.1](#commas--leading-trailing) 行首逗号:**不需要**。 + eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) + jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) + + ```javascript + // bad + const story = [ + once + , upon + , aTime + ]; + + // good + const story = [ + once, + upon, + aTime, + ]; + + // bad + const hero = { + firstName: 'Ada' + , lastName: 'Lovelace' + , birthYear: 1815 + , superPower: 'computers' + }; + + // good + const hero = { + firstName: 'Ada', + lastName: 'Lovelace', + birthYear: 1815, + superPower: 'computers', + }; + ``` + + + - [19.2](#commas--dangling) 增加结尾的逗号: **需要**。 + eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) + jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) + + > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas)。 + + ```javascript + // bad - git diff without trailing comma + const hero = { + firstName: 'Florence', + - lastName: 'Nightingale' + + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'] + }; + + // good - git diff with trailing comma + const hero = { + firstName: 'Florence', + lastName: 'Nightingale', + + inventorOf: ['coxcomb chart', 'modern nursing'], + }; + + // bad + const hero = { + firstName: 'Dana', + lastName: 'Scully' + }; + + const heroes = [ + 'Batman', + 'Superman' + ]; + + // good + const hero = { + firstName: 'Dana', + lastName: 'Scully', + }; + + const heroes = [ + 'Batman', + 'Superman', + ]; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 分号 + + + - [20.1](#semicolons--required) **使用分号** + eslint: [`semi`](http://eslint.org/docs/rules/semi.html) + jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) + + ```javascript + // bad + (function () { + const name = 'Skywalker' + return name + })() + + // good + (function () { + const name = 'Skywalker'; + return name; + }()); + + // good (防止函数在两个 IIFE 合并时被当成一个参数) + ;(() => { + const name = 'Skywalker'; + return name; + }()); + ``` + + [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). + +**[⬆ 返回目录](#table-of-contents)** + + +## 类型转换 + + + - [21.1](#coercion--explicit) 在语句开始时执行类型转换。 + + + - [21.2](#coercion--strings) 字符串: + + ```javascript + // => this.reviewScore = 9; + + // bad + const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() + + // bad + const totalScore = this.reviewScore.toString(); // isn't guaranteed to return a string + + // good + const totalScore = String(this.reviewScore); + ``` + + + - [21.3](#coercion--numbers) 数字:对数字使用 `parseInt` 转换,并带上类型转换的基数。 + eslint: [`radix`](http://eslint.org/docs/rules/radix) + + ```javascript + const inputValue = '4'; + + // bad + const val = new Number(inputValue); + + // bad + const val = +inputValue; + + // bad + const val = inputValue >> 0; + + // bad + const val = parseInt(inputValue); + + // good + const val = Number(inputValue); + + // good + const val = parseInt(inputValue, 10); + ``` + + + - [21.4](#coercion--comment-deviations) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 + + ```javascript + // good + /** + * 使用 parseInt 导致我的程序变慢, + * 改成使用位操作转换数字快多了。 + */ + const val = inputValue >> 0; + ``` + + + - [21.5](#coercion--bitwise) **注意:** 小心使用位操作运算符。数字会被当成 [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: + + ```javascript + 2147483647 >> 0 //=> 2147483647 + 2147483648 >> 0 //=> -2147483648 + 2147483649 >> 0 //=> -2147483647 + ``` + + + - [21.6](#coercion--booleans) 布尔运算: + + ```javascript + const age = 0; + + // bad + const hasAge = new Boolean(age); + + // good + const hasAge = Boolean(age); + + // best + const hasAge = !!age; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 命名规则 + + + - [22.1](#naming--descriptive) 避免单字母命名。命名应具备描述性。 + eslint: [`id-length`](http://eslint.org/docs/rules/id-length) + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + + - [22.2](#naming--camelCase) 使用驼峰式(camelCase、小驼峰)命名变量、对象、函数和实例。 + eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) + jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) + + ```javascript + // bad + const OBJEcttsssss = {}; + const this_is_my_object = {}; + function c() {} + + // good + const thisIsMyObject = {}; + function thisIsMyFunction() {} + ``` + + + - [22.3](#naming--PascalCase) 使用帕斯卡式(PascalCase、大驼峰式)命名构造函数或类。 + eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) + jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) + + Pascal命名法:单字之间不以空格断开或连接号(-)、底线(_)连结,第一个单字首字母采用大写字母;后续单字的首字母亦用大写字母 + + ```javascript + // bad + function user(options) { + this.name = options.name; + } + + const bad = new user({ + name: 'nope', + }); + + // good + class User { + constructor(options) { + this.name = options.name; + } + } + + const good = new User({ + name: 'yup', + }); + ``` + + + - [22.4](#naming--leading-underscore) 不要使用尾随或前导下划线来命名私有属性。 + eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) + jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) + + > 为什么? JavaScript没有在属性或方法方面的隐私权的概念。虽然一个领先的下划线是一个共同的公约,意思是“私人”,事实上,这些属性是完全公开的,因此,是你的公共API合同的一部分。这个约定可能会导致开发人员错误地认为一个变化不算是中断,或者不需要测试。TL;DR:如果你想要的东西是“私人的”,它不应该可见。 + + ```javascript + // bad + this.__firstName__ = 'Panda'; + this.firstName_ = 'Panda'; + this._firstName = 'Panda'; + + // good + this.firstName = 'Panda'; + ``` + + + - [22.5](#naming--self-this) 别保存 `this` 的引用。应使用箭头函数或函数绑定 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)。 + jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) + + ```javascript + // bad + function foo() { + const self = this; + return function () { + console.log(self); + }; + } + + // bad + function foo() { + const that = this; + return function () { + console.log(that); + }; + } + + // good + function foo() { + return () => { + console.log(this); + }; + } + ``` + + + - [22.6](#naming--filename-matches-export) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 + + ```javascript + // file 1 contents + class CheckBox { + // ... + } + export default CheckBox; + + // file 2 contents + export default function fortyTwo() { return 42; } + + // file 3 contents + export default function insideDirectory() {} + + // in some other file + // bad + import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename + import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export + import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export + + // bad + import CheckBox from './check_box'; // PascalCase import/export, snake_case filename + import forty_two from './forty_two'; // snake_case import/filename, camelCase export + import inside_directory from './inside_directory'; // snake_case import, camelCase export + import index from './inside_directory/index'; // requiring the index file explicitly + import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly + + // good + import CheckBox from './CheckBox'; // PascalCase export/import/filename + import fortyTwo from './fortyTwo'; // camelCase export/import/filename + import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" + // ^ supports both insideDirectory.js and insideDirectory/index.js + ``` + + + - [22.7](#naming--camelCase-default-export) 当你导出默认的函数时使用驼峰式命名camelCase。你的文件名必须和函数名完全保持一致。 + + ```javascript + function makeStyleGuide() { + } + + export default makeStyleGuide; + ``` + + + - [22.8](#naming--PascalCase-singleton) 当你导出构造函数、类、单例、函数库、空对象时使用帕斯卡式命名PascalCase。 + + ```javascript + const AirbnbStyleGuide = { + es6: { + } + }; + + export default AirbnbStyleGuide; + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 存取器 + + + - [23.1](#accessors--not-required) 属性的存取函数不是必须的。 + + + - [23.2](#accessors--no-getters-setters) 不要使用 getters/setters 有可能引起副作用,很难测试、维护、调试。如果你需要存取函数时使用 `getVal()` 和 `setVal(value)`。 + + ```javascript + // bad + class Dragon { + get age() { + // ... + } + + set age(value) { + // ... + } + } + + // good + class Dragon { + getAge() { + // ... + } + + setAge(value) { + // ... + } + } + ``` + + + - [23.3](#accessors--boolean-prefix) 如果属性或方法是布尔值,使用 `isVal()` 或 `hasVal()` 判断。 + + ```javascript + // bad + if (!dragon.age()) { + return false; + } + + // good + if (!dragon.hasAge()) { + return false; + } + ``` + + + - [23.4](#accessors--consistent) 可以创建 `get()` 和 `set()` 函数,但要保持一致。 + + ```javascript + class Jedi { + constructor(options = {}) { + const lightsaber = options.lightsaber || 'blue'; + this.set('lightsaber', lightsaber); + } + + set(key, val) { + this[key] = val; + } + + get(key) { + return this[key]; + } + } + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## 事件 + + + - [24.1](#events--hash) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: + + ```javascript + // bad + $(this).trigger('listingUpdated', listing.id); + + ... + + $(this).on('listingUpdated', (e, listingId) => { + // do something with listingId + }); + ``` + + prefer 更好的写法: + + ```javascript + // good + $(this).trigger('listingUpdated', { listingId: listing.id }); + + ... + + $(this).on('listingUpdated', (e, data) => { + // do something with data.listingId + }); + ``` + + **[⬆ 返回目录](#table-of-contents)** + +## jQuery + + + - [25.1](#jquery--dollar-prefix) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 + jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) + + ```javascript + // bad + const sidebar = $('.sidebar'); + + // good + const $sidebar = $('.sidebar'); + + // good + const $sidebarBtn = $('.sidebar-btn'); + ``` + + + - [25.2](#jquery--cache) 缓存 jQuery 查询。 + + ```javascript + // bad + function setSidebar() { + $('.sidebar').hide(); + + // ...stuff... + + $('.sidebar').css({ + 'background-color': 'pink' + }); + } + + // good + function setSidebar() { + const $sidebar = $('.sidebar'); + $sidebar.hide(); + + // ...stuff... + + $sidebar.css({ + 'background-color': 'pink' + }); + } + ``` + + + - [25.3](#jquery--queries) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 + [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) + + + - [25.4](#jquery--find) 对有作用域的 jQuery 对象查询使用 `find`。 + + ```javascript + // bad + $('ul', '.sidebar').hide(); + + // bad + $('.sidebar').find('ul').hide(); + + // good + $('.sidebar ul').hide(); + + // good + $('.sidebar > ul').hide(); + + // good + $sidebar.find('ul').hide(); + + // good (slower) + $sidebar.find('ul'); + + // good (faster) + $($sidebar[0]).find('ul'); + ``` + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 5 兼容性 + + + - [26.1](#es5-compat--kangax) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). + +**[⬆ 返回目录](#table-of-contents)** + + +## ECMAScript 6(ES 2015+)规范 + + + - [27.1](#es6-styles) 以下是链接到 ES6 的各个特性的列表。 + +1. [Arrow Functions](#arrow-functions) +1. [Classes](#classes--constructors) +1. [Object Shorthand](#es6-object-shorthand) +1. [Object Concise](#es6-object-concise) +1. [Object Computed Properties](#es6-computed-properties) +1. [Template Strings](#es6-template-literals) +1. [Destructuring](#destructuring) +1. [Default Parameters](#es6-default-parameters) +1. [Rest](#es6-rest) +1. [Array Spreads](#es6-array-spreads) +1. [Let and Const](#references) +1. [Iterators and Generators](#iterators-and-generators) +1. [Modules](#modules) + + + - [27.2](#tc39-proposals) Do not use [TC39 proposals](https://github.com/tc39/proposals) that have not reached stage 3. + + > Why? [They are not finalized](https://tc39.github.io/process-document/), and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet. + +**[⬆ 返回目录](#table-of-contents)** + + +## 测试 + + + - [28.1](#testing--yup) **Yup.** + + ```javascript + function foo() { + return true; + } + ``` + + + - [28.2](#testing--for-real) **No, but seriously**: + + - Whichever testing framework you use, you should be writing tests! + - Strive to write many small pure functions, and minimize where mutations occur. + - Be cautious about stubs and mocks - they can make your tests more brittle. + - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. + - 100% test coverage is a good goal to strive for, even if it's not always practical to reach it. + - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future. + +**[⬆ 返回目录](#table-of-contents)** + + +## 性能 + + - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) + - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) + - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) + - [Bang Function](https://jsperf.com/bang-function) + - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) + - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) + - [Long String Concatenation](https://jsperf.com/ya-string-concat) + - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) + - Loading... + +**[⬆ 返回目录](#table-of-contents)** + + +## 资源 + +**Learning ES6** + + - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) + - [ExploringJS](http://exploringjs.com/) + - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) + - [Comprehensive Overview of ES6 Features](http://es6-features.org/) + +**Read This** + + - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) + - old [Annotated ECMAScript 5.1](http://es5.github.com/) + +**Tools** + + - Code Style Linters + + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) + + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) + + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) + +**Other Style Guides** + + - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) + - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) + +**Other Styles** + + - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen + - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen + - [Popular JavaScript Coding Conventions on GitHub](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun + - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman + +**Further Reading** + + - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll + - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer + - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz + - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban + - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock + +**Books** + + - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford + - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov + - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders + - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas + - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw + - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig + - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch + - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault + - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg + - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy + - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon + - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov + - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman + - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke + - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson + +**Blogs** + + - [JavaScript Weekly](http://javascriptweekly.com/) + - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/) + - [Bocoup Weblog](https://bocoup.com/weblog) + - [Adequately Good](http://www.adequatelygood.com/) + - [NCZOnline](https://www.nczonline.net/) + - [Perfection Kills](http://perfectionkills.com/) + - [Ben Alman](http://benalman.com/) + - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) + - [Dustin Diaz](http://dustindiaz.com/) + - [nettuts](http://code.tutsplus.com/?s=javascript) + +**播客 Podcasts** + + - [JavaScript Air](https://javascriptair.com/) + - [JavaScript Jabber](https://devchat.tv/js-jabber/) + + +**[⬆ 返回目录](#table-of-contents)** + +## 使用人群 + + This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. + + - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) + - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) + - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) + - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) + - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) + - **Ascribe**: [ascribe/javascript](https://github.com/ascribe/javascript) + - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) + - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript) + - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) + - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) + - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) + - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) + - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) + - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) + - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) + - **DoSomething**: [DoSomething/eslint-config](https://github.com/DoSomething/eslint-config) + - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript) + - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) + - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript) + - **EvozonJs**: [evozonjs/javascript](https://github.com/evozonjs/javascript) + - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) + - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) + - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) + - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) + - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) + - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) + - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) + - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) + - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) + - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) + - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) + - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md) + - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) + - **KickorStick**: [kickorstick/javascript](https://github.com/kickorstick/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) + - **Lonely Planet**: [lonelyplanet/javascript](https://github.com/lonelyplanet/javascript) + - **M2GEN**: [M2GEN/javascript](https://github.com/M2GEN/javascript) + - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) + - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) + - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) + - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) + - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) + - **Muber**: [muber/javascript](https://github.com/muber/javascript) + - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) + - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) + - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) + - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) + - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) + - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) + - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) + - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) + - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) + - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) + - **Springload**: [springload/javascript](https://github.com/springload/javascript) + - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) + - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) + - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) + - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) + - **Target**: [target/javascript](https://github.com/target/javascript) + - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) + - **The Nerdery**: [thenerdery/javascript-standards](https://github.com/thenerdery/javascript-standards) + - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) + - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript) + - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) + - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) + - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) + +**[⬆ 返回目录](#table-of-contents)** + +## 翻译 + + This style guide is also available in other languages: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) + - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) + - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) + - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) + - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) + - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javascript-style-guide](https://github.com/mitsuruog/javascript-style-guide) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) + - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [giangpii/javascript-style-guide](https://github.com/giangpii/javascript-style-guide) + + +## JavaScript 编码规范说明 + + - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + + +## 一起来讨论 JavaScript + + - Find us on [gitter](https://gitter.im/airbnb/javascript). + + +## 贡献者 Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## 许可 License + +(The MIT License) + +Copyright (c) 2014-2017 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[⬆ 返回目录](#table-of-contents)** + + +## 修正案 Amendments + +We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts. + +# }; From 6506e57fc6d1629f4349693343443ec7687a2294 Mon Sep 17 00:00:00 2001 From: jack <1395093509@qq.com> Date: Fri, 10 Feb 2017 10:32:30 +0800 Subject: [PATCH 12/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/es6-zh-cn_v1.md | 2157 -------------------------- es6_zh-cn.md => docs/es6_zh-cn_v3.md | 8 +- docs/react-jsx_v1.md | 617 ++++++++ 3 files changed, 623 insertions(+), 2159 deletions(-) delete mode 100644 docs/es6-zh-cn_v1.md rename es6_zh-cn.md => docs/es6_zh-cn_v3.md (99%) create mode 100644 docs/react-jsx_v1.md diff --git a/docs/es6-zh-cn_v1.md b/docs/es6-zh-cn_v1.md deleted file mode 100644 index 9709f373da..0000000000 --- a/docs/es6-zh-cn_v1.md +++ /dev/null @@ -1,2157 +0,0 @@ - -source: https://github.com/yuche/javascript - -[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -# Airbnb JavaScript Style Guide() { - -**用更合理的方式写 JavaScript** - -ES5 的编码规范请查看[版本一](https://github.com/sivan/javascript-style-guide/blob/master/es5/README.md),[版本二](https://github.com/adamlu/javascript-style-guide)。 - -翻译自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。 - - -## 目录 - - 1. [类型](#types) - 1. [引用](#references) - 1. [对象](#objects) - 1. [数组](#arrays) - 1. [解构](#destructuring) - 1. [字符串](#strings) - 1. [函数](#functions) - 1. [箭头函数](#arrow-functions) - 1. [构造函数](#constructors) - 1. [模块](#modules) - 1. [Iterators & Generators ](#iterators-and-generators) - 1. [属性](#properties) - 1. [变量](#variables) - 1. [提升](#hoisting) - 1. [比较运算符 & 等号](#comparison-operators--equality) - 1. [代码块](#blocks) - 1. [注释](#comments) - 1. [空白](#whitespace) - 1. [逗号](#commas) - 1. [分号](#semicolons) - 1. [类型转换](#type-casting--coercion) - 1. [命名规则](#naming-conventions) - 1. [存取器](#accessors) - 1. [事件](#events) - 1. [jQuery](#jquery) - 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) - 1. [ECMAScript 6 编码规范](#ecmascript-6-styles) - 1. [测试](#testing) - 1. [性能](#performance) - 1. [资源](#resources) - 1. [使用人群](#in-the-wild) - 1. [翻译](#translation) - 1. [JavaScript 编码规范说明](#the-javascript-style-guide-guide) - 1. [一起来讨论 JavaScript](#chat-with-us-about-javascript) - 1. [Contributors](#contributors) - 1. [License](#license) - - -## 类型 - - - [1.1](#1.1) **基本类型**: 直接存取基本类型。 - - + `字符串` - + `数值` - + `布尔类型` - + `null` - + `undefined` - - ```javascript - const foo = 1; - let bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - [1.2](#1.2) **复制类型**: 通过引用的方式存取复杂类型。 - - + `对象` - + `数组` - + `函数` - - ```javascript - const foo = [1, 2]; - const bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 引用 - - - [2.1](#2.1) 对所有的引用使用 `const` ;不要使用 `var`。 - - > 为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。 - - ```javascript - // bad - var a = 1; - var b = 2; - - // good - const a = 1; - const b = 2; - ``` - - - [2.2](#2.2) 如果你一定需要可变动的引用,使用 `let` 代替 `var`。 - - > 为什么?因为 `let` 是块级作用域,而 `var` 是函数作用域。 - - ```javascript - // bad - var count = 1; - if (true) { - count += 1; - } - - // good, use the let. - let count = 1; - if (true) { - count += 1; - } - ``` - - - [2.3](#2.3) 注意 `let` 和 `const` 都是块级作用域。 - - ```javascript - // const 和 let 只存在于它们被定义的区块内。 - { - let a = 1; - const b = 1; - } - console.log(a); // ReferenceError - console.log(b); // ReferenceError - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 对象 - - - [3.1](#3.1) 使用字面值创建对象。 - - ```javascript - // bad - const item = new Object(); - - // good - const item = {}; - ``` - - - [3.2](#3.2) 如果你的代码在浏览器环境下执行,别使用 [保留字](http://es5.github.io/#x7.6.1) 作为键值。这样的话在 IE8 不会运行。 [更多信息](https://github.com/airbnb/javascript/issues/61)。 但在 ES6 模块和服务器端中使用没有问题。 - - ```javascript - // bad - const superman = { - default: { clark: 'kent' }, - private: true, - }; - - // good - const superman = { - defaults: { clark: 'kent' }, - hidden: true, - }; - ``` - - - [3.3](#3.3) 使用同义词替换需要使用的保留字。 - - ```javascript - // bad - const superman = { - class: 'alien', - }; - - // bad - const superman = { - klass: 'alien', - }; - - // good - const superman = { - type: 'alien', - }; - ``` - - - - [3.4](#3.4) 创建有动态属性名的对象时,使用可被计算的属性名称。 - - > 为什么?因为这样可以让你在一个地方定义所有的对象属性。 - - ```javascript - function getKey(k) { - return `a key named ${k}`; - } - - // bad - const obj = { - id: 5, - name: 'San Francisco', - }; - obj[getKey('enabled')] = true; - - // good - const obj = { - id: 5, - name: 'San Francisco', - [getKey('enabled')]: true, - }; - ``` - - - - [3.5](#3.5) 使用对象方法的简写。 - - ```javascript - // bad - const atom = { - value: 1, - - addValue: function (value) { - return atom.value + value; - }, - }; - - // good - const atom = { - value: 1, - - addValue(value) { - return atom.value + value; - }, - }; - ``` - - - - [3.6](#3.6) 使用对象属性值的简写。 - - > 为什么?因为这样更短更有描述性。 - - ```javascript - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - lukeSkywalker: lukeSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - }; - ``` - - - [3.7](#3.7) 在对象属性声明前把简写的属性分组。 - - > 为什么?因为这样能清楚地看出哪些属性使用了简写。 - - ```javascript - const anakinSkywalker = 'Anakin Skywalker'; - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - episodeOne: 1, - twoJedisWalkIntoACantina: 2, - lukeSkywalker, - episodeThree: 3, - mayTheFourth: 4, - anakinSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - anakinSkywalker, - episodeOne: 1, - twoJedisWalkIntoACantina: 2, - episodeThree: 3, - mayTheFourth: 4, - }; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 数组 - - - [4.1](#4.1) 使用字面值创建数组。 - - ```javascript - // bad - const items = new Array(); - - // good - const items = []; - ``` - - - [4.2](#4.2) 向数组添加元素时使用 Arrary#push 替代直接赋值。 - - ```javascript - const someStack = []; - - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - - [4.3](#4.3) 使用拓展运算符 `...` 复制数组。 - - ```javascript - // bad - const len = items.length; - const itemsCopy = []; - let i; - - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // good - const itemsCopy = [...items]; - ``` - - [4.4](#4.4) 使用 Array#from 把一个类数组对象转换成数组。 - - ```javascript - const foo = document.querySelectorAll('.foo'); - const nodes = Array.from(foo); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 解构 - - - [5.1](#5.1) 使用解构存取和使用多属性对象。 - - > 为什么?因为解构能减少临时引用属性。 - - ```javascript - // bad - function getFullName(user) { - const firstName = user.firstName; - const lastName = user.lastName; - - return `${firstName} ${lastName}`; - } - - // good - function getFullName(obj) { - const { firstName, lastName } = obj; - return `${firstName} ${lastName}`; - } - - // best - function getFullName({ firstName, lastName }) { - return `${firstName} ${lastName}`; - } - ``` - - - [5.2](#5.2) 对数组使用解构赋值。 - - ```javascript - const arr = [1, 2, 3, 4]; - - // bad - const first = arr[0]; - const second = arr[1]; - - // good - const [first, second] = arr; - ``` - - - [5.3](#5.3) 需要回传多个值时,使用对象解构,而不是数组解构。 - > 为什么?增加属性或者改变排序不会改变调用时的位置。 - - ```javascript - // bad - function processInput(input) { - // then a miracle occurs - return [left, right, top, bottom]; - } - - // 调用时需要考虑回调数据的顺序。 - const [left, __, top] = processInput(input); - - // good - function processInput(input) { - // then a miracle occurs - return { left, right, top, bottom }; - } - - // 调用时只选择需要的数据 - const { left, right } = processInput(input); - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## Strings - - - [6.1](#6.1) 字符串使用单引号 `''` 。 - - ```javascript - // bad - const name = "Capt. Janeway"; - - // good - const name = 'Capt. Janeway'; - ``` - - - [6.2](#6.2) 字符串超过 80 个字节应该使用字符串连接号换行。 - - [6.3](#6.3) 注:过度使用字串连接符号可能会对性能造成影响。[jsPerf](http://jsperf.com/ya-string-concat) 和 [讨论](https://github.com/airbnb/javascript/issues/40). - - ```javascript - // bad - 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.'; - - // bad - 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.'; - - // good - 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.'; - ``` - - - - [6.4](#6.4) 程序化生成字符串时,使用模板字符串代替字符串连接。 - - > 为什么?模板字符串更为简洁,更具可读性。 - - ```javascript - // bad - function sayHi(name) { - return 'How are you, ' + name + '?'; - } - - // bad - function sayHi(name) { - return ['How are you, ', name, '?'].join(); - } - - // good - function sayHi(name) { - return `How are you, ${name}?`; - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 函数 - - - [7.1](#7.1) 使用函数声明代替函数表达式。 - - > 为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得[箭头函数](#arrow-functions)可以取代函数表达式。 - - ```javascript - // bad - const foo = function () { - }; - - // good - function foo() { - } - ``` - - - [7.2](#7.2) 函数表达式: - - ```javascript - // 立即调用的函数表达式 (IIFE) - (() => { - console.log('Welcome to the Internet. Please follow me.'); - })(); - ``` - - - [7.3](#7.3) 永远不要在一个非函数代码块(`if`、`while` 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 - - [7.4](#7.4) **注意:** ECMA-262 把 `block` 定义为一组语句。函数声明不是语句。[阅读 ECMA-262 关于这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - let test; - if (currentUser) { - test = () => { - console.log('Yup.'); - }; - } - ``` - - - [7.5](#7.5) 永远不要把参数命名为 `arguments`。这将取代原来函数作用域内的 `arguments` 对象。 - - ```javascript - // bad - function nope(name, options, arguments) { - // ...stuff... - } - - // good - function yup(name, options, args) { - // ...stuff... - } - ``` - - - - [7.6](#7.6) 不要使用 `arguments`。可以选择 rest 语法 `...` 替代。 - - > 为什么?使用 `...` 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 `arguments` 是一个类数组。 - - ```javascript - // bad - function concatenateAll() { - const args = Array.prototype.slice.call(arguments); - return args.join(''); - } - - // good - function concatenateAll(...args) { - return args.join(''); - } - ``` - - - - [7.7](#7.7) 直接给函数的参数指定默认值,不要使用一个变化的函数参数。 - - ```javascript - // really bad - function handleThings(opts) { - // 不!我们不应该改变函数参数。 - // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 - // 但这样的写法会造成一些 Bugs。 - //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) - opts = opts || {}; - // ... - } - - // still bad - function handleThings(opts) { - if (opts === void 0) { - opts = {}; - } - // ... - } - - // good - function handleThings(opts = {}) { - // ... - } - ``` - - - [7.8](#7.8) 直接给函数参数赋值时需要避免副作用。 - - > 为什么?因为这样的写法让人感到很困惑。 - - ```javascript - var b = 1; - // bad - function count(a = b++) { - console.log(a); - } - count(); // 1 - count(); // 2 - count(3); // 3 - count(); // 3 - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## 箭头函数 - - - [8.1](#8.1) 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 - - > 为什么?因为箭头函数创造了新的一个 `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/)),通常情况下都能满足你的需求,而且这样的写法更为简洁。 - - > 为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。 - - ```javascript - // bad - [1, 2, 3].map(function (x) { - return x * x; - }); - - // good - [1, 2, 3].map((x) => { - return x * x; - }); - ``` - - - [8.2](#8.2) 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 `return` 都省略掉。如果不是,那就不要省略。 - - > 为什么?语法糖。在链式调用中可读性很高。 - - > 为什么不?当你打算回传一个对象的时候。 - - ```javascript - // good - [1, 2, 3].map(x => x * x); - - // good - [1, 2, 3].reduce((total, n) => { - return total + n; - }, 0); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 构造器 - - - [9.1](#9.1) 总是使用 `class`。避免直接操作 `prototype` 。 - - > 为什么? 因为 `class` 语法更为简洁更易读。 - - ```javascript - // bad - function Queue(contents = []) { - this._queue = [...contents]; - } - Queue.prototype.pop = function() { - const value = this._queue[0]; - this._queue.splice(0, 1); - return value; - } - - - // good - class Queue { - constructor(contents = []) { - this._queue = [...contents]; - } - pop() { - const value = this._queue[0]; - this._queue.splice(0, 1); - return value; - } - } - ``` - - - [9.2](#9.2) 使用 `extends` 继承。 - - > 为什么?因为 `extends` 是一个内建的原型继承方法并且不会破坏 `instanceof`。 - - ```javascript - // bad - const inherits = require('inherits'); - function PeekableQueue(contents) { - Queue.apply(this, contents); - } - inherits(PeekableQueue, Queue); - PeekableQueue.prototype.peek = function() { - return this._queue[0]; - } - - // good - class PeekableQueue extends Queue { - peek() { - return this._queue[0]; - } - } - ``` - - - [9.3](#9.3) 方法可以返回 `this` 来帮助链式调用。 - - ```javascript - // bad - Jedi.prototype.jump = function() { - this.jumping = true; - return true; - }; - - Jedi.prototype.setHeight = function(height) { - this.height = height; - }; - - const luke = new Jedi(); - luke.jump(); // => true - luke.setHeight(20); // => undefined - - // good - class Jedi { - jump() { - this.jumping = true; - return this; - } - - setHeight(height) { - this.height = height; - return this; - } - } - - const luke = new Jedi(); - - luke.jump() - .setHeight(20); - ``` - - - - [9.4](#9.4) 可以写一个自定义的 `toString()` 方法,但要确保它能正常运行并且不会引起副作用。 - - ```javascript - class Jedi { - constructor(options = {}) { - this.name = options.name || 'no name'; - } - - getName() { - return this.name; - } - - toString() { - return `Jedi - ${this.getName()}`; - } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 模块 - - - [10.1](#10.1) 总是使用模组 (`import`/`export`) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。 - - > 为什么?模块就是未来,让我们开始迈向未来吧。 - - ```javascript - // bad - const AirbnbStyleGuide = require('./AirbnbStyleGuide'); - module.exports = AirbnbStyleGuide.es6; - - // ok - import AirbnbStyleGuide from './AirbnbStyleGuide'; - export default AirbnbStyleGuide.es6; - - // best - import { es6 } from './AirbnbStyleGuide'; - export default es6; - ``` - - - [10.2](#10.2) 不要使用通配符 import。 - - > 为什么?这样能确保你只有一个默认 export。 - - ```javascript - // bad - import * as AirbnbStyleGuide from './AirbnbStyleGuide'; - - // good - import AirbnbStyleGuide from './AirbnbStyleGuide'; - ``` - - - [10.3](#10.3) 不要从 import 中直接 export。 - - > 为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。 - - ```javascript - // bad - // filename es6.js - export { es6 as default } from './airbnbStyleGuide'; - - // good - // filename es6.js - import { es6 } from './AirbnbStyleGuide'; - export default es6; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## Iterators and Generators - - - [11.1](#11.1) 不要使用 iterators。使用高阶函数例如 `map()` 和 `reduce()` 替代 `for-of`。 - - > 为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。 - - ```javascript - const numbers = [1, 2, 3, 4, 5]; - - // bad - let sum = 0; - for (let num of numbers) { - sum += num; - } - - sum === 15; - - // good - let sum = 0; - numbers.forEach((num) => sum += num); - sum === 15; - - // best (use the functional force) - const sum = numbers.reduce((total, num) => total + num, 0); - sum === 15; - ``` - - - [11.2](#11.2) 现在还不要使用 generators。 - - > 为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators) - -**[⬆ 返回目录](#table-of-contents)** - - -## 属性 - - - [12.1](#12.1) 使用 `.` 来访问对象的属性。 - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - // bad - const isJedi = luke['jedi']; - - // good - const isJedi = luke.jedi; - ``` - - - [12.2](#12.2) 当通过变量访问属性时使用中括号 `[]`。 - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - function getProp(prop) { - return luke[prop]; - } - - const isJedi = getProp('jedi'); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 变量 - - - [13.1](#13.1) 一直使用 `const` 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。[地球队长](http://www.wikiwand.com/en/Captain_Planet)已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。) - - ```javascript - // bad - superPower = new SuperPower(); - - // good - const superPower = new SuperPower(); - ``` - - - [13.2](#13.2) 使用 `const` 声明每一个变量。 - - > 为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 - - ```javascript - // bad - const items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - - // bad - // (compare to above, and try to spot the mistake) - const items = getItems(), - goSportsTeam = true; - dragonball = 'z'; - - // good - const items = getItems(); - const goSportsTeam = true; - const dragonball = 'z'; - ``` - - - [13.3](#13.3) 将所有的 `const` 和 `let` 分组 - - > 为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。 - - ```javascript - // bad - let i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - let i; - const items = getItems(); - let dragonball; - const goSportsTeam = true; - let len; - - // good - const goSportsTeam = true; - const items = getItems(); - let dragonball; - let i; - let length; - ``` - - - [13.4](#13.4) 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。 - - > 为什么?`let` 和 `const` 是块级作用域而不是函数作用域。 - - ```javascript - // good - function() { - test(); - console.log('doing stuff..'); - - //..other stuff.. - - const name = getName(); - - if (name === 'test') { - return false; - } - - return name; - } - - // bad - unnecessary function call - function(hasName) { - const name = getName(); - - if (!hasName) { - return false; - } - - this.setFirstName(name); - - return true; - } - - // good - function(hasName) { - if (!hasName) { - return false; - } - - const name = getName(); - this.setFirstName(name); - - return true; - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## Hoisting - - - [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)相当重要。 - - ```javascript - // 我们知道这样运行不了 - // (假设 notDefined 不是全局变量) - function example() { - console.log(notDefined); // => throws a ReferenceError - } - - // 由于变量提升的原因, - // 在引用变量后再声明变量是可以运行的。 - // 注:变量的赋值 `true` 不会被提升。 - function example() { - console.log(declaredButNotAssigned); // => undefined - var declaredButNotAssigned = true; - } - - // 编译器会把函数声明提升到作用域的顶层, - // 这意味着我们的例子可以改写成这样: - function example() { - let declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - - // 使用 const 和 let - function example() { - console.log(declaredButNotAssigned); // => throws a ReferenceError - console.log(typeof declaredButNotAssigned); // => throws a ReferenceError - const declaredButNotAssigned = true; - } - ``` - - - [14.2](#14.2) 匿名函数表达式的变量名会被提升,但函数内容并不会。 - - ```javascript - function example() { - console.log(anonymous); // => undefined - - anonymous(); // => TypeError anonymous is not a function - - var anonymous = function() { - console.log('anonymous function expression'); - }; - } - ``` - - - [14.3](#14.3) 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。 - - ```javascript - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - superPower(); // => ReferenceError superPower is not defined - - var named = function superPower() { - console.log('Flying'); - }; - } - - // the same is true when the function name - // is the same as the variable name. - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - var named = function named() { - console.log('named'); - } - } - ``` - - - [14.4](#14.4) 函数声明的名称和函数体都会被提升。 - - ```javascript - function example() { - superPower(); // => Flying - - function superPower() { - console.log('Flying'); - } - } - ``` - - - 想了解更多信息,参考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。 - -**[⬆ 返回目录](#table-of-contents)** - - -## 比较运算符 & 等号 - - - [15.1](#15.1) 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. - - [15.2](#15.2) 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: - - + **对象** 被计算为 **true** - + **Undefined** 被计算为 **false** - + **Null** 被计算为 **false** - + **布尔值** 被计算为 **布尔的值** - + **数字** 如果是 **+0、-0、或 NaN** 被计算为 **false**, 否则为 **true** - + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** - - ```javascript - if ([0]) { - // true - // An array is an object, objects evaluate to true - } - ``` - - - [15.3](#15.3) 使用简写。 - - ```javascript - // bad - if (name !== '') { - // ...stuff... - } - - // good - if (name) { - // ...stuff... - } - - // bad - if (collection.length > 0) { - // ...stuff... - } - - // good - if (collection.length) { - // ...stuff... - } - ``` - - - [15.4](#15.4) 想了解更多信息,参考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。 - -**[⬆ 返回目录](#table-of-contents)** - - -## 代码块 - - - [16.1](#16.1) 使用大括号包裹所有的多行代码块。 - - ```javascript - // bad - if (test) - return false; - - // good - if (test) return false; - - // good - if (test) { - return false; - } - - // bad - function() { return false; } - - // good - function() { - return false; - } - ``` - - - [16.2](#16.2) 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 - - ```javascript - // bad - if (test) { - thing1(); - thing2(); - } - else { - thing3(); - } - - // good - if (test) { - thing1(); - thing2(); - } else { - thing3(); - } - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## 注释 - - - [17.1](#17.1) 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 - - ```javascript - // bad - // make() returns a new element - // based on the passed in tag name - // - // @param {String} tag - // @return {Element} element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the passed in tag name - * - * @param {String} tag - * @return {Element} element - */ - function make(tag) { - - // ...stuff... - - return element; - } - ``` - - - [17.2](#17.2) 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 - - ```javascript - // bad - const active = true; // is current tab - - // good - // is current tab - const active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - ``` - - - [17.3](#17.3) 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 - - - [17.4](#17.4) 使用 `// FIXME`: 标注问题。 - - ```javascript - class Calculator { - constructor() { - // FIXME: shouldn't use a global here - total = 0; - } - } - ``` - - - [17.5](#17.5) 使用 `// TODO`: 标注问题的解决方式。 - - ```javascript - class Calculator { - constructor() { - // TODO: total should be configurable by an options param - this.total = 0; - } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 空白 - - - [18.1](#18.1) 使用 2 个空格作为缩进。 - - ```javascript - // bad - function() { - ∙∙∙∙const name; - } - - // bad - function() { - ∙const name; - } - - // good - function() { - ∙∙const name; - } - ``` - - - [18.2](#18.2) 在花括号前放一个空格。 - - ```javascript - // bad - function test(){ - console.log('test'); - } - - // good - function test() { - console.log('test'); - } - - // bad - dog.set('attr',{ - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - - // good - dog.set('attr', { - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - ``` - - - [18.3](#18.3) 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 - - ```javascript - // bad - if(isJedi) { - fight (); - } - - // good - if (isJedi) { - fight(); - } - - // bad - function fight () { - console.log ('Swooosh!'); - } - - // good - function fight() { - console.log('Swooosh!'); - } - ``` - - - [18.4](#18.4) 使用空格把运算符隔开。 - - ```javascript - // bad - const x=y+5; - - // good - const x = y + 5; - ``` - - - [18.5](#18.5) 在文件末尾插入一个空行。 - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this); - ``` - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this);↵ - ↵ - ``` - - ```javascript - // good - (function(global) { - // ...stuff... - })(this);↵ - ``` - - - [18.5](#18.5) 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 - - ```javascript - // bad - $('#items').find('.selected').highlight().end().find('.open').updateCount(); - - // bad - $('#items'). - find('.selected'). - highlight(). - end(). - find('.open'). - updateCount(); - - // good - $('#items') - .find('.selected') - .highlight() - .end() - .find('.open') - .updateCount(); - - // bad - const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) - .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - const leds = stage.selectAll('.led') - .data(data) - .enter().append('svg:svg') - .classed('led', true) - .attr('width', (radius + margin) * 2) - .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - ``` - - - [18.6](#18.6) 在块末和新语句前插入空行。 - - ```javascript - // bad - if (foo) { - return bar; - } - return baz; - - // good - if (foo) { - return bar; - } - - return baz; - - // bad - const obj = { - foo() { - }, - bar() { - }, - }; - return obj; - - // good - const obj = { - foo() { - }, - - bar() { - }, - }; - - return obj; - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## 逗号 - - - [19.1](#19.1) 行首逗号:**不需要**。 - - ```javascript - // bad - const story = [ - once - , upon - , aTime - ]; - - // good - const story = [ - once, - upon, - aTime, - ]; - - // bad - const hero = { - firstName: 'Ada' - , lastName: 'Lovelace' - , birthYear: 1815 - , superPower: 'computers' - }; - - // good - const hero = { - firstName: 'Ada', - lastName: 'Lovelace', - birthYear: 1815, - superPower: 'computers', - }; - ``` - - - [19.2](#19.2) 增加结尾的逗号: **需要**。 - - > 为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的[尾逗号问题](es5/README.md#commas)。 - - ```javascript - // bad - git diff without trailing comma - const hero = { - firstName: 'Florence', - - lastName: 'Nightingale' - + lastName: 'Nightingale', - + inventorOf: ['coxcomb graph', 'modern nursing'] - } - - // good - git diff with trailing comma - const hero = { - firstName: 'Florence', - lastName: 'Nightingale', - + inventorOf: ['coxcomb chart', 'modern nursing'], - } - - // bad - const hero = { - firstName: 'Dana', - lastName: 'Scully' - }; - - const heroes = [ - 'Batman', - 'Superman' - ]; - - // good - const hero = { - firstName: 'Dana', - lastName: 'Scully', - }; - - const heroes = [ - 'Batman', - 'Superman', - ]; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 分号 - - - [20.1](#20.1) **使用分号** - - ```javascript - // bad - (function() { - const name = 'Skywalker' - return name - })() - - // good - (() => { - const name = 'Skywalker'; - return name; - })(); - - // good (防止函数在两个 IIFE 合并时被当成一个参数) - ;(() => { - const name = 'Skywalker'; - return name; - })(); - ``` - - [Read more](http://stackoverflow.com/a/7365214/1712802). - -**[⬆ 返回目录](#table-of-contents)** - - -## 类型转换 - - - [21.1](#21.1) 在语句开始时执行类型转换。 - - [21.2](#21.2) 字符串: - - ```javascript - // => this.reviewScore = 9; - - // bad - const totalScore = this.reviewScore + ''; - - // good - const totalScore = String(this.reviewScore); - ``` - - - [21.3](#21.3) 对数字使用 `parseInt` 转换,并带上类型转换的基数。 - - ```javascript - const inputValue = '4'; - - // bad - const val = new Number(inputValue); - - // bad - const val = +inputValue; - - // bad - const val = inputValue >> 0; - - // bad - const val = parseInt(inputValue); - - // good - const val = Number(inputValue); - - // good - const val = parseInt(inputValue, 10); - ``` - - - [21.4](#21.4) 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 - - ```javascript - // good - /** - * 使用 parseInt 导致我的程序变慢, - * 改成使用位操作转换数字快多了。 - */ - const val = inputValue >> 0; - ``` - - - [21.5](#21.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: - - ```javascript - 2147483647 >> 0 //=> 2147483647 - 2147483648 >> 0 //=> -2147483648 - 2147483649 >> 0 //=> -2147483647 - ``` - - - [21.6](#21.6) 布尔: - - ```javascript - const age = 0; - - // bad - const hasAge = new Boolean(age); - - // good - const hasAge = Boolean(age); - - // good - const hasAge = !!age; - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 命名规则 - - - [22.1](#22.1) 避免单字母命名。命名应具备描述性。 - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - [22.2](#22.2) 使用驼峰式命名对象、函数和实例。 - - ```javascript - // bad - const OBJEcttsssss = {}; - const this_is_my_object = {}; - function c() {} - - // good - const thisIsMyObject = {}; - function thisIsMyFunction() {} - ``` - - - [22.3](#22.3) 使用帕斯卡式命名构造函数或类。 - - ```javascript - // bad - function user(options) { - this.name = options.name; - } - - const bad = new user({ - name: 'nope', - }); - - // good - class User { - constructor(options) { - this.name = options.name; - } - } - - const good = new User({ - name: 'yup', - }); - ``` - - - [22.4](#22.4) 使用下划线 `_` 开头命名私有属性。 - - ```javascript - // bad - this.__firstName__ = 'Panda'; - this.firstName_ = 'Panda'; - - // good - this._firstName = 'Panda'; - ``` - - - [22.5](#22.5) 别保存 `this` 的引用。使用箭头函数或 Function#bind。 - - ```javascript - // bad - function foo() { - const self = this; - return function() { - console.log(self); - }; - } - - // bad - function foo() { - const that = this; - return function() { - console.log(that); - }; - } - - // good - function foo() { - return () => { - console.log(this); - }; - } - ``` - - - [22.6](#22.6) 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。 - - ```javascript - // file contents - class CheckBox { - // ... - } - export default CheckBox; - - // in some other file - // bad - import CheckBox from './checkBox'; - - // bad - import CheckBox from './check_box'; - - // good - import CheckBox from './CheckBox'; - ``` - - - [22.7](#22.7) 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。 - - ```javascript - function makeStyleGuide() { - } - - export default makeStyleGuide; - ``` - - - [22.8](#22.8) 当你导出单例、函数库、空对象时使用帕斯卡式命名。 - - ```javascript - const AirbnbStyleGuide = { - es6: { - } - }; - - export default AirbnbStyleGuide; - ``` - - -**[⬆ 返回目录](#table-of-contents)** - - -## 存取器 - - - [23.1](#23.1) 属性的存取函数不是必须的。 - - [23.2](#23.2) 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 - - ```javascript - // bad - dragon.age(); - - // good - dragon.getAge(); - - // bad - dragon.age(25); - - // good - dragon.setAge(25); - ``` - - - [23.3](#23.3) 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 - - ```javascript - // bad - if (!dragon.age()) { - return false; - } - - // good - if (!dragon.hasAge()) { - return false; - } - ``` - - - [23.4](#23.4) 创建 `get()` 和 `set()` 函数是可以的,但要保持一致。 - - ```javascript - class Jedi { - constructor(options = {}) { - const lightsaber = options.lightsaber || 'blue'; - this.set('lightsaber', lightsaber); - } - - set(key, val) { - this[key] = val; - } - - get(key) { - return this[key]; - } - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 事件 - - - [24.1](#24.1) 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: - - ```javascript - // bad - $(this).trigger('listingUpdated', listing.id); - - ... - - $(this).on('listingUpdated', function(e, listingId) { - // do something with listingId - }); - ``` - - 更好的写法: - - ```javascript - // good - $(this).trigger('listingUpdated', { listingId : listing.id }); - - ... - - $(this).on('listingUpdated', function(e, data) { - // do something with data.listingId - }); - ``` - - **[⬆ 返回目录](#table-of-contents)** - - -## jQuery - - - [25.1](#25.1) 使用 `$` 作为存储 jQuery 对象的变量名前缀。 - - ```javascript - // bad - const sidebar = $('.sidebar'); - - // good - const $sidebar = $('.sidebar'); - ``` - - - [25.2](#25.2) 缓存 jQuery 查询。 - - ```javascript - // bad - function setSidebar() { - $('.sidebar').hide(); - - // ...stuff... - - $('.sidebar').css({ - 'background-color': 'pink' - }); - } - - // good - function setSidebar() { - const $sidebar = $('.sidebar'); - $sidebar.hide(); - - // ...stuff... - - $sidebar.css({ - 'background-color': 'pink' - }); - } - ``` - - - [25.3](#25.3) 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) - - [25.4](#25.4) 对有作用域的 jQuery 对象查询使用 `find`。 - - ```javascript - // bad - $('ul', '.sidebar').hide(); - - // bad - $('.sidebar').find('ul').hide(); - - // good - $('.sidebar ul').hide(); - - // good - $('.sidebar > ul').hide(); - - // good - $sidebar.find('ul').hide(); - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## ECMAScript 5 兼容性 - - - [26.1](#26.1) 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容性](http://kangax.github.com/es5-compat-table/). - -**[⬆ 返回目录](#table-of-contents)** - - -## ECMAScript 6 规范 - - - [27.1](#27.1) 以下是链接到 ES6 的各个特性的列表。 - -1. [Arrow Functions](#arrow-functions) -1. [Classes](#constructors) -1. [Object Shorthand](#es6-object-shorthand) -1. [Object Concise](#es6-object-concise) -1. [Object Computed Properties](#es6-computed-properties) -1. [Template Strings](#es6-template-literals) -1. [Destructuring](#destructuring) -1. [Default Parameters](#es6-default-parameters) -1. [Rest](#es6-rest) -1. [Array Spreads](#es6-array-spreads) -1. [Let and Const](#references) -1. [Iterators and Generators](#iterators-and-generators) -1. [Modules](#modules) - -**[⬆ 返回目录](#table-of-contents)** - - -## 测试 - - - [28.1](#28.1) **Yup.** - - ```javascript - function() { - return true; - } - ``` - -**[⬆ 返回目录](#table-of-contents)** - - -## 性能 - - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](http://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](http://jsperf.com/ya-string-concat) - - Loading... - -**[⬆ 返回目录](#table-of-contents)** - - -## 资源 - -**Learning ES6** - - - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) - - [ExploringJS](http://exploringjs.com/) - - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) - - [Comprehensive Overview of ES6 Features](http://es6-features.org/) - -**Read This** - - - [Annotated ECMAScript 5.1](http://es5.github.com/) - -**Tools** - - - Code Style Linters - + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) - + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) - -**Other Styleguides** - - - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) - -**Other Styles** - - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen - - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman - -**Further Reading** - - - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban - - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock - -**Books** - - - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov - - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke - -**Blogs** - - - [DailyJS](http://dailyjs.com/) - - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) - - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) - - [Perfection Kills](http://perfectionkills.com/) - - [Ben Alman](http://benalman.com/) - - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) - -**Podcasts** - - - [JavaScript Jabber](http://devchat.tv/js-jabber/) - - -**[⬆ 返回目录](#table-of-contents)** - - -## 使用人群 - - This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. - - - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) - - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) - - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) - - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) - - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) - - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) - - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) - - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) - - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) - - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) - - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) - - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - - **Muber**: [muber/javascript](https://github.com/muber/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) - - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) - - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) - - **Target**: [target/javascript](https://github.com/target/javascript) - - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) - - **Userify**: [userify/javascript](https://github.com/userify/javascript) - - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) - - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) - - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) - -**[⬆ 返回目录](#table-of-contents)** - - -## 翻译 - - This style guide is also available in other languages: - - - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [yuche/javascript](https://github.com/yuche/javascript) - - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) - - -## JavaScript 编码规范说明 - - - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - - -## 一起来讨论 JavaScript - - - Find us on [gitter](https://gitter.im/airbnb/javascript). - -## Contributors - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## License - -(The MIT License) - -Copyright (c) 2014 Airbnb - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[⬆ 返回目录](#table-of-contents)** - -# }; diff --git a/es6_zh-cn.md b/docs/es6_zh-cn_v3.md similarity index 99% rename from es6_zh-cn.md rename to docs/es6_zh-cn_v3.md index 5ec25dd23e..6d4738da4c 100644 --- a/es6_zh-cn.md +++ b/docs/es6_zh-cn_v3.md @@ -19,8 +19,12 @@ NOTE: 本文对[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascr 该文档保留了部分还属于 ES5 范畴的注意内容,由 old: ES5 标记。 -ES5 的编码规范请查看[版本一](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v1.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v2.md),[版本三: 推荐](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v3.md)。 -ES6 的编码规范请查看下文(最新版),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es6_zh-cn_v2.md),[版本二](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es6_zh-cn_v2.md),[版本三](#table-of-contents) +相关链接: + +- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) +- [ES6/ES2015 版](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es6_zh-cn_v3.md) +- [ES5 版](https://github.com/webcoding/javascript-style-guide/tree/master/docs/es5_zh-cn_v3.md) +- [React/JSX 版](https://github.com/webcoding/javascript-style-guide/tree/master/docs/react-jsx_v1.md) ## Usage diff --git a/docs/react-jsx_v1.md b/docs/react-jsx_v1.md new file mode 100644 index 0000000000..48c2bb1553 --- /dev/null +++ b/docs/react-jsx_v1.md @@ -0,0 +1,617 @@ + +source: https://github.com/JasonBoy/javascript/tree/master/react + +# Airbnb React/JSX 编码规范 + +*算是最合理的React/JSX编码规范之一了* + +## 内容目录 + + 1. [基本规范](#basic-rules-基本规范) + 1. [Class vs React.createClass vs stateless](#创建模块) + 1. [命名](#naming-命名) + 1. [声明模块](#declaration-声明模块) + 1. [代码对齐](#alignment-代码对齐) + 1. [单引号还是双引号](#quotes-单引号还是双引号) + 1. [空格](#spacing-空格) + 1. [属性](#props-属性) + 1. [Refs引用](#refs) + 1. [括号](#parentheses-括号) + 1. [标签](#tags-标签) + 1. [函数/方法](#methods-函数) + 1. [模块生命周期](#ordering-react-模块生命周期) + 1. [isMounted](#ismounted) + +## Basic Rules 基本规范 + + - 每个文件只写一个模块. + - 但是多个[无状态模块](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions)可以放在单个文件中. eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless). + - 推荐使用JSX语法. + - 不要使用 `React.createElement`,除非从一个非JSX的文件中初始化你的app. + +## 创建模块 + Class vs React.createClass vs stateless + + - 如果你的模块有内部状态或者是`refs`, 推荐使用 `class extends React.Component` 而不是 `React.createClass` ,除非你有充足的理由来使用这些方法. + eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md) + + ```jsx + // bad + const Listing = React.createClass({ + // ... + render() { + return
    {this.state.hello}
    ; + } + }); + + // good + class Listing extends React.Component { + // ... + render() { + return
    {this.state.hello}
    ; + } + } + ``` + + 如果你的模块没有状态或是没有引用`refs`, 推荐使用普通函数(非箭头函数)而不是类: + + ```jsx + // bad + class Listing extends React.Component { + render() { + return
    {this.props.hello}
    ; + } + } + + // bad (relying on function name inference is discouraged) + const Listing = ({ hello }) => ( +
    {hello}
    + ); + + // good + function Listing({ hello }) { + return
    {hello}
    ; + } + ``` + +## Naming 命名 + + - **扩展名**: React模块使用 `.jsx` 扩展名. +  - **文件名**: 文件名使用帕斯卡命名. 如, `ReservationCard.jsx`. +  - **引用命名**: React模块名使用帕斯卡命名,实例使用骆驼式命名. eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md) + + ```jsx + // bad + import reservationCard from './ReservationCard'; + + // good + import ReservationCard from './ReservationCard'; + + // bad + const ReservationItem = ; + + // good + const reservationItem = ; + ``` + + - **模块命名**: 模块使用当前文件名一样的名称. 比如 `ReservationCard.jsx` 应该包含名为 `ReservationCard`的模块. 但是,如果整个文件夹是一个模块,使用 `index.js`作为入口文件,然后直接使用 `index.js` 或者文件夹名作为模块的名称: + + ```jsx + // bad + import Footer from './Footer/Footer'; + + // bad + import Footer from './Footer/index'; + + // good + import Footer from './Footer'; + ``` + - **高阶模块命名**: 对于生成一个新的模块,其中的模块名 `displayName` 应该为高阶模块名和传入模块名的组合. 例如, 高阶模块 `withFoo()`, 当传入一个 `Bar` 模块的时候, 生成的模块名 `displayName` 应该为 `withFoo(Bar)`. + + > 为什么?一个模块的 `displayName` 可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好的Debug. + + ```jsx + // bad + export default function withFoo(WrappedComponent) { + return function WithFoo(props) { + return ; + } + } + + // good + export default function withFoo(WrappedComponent) { + function WithFoo(props) { + return ; + } + + const wrappedComponentName = WrappedComponent.displayName + || WrappedComponent.name + || 'Component'; + + WithFoo.displayName = `withFoo(${wrappedComponentName})`; + return WithFoo; + } + ``` + + - **属性命名**: 避免使用DOM相关的属性来用作其他的用途。 + + > 为什么?对于`style` 和 `className`这样的属性名,我们都会默认它们代表一些特殊的含义,如元素的样式,CSS class的名称。在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起bug。 + + ```jsx + // bad + + + // good + + ``` + +## Declaration 声明模块 + + - 不要使用 `displayName` 来命名React模块,而是使用引用来命名模块, 如 class 名称. + + ```jsx + // bad + export default React.createClass({ + displayName: 'ReservationCard', + // stuff goes here + }); + + // good + export default class ReservationCard extends React.Component { + } + ``` + +## Alignment 代码对齐 + + - 遵循以下的JSX语法缩进/格式. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) + + ```jsx + // bad + + + // good, 有多行属性的话, 新建一行关闭标签 + + + // 若能在一行中显示, 直接写成一行 + + + // 子元素按照常规方式缩进 + + + + ``` + +## Quotes 单引号还是双引号 + + - 对于JSX属性值总是使用双引号(`"`), 其他均使用单引号(`'`). eslint: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes) + + > 为什么? HTML属性也是用双引号, 因此JSX的属性也遵循此约定. + + ```jsx + // bad + + + // good + + + // bad + + + // good + + ``` + +## Spacing 空格 + + - 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行. eslint: [`no-multi-spaces`](http://eslint.org/docs/rules/no-multi-spaces), [`react/jsx-space-before-closing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md) + + ```jsx + // bad + + + // very bad + + + // bad + + + // good + + ``` + + - 不要在JSX `{}` 引用括号里两边加空格. eslint: [`react/jsx-curly-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md) + + ```jsx + // bad + + + // good + + ``` + +## Props 属性 + + - JSX属性名使用骆驼式风格`camelCase`. + + ```jsx + // bad + + + // good + + ``` + + - 如果属性值为 `true`, 可以直接省略. eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md) + + ```jsx + // bad +