本文譯自Javascript大師Douglas Crockford的文章(2016.10.27版)憔杨,以饗英文不太好的同學(xué)福青。如果英文過關(guān)旦签,建議讀原文
原文 Code Conventions for the JavaScript Programming Language
排版規(guī)則
粗體:Javascript關(guān)鍵詞拾稳、操作符等
斜體:譯者補(bǔ)充說明
部分代碼塊為譯者所加颊乘,前面注有:// 譯者加
譯文全文
本文是Javascript編程過程中用到的一系列約定和規(guī)則参淹。
對于一個組織來說,從長遠(yuǎn)來看乏悄,軟件的價值與其代碼的質(zhì)量成正比例浙值。在一個程序的整個生命中,會經(jīng)歷很多雙眼檩小、很多雙手开呐。如果一個程序能清晰的傳達(dá)其結(jié)構(gòu)和特性,那么未來修改時规求,這些結(jié)構(gòu)和特性被破壞的可能性會較小筐付。代碼約定就是為了降低程序的脆弱性(brittleness)。
JavaScript代碼都是要公開發(fā)布的阻肿,所以它總是需要具有發(fā)布質(zhì)量瓦戚。簡潔是有價值的。
JavaScript文件
應(yīng)該保存為 .js 文件丛塌。
JavaScript代碼不應(yīng)該嵌入到HTML文件中较解,除非此代碼僅為這一個HTML文件所用(譯的欠妥)。將JavaScript嵌入到HTML會增大文件姨伤,導(dǎo)致無法通過緩存哨坪、壓縮等機(jī)制減小文件。
空白(Whitespace)
下面這些規(guī)則一直以來都是文字書寫的好習(xí)慣乍楚。除非有明顯的好處当编,否則不應(yīng)該被打破。
空行(blank line)徒溪,將邏輯上有關(guān)聯(lián)的代碼放在一塊忿偷,可以提高可讀性金顿。
空格(blank space),在以下情況中都應(yīng)該使用:
- 一個關(guān)鍵詞后跟一個 ( 時鲤桥,應(yīng)該用一個空格隔開揍拆。應(yīng)用空格,可以使不是調(diào)用的地方看起來不像調(diào)用(Spaces are used to make things that are not invocations look less like invocations.)茶凳。如下例嫂拴,if 或 while 之后應(yīng)該有空格:
while (true) {
- 在function和調(diào)用( ( )之間不應(yīng)該有空格。這樣可以將關(guān)鍵詞和函數(shù)調(diào)用區(qū)分開來贮喧。
// 譯者加
some_func(a, b);
- 關(guān)鍵詞 function 后面總是要跟空格
// 譯者加
function foo() { ...
var bar = function () { ...
- 一元操作符和其操作數(shù)之間不應(yīng)該有空格筒狠,除非操作符是一個單詞,比如 typeof
// 譯者加
some_var++;
++some_var;
- 二元操作符和其操作數(shù)之間總是用一個空格隔開箱沦,這幾個例外:. ( [
// 譯者加
var c = a + b;
- 逗號 , 之后總是跟空格或換行
- 用于語句結(jié)束的分號 ; 后總是需要換行
- for 語句中的分號 ; 后總是跟一個空格
// 譯者加
for (var i = 0; i < 5; i++) {
每一個語句都應(yīng)該跟當(dāng)前縮進(jìn)對齊辩恼,最外層縮進(jìn)對齊編輯區(qū)左側(cè)。當(dāng)上一行的最后一個標(biāo)識為 { [ ( 時谓形,本行縮進(jìn)4個空格灶伊;相應(yīng)的關(guān)閉標(biāo)識 } ] ) 應(yīng)新起一行,縮進(jìn)減4個空格
// 譯者加
function foo() {
while (true) {
console.log('hello');
}
}
三元操作符看起來比較暈寒跳,所以聘萨,? 總是新起一行并縮進(jìn)4個空格,: 也總是新起一行冯袍,與 ? 對齊匈挖。條件判斷需用 ( ) 包起來:
var integer = function (
value,
default_value
) {
value = resolve(value);
return (typeof value === "number")
? Math.floor(value)
: (typeof value === "string")
? value.charCodeAt(0)
: default_value;
};
如果 . 是一行的第一個字符,縮進(jìn)4個空格康愤。
應(yīng)避免一行過于長儡循。如果一個語句在一行放不下,那就有必要將它分開征冷。最好在 { [ ( , 之后換行择膝,或者 . ? : 之前換行。如果在這地方換行不合適检激,那就在操作符之后換行肴捉,新行縮進(jìn)8個空格。但是這8個空格不改變當(dāng)前縮進(jìn)叔收。
塊(case catch default else finally)不是語句齿穗,故不需要縮進(jìn)。
Tab(制表符)和空格不應(yīng)該混用饺律。二者只選其一窃页,以避免兩者都用帶來的問題。個人偏好設(shè)置并不可靠。Tab或空格并不比對方更具優(yōu)勢脖卖。50年前乒省,Tab的優(yōu)勢在于節(jié)省內(nèi)存,但摩爾定律已經(jīng)否定了這一優(yōu)勢(此處不是很懂)畦木。空格比Tab有一個明確的優(yōu)勢:目前還沒有一個可依賴的標(biāo)準(zhǔn)規(guī)定一個Tab占幾個空格袖扛,但毫無爭議的是,一個空格確切的占一個空格十籍。所以蛆封,總是使用空格。如果必須妓雾,你可以在編輯時使用Tab娶吞,但確保在提交(commit)時轉(zhuǎn)為空格⌒狄觯或許有一天我們將最終有一個關(guān)于Tab的統(tǒng)一標(biāo)準(zhǔn),不過在那天到來之前机断,使用空格楷拳,是明智的選擇。
注釋(Comment)
應(yīng)該不吝嗇注釋吏奸。留下一些信息欢揖,有助于未來的人(包括你自己)理解你做了什么、為什么要做奋蔚。注釋應(yīng)該好好寫她混、寫清楚,要像寫代碼一樣寫注釋泊碑。一些小幽默也是鼓勵的坤按,不過困難和抱怨就不要寫了。
有一點很重要:注釋應(yīng)該保持“新鮮”(up-to-date)馒过。錯誤的注釋反而會使程序更難讀臭脓、更難懂。
注釋應(yīng)該有意義腹忽,著重說明不能從代碼里一眼就看出來的隱含意義来累。但也不要浪費時間寫下面這樣的注釋:
i = 0; // 把i設(shè)為0
變量聲明(Variable Declarations)
所有的變量都應(yīng)該在使用之前聲明。JavaScript并不強(qiáng)制如此窘奏,但這樣做可以使程序更易讀嘹锁,并且容易發(fā)現(xiàn)未聲明的變量可能是隱式的(這句譯的欠妥)。隱式的全局變量應(yīng)該杜絕着裹。盡量少用全局變量领猾。
推薦:變量的聲明語句后跟注釋。如果可能,以字母順序排列:
var currentEntry; // currently selected table entry
var level; // indentation level
var size; // size of table
JavaScript中的var沒有作用域瘤运,故窍霞,將變量聲明在塊(block)中會給有C語言經(jīng)歷的同學(xué)造成迷惑。
函數(shù)聲明(Function Declaration)
所有的function都應(yīng)該先聲明后使用拯坟。內(nèi)部函數(shù)(inner function)應(yīng)跟在var后面(即先聲明變量但金,再聲明內(nèi)部函數(shù)),這樣可以清楚知道當(dāng)前作用域(scope)有哪些變量郁季。
函數(shù)名和 ( 之間不應(yīng)該有空格冷溃;) 和 { 之間應(yīng)該有一個空格。函數(shù)體縮進(jìn)4個空格梦裂,} 應(yīng)和函數(shù)聲明的那一行開始對齊似枕。
function outer(c, d) {
var e = c * d;
var x; // no use
function inner(a, b) {
return (e * a) + b;
}
return inner(0, 1);
}
這樣的約定很適合JavaScript,因為在Javascript中函數(shù)和object定義(object literal)可以在任何可以放表達(dá)式的地方年柠≡浼撸可以最大程度的提高內(nèi)部函數(shù)和復(fù)雜結(jié)構(gòu)的可讀性。
function getElementsByClassName(className) {
var results = [];
walkTheDOM(document.body, function (node) {
var array; // array of class names
var ncn = node.className; // the node's classname
// If the node has a class name, then split it into a list of simple names.
// If any of them match the requested name, then append the node to the list of results.
if (ncn && ncn.split(" ").indexOf(className) >= 0) {
results.push(node);
}
});
return results;
}
如果是匿名函數(shù)冗恨,在關(guān)鍵詞 function 和 ( 之間應(yīng)該有一個空格答憔。如果沒有,看起來好像這個函數(shù)的名字是function掀抹,這當(dāng)然是錯誤的解讀虐拓。
div.onclick = function (e) {
return false;
};
that = {
method: function () {
return this.datum;
},
datum: 0
};
應(yīng)盡量少用全局函數(shù)
如果一個函數(shù)被即刻調(diào)用(to be invoked immediately),那么整個調(diào)用表達(dá)式應(yīng)用 ( ) 包起來傲武,以清楚地表達(dá):產(chǎn)生的值是函數(shù)執(zhí)行的結(jié)果蓉驹,而非函數(shù)本身
var collection = (function () {
var keys = [];
var values = [];
return {
get: function (key) {
var at = keys.indexOf(key);
if (at >= 0) {
return values[at];
}
},
set: function (key, value) {
var at = keys.indexOf(key);
if (at < 0) {
at = keys.length;
}
keys[at] = key;
values[at] = value;
},
remove: function (key) {
var at = keys.indexOf(key);
if (at >= 0) {
keys.splice(at, 1);
values.splice(at, 1);
}
}
};
}());
命名(Names)
名字應(yīng)該由26個字母的大小寫(A...Z, a...z),10個數(shù)字(0...9)揪利,以及下劃線:_ 組合而成态兴。避免使用國際字符,因為他們可能被誤讀土童。不要使用 $ 和 \
不要將 _ 用于名字首字母诗茎。這樣做有時是為了表示“私有”(privacy),但并不能真正的提供私有(保護(hù))献汗。如果私有很重要敢订,應(yīng)使用閉包(closure)。避免使用這種名不副實的做法罢吃。
絕大多數(shù)變量名和function名應(yīng)該以小寫字母開頭楚午。
必須用 new 調(diào)用的構(gòu)造函數(shù)(constructor function),名字應(yīng)該以大寫字母開頭尿招。如果 new 缺失矾柜,JavaScript并不視為一個編譯時(compile-time)錯誤阱驾,也不視為一個運(yùn)行時(run-time)錯誤,但卻可能有非預(yù)期的結(jié)果怪蔑。首字母大寫是我們唯一的保護(hù)機(jī)制里覆。
全局變量應(yīng)該全部用大寫字母組成。
語句(Statements)
簡單語句(Simple Statements)
一行應(yīng)最多包含一個語句缆瓣。每一個簡單語句都應(yīng)以 ; 結(jié)束喧枷。注意:函數(shù)定義(function literal)或?qū)ο蠖x(object literal)的賦值語句(assignment statement)也是賦值語句,故也應(yīng)以 ; 結(jié)束弓坞。
// 譯者加
var foo = {
name: 'x'
};
var bar = function () {
return 'bar';
};
JavaScript允許任何表達(dá)式(expression)作為語句(statement)使用隧甚。這會掩蓋一些錯誤,有 ; 時更甚渡冻∑莅猓可以作為語句的表達(dá)式僅建議:賦值、調(diào)用族吻、delete
復(fù)合語句(Compound Statements)
復(fù)合語句:由 { } 包起來的一系列語句
- 包起來的語句應(yīng)縮進(jìn)4個空格
- { 應(yīng)該在復(fù)合語句開始的那一行行尾
- } 應(yīng)該新起一行帽借,并與相應(yīng)的 { 所在行行首對齊
- { } 應(yīng)包起所有的語句,即使只有一條語句(當(dāng)它作為控制結(jié)構(gòu)(control structure)的一部分時超歌,比如 if for)宜雀,這樣可以避免后續(xù)添加語句時引入錯誤。
// 譯者加
if (true) {
console.log('true');
// do_some_th
} else {
console.log('false');
}
標(biāo)簽(Labels)
應(yīng)避免使用語句標(biāo)簽握础。只有下面語句才可應(yīng)用:while do for switch
return語句
返回的值表達(dá)式(value expression)必須與 return 關(guān)鍵詞在同一行,以避免插入 ; (后半句不很懂)
if語句
if 類的語句應(yīng)該有如下形式:
if (condition) {
statements
}
if (condition) {
statements
} else {
statements
}
if (condition) {
statements
} else if (condition) {
statements
} else {
statements
}
for語句
for 類的語句應(yīng)該有如下形式:
for (initialization; condition; update) {
statements
}
while語句
while 語句應(yīng)該有如下形式:
while (condition) {
statements
}
do語句
do 語句應(yīng)該有如下形式:
do {
statements
} while (condition);
跟其他復(fù)合語句不同的是:do 語句總是以 ; 結(jié)束
switch語句
switch 語句應(yīng)該有如下形式:
switch (expression) {
case expression:
statements
default:
statements
}
case 與 switch 對齊悴品,以避免縮進(jìn)太多禀综。因 case 不是語句,故不需要“像”語句苔严。
每一組statements(default除外)都應(yīng)該以 break / return / throw 結(jié)束定枷。不應(yīng)穿透(fall through)
continue語句
避免使用該語句。因它會使控制流(control flow)不清晰届氢。
with語句
{ } 和 [ ]
使用 { } 欠窒,而不用 new Object()。
使用 [ ] 退子,而不用 new Array()岖妄。
當(dāng)成員的名字是整數(shù)序列時,使用數(shù)組(array)寂祥。
當(dāng)成員的名字是無序字符串或名字時荐虐,使用對象(object)。
逗號操作符(, Operator)
不應(yīng)使用逗號操作符丸凭。(并不是指我們廣泛使用的逗號分隔符)
賦值表達(dá)式(Assignment Expressions)
避免在 if while 語句的條件判斷中進(jìn)行賦值操作 福扬,下面這句:
if (a = b) {
是正確的嗎腕铸?還是下面這句:
if (a == b) {
才是本意?所以铛碑,應(yīng)避免這種容易引起歧義的寫法
=== and !== 操作符
請使用 === 和 !== 操作符狠裹。== 和 != 有強(qiáng)制轉(zhuǎn)換,應(yīng)避免使用汽烦。
引起混淆的加號涛菠、減號
不要在 + 后面跟另一個 + 或 ++,因這種做法會引起混淆刹缝。而是應(yīng)該加上 **( ) ** 碗暗,以清晰表達(dá)你的意圖。
total = subtotal + +myInput.value;
最好改寫為:
total = subtotal + (+myInput.value);
如此梢夯,+ + 就不會誤讀為 ++言疗。
eval是惡魔(eval is Evil)
eval 函數(shù)是JavaScript中最被誤用的,不要用它颂砸。
eval 有別名噪奄。(不很懂)
不要使用 Function 構(gòu)造函數(shù)。(不很懂)
不要給 setTimeout setInterval 傳字符串人乓。
用心編程勤篮,你也能長滿大胡子 ;)