- Rick Waldron @rwaldron, github
- Mathias Bynens @mathias, github
- Schalk Neethling @ossreleasefeed, github
- Kit Cambridge @kitcambridge, github
- Raynos github
- Matias Arriola @MatiasArriola, github
- John Fischer @jfroffice, github
- Idan Gazit @idangazit, github
- Leo Balter @leobalter, github
- Breno Oliveira @garu_rj, github
- Leo Beto Souza @leobetosouza, github
- Ryuichi Okumura @okuryu, github
- Pascal Precht @PascalPrecht, github
- EngForDev engfordev - Hwan Min Hong / MinTaek Kwon @leoinsight / Tw Shim @marocchino, github / Nassol Kim @nassol99, github / Juntai Park @rkJun, github / Minkyu Shim / Gangmin Won / Justin Yoo @justinchronicle / Daeyup Lee
- Marco Trulla @marcotrulla, github
- Alex Navasardyan @alexnavasardyan, github
- Mihai Paun @mihaipaun, github
- Evgeny Mandrikov @_godin_, github
- Sofish Lin @sofish, github
- Дејан Димић @dejan_dimic, github
- Miloš Gavrilović @gavrisimo, github
- Firede @firede github
- monkadd github
- Stephan Lindauer @stephanlindauer, github
- Thomas P @dragon5689 github
- Yotam Ofek @yotamofek github
- Lijung Chi @chilijung github
Rebecca Murphey
"Part of being a good steward to a successful project is realizing that writing code for yourself is a Bad Idea™. If thousands of people are using your code, then write your code for maximum clarity, not your personal preference of how to get clever within the spec."
Idan Gazit
- German
- French
- Spanish
- Portuguese - Brazil
- Korean
- Japanese
- Italian
- Russian
- Romanian
- 简体中文
- 繁體中文
- Serbian - cyrilic alphabet
- Serbian - latin aplphabet
下列的資源: 1)並不完全, 2) 必讀。雖然我不是每次都認同下面的撰寫風格。但是他們都具有一致性。此外,這些都具有權威性。
- Baseline For Front End Developers
- Eloquent JavaScript
- JavaScript, JavaScript
- Adventures in JavaScript Development
- Perfection Kills
- Douglas Crockford's Wrrrld Wide Web
- JS Assessment
- Leveraging Code Quality Tools by Anton Kovalyov
project 應該都要有一般的程式碼檢驗,測試和壓縮準備給發佈使用。關於這個任務現在已經有由 Ben Alman 所開發的 grunt 屬於第一,已經替代了這個 repo "kits/" 目錄做為官方工具。
專案 必須 包含一些 unit, reference, implementation 或是 functional 測試。寫一些 demos 並不代表 "測試"。下面列了一系列的 frameworks, 沒有哪個 framework 比另一個還好。
- Whitespace
- Beautiful Syntax
- Type Checking (Courtesy jQuery Core Style Guidelines)
- Conditional Evaluation
- Practical Style
- Naming
- Misc
- Native & Host Objects
- Comments
- One Language Code
這個下面的章節提出了一些 合理的 風格指導給 JavaScript 開發,但這些都不是規定。最重要的是 一致的程式碼撰寫風格。不論你選擇哪種風格在你的專案中使用都需要有些原則。這個文件是用來建立一個專案一致性、易讀性、可維持性的共識
-
永遠不要把 spaces 和 tabs 混著用。
-
在開始專案前,請先選擇要用 spaces 或是用 real tabs,根據這個 規則
- 為了易讀性,我一直建議設定你的編輯器到縮排兩個字元 &mdash,這代表兩個 spaces 或是兩個空格代表一個 tab。
-
如果你的編輯器,開著 "顯示不可見字符" 這個設定的好處是:
- 一致性
- 去掉行末的空白
- 去掉空行的空格
- 提交或比對更易讀
-
Beautiful Syntax A. 小括號、大括號、換行
// if/else/for/while/try 同常有小括號、大括號和多行 // 有助於易讀性 // 2.A.1.1 // 難辨是的語法 if(condition) doSomething(); while(condition) iterating++; for(var i=0;i<100;i++) someIterativeFn(); // 2.A.1.1 // 用空白來增進易讀性 if ( condition ) { // statements } while ( condition ) { // statements } for ( var i = 0; i < 100; i++ ) { // statements } // 更好的方式: var i, length = 100; for ( i = 0; i < length; i++ ) { // statements } // 或是: var i = 0, length = 100; for ( ; i < length; i++ ) { // statements } var prop; for ( prop in object ) { // statements } if ( true ) { // statements } else { // statements }
B. 賦值、宣告、函式 ( 命名, 表達式, 建構式 )
// 2.B.1.1 // 變數 var foo = "bar", num = 1, undef; // 字元符號: var array = [], object = {}; // 2.B.1.2 // 只用一個 `var` 在一個 function 中增進易讀性。 // 並且讓你的宣告更清楚(也減少了一些 keystrokes) // 不好 var foo = ""; var bar = ""; var qux; // 好 var foo = "", bar = "", quux; // or.. var // Comment on these foo = "", bar = "", quux; // 2.B.1.3 // var 的宣告應一直在一開始的 scope。 // 同樣的應用來自於 ECMAScript 6 的變數 // 不好 function foo() { // some statements here var bar = "", qux; } // 好 function foo() { var bar = "", qux; // all statements after the variables declarations. }
// 2.B.2.1 // 命名函式宣告 function foo( arg1, argN ) { } // 用法 foo( arg1, argN ); // 2.B.2.2 // 命名函式宣告 function square( number ) { return number * number; } // 用法 square( 10 ); // Really contrived continuation passing style function square( number, callback ) { callback( number * number ); } square( 10, function( square ) { // callback statements }); // 2.B.2.3 // 函式表達式 var square = function( number ) { // 返回有值的和相關的 return number * number; }; // Function Expression with Identifier // This preferred form has the added value of being // able to call itself and have an identity in stack traces: var factorial = function factorial( number ) { if ( number < 2 ) { return 1; } return number * factorial( number - 1 ); }; // 2.B.2.4 // 函式宣告 function FooBar( options ) { this.options = options; } // 使用 var fooBar = new FooBar({ a: "alpha" }); fooBar.options; // { a: "alpha" }
C. 例外,些微的不同
// 2.C.1.1 // 函式與 callback 函式 foo(function() { // 注意第一個函式的小括號和 `function` 沒有空格 }); // 函式接受 `array` 作為一個參數,沒有空格 foo([ "alpha", "beta" ]); // 2.C.1.2 // 函式接受 `object` 作為一個參數,沒有空格 foo({ a: "alpha", b: "beta" }); // 單一一個變數,沒有空格 foo("bar"); // 分組用的小括號內部,沒有空格 if ( !("foo" in obj) ) { }
D. 一致性
在 2.A-2.C 中, 空白建議用四個,用來簡化和更高的目的:一致性。 這是非常重要的規格喜好,像是 "inner whitespace" 應該是個 optional,但是應該只有一個風格橫跨所有的企劃。
// 2.D.1.1 if (condition) { // statements } while (condition) { // statements } for (var i = 0; i < 100; i++) { // statements } if (true) { // statements } else { // statements }
E. 引號
不論你使用的是雙引號或是單引號都可以,在 JavaScript 中沒有差別。最重要的是要保持一致性。 絕對不要同時混著兩個用,選擇了一個就保持下去。
F. 行末和空行
留白會破壞區別使得更困難閱讀。考慮包含一個 pre-commit hook 來移除行末的空白和空白行。
A. 實際類型
String:
typeof variable === "string"
Number:
typeof variable === "number"
Boolean:
typeof variable === "boolean"
Object:
typeof variable === "object"
Array:
Array.isArray( arrayLikeObject ) (wherever possible)
Node:
elem.nodeType === 1
null:
variable === null
null or undefined:
variable == null
undefined:
Global Variables:
typeof variable === "undefined"
Local Variables:
variable === undefined
Properties:
object.prop === undefined object.hasOwnProperty( prop ) "prop" in object
B. 轉換類型
思考下面這些的含意
給定 HTML:
<input type="text" id="foo-input" value="1">
// 3.B.1.1 // `foo` 被賦予值 `0` 他的類別為 `number` var foo = 0; // typeof foo; // "number" ... // 後續的程式中,你要更新 `foo` // 賦予他一個新的值 foo = document.getElementById("foo-input").value; // 如果你現在測試 `typeof foo` ,結果應該是 `string` // 這代表如果你要測試 `foo` 像是: if ( foo === 1 ) { importantTask(); } // `importantTask()` 永遠不會執行, 因為 `foo` 被認定為 "1" // 3.B.1.2 // 你可以用巧妙的方式 + / - 來強制轉換類型 foo = +document.getElementById("foo-input").value; // ^ + 易完運算會把右邊的運算對象轉為 `number` // typeof foo; // "number" if ( foo === 1 ) { importantTask(); } // `importantTask()` 會被呼叫
對於強制類型轉換這裡有些例子:
// 3.B.2.1 var number = 1, string = "1", bool = false; number; // 1 number + ""; // "1" string; // "1" +string; // 1 +string++; // 1 string; // 2 bool; // false +bool; // 0 bool + ""; // "false"
// 3.B.2.2 var number = 1, string = "1", bool = true; string === number; // false string === number + ""; // true +string === number; // true bool === number; // false +bool === number; // true bool === string; // false bool === !!string; // true
// 3.B.2.3 var array = [ "a", "b", "c" ]; !!~array.indexOf("a"); // true !!~array.indexOf("b"); // true !!~array.indexOf("c"); // true !!~array.indexOf("d"); // false // 上面都是 "不必要的聰明" // 用比較明確的方案來返回值 // 像是 indexOf: if ( array.indexOf( "a" ) >= 0 ) { // ... }
// 3.B.2.4 var num = 2.5; parseInt( num, 10 ); // 同等... ~~num; num >> 0; num >>> 0; // 結果 2 // 記得負數的結果會完全不一樣... var neg = -2.5; parseInt( neg, 10 ); // 同等於... ~~neg; neg >> 0; // All result in -2 // 然而... neg >>> 0; // Will result in 4294967294
-
// 4.1.1 // 判斷一個 array 有沒有 length,相對於下面的: if ( array.length > 0 ) ... // 請使用這個 if ( array.length ) ... // 4.1.2 // 你要判斷一個 array 是否回空,相對於這個: if ( array.length === 0 ) ... // 請使用這個 if ( !array.length ) ... // 4.1.3 // 判斷一個 string 是否為空,相對於這個: if ( string !== "" ) ... // 請使用這個 if ( string ) ... // 4.1.4 // 判斷一個 sting 是空值,相對於這個 if ( string === "" ) ... // 判斷真假,請使用這個 if ( !string ) ... // 4.1.5 // 當只是判斷一個參數是真 // 與其用這個: if ( foo === true ) ... // ...判斷像你所想的,更好的是用: if ( foo ) ... // 4.1.6 // 當要判斷他為假 // 與其用這個: if ( foo === false ) ... // 用 negation 來強迫用 ture 的方式定義 if ( !foo ) ... // ...注意,這些都會符合: 0, "", null, undefined, NaN // 你 _必須_ 測試 boolean false,的時候用: if ( foo === false ) ... // 4.1.7 // 如果要確定一個參數可能為 null 或是 undefined, 但 NOT false, "" 或 0, // 與其用這個: if ( foo === null || foo === undefined ) ... // ...建議用 == type 來強制,像是: if ( foo == null ) ... // 記得使用 == 這會符合 `null` 同時在 `null` 和 `undefined` 的情形下 // 但不會在 `false`, "" or 0 null == undefined
ALWAYS evaluate for the best, most accurate result - the above is a guideline, not a dogma.
永遠 evaluate 最好是越精確、越明確越好 - 以上都是 guideline,不是教條。
// 4.2.1 // 強迫轉型和值的比較 // `===` 較 `==` 好 (除非你需要的是更寬鬆的比較) // === 不會強迫轉型,這代表 "1" === 1; // false // == 做強迫轉型,這代表: "1" == 1; // true // 4.2.2 // Booleans, Truthies & Falsies // Booleans: true, false // Truthy: "foo", 1 // Falsy: "", 0, null, undefined, NaN, void 0
-
// 5.1.1 // 一個實用的模組 (function( global ) { var Module = (function() { var data = "secret"; return { // This is some boolean property bool: true, // Some string value string: "a string", // An array property array: [ 1, 2, 3, 4 ], // An object property object: { lang: "en-Us" }, getData: function() { // get the current value of `data` return data; }, setData: function( value ) { // set the value of `data` and return it return ( data = value ); } }; })(); // Other things might happen here // expose our module to the global object global.Module = Module; })( this );
// 5.2.1 // 一個實用的建構式 (function( global ) { function Ctor( foo ) { this.foo = foo; return this; } Ctor.prototype.getFoo = function() { return this.foo; }; Ctor.prototype.setFoo = function( val ) { return ( this.foo = val ); }; // To call constructor's without `new`, you might do this: var ctor = function( foo ) { return new Ctor( foo ); }; // expose our constructor to the global object global.ctor = ctor; })( this );
-
A. 人不是 compiler/comperessor,所以不要試著變成機器。
下面的 code 是在示範過度的命名方式:
// 6.A.1.1 // Example of code with poor names function q(s) { return document.querySelectorAll(s); } var i,a=[],els=q("#foo"); for(i=0;i<els.length;i++){a.push(els[i]);}
不要懷疑,如果你現在還在寫類似這樣的程式 - 希望可以在今天停止
這裡有相同的邏輯程式,但是用比較好、和易懂的命名方式(和一個易讀的架構)
// 6.A.2.1 // Example of code with improved names function query( selector ) { return document.querySelectorAll( selector ); } var idx = 0, elements = [], matches = query("#foo"), length = matches.length; for ( ; idx < length; idx++ ) { elements.push( matches[ idx ] ); }
此外一些其他的命名:
// 6.A.3.1 // Naming strings `dog` is a string // 6.A.3.2 // Naming arrays `dogs` is an array of `dog` strings // 6.A.3.3 // 命名 functions, objects, instances, etc camelCase; function and var declarations // 6.A.3.4 // 命名 constructors, prototypes, etc. PascalCase; constructor function // 6.A.3.5 // 命名 regular expressions rDesc = //; // 6.A.3.6 // 從 Google Closure Library 程式風格指導: functionNamesLikeThis; variableNamesLikeThis; ConstructorNamesLikeThis; EnumNamesLikeThis; methodNamesLikeThis; SYMBOLIC_CONSTANTS_LIKE_THIS;
B. 面對
this
除了大家都知道的
call
和apply
外,總是優先選擇.bind( this )
或是一個功能上等於他的。 創一個BoundFunction
的宣告給予後續的調度。當沒有比較好的選擇的時候才使用別名。// 6.B.1 function Device( opts ) { this.value = null; // 開啟一個非同步的 stream, // 這會被持續的呼叫 stream.read( opts.path, function( data ) { // Update this instance's current value // with the most recent value from the // data stream this.value = data; }.bind(this) ); // Throttle the frequency of events emitted from // this Device instance setInterval(function() { // Emit a throttled event this.emit("event"); }.bind(this), opts.freq || 100 ); } // Just pretend we've inherited EventEmitter ;)
當不能運行,同樣功能的
.bind
在不同的 JavaScript 函式庫中都有出現When unavailable, functional equivalents to
.bind
exist in many modern JavaScript libraries.// 6.B.2 // eg. lodash/underscore, _.bind() function Device( opts ) { this.value = null; stream.read( opts.path, _.bind(function( data ) { this.value = data; }, this) ); setInterval(_.bind(function() { this.emit("event"); }, this), opts.freq || 100 ); } // eg. jQuery.proxy function Device( opts ) { this.value = null; stream.read( opts.path, jQuery.proxy(function( data ) { this.value = data; }, this) ); setInterval( jQuery.proxy(function() { this.emit("event"); }, this), opts.freq || 100 ); } // eg. dojo.hitch function Device( opts ) { this.value = null; stream.read( opts.path, dojo.hitch( this, function( data ) { this.value = data; }) ); setInterval( dojo.hitch( this, function() { this.emit("event"); }), opts.freq || 100 ); }
As a last resort, create an alias to
this
usingself
as an Identifier. This is extremely bug prone and should be avoided whenever possible.提供一個候選,創一個
this
的別名用self
來當做辨識。這很有可能會造成 bug ,盡可能要避免。// 6.B.3 function Device( opts ) { var self = this; this.value = null; stream.read( opts.path, function( data ) { self.value = data; }); setInterval(function() { self.emit("event"); }, opts.freq || 100 ); }
C. 使用
thisArg
好幾個 ES 5.1 prototype 的方法都內建了一個特殊的
thisArg
標記, 盡可能的避免使用他// 6.C.1 var obj; obj = { f: "foo", b: "bar", q: "qux" }; Object.keys( obj ).forEach(function( key ) { // |this| now refers to `obj` console.log( this[ key ] ); }, obj ); // <-- the last arg is `thisArg` // Prints... // "foo" // "bar" // "qux"
thisArg
可以被用在Array.prototype.every
,Array.prototype.forEach
,Array.prototype.some
,Array.prototype.map
,Array.prototype.filter
-
這個部分會說明一些想法和概念,但這些都不是教條。相反的鼓勵對現在的用法保持好奇,且嘗試完成用更好了 JavaScript 的程式任務。
A. 避免使用
switch
,modern method tracing 會把 switch 的表達是列為黑名單。目前似乎對新版本的 Firefox 和 chrome 對
switch
表達式都有改進。 http://jsperf.com/switch-vs-object-literal-vs-module值得注意的是,改進的可以在這裡看到: rwaldron#13
// 7.A.1.1 // An example switch statement switch( foo ) { case "alpha": alpha(); break; case "beta": beta(); break; default: // something to default to break; } // 7.A.1.2 // 一個支持組合和重複使用的方法是用 object 儲存 "cases" 和一個 function 來調用。 var cases, delegator; // Example returns for illustration only. cases = { alpha: function() { // statements // a return return [ "Alpha", arguments.length ]; }, beta: function() { // statements // a return return [ "Beta", arguments.length ]; }, _default: function() { // statements // a return return [ "Default", arguments.length ]; } }; delegator = function() { var args, key, delegate; // Transform arguments list into an array args = [].slice.call( arguments ); // shift the case key from the arguments key = args.shift(); // Assign the default case handler delegate = cases._default; // Derive the method to delegate operation to if ( cases.hasOwnProperty( key ) ) { delegate = cases[ key ]; } // The scope arg could be set to something specific, // in this case, |null| will suffice return delegate.apply( null, args ); }; // 7.A.1.3 // Put the API in 7.A.1.2 to work: delegator( "alpha", 1, 2, 3, 4, 5 ); // [ "Alpha", 5 ] // Of course, the `case` key argument could easily be based // on some other arbitrary condition. var caseKey, someUserInput; // Possibly some kind of form input? someUserInput = 9; if ( someUserInput > 10 ) { caseKey = "alpha"; } else { caseKey = "beta"; } // or... caseKey = someUserInput > 10 ? "alpha" : "beta"; // And then... delegator( caseKey, someUserInput ); // [ "Beta", 1 ] // And of course... delegator(); // [ "Default", 0 ]
B. 提早的 returns 提昇了程式的易讀性和些微的效能差別
// 7.B.1.1 // Bad: function returnLate( foo ) { var ret; if ( foo ) { ret = "foo"; } else { ret = "quux"; } return ret; } // Good: function returnEarly( foo ) { if ( foo ) { return "foo"; } return "quux"; }
-
最基本的原則:
為了加強這個概念,請觀看下列的演說:
<iframe src="http://blip.tv/play/g_Mngr6LegI.html" width="480" height="346" frameborder="0" allowfullscreen></iframe>http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
-
程式應該都要用同一個語言編寫,不論是哪一種語言,那個決定權決定於維護者。
所有只用這個風格指南的 project 都不允許用前置的逗號代碼格式,除非是明確的指定或作者要求。
Principles of Writing Consistent, Idiomatic JavaScript by Rick Waldron and Contributors is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at github.com/rwldrn/idiomatic.js.