diff --git a/README.md b/README.md index 28cf734..d69a481 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# javascript-style-guide -Airbnb JavaScript Style Guide 中文版 +# Airbnb JavaScript Style Guide + +由于之前的翻译版本内容比较旧,所以重新翻译了一遍最常用的 ES5 版。 + +[《Airbnb JavaScript Style Guide 中文版》](es5/README.md) + +翻译自[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript),另有 [ES6 版](https://github.com/yuche/javascript)。 \ No newline at end of file diff --git a/es5/README.md b/es5/README.md new file mode 100644 index 0000000..3cb8b80 --- /dev/null +++ b/es5/README.md @@ -0,0 +1,1730 @@ +# 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; + ``` + + - 超过 80 个字符的字符串应该使用连接符写成多行。 + - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[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 + // 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); + }; + } + ``` + + - 给函数命名。这在做堆栈轨迹时很有帮助。 + + ```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(key, val) { + this[key] = val; + }; + + Jedi.prototype.get = function(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() { + 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(); + }; + ``` + +**[⬆ 回到顶部](#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) + - **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) + - **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) + - **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) + - **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) + +## 翻译 + + 这份风格指南也提供了其它语言的版本: + + - ![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)** + +# };