Airbnb 是一家位於美國舊金山的公司,本文是其內部的 JavaScript 風格指南/編碼規範,在 Github 上有 11,000+ Star,2100+ fork,前端開發者可參考。
- 基本型別:當你訪問基本型別時,你是直接對它的值進行操作。
string
number
boolean
null
undefined
123456var foo = 1,bar = foo;bar = 9;console.log(foo, bar); // => 1, 9
object
array
function
1 2 3 4 5 6 |
var foo = [1, 2], bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 |
物件
- 使用字面量語法來建立物件
-
12345// badvar item = new Object();// goodvar item = {};
- 不要使用保留字作為“鍵值”,因為在IE8不能執行。More info
-
1234567891011// badvar superman = {default: { clark: 'kent' },private: true};// goodvar superman = {defaults: { clark: 'kent' },hidden: true};
- 使用容易理解的同義詞來替代保留字
-
1234567891011121314// badvar superman = {class: 'alien'};// badvar superman = {klass: 'alien'};// goodvar superman = {type: 'alien'};
-
陣列
- 使用字面量語法來建立陣列
-
12345// badvar items = new Array();// goodvar items = [];
- 如果你不知道陣列長度,陣列新增成員使用push方法。
-
1234567var someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');
- 當你需要複製一個陣列時,使用陣列的slice方法。jsPerf
-
1234567891011var len = items.length,itemsCopy = [],i;// badfor (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
- 當你需要把“類似陣列物件”轉為陣列時,使用陣列的slice方法。
-
1234function trigger() {var args = Array.prototype.slice.call(arguments);...}
-
字串
- 字串使用單引號‘’
-
1234567891011// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
- 大於80個元素的字串需要通過分隔符進行多行操作。
- 注意:如果在長字串中過度使用分隔符會影響效能。. jsPerf & Discussion
-
12345678910111213// badvar 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.';// badvar errorMessage = 'This is a super long error that was thrown becauseof Batman. When you stop to think about how Batman had anything to dowith this, you would get nowherefast.';// goodvar 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.
-
1234567891011121314151617181920212223242526272829303132333435363738var 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;// badfunction inbox(messages) {items = '<ul>';for (i = 0; i < length; i++) {items += '<li>' + messages[i].message + '</li>';}return items + '</ul>';}// goodfunction inbox(messages) {items = [];for (i = 0; i < length; i++) {items[i] = messages[i].message;}return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
-
函式
- 函式表示式
-
1234567891011121314// anonymous function expressionvar anonymous = function() {return true;};// named function expressionvar named = function named() {return true;};// immediately-invoked function expression (IIFE)(function() {console.log('Welcome to the Internet. Please follow me.');})();
- 不要直接在非函式塊(if,while等)裡宣告一個函式,最好將函式賦值給一個變數。雖然瀏覽器允許你在非函式塊中宣告函式,但是由於不同的瀏覽器對Javascript的解析方式不同,這樣做就很可能造成麻煩。
- 注意:ECMA-262 將塊定義為一組語句,而函式宣告不是語句。Read ECMA-262′s note on this issue
-
1234567891011121314// badif (currentUser) {function test() {console.log('Nope.');}}// goodvar test;if (currentUser) {test = function test() {console.log('Yup.');};}
- 不要將引數命名為arguments,它將在每個函式的作用範圍內優先於arguments物件。
-
123456789// badfunction nope(name, options, arguments) {// ...stuff...}// goodfunction yup(name, options, args) {// ...stuff...}
-
屬性
- 使用點符號 . 來訪問屬性
-
12345678910var luke = {jedi: true,age: 28};// badvar isJedi = luke['jedi'];// goodvar isJedi = luke.jedi;
- 當你在變數中訪問屬性時,使用[ ]符號
-
12345678910var luke = {jedi: true,age: 28};function getProp(prop) {return luke[prop];}var isJedi = getProp('jedi');
-
變數
- 使用var來宣告變數,否則將宣告全域性變數,我們需要儘量避免汙染全域性名稱空間,Captain Planet這樣警告過我們。
-
12345// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
- 多個變數時只使用一個var宣告,每個變數佔一新行。
-
123456789// badvar items = getItems();var goSportsTeam = true;var dragonball = 'z';// goodvar items = getItems(),goSportsTeam = true,dragonball = 'z';
- 最後再宣告未賦值的變數,這對你之後需要依賴之前變數的變數賦值會有幫助。
-
123456789101112131415161718// badvar i, len, dragonball,items = getItems(),goSportsTeam = true;// badvar i, items = getItems(),dragonball,goSportsTeam = true,len;// goodvar items = getItems(),goSportsTeam = true,dragonball,length,i;·
- 在範圍內將變數賦值置頂,這有助於避免變數宣告和賦值提升相關的問題
-
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// badfunction() {test();console.log('doing stuff..');//..other stuff..var name = getName();if (name === 'test') {return false;}return name;}// goodfunction() {var name = getName();test();console.log('doing stuff..');//..other stuff..if (name === 'test') {return false;}return name;}// badfunction() {var name = getName();if (!arguments.length) {return false;}return true;}// goodfunction() {if (!arguments.length) {return false;}var name = getName();return true;}
-
提升
- 變數宣告在範圍內提升,但賦值並沒有提升
-
1234567891011121314151617181920212223// we know this wouldn't work (assuming there// is no notDefined global variable)function example() {console.log(notDefined); // => throws a ReferenceError}// creating a variable declaration after you// reference the variable will work due to// variable hoisting. Note: the assignment// value of `true` is not hoisted.function example() {console.log(declaredButNotAssigned); // => undefinedvar declaredButNotAssigned = true;}// The interpreter is hoisting the variable// declaration to the top of the scope.// Which means our example could be rewritten as:function example() {var declaredButNotAssigned;console.log(declaredButNotAssigned); // => undefineddeclaredButNotAssigned = true;}
- 匿名函式表示式提升變數名,但函式賦值並沒有提升,
-
123456789· function example() {console.log(anonymous); // => undefinedanonymous(); // => TypeError anonymous is not a functionvar anonymous = function() {console.log('anonymous function expression');};}
- 命名函式表示式提升變數名,但函式名字和函式體並沒有提升。
-
1234567891011121314151617181920212223function example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionsuperPower(); // => ReferenceError superPower is not definedvar 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); // => undefinednamed(); // => TypeError named is not a functionvar named = function named() {console.log('named');}}
- 函式宣告能提升他們的函式名和函式體
-
1234567function example() {superPower(); // => Flyingfunction superPower() {console.log('Flying');}}
- 更多的資訊在JavaScript Scoping & Hoisting by Ben Cherry
-
條件表示式和相等
- 條件表示式強制使用 ToBoolean方法進行解析,並遵從以下簡單的規則Object :返回 true
- Undefined: 返回 false
- Null: 返回 false
- Booleans :返回 boolean的值
- Numbers :如果是+0, -0, or NaN返回 false, 其他則 true
- Strings :空字串
''
返回 false 其他返回true -
1234if ([0]) {// true// An array is an object, objects evaluate to true}
- 使用簡易方式
-
12345678910111213141516171819// badif (name !== '') {// ...stuff...}// goodif (name) {// ...stuff...}// badif (collection.length > 0) {// ...stuff...}// goodif (collection.length) {// ...stuff...}
- 更多的資訊檢視 Truth Equality and JavaScript by Angus Croll
塊
- 在多行塊中使用大括號
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// bad if (test) return false; // good if (test) return false; // good if (test) { return false; } // bad function() { return false; } // good function() { return false; } |
註釋
- 多行註釋使用
/** ... */
,包括描述,指定型別、所有引數的值和返回值 -
123456789101112131415161718192021222324252627// bad// make() returns a new element// based on the passed in tag name//// @param <String> tag// @return <Element> elementfunction 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;}
- 單行註釋使用 //, 在註釋的內容前另起一行進行單行註釋,並在註釋前留一空行。
-
12345678910111213141516171819202122232425// badvar active = true; // is current tab// good// is current tabvar active = true;// badfunction getType() {console.log('fetching type...');// set the default type to 'no type'var type = this._type || 'no type';return type;}// goodfunction getType() {console.log('fetching type...');// set the default type to 'no type'var type = this._type || 'no type';return type;}
- 在你的註釋加上FIXME或TODO的字首可以幫助其他開發者迅速理解你指出的問題和需要的幫助,以及你建議需要完善的解決問題,這跟常規註釋不一樣因為其具有可行動性,FIXME——是需要解決的而TODO——是需要完善的
- 使用
// FIXME:
來標記問題 -
1234567function Calculator() {// FIXME: shouldn't use a global heretotal = 0;return this;}
- 使用 // TODO:給待解決問題進行標註
-
1234567function Calculator() {// TODO: total should be configurable by an options paramthis.total = 0;return this;}
-
空格
- 使用tabs設定兩個空格
-
1234567891011121314// badfunction() {∙∙∙∙var name;}// badfunction() {∙var name;}// goodfunction() {∙∙var name;}
- 分支前面空一格
-
123456789101112131415161718192021// badfunction test(){console.log('test');}// goodfunction test() {console.log('test');}// baddog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog'});// gooddog.set('attr', {age: '1 year',breed: 'Bernese Mountain Dog'});
- 操作符前後空一格
-
12345// badvar x=y+5;// goodvar x = y + 5;
- 檔案末尾用換行符結束
-
1234567891011121314// bad(function(global) {// ...stuff...})(this);// bad(function(global) {// ...stuff...})(this);// good(function(global) {// ...stuff...})(this)
- 使用長方法鏈時使用縮排
-
1234567891011121314151617181920212223242526// bad$('#items').find('.selected').highlight().end().find('.open').updateCount();// good$('#items').find('.selected').highlight().end().find('.open').updateCount();// badvar 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);// goodvar 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);
-
逗號
- 不要在句首使用逗號
-
12345678910111213141516171819202122232425// badvar once, upon, aTime;// goodvar once,upon,aTime;// badvar hero = {firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength'};// goodvar hero = {firstName: 'Bob',lastName: 'Parr',heroName: 'Mr. Incredible',superPower: 'strength'};
- 不要使用多餘的逗號,這在IE6/7 和 IE9的混雜模式中會造成問題,同樣,在ES3中有些實現,如果使用多餘的逗號將增加陣列的長度,這在ES5中有闡明(source):
-
123456789101112131415161718192021// badvar hero = {firstName: 'Kevin',lastName: 'Flynn',};var heroes = ['Batman','Superman',];// goodvar hero = {firstName: 'Kevin',lastName: 'Flynn'};var heroes = ['Batman','Superman'];
-
分號
1234567891011121314151617// bad(function() {var name = 'Skywalker'return name})()// good(function() {var name = 'Skywalker';return name;})();// good (guards against the function becoming an argument when two files with IIFEs are concatenated);(function() {var name = 'Skywalker';return name;})(); -
轉型&強制
- 在語句的開頭執行強制轉型。
- Strings:
-
12345678910111213// => this.reviewScore = 9;// badvar totalScore = this.reviewScore + '';// goodvar totalScore = '' + this.reviewScore;// badvar totalScore = '' + this.reviewScore + ' total score';// goodvar totalScore = this.reviewScore + ' total score';
- 使用
parseInt
對
Numbers
型進行強制轉型。
-
12345678910111213141516171819var inputValue = '4';// badvar val = new Number(inputValue);// badvar val = +inputValue;// badvar val = inputValue >> 0;// badvar val = parseInt(inputValue);// goodvar val = Number(inputValue);// goodvar val = parseInt(inputValue, 10);
- 如果出於某種原因你需要做些特別的事,而
parseInt
是你的瓶頸,出於效能考慮你需要使用
位移,那麼留下你的註釋來解釋原因。 -
1234567// 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;
- 注意:小心位移操作符,Numbers代表著64位的值,而位移操作符只能返回32位的整型,位移對於大於32位的整型的值會有不好的影響,32位最大的有符號整型是2,147,483,647。
- (有關討論:Discussion)
-
1232147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -2147483647
- Booleans:
-
12345678910var age = 0;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
-
命名規則
- 不要使用一個字母命名,而應該用單詞描述
-
123456789// badfunction q() {// ...stuff...}// goodfunction query() {// ..stuff..}
- 使用駝峰法來給物件、函式、例項命名。
-
1234567891011121314// badvar OBJEcttsssss = {};var this_is_my_object = {};function c() {}var u = new user({name: 'Bob Parr'});// goodvar thisIsMyObject = {};function thisIsMyFunction() {}var user = new User({name: 'Bob Parr'});
- 使用駝峰式大小寫給建構函式和類命名。
-
1234567891011121314151617// badfunction user(options) {this.name = options.name;}var bad = new user({name: 'nope'});// goodfunction User(options) {this.name = options.name;}var good = new User({name: 'yup'});
- 使用下劃線字首_來命名私有屬性。
-
123456// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';
- 儲存this的引用使用_this
-
1234567891011121314151617181920212223// badfunction() {var self = this;return function() {console.log(self);};}// badfunction() {var that = this;return function() {console.log(that);};}// goodfunction() {var _this = this;return function() {console.log(_this);};}
- 給你的函式命名,這有助於堆疊重寫
-
123456789// badvar log = function(msg) {console.log(msg);};// goodvar log = function log(msg) {console.log(msg);};
- 注意:IE8以下還有一些關於命名函式表示式的怪癖。詳情見http://kangax.github.io/nfe/
-
訪問器
- 如果你建立訪問函式使用getVal() 和 setVal(‘hello’)
-
1234567891011// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
如果這個屬性是boolean,使用isVal() 或者 hasVal()
1 2 3 4 5 6 7 8 9 |
// bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; } |
.也可以使用get() 和 set()函式來建立,但是必須一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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]; }; |
-
-
構造器
- 給原型物件新增方法,相比用新物件重寫原型,重寫原型會有繼承問題。如果你要重寫原型方法,請重寫基礎方法。
-
1234567891011121314151617181920212223function Jedi() {console.log('new jedi');}// badJedi.prototype = {fight: function fight() {console.log('fighting');},block: function block() {console.log('blocking');}};// goodJedi.prototype.fight = function fight() {console.log('fighting');};Jedi.prototype.block = function block() {console.log('blocking');};
- 方法返回this有助於方法鏈
-
1234567891011121314151617181920212223242526272829// badJedi.prototype.jump = function() {this.jumping = true;return true;};Jedi.prototype.setHeight = function(height) {this.height = height;};var luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// goodJedi.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()方法。但必須保證可以成功執行並沒有別的影響
-
123456789101112function 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();};
- 將為事件載入資料時(不管是DOM事件還是其他專用事件的,比如Backbone事件)用雜湊表來取代原始值。因為這將允許後續新增更多的資料載入事件而不用更新每個事件的處理程式。例如:
-
1 2 3 4 5 6 7 8 |
// bad $(this).trigger('listingUpdated', listing.id); ... $(this).on('listingUpdated', function(e, listingId) { // do something with listingId }); |
更好的方式:
1 2 3 4 5 6 7 8 |
// good $(this).trigger('listingUpdated', { listingId : listing.id }); ... $(this).on('listingUpdated', function(e, data) { // do something with data.listingId }); |
模組
- 模組應該以 “!”開始,以確保當模組忘記包含最後一個分號時,在指令碼連線時不會報錯。
- 文件需要用駝峰法命名,同一檔案內要用一樣的名字以及要與單個輸出相匹配。
- 增加一個叫
noConflict()
的方法,使模組輸出早期版本並返回。
- 在模組開始的部位宣告
'use strict'
。
-
123456789101112131415161718// 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);
jQuery
- JQuery物件變數字首使用$
-
12345// badvar sidebar = $('.sidebar');// goodvar $sidebar = $('.sidebar');
- jQuery快取查詢
-
12345678910111213141516171819202122// badfunction setSidebar() {$('.sidebar').hide();// ...stuff...$('.sidebar').css({'background-color': 'pink'});}// goodfunction setSidebar() {var $sidebar = $('.sidebar');$sidebar.hide();// ...stuff...$sidebar.css({'background-color': 'pink'});}
- 在DOM查詢中使用層疊樣式
$('.sidebar ul')
或
parent > child$('.sidebar > ul')
. jsPerf
- 使用find來查詢jQuery物件
-
1234567891011121314// bad$('ul', '.sidebar').hide();// bad$('.sidebar').find('ul').hide();// good$('.sidebar ul').hide();// good$('.sidebar > ul').hide();// good$sidebar.find('ul').hide();