這是一份旨在加強團隊的開發協做,提升代碼質量和打造開發基石的編碼風格規範,其中包含了 HTML, JavaScript 和 CSS/SCSS 這幾個部分。咱們知道,當一個團隊開始指定並實行編碼規範的話,錯誤就會變得更加顯而易見。若是一段特定的代碼不符合規範的話,它有可能只是代碼風格錯誤,而也有可能會是 bug。早期指定規範就使得代碼審覈得以更好的開展,而且能夠更精確的地定位到錯誤。只要開發者們可以保證源代碼源文件都嚴格遵循規範,那接下去所使用的混淆、壓縮和編譯工具則可投其所好不盡相同。javascript
如下章節列舉了一些可應用在 HTML, JavaScript 和 CSS/SCSS 上的通用規則。css
在 web 項目中,全部的文件名應該都遵循同一命名約定。以可讀性而言,減號(-)是用來分隔文件名的不二之選。同時它也是常見的 URL 分隔符(i.e. //example.com/blog/my-blog-entry
or //s.example.com/images/big-black-background.jpg
),因此理所固然的,減號應該也是用來分隔資源名稱的好選擇。html
請確保文件命名老是以字母開頭而不是數字。而以特殊字符開頭命名的文件,通常都有特殊的含義與用處(好比 compass[1] 中的下劃線就是用來標記跳過直接編譯的文件用的)。前端
資源的字母名稱必須全爲小寫,這是由於在某些對大小寫字母敏感的操做系統中,當文件經過工具壓縮混淆後,或者人爲修改事後,大小寫不一樣而致使引用文件不一樣的錯誤,很難被發現。java
還有一些狀況下,須要對文件增長先後綴或特定的擴展名(好比 .min.js, .min.css),抑或一串前綴(好比 3fa89b.main.min.css
)。這種狀況下,建議使用點分隔符來區分這些在文件名中帶有清晰意義的元數據。
git
不推薦github
MyScript.js
myCamelCaseName.css
i_love_underscores.html
1001-scripts.js
my-file-min.css
推薦web
my-script.js
my-camel-case-name.css
i-love-underscores.html
thousand-and-one-scripts.js
my-file.min.css
不要指定引入資源所帶的具體協議。編程
當引入圖片或其餘媒體文件,還有樣式和腳本時,URLs 所指向的具體路徑,不要指定協議部分(http:
, https:
),除非這二者協議都不可用。canvas
不指定協議使得 URL 從絕對的獲取路徑轉變爲相對的,在請求資源協議沒法肯定時很是好用,並且還能爲文件大小節省幾個字節。
不推薦
<script src="http://cdn.com/foundation.min.js"></script>
推薦
<script src="//cdn.com/foundation.min.js"></script>
不推薦
.example {
background: url(http://static.example.com/images/bg.jpg);
}
推薦
example {
background: url(//static.example.com/images/bg.jpg);
}
一次縮進兩個空格。
<ul> <li>Fantastic</li> <li>Great</li> <li> <a href="#">Test</a> </li> </ul>
@media screen and (min-width: 1100px) {
body {
font-size: 100%;
}
}
(function(){ var x = 10; function y(a, b) { return { result: (a + b) * x } } }());
註釋是你本身與你的小夥伴們瞭解代碼寫法和目的的惟一途徑。特別是在寫一些看似瑣碎的可有可無的代碼時,因爲記憶點不深入,註釋就變得尤其重要了。
編寫自解釋代碼只是一個傳說,沒有任何代碼是能夠徹底自解釋的。而代碼註釋,則是永遠也不嫌多。
當你寫註釋時必定要注意:不要寫你的代碼都幹了些什麼,而要寫你的代碼爲何要這麼寫,背後的考量是什麼。固然也能夠加入所思考問題或是解決方案的連接地址。
不推薦
var offset = 0; if(includeLabels) { // Add offset of 20 offset = 20; }
推薦
var offset = 0; if(includeLabels) { // If the labels are included we need to have a minimum offset of 20 pixels // We need to set it explicitly because of the following bug: http://somebrowservendor.com/issue-tracker/ISSUE-1 offset = 20; }
推薦使用 HTML5 的文檔類型申明: <!DOCTYPE html>
.
(建議使用 text/html 格式的 HTML。避免使用 XHTML。XHTML 以及它的屬性,好比 application/xhtml+xml
在瀏覽器中的應用支持與優化空間都十分有限)。
HTML 中最好不要將無內容元素[1] 的標籤閉合,例如:使用 <br>
而非 <br />
.
通常狀況下,建議使用能經過標準規範驗證的 HTML 代碼,除非在性能優化和控制文件大小上不得不作出讓步。
使用諸如 W3C HTML validator 這樣的工具來進行檢測。
規範化的 HTML 是顯現技術要求與侷限的顯著質量基線,它促進了 HTML 被更好地運用。
不推薦
<title>Test</title> <article>This is only a test.
推薦
<!DOCTYPE html> <meta charset="utf-8"> <title>Test</title> <article>This is only a test.</article>
HTML5 規範中規定了 HTML 標籤是能夠省略的。但從可讀性來講,在開發的源文件中最好不要這樣作,由於省略標籤可能會致使一些問題。
省略一些可選的標籤確實使得頁面大小減小,這頗有用,尤爲是對於一些大型網站來講。爲了達到這一目的,咱們能夠在開發後期對頁面進行壓縮處理,在這個環節中這些可選的標籤徹底就能夠省略掉了。
出於性能考慮,腳本異步加載很關鍵。一段腳本放置在 <head>
內,好比 <script src="main.js"></script>
,其加載會一直阻塞 DOM 解析,直至它徹底地加載和執行完畢。這會形成頁面顯示的延遲。特別是一些重量級的腳本,對用戶體驗來講那真是一個巨大的影響。
異步加載腳本可緩解這種性能影響。若是隻需兼容 IE10+,可將 HTML5 的 async 屬性加至腳本中,它可防止阻塞 DOM 的解析,甚至你能夠將腳本引用寫在 <head>
裏也沒有影響。
如需兼容老舊的瀏覽器,實踐代表可以使用用來動態注入腳本的腳本加載器。你能夠考慮 yepnope 或 labjs。注入腳本的一個問題是:一直要等到 CSS 對象文檔已就緒,它們纔開始加載(短暫地在 CSS 加載完畢以後),這就對須要及時觸發的 JS 形成了必定的延遲,這多多少少也影響了用戶體驗吧。
終上所述,兼容老舊瀏覽器(IE9-)時,應該遵循如下最佳實踐。
腳本引用寫在 body 結束標籤以前,並帶上 async 屬性。這雖然在老舊瀏覽器中不會異步加載腳本,但它只阻塞了 body 結束標籤以前的 DOM 解析,這就大大下降了其阻塞影響。而在現代瀏覽器中,腳本將在 DOM 解析器發現 body 尾部的 script 標籤才進行加載,此時加載屬於異步加載,不會阻塞 CSSOM(但其執行仍發生在 CSSOM 以後)。
全部瀏覽器中,推薦
<html> <head> <link rel="stylesheet" href="main.css"> </head> <body> <!-- body goes here --> <script src="main.js" async></script> </body> </html>
只在現代瀏覽器中,推薦
<html> <head> <link rel="stylesheet" href="main.css"> <script src="main.js" async></script> </head> <body> <!-- body goes here --> </body> </html>
根據元素(有時被錯誤地稱做「標籤」)其被創造出來時的初始意義來使用它。打個比方,用 heading 元素來定義頭部標題,p 元素來定義文字段落,用 a 元素來定義連接錨點,等等。
有根據有目的地使用 HTML 元素,對於可訪問性、代碼重用、代碼效率來講意義重大。
如下示例列出了一些的語義化 HTML 主要狀況:
不推薦
<b>My page title</b> <div class="top-navigation"> <div class="nav-item"><a href="#home">Home</a></div> <div class="nav-item"><a href="#news">News</a></div> <div class="nav-item"><a href="#about">About</a></div> </div> <div class="news-page"> <div class="page-section news"> <div class="title">All news articles</div> <div class="news-article"> <h2>Bad article</h2> <div class="intro">Introduction sub-title</div> <div class="content">This is a very bad example for HTML semantics</div> <div class="article-side-notes">I think I'm more on the side and should not receive the main credits</div> <div class="article-foot-notes"> This article was created by David <div class="time">2014-01-01 00:00</div> </div> </div> <div class="section-footer"> Related sections: Events, Public holidays </div> </div> </div> <div class="page-footer"> Copyright 2014 </div>
推薦
<!-- The page header should go into a header element --> <header> <!-- As this title belongs to the page structure it's a heading and h1 should be used --> <h1>My page title</h1> </header> <!-- All navigation should go into a nav element --> <nav class="top-navigation"> <!-- A listing of elements should always go to UL (OL for ordered listings) --> <ul> <li class="nav-item"><a href="#home">Home</a></li> <li class="nav-item"><a href="#news">News</a></li> <li class="nav-item"><a href="#about">About</a></li> </ul> </nav> <!-- The main part of the page should go into a main element (also use role="main" for accessibility) --> <main class="news-page" role="main"> <!-- A section of a page should go into a section element. Divide a page into sections with semantic elements. --> <section class="page-section news"> <!-- A section header should go into a section element --> <header> <!-- As a page section belongs to the page structure heading elements should be used (in this case h2) --> <h2 class="title">All news articles</h2> </header> <!-- If a section / module can be seen as an article (news article, blog entry, products teaser, any other re-usable module / section that can occur multiple times on a page) a article element should be used --> <article class="news-article"> <!-- An article can contain a header that contains the summary / introduction information of the article --> <header> <!-- As a article title does not belong to the overall page structure there should not be any heading tag! --> <div class="article-title">Good article</div> <!-- Small can optionally be used to reduce importance --> <small class="intro">Introduction sub-title</small> </header> <!-- For the main content in a section or article there is no semantic element --> <div class="content"> <p>This is a good example for HTML semantics</p> </div> <!-- For content that is represented as side note or less important information in a given context use aside --> <aside class="article-side-notes"> <p>I think I'm more on the side and should not receive the main credits</p> </aside> <!-- Articles can also contain footers. If you have footnotes for an article place them into a footer element --> <footer class="article-foot-notes"> <!-- The time element can be used to annotate a timestamp. Use the datetime attribute to specify ISO time while the actual text in the time element can also be more human readable / relative --> <p>This article was created by David <time datetime="2014-01-01 00:00" class="time">1 month ago</time></p> </footer> </article> <!-- In a section, footnotes or similar information can also go into a footer element --> <footer class="section-footer"> <p>Related sections: Events, Public holidays</p> </footer> </section> </main> <!-- Your page footer should go into a global footer element --> <footer class="page-footer"> Copyright 2014 </footer>
對頁面上的媒體而言,像圖片、視頻、canvas 動畫等,要確保其有可替代的接入接口。圖片文件咱們可採用有意義的備選文本(alt),視頻和音頻文件咱們能夠爲其加上說明文字或字幕。
提供可替代內容對可用性來講十分重要。試想,一位盲人用戶如何能知曉一張圖片是什麼,要是沒有 @alt 的話。
(圖片的 alt 屬性是可不填寫內容的,純裝飾性的圖片就可用這麼作:alt=""
)。
不推薦
<img src="luke-skywalker.jpg">
推薦
<img src="luke-skywalker.jpg" alt="Luke skywalker riding an alien horse">
儘可能用 alt 標籤去描述圖片,設想你須要對於那些只能經過語音或者看不見圖片的用戶表達圖片究竟是什麼。
不推薦
<img src="huge-spaceship-approaching-earth.jpg" alt="Header image">
推薦
<img src="huge-spaceship-approaching-earth.jpg" alt="A huge spaceship that is approaching the earth">
理解 web 中如何和爲什麼區分不一樣的關注點,這很重要。這裏的關注點主要指的是:信息(HTML 結構)、外觀(CSS)和行爲(JavaScript)。爲了使它們成爲可維護的乾淨整潔的代碼,咱們要儘量的將它們分離開來。
嚴格地保證結構、表現、行爲三者分離,並儘可能使三者之間沒有太多的交互和聯繫。
就是說,儘可能在文檔和模板中只包含結構性的 HTML;而將全部表現代碼,移入樣式表中;將全部動做行爲,移入腳本之中。
在此以外,爲使得它們之間的聯繫儘量的小,在文檔和模板中也儘可能少地引入樣式和腳本文件。
清晰的分層意味着:
<style>.no-good {}</style>
)<hr style="border-top: 5px solid black">
)<script>alert('no good')</script>
)<b>
, <u>
, <center>
, <font>
, <b>
)不推薦
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="base.css"> <link rel="stylesheet" href="grid.css"> <link rel="stylesheet" href="type.css"> <link rel="stylesheet" href="modules/teaser.css"> </head> <body> <h1 style="font-size: 3rem"></h1> <b>I'm a subtitle and I'm bold!</b> <center>Dare you center me!</center> <script> alert('Just dont...'); </script> <div class="red">I'm important!</div> </body> </html>
推薦
<!DOCTYPE html> <html> <head> <!-- Concatinate your style sheets into a single one --> <link rel="stylesheet" href="main.css"> </head> <body> <!-- Don't use style attributes but assign sensible classes and apply styles in the stylesheet --> <h1 class="title"></h1> <!-- Don't use presentational elements and assign sensible classes --> <div class="sub-title">I'm a subtitle and I'm bold!</div> <!-- Maybe your comments get centered in your presentation but that decision is up to the stylesheet --> <span class="comment">Dare you center me!</span> <!-- You wanted to make it red because it's important so then also name the class important and decide in the stylesheet what you want to do with it --> <div class="important">I'm important!</div> <!-- Put all your scripts into files and concatinate them into a single one --> <script async src="main.js"></script> </body> </html>
不要讓非內容信息污染了你的 HTML。如今貌似有一種傾向:經過 HTML 來解決設計問題,這是顯然是不對的。HTML 就應該只關注內容。
HTML 標籤的目的,就是爲了避免斷地展現內容信息。
img
元素當作專門用來作視覺設計的元素如下例子展現了誤將 HTML 用來解決設計問題的這兩種狀況:
不推薦
<!-- We should not introduce an additional element just to solve a design problem --> <span class="text-box"> <span class="square"></span> See the square next to me? </span>
.text-box > .square { display: inline-block; width: 1rem; height: 1rem; background-color: red; }
推薦
<!-- That's clean markup! --> <span class="text-box"> See the square next to me? </span>
/* We use a :before pseudo element to solve the design problem of placing a colored square in front of the text content */ .text-box:before { content: ""; display: inline-block; width: 1rem; height: 1rem; background-color: red; }
圖片和 SVG 圖形能被引入到 HTML 中的惟一理由是它們呈現出了與內容相關的一些信息。
不推薦
<!-- Content images should never be used for design elements! --> <span class="text-box"> <img src="square.svg" alt="Square" /> See the square next to me? </span>
推薦
<!-- That's clean markup! --> <span class="text-box"> See the square next to me? </span>
/* We use a :before pseudo element with a background image to solve the problem */ .text-box:before { content: ""; display: inline-block; width: 1rem; height: 1rem; background: url(square.svg) no-repeat; background-size: 100%; }
省略樣式表與腳本上的 type 屬性。鑑於 HTML5 中以上二者默認的 type 值就是 text/css 和 text/javascript,因此 type 屬性通常是能夠忽略掉的。甚至在老舊版本的瀏覽器中這麼作也是安全可靠的。
不推薦
<link rel="stylesheet" href="main.css" type="text/css"> <script src="main.js" type="text/javascript"></script>
推薦
<link rel="stylesheet" href="main.css"> <script src="main.js"></script>
使用雙引號(「」) 而不是單引號(」) 。
不推薦
<div class='news-article'></div>
推薦
<div class="news-article"></div>
老是將代碼包裹成一個 IIFE(Immediately-Invoked Function Expression),用以建立獨立隔絕的定義域。這一舉措可防止全局命名空間被污染。
IIFE 還可確保你的代碼不會輕易被其它全局命名空間裏的代碼所修改(i.e. 第三方庫,window 引用,被覆蓋的未定義的關鍵字等等)。
不推薦
var x = 10, y = 100; // Declaring variables in the global scope is resulting in global scope pollution. All variables declared like this // will be stored in the window object. This is very unclean and needs to be avoided. console.log(window.x + ' ' + window.y);
推薦
// We declare a IIFE and pass parameters into the function that we will use from the global space (function(log, w, undefined){ 'use strict'; var x = 10, y = 100; // Will output 'true true' log((w.x === undefined) + ' ' + (w.y === undefined)); }(window.console.log, window));
不管什麼時候,想要建立一個新的封閉的定義域,那就用 IIFE。它不只避免了干擾,也使得內存在執行完後當即釋放。
全部腳本文件建議都從 IIFE 開始。
當即執行的函數表達式的執行括號應該寫在外包括號內。雖然寫在內仍是寫在外都是有效的,但寫在內使得整個表達式看起來更像一個總體,所以推薦這麼作。
不推薦
(function(){})();
推薦
(function(){}());
so,用下列寫法來格式化你的 IIFE 代碼:
(function(){ 'use strict'; // Code goes here }());
若是你想引用全局變量或者是外層 IIFE 的變量,能夠經過下列方式傳參:
(function($, w, d){ 'use strict'; $(function() { w.alert(d.querySelectorAll('div').length); }); }(jQuery, window, document));
ECMAScript 5 嚴格模式可在整個腳本或獨個方法內被激活。它對應不一樣的 javascript 語境會作更加嚴格的錯誤檢查。嚴格模式也確保了 javascript 代碼更加的健壯,運行的也更加快速。
嚴格模式會阻止使用在將來極可能被引入的預留關鍵字。
你應該在你的腳本中啓用嚴格模式,最好是在獨立的 IIFE 中應用它。避免在你的腳本第一行使用它而致使你的全部腳本都啓動了嚴格模式,這有可能會引起一些第三方類庫的問題。
不推薦
// Script starts here 'use strict'; (function(){ // Your code starts here }());
推薦
(function(){ 'use strict'; // Your code starts here }());
老是使用 var
來聲明變量。如不指定 var,變量將被隱式地聲明爲全局變量,這將對變量難以控制。若是沒有聲明,變量處於什麼定義域就變得不清(能夠是在 Document 或 Window 中,也能夠很容易地進入本地定義域)。因此,請老是使用 var 來聲明變量。
採用嚴格模式帶來的好處是,當你手誤輸入錯誤的變量名時,它能夠經過報錯信息來幫助你定位錯誤出處。
不推薦
x = 10;
y = 100;
推薦
var x = 10, y = 100;
在 JavaScript 中變量和方法定義會自動提高到執行以前。JavaScript 只有 function 級的定義域,而無其餘不少編程語言中的塊定義域,因此使得你在某一 function 內的某語句和循環體中定義了一個變量,此變量可做用於整個 function 內,而不只僅是在此語句或循環體中,由於它們的聲明被 JavaScript 自動提高了。
咱們經過例子來看清楚這究竟是怎麼一回事:
原 function
(function(log){ 'use strict'; var a = 10; for(var i = 0; i < a; i++) { var b = i * i; log(b); } if(a === 10) { var f = function() { log(a); }; f(); } function x() { log('Mr. X!'); } x(); }(window.console.log));
被 JS 提高事後
(function(log){ 'use strict'; // All variables used in the closure will be hoisted to the top of the function var a, i, b, f; // All functions in the closure will be hoisted to the top function x() { log('Mr. X!'); } a = 10; for(i = 0; i < a; i++) { b = i * i; log(b); } if(a === 10) { // Function assignments will only result in hoisted variables but the function body will not be hoisted // Only by using a real function declaration the whole function will be hoisted with its body f = function() { log(a); }; f(); } x(); }(window.console.log));
根據以上提高過程,你是否可理解如下代碼?
有效代碼
(function(log){ 'use strict'; var a = 10; i = 5; x(); for(var i; i < a; i++) { log(b); var b = i * i; } if(a === 10) { f = function() { log(a); }; f(); var f; } function x() { log('Mr. X!'); } }(window.console.log));
正如你所看到的這段使人充滿困惑與誤解的代碼致使了出人意料的結果。只有良好的聲明習慣,也就是下一章節咱們要提到的聲明規則,才能儘量的避免這類錯誤風險。
提高聲明
爲避免上一章節所述的變量和方法定義被自動提高形成誤解,把風險降到最低,咱們應該手動地顯示地去聲明變量與方法。也就是說,全部的變量以及方法,應當定義在 function 內的首行。
只用一個 var
關鍵字聲明,多個變量用逗號隔開。
不推薦
(function(log){ 'use strict'; var a = 10; var b = 10; for(var i = 0; i < 10; i++) { var c = a * b * i; } function f() { } var d = 100; var x = function() { return d * d; }; log(x()); }(window.console.log));
推薦
(function(log){ 'use strict'; var a = 10, b = 10, i, c, d, x; function f() { } for(i = 0; i < 10; i++) { c = a * b * i; } d = 100; x = function() { return d * d; }; log(x()); }(window.console.log));
把賦值儘可能寫在變量申明中。
不推薦
var a, b, c; a = 10; b = 10; c = 100;
推薦
var a = 10, b = 10, c = 100;
老是使用 ===
精確的比較操做符,避免在判斷的過程當中,由 JavaScript 的強制類型轉換所形成的困擾。
若是你使用 ===
操做符,那比較的雙方必須是同一類型爲前提的條件下才會有效。
在只使用 ==
的狀況下,JavaScript 所帶來的強制類型轉換使得判斷結果跟蹤變得複雜,下面的例子能夠看出這樣的結果有多怪了:
(function(log){ 'use strict'; log('0' == 0); // true log('' == false); // true log('1' == true); // true log(null == undefined); // true var x = { valueOf: function() { return 'X'; } }; log(x == 'X'); }(window.console.log));
當咱們在一個 if 條件語句中使用變量或表達式時,會作真假判斷。if(a == true)
是不一樣於 if(a)
的。後者的判斷比較特殊,咱們稱其爲真假判斷。這種判斷會經過特殊的操做將其轉換爲 true 或 false,下列表達式通通返回 false:false
, 0
, undefined
, null
, NaN
, ''
(空字符串).
這種真假判斷在咱們只求結果而不關心過程的狀況下,很是的有幫助。
如下示例展現了真假判斷是如何工做的:
(function(log){ 'use strict'; function logTruthyFalsy(expr) { if(expr) { log('truthy'); } else { log('falsy'); } } logTruthyFalsy(true); // truthy logTruthyFalsy(1); // truthy logTruthyFalsy({}); // truthy logTruthyFalsy([]); // truthy logTruthyFalsy('0'); // truthy logTruthyFalsy(false); // falsy logTruthyFalsy(0); // falsy logTruthyFalsy(undefined); // falsy logTruthyFalsy(null); // falsy logTruthyFalsy(NaN); // falsy logTruthyFalsy(''); // falsy }(window.console.log));
邏輯操做符 ||
和 &&
也可被用來返回布爾值。若是操做對象爲非布爾對象,那每一個表達式將會被自左向右地作真假判斷。基於此操做,最終總有一個表達式被返回回來。這在變量賦值時,是能夠用來簡化你的代碼的。
不推薦
if(!x) { if(!y) { x = 1; } else { x = y; } }
推薦
x = x || y || 1;
這一小技巧常常用來給方法設定默認的參數。
(function(log){ 'use strict'; function multiply(a, b) { a = a || 1; b = b || 1; log('Result ' + a * b); } multiply(); // Result 1 multiply(10); // Result 10 multiply(3, NaN); // Result 3 multiply(9, 5); // Result 45 }(window.console.log));
老是使用分號,由於隱式的代碼嵌套會引起難以察覺的問題。固然咱們更要從根本上來杜絕這些問題[1] 。如下幾個示例展現了缺乏分號的危害:
// 1. MyClass.prototype.myMethod = function() { return 42; } // No semicolon here. (function() { // Some initialization code wrapped in a function to create a scope for locals. })(); var x = { 'i': 1, 'j': 2 } // No semicolon here. // 2. Trying to do one thing on Internet Explorer and another on Firefox. // I know you'd never write code like this, but throw me a bone. [ffVersion, ieVersion][isIE](); var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] // No semicolon here. // 3. conditional execution a la bash -1 == resultOfOperation() || die();
So what happens?
x[ffVersion, ieVersion][isIE]()
.die
老是被調用。由於數組減 1 的結果是 NaN
,它不等於任何東西(不管 resultOfOperation
是否返回 NaN
)。因此最終的結果是 die()
執行完所得到值將賦給 THINGS_TO_EAT
.Why?
JavaScript 中語句要以分號結束,不然它將會繼續執行下去,無論換不換行。以上的每個示例中,函數聲明或對象或數組,都變成了在一句語句體內。要知道閉合圓括號並不表明語句結束,JavaScript 不會終結語句,除非它的下一個 token 是一箇中綴符[2] 或者是圓括號操做符。
這真是讓人大吃一驚,因此乖乖地給語句末加上分號吧。
澄清:分號與函數
分號須要用在表達式的結尾,而並不是函數聲明的結尾。區分它們最好的例子是:
var foo = function() { return true; }; // semicolon here. function foo() { return true; } // no semicolon here.
嵌套函數是很是有用的,好比用在持續建立和隱藏輔助函數的任務中。你能夠很是自由隨意地使用它們。
切勿在語句塊內聲明函數,在 ECMAScript 5 的嚴格模式下,這是不合法的。函數聲明應該在定義域的頂層。但在語句塊內可將函數申明轉化爲函數表達式賦值給變量。
不推薦
if (x) { function foo() {} }
推薦
if (x) { var foo = function() {}; }
基本上你沒法避免出現異常,特別是在作大型開發時(使用應用開發框架等等)。
在沒有自定義異常的狀況下,從有返回值的函數中返回錯誤信息必定很是的棘手,更別提多不優雅了。很差的解決方案包括了傳第一個引用類型來接納錯誤信息,或老是返回一個對象列表,其中包含着可能的錯誤對象。以上方式基本上是比較簡陋的異常處理方式。適時可作自定義異常處理。
在複雜的環境中,你能夠考慮拋出對象而不只僅是字符串(默認的拋出值)。
if(name === undefined) { throw { name: 'System Error', message: 'A name should always be specified!' } }
老是優先考慮使用標準特性。爲了最大限度地保證擴展性與兼容性,老是首選標準的特性,而不是非標準的特性(例如:首選 string.charAt(3)
而不是 string[3]
;首選 DOM 的操做方法來得到元素引用,而不是某一應用特定的快捷方法)。
若是你想在 JavaScript 中繼承你的對象,請遵循一個簡易的模式來建立此繼承。若是你預計你會趕上覆雜對象的繼承,那能夠考慮採用一個繼承庫,好比 Proto.js by Axel Rauschmayer.
簡易繼承請用如下方式:
(function(log){ 'use strict'; // Constructor function function Apple(name) { this.name = name; } // Defining a method of apple Apple.prototype.eat = function() { log('Eating ' + this.name); }; // Constructor function function GrannySmithApple() { // Invoking parent constructor Apple.prototype.constructor.call(this, 'Granny Smith'); } // Set parent prototype while creating a copy with Object.create GrannySmithApple.prototype = Object.create(Apple.prototype); // Set constructor to the sub type, otherwise points to Apple GrannySmithApple.prototype.constructor = GrannySmithApple; // Calling a super method GrannySmithApple.prototype.eat = function() { // Be sure to apply it onto our current object with call(this) Apple.prototype.eat.call(this); log('Poor Grany Smith'); }; // Instantiation var apple = new Apple('Test Apple'); var grannyApple = new GrannySmithApple(); log(apple.name); // Test Apple log(grannyApple.name); // Granny Smith // Instance checks log(apple instanceof Apple); // true log(apple instanceof GrannySmithApple); // false log(grannyApple instanceof Apple); // true log(grannyApple instanceof GrannySmithApple); // true // Calling method that calls super method grannyApple.eat(); // Eating Granny Smith\nPoor Grany Smith }(window.console.log));
閉包的建立也許是 JS 最有用也是最易被忽略的能力了。關於閉包如何工做的合理解釋。
在簡單的循環語句中加入函數是很是容易造成閉包而帶來隱患的。下面的例子就是一個典型的陷阱:
不推薦
(function(log, w){ 'use strict'; // numbers and i is defined in the current function closure var numbers = [1, 2, 3], i; for(i = 0; i < numbers.length; i++) { w.setTimeout(function() { // At the moment when this gets executed the i variable, coming from the outer function scope // is set to 3 and the current program is alerting the message 3 times // 'Index 3 with number undefined // If you understand closures in javascript you know how to deal with those cases // It's best to just avoid functions / new closures in loops as this prevents those issues w.alert('Index ' + i + ' with number ' + numbers[i]); }, 0); } }(window.console.log, window));
接下來的改進雖然已經解決了上述例子中的問題或 bug,但仍是違反了不在循環中建立函數或閉包的原則。
不推薦
(function(log, w){ 'use strict'; // numbers and i is defined in the current function closure var numbers = [1, 2, 3], i; for(i = 0; i < numbers.length; i++) { // Creating a new closure scope with an IIFE solves the problem // The delayed function will use index and number which are // in their own closure scope (one closure per loop iteration). // --- // Still this is not recommended as we violate our rule to not // create functions within loops and we are creating two! (function(index, number){ w.setTimeout(function() { // Will output as expected 0 > 1, 1 > 2, 2 > 3 w.alert('Index ' + index + ' with number ' + number); }, 0); }(i, numbers[i])); } }(window.console.log, window));
接下來的改進已解決問題,並且也遵循了規範。但是,你會發現看上去彷佛過於複雜繁冗了,應該會有更好的解決方案吧。
不徹底推薦
(function(log, w){ 'use strict'; // numbers and i is defined in the current function closure var numbers = [1, 2, 3], i; // Create a function outside of the loop that will accept arguments to create a // function closure scope. This function will return a function that executes in this // closure parent scope. function alertIndexWithNumber(index, number) { return function() { w.alert('Index ' + index + ' with number ' + number); }; } // First parameter is a function call that returns a function. // --- // This solves our problem and we don't create a function inside our loop for(i = 0; i < numbers.length; i++) { w.setTimeout(alertIndexWithNumber(i, numbers[i]), 0); } }(window.console.log, window));
將循環語句轉換爲函數執行的方式問題能獲得立馬解決,每一次循環都會對應地建立一次閉包。函數式的風格更加值得推薦,並且看上去也更加地天然和可預料。
推薦
(function(log, w){ 'use strict'; // numbers and i is defined in the current function closure var numbers = [1, 2, 3], i; numbers.forEach(function(number, index) { w.setTimeout(function() { w.alert('Index ' + index + ' with number ' + number); }, 0); }); }(window.console.log, window));
eval()
不但混淆語境還很危險,總會有比這更好、更清晰、更安全的另外一種方案來寫你的代碼,所以儘可能不要使用 eval 函數。
只在對象構造器、方法和在設定的閉包中使用 this
關鍵字。this 的語義在此有些誤導。它時而指向全局對象(大多數時),時而指向調用者的定義域(在 eval 中),時而指向 DOM 樹中的某一節點(當用事件處理綁定到 HTML 屬性上時),時而指向一個新建立的對象(在構造器中),還時而指向其它的一些對象(若是函數被 call()
和 apply()
執行和調用時)。
正由於它是如此容易地被搞錯,請限制它的使用場景:
函數式編程讓你能夠簡化代碼並縮減維護成本,由於它容易複用,又適當地解耦和更少的依賴。
接下來的例子中,在一組數字求和的同一問題上,比較了兩種解決方案。第一個例子是經典的程序處理,而第二個例子則是採用了函數式編程和 ECMA Script 5.1 的數組方法。
例外:每每在重代碼性能輕代碼維護的狀況之下,要選擇最優性能的解決方案而非維護性高的方案(好比用簡單的循環語句代替 forEach)。
不推薦
(function(log){ 'use strict'; var arr = [10, 3, 7, 9, 100, 20], sum = 0, i; for(i = 0; i < arr.length; i++) { sum += arr[i]; } log('The sum of array ' + arr + ' is: ' + sum) }(window.console.log));
推薦
(function(log){ 'use strict'; var arr = [10, 3, 7, 9, 100, 20]; var sum = arr.reduce(function(prevValue, currentValue) { return prevValue + currentValue; }, 0); log('The sum of array ' + arr + ' is: ' + sum); }(window.console.log));
另外一個例子經過某一規則對一個數組進行過濾匹配來建立一個新的數組。
不推薦
(function(log){ 'use strict'; var numbers = [11, 3, 7, 9, 100, 20, 14, 10], numbersGreaterTen = [], i; for(i = 0; i < numbers.length; i++) { if(numbers[i] > 10) { numbersGreaterTen.push(numbers[i]); } } log('From the list of numbers ' + numbers + ' only ' + numbersGreaterTen + ' are greater than ten'); }(window.console.log));
推薦
(function(log){ 'use strict'; var numbers = [11, 3, 7, 9, 100, 20, 14, 10]; var numbersGreaterTen = numbers.filter(function(element) { return element > 10; }); log('From the list of numbers ' + numbers + ' only ' + numbersGreaterTen + ' are greater than ten'); }(window.console.log));
建議使用 ECMA Script 5 中新增的語法糖和函數。這將簡化你的程序,並讓你的代碼更加靈活和可複用。
用 ECMA5 的迭代方法來迭代數組。使用 Array.forEach
或者若是你要在特殊場合下中斷迭代,那就用 Array.every
。
(function(log){ 'use strict'; // Iterate over an array and break at a certain condition [1, 2, 3, 4, 5].every(function(element, index, arr) { log(element + ' at index ' + index + ' in array ' + arr); if(index !== 5) { return true; } }); // Defining a simple javascript object var obj = { a: 'A', b: 'B', 'c-d-e': 'CDE' }; // Iterating over the object keys Object.keys(obj).forEach(function(element, index, arr) { log('Key ' + element + ' has value ' + obj[element]); }); }(window.console.log));
switch 在全部的編程語言中都是個很是錯誤的難以控制的語句,建議用 if else 來替換它。
用數組和對象字面量來代替數組和對象構造器。數組構造器很容易讓人在它的參數上犯錯。
不推薦
// Length is 3. var a1 = new Array(x1, x2, x3); // Length is 2. var a2 = new Array(x1, x2); // If x1 is a number and it is a natural number the length will be x1. // If x1 is a number but not a natural number this will throw an exception. // Otherwise the array will have one element with x1 as its value. var a3 = new Array(x1); // Length is 0. var a4 = new Array();
正因如此,若是將代碼傳參從兩個變爲一個,那數組頗有可能發生意料不到的長度變化。爲避免此類怪異情況,請老是採用更多可讀的數組字面量。
推薦
var a = [x1, x2, x3]; var a2 = [x1, x2]; var a3 = [x1]; var a4 = [];
對象構造器不會有相似的問題,可是爲了可讀性和統一性,咱們應該使用對象字面量。
不推薦
var o = new Object(); var o2 = new Object(); o2.a = 0; o2.b = 1; o2.c = 2; o2['strange key'] = 3;
應該寫成這樣:
推薦
var o = {}; var o2 = { a: 0, b: 1, c: 2, 'strange key': 3 };
修改內建的諸如 Object.prototype
和 Array.prototype
是被嚴厲禁止的。修改其它的內建對象好比 Function.prototype
,雖危害沒那麼大,但始終仍是會致使在開發過程當中難以 debug 的問題,應當也要避免。
你能夠經過自定義 toString()
來控制對象字符串化。這很好,但你必須保證你的方法老是成功並不會有其它反作用。若是你的方法達不到這樣的標準,那將會引起嚴重的問題。若是 toString()
調用了一個方法,這個方法作了一個斷言[3] ,當斷言失敗,它可能會輸出它所在對象的名稱,固然對象也須要調用 toString()
。
通常在語法和語義上真正須要時才謹慎地使用圓括號。不要用在一元操做符上,例如 delete
, typeof
和 void
,或在關鍵字以後,例如 return
, throw
, case
, new
等。
統一使用單引號(‘),不使用雙引號(「)。這在建立 HTML 字符串很是有好處:
var msg = 'This is some HTML <div class="makes-sense"></div>';
用三元操做符分配或返回語句。在比較簡單的狀況下使用,避免在複雜的狀況下使用。沒人願意用 10 行三元操做符把本身的腦子繞暈。
不推薦
if(x === 10) { return 'valid'; } else { return 'invalid'; }
推薦
return x === 10 ? 'valid' : 'invalid';
ID和class(類)名老是使用能夠反應元素目的和用途的名稱,或其餘通用名稱。代替表象和晦澀難懂的名稱。
應該首選具體和反映元素目的的名稱,由於這些是最能夠理解的,並且發生變化的可能性最小。
通用名稱只是多個元素的備用名,他們兄弟元素之間是同樣的,沒有特別意義。
區分他們,使他們具備特殊意義,一般須要爲「幫手」。
儘管class(類)名和ID 的語義化對於計算機解析來講沒有什麼實際的意義,
語義化的名稱 一般是正確的選擇,由於它們所表明的信息含義,不包含表現的限制。
不推薦
.fw-800 { font-weight: 800; } .red { color: red; }
推薦
.heavy { font-weight: 800; } .important { color: red; }
通常狀況下ID不該該被應用於樣式。
ID的樣式不能被複用而且每一個頁面中你只能使用一次ID。
使用ID惟一有效的是肯定網頁或整個站點中的位置。
儘管如此,你應該始終考慮使用class,而不是id,除非只使用一次。
不推薦
#content .title { font-size: 2em; }
推薦
.content .title { font-size: 2em; }
另外一個反對使用ID的觀點是含有ID選擇器權重很高。
一個只包含一個ID選擇器權重高於包含1000個class(類)名的選擇器,這使得它很奇怪。
// 這個選擇器權重高於下面的選擇器 #content .title { color: red; } // than this selector! html body div.content.news-content .title.content-title.important { color: blue; }
當構建選擇器時應該使用清晰, 準確和有語義的class(類)名。不要使用標籤選擇器。 若是你只關心你的class(類)名
,而不是你的代碼元素,這樣會更容易維護。
從分離的角度考慮,在表現層中不該該分配html標記/語義。
它多是一個有序列表須要被改爲一個無序列表,或者一個div將被轉換成article。
若是你只使用具備實際意義的class(類)名,
而且不使用元素選擇器,那麼你只須要改變你的html標記,而不用改動你的CSS。
不推薦
div.content > header.content-header > h2.title { font-size: 2em; }
推薦
.content > .content-header > .title { font-size: 2em; }
不少前端開發人員寫選擇器鏈的時候不使用 直接子選擇器(注:直接子選擇器和後代選擇器的區別)。
有時,這可能會致使疼痛的設計問題而且有時候可能會很耗性能。
然而,在任何狀況下,這是一個很是很差的作法。
若是你不寫很通用的,須要匹配到DOM末端的選擇器, 你應該老是考慮直接子選擇器。
考慮下面的DOM:
<article class="content news-content"> <span class="title">News event</span> <div class="content-body"> <div class="title content-title"> Check this out </div> <p>This is a news article content</p> <div class="teaser"> <div class="title">Buy this</div> <div class="teaser-content">Yey!</div> </div> </div> </article>
下面的CSS將應用於有title類的所有三個元素。
而後,要解決content類下的title類 和 teaser類下的title類下不一樣的樣式,這將須要更精確的選擇器再次重寫他們的樣式。
不推薦
.content .title { font-size: 2rem; }
推薦
.content > .title { font-size: 2rem; } .content > .content-body > .title { font-size: 1.5rem; } .content > .content-body > .teaser > .title { font-size: 1.2rem; }
CSS提供了各類縮寫屬性(如 font 字體)應該儘量使用,即便在只設置一個值的狀況下。
使用縮寫屬性對於代碼效率和可讀性是有頗有用的。
不推薦
border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
推薦
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;
省略「0」值後面的單位。不要在0值後面使用單位,除非有值。
不推薦
padding-bottom: 0px;
margin: 0em;
推薦
padding-bottom: 0;
margin: 0;
在可能的狀況下,使用3個字符的十六進制表示法。
顏色值容許這樣表示,
3個字符的十六進制表示法更簡短。
始終使用小寫的十六進制數字。
不推薦
color: #FF33AA;
推薦
color: #f3a;
使用連字符(中劃線)分隔ID和Class(類)名中的單詞。爲了加強課理解性,在選擇器中不要使用除了連字符(中劃線)覺得的任何字符(包括沒有)來鏈接單詞和縮寫。
另外,做爲該標準,預設屬性選擇器能識別連字符(中劃線)做爲單詞[attribute|=value]
的分隔符,
因此最好的堅持使用連字符做爲分隔符。
不推薦
.demoimage {} .error_status {}
推薦
#video-id {} .ads-sample {}
避免用戶代理檢測以及CSS「hacks」 – 首先嚐試不一樣的方法。經過用戶代理檢測或特殊的CSS濾鏡,變通的方法和 hacks 很容易解決樣式差別。爲了達到並保持一個有效的和可管理的代碼庫,這兩種方法都應該被認爲是最後的手段。換句話說,從長遠來看,用戶代理檢測和hacks
會傷害項目,做爲項目每每應該採起阻力最小的途徑。也就是說,輕易容許使用用戶代理檢測和hacks 之後將過於頻繁。
這是一個選擇器內書寫CSS屬性順序的大體輪廓。這是爲了保證更好的可讀性和可掃描重要。
做爲最佳實踐,咱們應該遵循如下順序(應該按照下表的順序):
不推薦
.box { font-family: 'Arial', sans-serif; border: 3px solid #ddd; left: 30%; position: absolute; text-transform: uppercase; background-color: #eee; right: 30%; isplay: block; font-size: 1.5rem; overflow: hidden; padding: 1em; margin: 1em; }
推薦
.box { display: block; position: absolute; left: 30%; right: 30%; overflow: hidden; margin: 1em; padding: 1em; background-color: #eee; border: 3px solid #ddd; font-family: 'Arial', sans-serif; font-size: 1.5rem; text-transform: uppercase; }
爲了保證一致性和可擴展性,每一個聲明應該用分號結束,每一個聲明換行。
不推薦
.test { display: block; height: 100px }
推薦
.test { display: block; height: 100px; }
屬性名的冒號後使用一個空格。出於一致性的緣由,
屬性和值(但屬性和冒號之間沒有空格)的之間始終使用一個空格。
不推薦
h3 { font-weight:bold; }
推薦
h3 { font-weight: bold; }
每一個選擇器和屬性聲明老是使用新的一行。
不推薦
a:focus, a:active { position: relative; top: 1px; }
推薦
h1, h2, h3 { font-weight: normal; line-height: 1.2; }
規則之間始終有一個空行(雙換行符)分隔。
推薦
html { background: #fff; } body { margin: auto; width: 50%; }
屬性選擇器或屬性值用雙引號(」」),而不是單引號(」)括起來。
URI值(url())不要使用引號。
不推薦
@import url('//cdn.com/foundation.css'); html { font-family: 'open sans', arial, sans-serif; } body:after { content: 'pause'; }
推薦
@import url(//cdn.com/foundation.css); html { font-family: "open sans", arial, sans-serif; } body:after { content: "pause"; }
在Sass中你能夠嵌套選擇器,這可使代碼變得更清潔和可讀。嵌套全部的選擇器,但儘可能避免嵌套沒有任何內容的選擇器。
若是你須要指定一些子元素的樣式屬性,而父元素將不什麼樣式屬性,
可使用常規的CSS選擇器鏈。
這將防止您的腳本看起來過於複雜。
不推薦
// Not a good example by not making use of nesting at all .content { display: block; } .content > .news-article > .title { font-size: 1.2em; }
不推薦
// Using nesting is better but not in all cases // Avoid nesting when there is no attributes and use selector chains instead .content { display: block; > .news-article { > .title { font-size: 1.2em; } } }
推薦
// This example takes the best approach while nesting but use selector chains where possible .content { display: block; > .news-article > .title { font-size: 1.2em; } }
嵌套選擇器和CSS屬性之間空一行。
不推薦
.content { display: block; > .news-article { background-color: #eee; > .title { font-size: 1.2em; } > .article-footnote { font-size: 0.8em; } } }
推薦
.content { display: block; > .news-article { background-color: #eee; > .title { font-size: 1.2em; } > .article-footnote { font-size: 0.8em; } } }
當使用Sass的嵌套功能的時候,
重要的是有一個明確的嵌套順序,
如下內容是一個SCSS塊應具備的順序。
The following example should illustrate how this ordering will achieve a clear structure while making use of the Sass parent selector.
Recommended
.product-teaser { // 1. Style attributes display: inline-block; padding: 1rem; background-color: whitesmoke; color: grey; // 2. Pseudo selectors with parent selector &:hover { color: black; } // 3. Pseudo elements with parent selector &:before { content: ""; display: block; border-top: 1px solid grey; } &:after { content: ""; display: block; border-top: 1px solid grey; } // 4. State classes with parent selector &.active { background-color: pink; color: red; // 4.2. Pseuso selector in state class selector &:hover { color: darkred; } } // 5. Contextual media queries @media screen and (max-width: 640px) { display: block; font-size: 2em; } // 6. Sub selectors > .content > .title { font-size: 1.2em; // 6.5. Contextual media queries in sub selector @media screen and (max-width: 640px) { letter-spacing: 0.2em; text-transform: uppercase; } } }