首先吐槽一句,原本想上傳word文檔的,但是發現博客不能上傳word文檔,這就很尷尬了。css
首先聲明該規範不是本人寫的,網上搜前端規範發現這個很詳細就先複製下來作筆記,固然不可能啥都按規範來,每一個公司的規範都不同..僅供參考html
前端開發規範文檔前端
1 代碼風格html5
1.1 縮進node
**【強制】**使用 4 個空格做爲一個縮進層級,不容許使用 2 個空格或 tab 字符;ios
2 屬性git
2.1 屬性引號github
**【強制】**對於屬性的定義使用雙引號,不容許使用單引號,不容許不使用引號;web
示例:ajax
<!-- Not so great -->
<img class='avatar' src="./img/avatar.png" alt='avatar'>
<!-- Better -->
<img class="avatar" src="./img/avatar.png" alt="avatar">
2.2 屬性大小寫
**【強制】**屬性名應該小寫,不容許大寫或大小寫混合;
示例:
<!-- Not so great -->
<table cellSpacing="0">...</table>
<!-- Better -->
<table cellspacing="0">...</table>
2.3 屬性布爾值
**【建議】**布爾類型的屬性,建議不添加屬性值,至少同一項目要保持一致;
示例:
<input type="text" disabled>
<input type="checkbox" checked>
2.4 屬性聲明順序
**【建議】**HTML 屬性建議儘可能按照如下給出的順序依次排列,確保代碼的易讀性。
class 用於標識高度可複用組件,所以應該排在首位。id 用於標識具體組件,應當謹慎使用(例如,頁面內的書籤),建議預留更多的id命名給技術,所以排在第二位。
<a class="..." id="..." data-modal="toggle" href="#">Example link</a>
<input class="form-control" type="text">
<img src="..." alt="...">
2.5 自定義屬性
**【建議】**使用自定義屬性做爲JS的hook,建議以data-爲前綴;
示例:
<input data-role="getPic" type="button">
2.6 連接屬性
**【強制】**禁止 a 標籤的 href 取值爲空或不寫 href 屬性,重構時默承認用 # 代替;
若是不須要使用連接功能,請不要使用不帶 href 的 a 標籤,既不符合標籤的語義,也可能會產生未知的兼容性問題;
示例:
<!-- Not so great -->
<a href="" title="title">歡聚時代</a>
<a class="xxx">歡聚時代</a>
<!-- Better -->
<a href="#" title="title">歡聚時代</a>
3 標籤
3.1 標籤大小寫
**【強制】**標籤名應該小寫,不容許大寫或大小寫混合;
示例:
<!-- Not so great -->
<DIV clsss="xxx">...</DIV>
<!-- Better -->
<div clsss="xxx">...</div>
3.2 標籤自閉合
**【建議】**對於無需自閉合的標籤,建議不自閉合,至少同一項目要保持一致;
常見無需自閉合標籤有input、img、br、hr等
示例:
<input type="checkbox" value="1">
3.3 標籤嵌套
**【強制】**標籤使用必須符合標籤嵌套規則;
例如:內聯元素不能嵌套塊元素,<p>元素和<h1~6>元素不能嵌套塊元素等,詳見 Allowed nesting of elements in HTML 4 Strict (and XHTML 1.0 Strict) 與 HTML5 Content models;
**【建議】**實用爲王,儘可能遵循 HTML 標準和語義,可是不要以犧牲實用性爲代價。任什麼時候候都要儘可能使用最少的標籤並保持最小的複雜度。
<!-- Not so great -->
<span class="avatar">
<img src="...">
</span>
<!-- Better -->
<img class="avatar" src="...">
3.4 避免過期標籤
**【強制】**不容許使用過期的舊標籤,請使用新標籤或者CSS代替:
請參詳:http://www.w3schools.com/tags/
4 head設定
4.1 doctype
**【強制】**doctype使用 HTML5 的 doctype 來啓用標準模式。
其中 doctype 建議使用大寫的 DOCTYPE; 關於doctype該使用大寫仍是小寫的討論
示例:
<!DOCTYPE html>
4.2 頁面編碼
**【強制】**頁面必須明確指定字符編碼,讓瀏覽器快速肯定適合網頁內容的渲染方式。指定字符編碼的 meta 必須是 head 的第一個直接子元素。建議使用無 BOM 的 UTF-8 編碼;
示例:
<meta charset="UTF-8">
4.3 兼容模式
**【建議】**PC端啓用 IE Edge 模式,並針對360瀏覽器啓用webkit渲染模式;
示例:
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
4.4 引入CSS
**【強制】**引入 CSS 時必須指明 rel="stylesheet";
建議在 head 中引入頁面須要的全部 CSS 資源,由於在頁面渲染的過程當中,新的CSS可能致使元素的樣式從新計算和繪製,頁面閃爍;
示例:
<link rel="stylesheet" src="global.css">
4.5 引入JavaScript
**【建議】**JavaScript應當放在頁面尾部;出於性能方面的考慮,如非必要,請遵照此條建議;
示例:
<body>
<!-- a lot of elements -->
<script src="main.js"></script>
</body>
4.6 favicon
**【強制】**保證 favicon 可訪問;
在未指定 favicon 時,大多數瀏覽器會請求 Web Server 根目錄下的 favicon.ico 。爲了保證favicon可訪問,避免404,必須遵循如下兩種方法之一:
示例:
<link type="image/x-icon" rel="shortcut icon" href="path/to/favicon.ico">
附:工做流中默認的PC端head設定
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="Keywords" content="多玩遊戲">
<meta name="description" content="多玩遊戲">
<!-- a lot of elements -->
</head>
<body>
<!-- a lot of elements -->
</body>
</html>
附:工做流中默認的移動端head設定
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="format-detection" content="telephone=no,address=no,email=no">
<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL">
<meta name="keywords" content="多玩遊戲">
<meta name="description" content="多玩遊戲">
<!-- a lot of elements -->
</head>
<body>
<!-- a lot of elements -->
</body>
</html>
注意:當該項目有相關的app在app store中,設置metaapple-itunes-app,如上面最後一條,並填上對應的app-id。詳細請看:Promoting Apps with Smart App Banners
更詳細的meta屬性設置能夠參詳:https://github.com/hzlzh/cool-head
5 圖片
**【強制】**禁止 img 的 src 取值爲空;延遲加載的圖片也要增長默認的 src;
src 取值爲空,會致使部分瀏覽器從新加載一次當前頁面,參考 Yahoo performance rules
**【建議】**爲重要圖片添加 alt 屬性;
能夠提升圖片加載失敗時的用戶體驗。
**【建議】**添加 width 和 height 屬性,以免頁面抖動;
**【建議】**有下載需求或者預期會靈活變更的圖片採用 img 標籤實現,無下載需求的圖片採用 CSS 背景圖實現;
6 表單
**【強制】**有文本標題的控件必須使用 label 標籤將其與其標題相關聯;
有兩種方式:
推薦使用第一種,減小沒必要要的 id。若是 DOM 結構不容許直接嵌套,則應使用第二種。
示例:
<label><input name="confirm" type="checkbox" value="on"> 我已確認上述條款</label>
<label for="username">用戶名:</label> <input id="username" name="username" type="checkbox">
**【建議】**儘可能不要使用按鈕類元素的 name 屬性;
因爲瀏覽器兼容性問題,使用按鈕的 name 屬性會帶來許多難以發現的問題。具體狀況可參考 此文;
**【建議】**在針對移動設備開發的頁面時,根據內容類型指定輸入框的 type 屬性;
根據內容類型指定輸入框類型,能得到能友好的輸入體驗。
示例:
<input type="number" value="1">
7 註釋
【建議】對超過10行的頁面模塊進行註釋, 以下降開發人員的嵌套成本和後期的維護成本。建議使用結尾註釋方式,例如:
當模塊代碼量較少時,能夠省略 start。
<!-- 文章內容 start -->
<section id="post">
do some things...
</section>
<!-- 文章內容 end -->
或者標註模塊的class或者id:
<!-- #post start -->
<section id="post">
do some things...
</section>
<!-- #post end -->
1 命名規範
該命名規範主要解決如下問題:
1.1 命名思想
【強制】 區塊、模塊、組件等一個整個的結構遵循BEM命名思想;
當你能肯定組件內最後一級的結構不會再發生變化時,最後一級可省略類名,使用兩層嵌套;
相關連接:
1.2 多單詞鏈接
【強制】 (全部的)多個單詞使用小駝峯式命名,不容許使用中劃線或者下劃線鏈接多個單詞;
多個單詞使用小駝峯式命名,以提高名稱的識別度,例如:newsList;
1.3 命名空間
【強制】 在合適的地方使用命名空間;
狀態類或擴展類通常出如今組件的父級節點,而且不容許單獨使用。舉個例子,同一個頁面有可能會在不一樣的地方都會使用is-active,而且每一個is-active所操縱的節點的是不一樣的,因此要使用.ui-userCard.is-active 或 .ui-userCard .is-active來定義
1.4 圖片命名
1.5 區塊命名
【推薦】 通常區塊均可以劃分爲頭部、身體和尾部,所以建議給你的區塊分別以 hd、bd、ft來劃分;
示例:
.ui-card__hd {
margin: 0;
}
.ui-card__bd {
margin: 0;
}
.ui-card__ft {
margin: 0;
}
附:命名示例
2 代碼風格
2.1 縮進
【強制】 使用 4 個空格作爲一個縮進層級,不容許使用 2 個空格 或 tab 字符;
示例:
/* Not so great */
.selector {
margin: 0;
}
/* Better */
.selector {
margin: 0;
}
2.2 空格
【強制】 選擇器 與 {之間必須包含空格;
示例:
/* Not so great */
.selector{
}
/* Better */
.selector {
}
【強制】 >、+、~ 選擇器的兩邊各保留一個空格;
示例:
/* Not so great */
main>nav {
padding: 10px;
}
label+input {
margin-left: 5px;
}
input:checked~button {
background-color: #69C;
}
/* Better */
main > nav {
padding: 10px;
}
label + input {
margin-left: 5px;
}
input:checked ~ button {
background-color: #69C;
}
【強制】 屬性名 與以後的 : 之間不容許包含空格, :與 屬性值 之間必須包含空格;
示例:
/* Not so great */
margin :0;
/* Better */
margin: 0;
【強制】 列表型屬性值 書寫在單行時,,後必須跟一個空格;
示例:
/* Not so great */
font-family: Arial,sans-serif;
box-shadow: 0 0 2px rgba(0,128,0,.3);
/* Better */
font-family: Arial, sans-serif;
box-shadow: 0 0 2px rgba(0, 128, 0, .3);
2.3 選擇器
【強制】 當一個 rule 包含多個 selector 時,每一個選擇器聲明必須獨佔一行;
示例:
/* Not so great */
.post, .page, .comment {
line-height: 1.5;
}
/* Better */
.post,
.page,
.comment {
line-height: 1.5;
}
2.4 屬性
【強制】 屬性定義必須另起一行;
示例:
/* Not so great */
.selector { margin: 0; padding: 0;}
/* Better */
.selector {
margin: 0;
padding: 0;
}
【強制】 屬性定義後必須以分號結尾;
示例:
/* Not so great */
.selector {
margin: 0
}
/* Better */
.selector {
margin: 0;
}
3. 通用
3.1 選擇器
【強制】 如無必要,不得爲id、class選擇器添加 類型選擇器 進行限定;
在性能和維護性上,都有必定的影響。
示例:
/* Not so great */
dialog#error,
p.danger-message {
font-color: #c00;
}
/* Better */
#error,
.danger-message {
font-color: #c00;
}
【建議】 選擇器的嵌套層級應該不大於 3 級,位置靠後的限定條件應可能精確;
在性能和維護性上,都有必定的影響。
示例:
/* Not so great */
.comment ul li a span {}
#top-hero .hero-avatar li.avatar .pic em {}
/* Better */
.comment .date {}
#top-hero .pic em {}
3.2 屬性
3.2.1 屬性書寫順序
【建議】 同一 rule set 下的屬性在書寫時,應按功能進行分組,並以 Formatting Model > Box Model > Typographic > Visual 的順序書寫,以提升代碼的可讀性。
Positioning 處在第一位,由於他可使一個元素脫離正常文本流,而且覆蓋盒模型相關的樣式。盒模型緊跟其後,由於他決定了一個組件的大小和位置。其餘屬性只在組件 內部 起做用或者不會對前面兩種狀況的結果產生影響,因此他們排在後面。
詳情資料 Twitter的strictPropertyOrder
3.2.2 屬性引號
【強制】 屬性選擇器中的值必須用雙引號包圍。不容許使用單引號,不容許不使用引號。
示例:
/* Not so great */
article[character='juliet'] {
voice-family: "Vivien Leigh", victoria, female
}
/* Better */
article[character="juliet"] {
voice-family: "Vivien Leigh", victoria, female
}
3.2.3 屬性簡寫
簡寫形式能夠在必定程度上壓縮樣式,但並不意味着你能夠對全部能夠簡寫的屬性聲明都使用簡寫。過分使用簡寫形式的屬性聲明會致使代碼混亂,會對屬性值帶來沒必要要的覆蓋從而引發意外的反作用,而且不能充分利用CSS的繼承。常見的濫用簡寫屬性聲明的狀況以下:
若是你只需定義其中的一兩個屬性,而不是所有,儘可能分開來寫:
/* Better */
.selector {
margin-bottom: 10px;
background-color: red;
background-image: url(image.jpg);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
/* Not so great */
.selector {
margin: 0 0 10px;
background: red;
background: url(image.jpg);
border-radius: 3px 3px 0 0;
}
4 值與單位
4.1 文本
【強制】 文本內容必須用雙引號包圍,不容許使用單引號;
文本類型的內容可能在選擇器、屬性值等內容中。
示例:
/* Not so great */
html[lang|=zh] q:before {
font-family: 'Microsoft YaHei', sans-serif;
content: '「';
}
/* Better */
html[lang|="zh"] q:after {
font-family: "Microsoft YaHei", sans-serif;
content: "「";
}
4.2 數值
【強制】 當數值爲 0 - 1 之間的小數時,省略整數部分的 0;
示例:
/* Not so great */
.selector {
opacity: 0.8;
}
/* Better */
.selector {
opacity: .8;
}
4.3 長度
【強制】 長度爲 0 時須省略單位 (也只有長度單位可省);
示例:
/* Not so great */
.selector {
margin: 0px 10px;
}
/* Better */
.selector {
margin: 0 10px;
}
4.4 url()
【強制】 url() 函數中的路徑不加引號;
示例:
/* Not so great */
.selector {
background: url("bg.png");
}
/* Better */
.selector {
background: url(bg.png);
}
4.5 顏色
【強制】 RGB顏色值必須使用十六進制記號形式 #rrggbb,不容許使用 rgb(),帶有alpha的顏色信息可使用 rgba();顏色值不容許使用命名色值;
示例:
/* Not so great */
.selector {
box-shadow: 0 0 2px rgba(0,128,0,.3);
border-color: rgb(0, 128, 0);
color: gray;
}
/* Better */
.selector {
box-shadow: 0 0 2px rgba(0, 128, 0, .3);
border-color: #008000;
color: #999;
}
【建議】 顏色值中的英文字符采用小寫,至少要保證同一項目內一致;
示例:
/* Not so great */
.selector {
color: #0073AA;
}
/* Better */
.selector {
color: #0073aa;
}
4.6 2D位置
【強制】 必須同時給出水平和垂直方向的位置;
2D 位置初始值爲 0% 0%,但在只有一個方向的值時,另外一個方向的值會被解析爲 center。爲避免理解上的困擾,應同時給出兩個方向的值。 background-position屬性值的定義
示例:
/* Not so great */
.selector {
background-position: top; /* 50% 0% */
}
/* Better */
.selector {
background-position: center top; /* 50% 0% */
}
5. 文本排版
5.1 字體族
【強制】 font-family 屬性中的字體族名稱應使用字體的英文 Family Name,其中若有空格,須放置在引號中;
常見的字體族名稱以下:
字體 |
操做系統 |
Family Name |
宋體 (中易宋體) |
Windows |
SimSun |
黑體 (中易黑體) |
Windows |
SimHei |
微軟雅黑 |
Windows |
Microsoft YaHei |
微軟正黑 |
Windows |
Microsoft JhengHei |
華文黑體 |
Mac/iOS |
STHeiti |
冬青黑體 |
Mac/iOS |
Hiragino Sans GB |
文泉驛正黑 |
Linux |
WenQuanYi Zen Hei |
文泉驛微米黑 |
Linux |
WenQuanYi Micro Hei |
【強制】 font-family 應當遵循如下順序:
詳細說明可參考 如何保證網頁的字體在各平臺都儘可能顯示爲最高質量的黑體?
【強制】 font-family 不區分大小寫,但在同一個項目中,一樣的 Family Name 大小寫必須統一;
示例:
/* Not so great */
body {
font-family: arial, sans-serif;
}
h1 {
font-family: Arial, "Microsoft YaHei", sans-serif;
}
/* Better */
body {
font-family: Arial, sans-serif;
}
h1 {
font-family: Arial, "Microsoft YaHei", sans-serif;
}
5.2 字重
【強制】 font-weight 屬性必須使用數值方式描述;
CSS 的字重分 100 – 900 共九檔,但目前受字體自己質量和瀏覽器的限制,實際上支持 400 和 700 兩檔,分別等價於關鍵詞 normal 和 bold。
瀏覽器自己使用一系列啓發式規則來進行匹配,在 >700 時通常匹配字體的 Regular 字重,>=700 時匹配 Bold 字重。
但已有瀏覽器開始支持 =600 時匹配 Semibold 字重 (見此表),故使用數值描述增長了靈活性,也更簡短。
示例:
/* Not so great */
.selector {
font-weight: bold;
}
/* Better */
.selector {
font-weight: 700;
}
6 變換與動畫
【強制】 使用 transition 定義屬性時應遵循如下順序:
transition:[ transition-property ] || [ transition-duration ] || [ transition-timing-function ] || [ transition-delay ]
若是順序錯亂,在某些安卓瀏覽器上會讓動畫失效。
示例:
/* Not so great */
.selector {
transition: color .2s 0 ease-in;
}
/* Better */
.selector {
transition: color .2s ease-in 0;
}
【建議】 儘量在瀏覽器能高效實現的屬性上添加過渡和動畫:
在可能的狀況下應選擇這樣四種變換:
詳見 High Performance Animations
7 媒體查詢
【強制】 Media Query 不得單獨編排,必須與相關的規則一塊兒定義;
不要將他們一塊兒放到一個獨立的樣式文件中,或者丟在文檔的最底部,這樣作只會讓你們之後更容易忘記他們。
示例:
/* Not so great */
/* header styles */
/* main styles */
/* footer styles */
@media (...) {
/* header styles */
/* main styles */
/* footer styles */
}
/* Better */
/* header styles */
@media (...) {
/* header styles */
}
/* main styles */
@media (...) {
/* main styles */
}
/* footer styles */
@media (...) {
/* footer styles */
}
8 兼容性
8.1 屬性前綴
【強制】 帶私有前綴的屬性由長到短排列,按冒號位置對齊;
標準屬性放在最後,按冒號對齊方便閱讀與編輯。
示例:
/* Not so great */
.selector {
transition: color .2s ease-in 0;
-webkit-transition: color .2s ease-in 0;
-moz-transition: color .2s ease-in 0;
}
/* Better */
.selector {
-webkit-transition: color .2s ease-in 0;
-moz-transition: color .2s ease-in 0;
transition: color .2s ease-in 0;
}
8.2 hack
【建議】 若是有其餘解決方案,請不要使用hack;
9 代碼註釋
代碼是由人編寫並維護的。請確保你的代碼可以自描述、註釋良好而且易於他人理解。好的代碼註釋可以傳達上下文關係和代碼目的。不要簡單地重申組件或 class 名稱。
9.1 單行註釋
【強制】 星號與內容之間必須保留一個空格;
示例:
/* 新聞中心表格隔行變色 */
9.2 多行註釋
【強制】 星號要一列對齊,星號與內容之間必須保留一個空格;
示例:
/**
* Sometimes you need to include optional context for the entire component. Do that up here if it's important enough.
*/
9.3 文件註釋
【強制】 文件頂部必須包含文件註釋,用 @file 標識文件說明。星號要一列對齊,星號與內容之間必須保留一個空格,標識符冒號與內容之間必須保留一個空格;
/**
* @file: 文件概要描述
* @author: author-name(mail-name@domain.com)
* author-name2(mail-name2@domain.com)
* @update: 2015-04-29 00:02:45
*/
本文檔的目標是使JavaScript代碼風格保持一致,容易被理解和被維護。雖然本文檔是針對JavaScript設計的,可是在使用各類JavaScript的預編譯語言時(如TypeScript等)時,適用的部分也應儘可能遵循本文檔的約定。
JavaScript
文件使用無 BOM
的 UTF-8
編碼。解釋:
UTF-8 編碼具備更普遍的適應性。BOM 在使用程序或工具處理文件時可能形成沒必要要的干擾。
4
個空格作爲一個縮進層級,不容許使用 2
個空格 或 tab
字符。switch
下的 case
和 default
必須增長一個縮進層級。示例:
// good
switch (variable) {
case '1':
// do...
break;
case '2':
// do...
break;
default:
// do...
}
// bad
switch (variable) {
case '1':
// do...
break;
case '2':
// do...
break;
default:
// do...
}
示例:
var a = !arr.length;
a++;
a = b + c;
{
前必須有一個空格。示例:
// good
if (condition) {
}
while (condition) {
}
function funcName() {
}
// bad
if (condition){
}
while (condition){
}
function funcName(){
}
if / else / for / while / function / switch / do / try / catch / finally
關鍵字後,必須有一個空格。示例:
// good
if (condition) {
}
while (condition) {
}
(function () {
})();
// bad
if(condition) {
}
while(condition) {
}
(function() {
})();
:
以後必須有空格,:
以前不容許有空格。示例:
// good
var obj = {
a: 1,
b: 2,
c: 3
};
// bad
var obj = {
a : 1,
b:2,
c :3
};
(
之間不容許有空格。示例:
// good
function funcName() {
}
var funcName = function funcName() {
};
funcName();
// bad
function funcName () {
}
var funcName = function funcName () {
};
funcName ();
,
和 ;
前不容許有空格。示例:
// good
callFunc(a, b);
// bad
callFunc(a , b) ;
if / for / while / switch / catch
等語句中,()
和 []
內緊貼括號部分不容許有空格。示例:
// good
callFunc(param1, param2, param3);
save(this.list[this.indexes[i]]);
needIncream && (variable += increament);
if (num > list.length) {
}
while (len--) {
}
// bad
callFunc( param1, param2, param3 );
save( this.list[ this.indexes[ i ] ] );
needIncreament && ( variable += increament );
if ( num > list.length ) {
}
while ( len-- ) {
}
{}
和 []
內緊貼括號部分不容許包含空格。解釋:
聲明包含元素的數組與對象,只有當內部元素的形式較爲簡單時,才容許寫在一行。元素複雜的狀況,仍是應該換行書寫。
示例:
// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: 'obj'};
var obj3 = {
name: 'obj',
age: 20,
sex: 1
};
// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: 'obj' };
var obj3 = {name: 'obj', age: 20, sex: 1};
120
個字符。解釋:
超長的不可分割的代碼容許例外,好比複雜的正則表達式。長字符串不在例外之列。
示例:
// good
if (user.isAuthenticated()
&& user.isInRole('admin')
&& user.hasAuthority('add-admin')
|| user.hasAuthority('delete-admin')
) {
// Code
}
var result = number1 + number2 + number3
+ number4 + number5;
// bad
if (user.isAuthenticated() &&
user.isInRole('admin') &&
user.hasAuthority('add-admin') ||
user.hasAuthority('delete-admin')) {
// Code
}
var result = number1 + number2 + number3 +
number4 + number5;
,
或 ;
前換行。示例:
// good
var obj = {
a: 1,
b: 2,
c: 3
};
foo(
aVeryVeryLongArgument,
anotherVeryLongArgument,
callback
);
// bad
var obj = {
a: 1
, b: 2
, c: 3
};
foo(
aVeryVeryLongArgument
, anotherVeryLongArgument
, callback
);
示例:
// 僅爲按邏輯換行的示例,不表明setStyle的最優實現
function setStyle(element, property, value) {
if (element == null) {
return;
}
element.style[property] = value;
}
120
時,根據邏輯條件合理縮進。示例:
// 較複雜的邏輯條件組合,將每一個條件獨立一行,邏輯運算符放置在行首進行分隔,或將部分邏輯按邏輯組合進行分隔。
// 建議最終將右括號 ) 與左大括號 { 放在獨立一行,保證與 if 內語句塊能容易視覺辨識。
if (user.isAuthenticated()
&& user.isInRole('admin')
&& user.hasAuthority('add-admin')
|| user.hasAuthority('delete-admin')
) {
// Code
}
// 按必定長度截斷字符串,並使用 + 運算符進行鏈接。
// 分隔字符串儘可能按語義進行,如不要在一個完整的名詞中間斷開。
// 特別的,對於HTML片斷的拼接,經過縮進,保持和HTML相同的結構。
var html = '' // 此處用一個空字符串,以便整個HTML片斷都在新行嚴格對齊
+ '<article>'
+ '<h1>Title here</h1>'
+ '<p>This is a paragraph</p>'
+ '<footer>Complete</footer>'
+ '</article>';
// 也可以使用數組來進行拼接,相對 + 更容易調整縮進。
var html = [
'<article>',
'<h1>Title here</h1>',
'<p>This is a paragraph</p>',
'<footer>Complete</footer>',
'</article>'
];
html = html.join('');
// 當參數過多時,將每一個參數獨立寫在一行上,並將結束的右括號 ) 獨立一行。
// 全部參數必須增長一個縮進。
foo(
aVeryVeryLongArgument,
anotherVeryLongArgument,
callback
);
// 也能夠按邏輯對參數進行組合。
// 最經典的是baidu.format函數,調用時將參數分爲「模板」和「數據」兩塊
baidu.format(
dateFormatTemplate,
year, month, date, hour, minute, second
);
// 當函數調用時,若是有一個或以上參數跨越多行,應當每個參數獨立一行。
// 這一般出如今匿名函數或者對象初始化等做爲參數時,如setTimeout函數等。
setTimeout(
function () {
alert('hello');
},
200
);
order.data.read(
'id=' + me.model.id,
function (data) {
me.attchToModel(data.result);
callback();
},
300
);
// 鏈式調用較長時採用縮進進行調整。
$('#items')
.find('.selected')
.highlight()
.end();
// 三元運算符由3部分組成,所以其換行應當根據每一個部分的長度不一樣,造成不一樣的狀況。
var result = thisIsAVeryVeryLongCondition
? resultA : resultB;
var result = condition
? thisIsAVeryVeryLongResult
: resultB;
// 數組和對象初始化的混用,嚴格按照每一個對象的 { 和結束 } 在獨立一行的風格書寫。
var array = [
{
// ...
},
{
// ...
}
];
if...else...
、try...catch...finally
等語句,推薦使用在 }
號後添加一個換行 的風格,使代碼層次結構更清晰,閱讀性更好。示例:
if (condition) {
// some statements;
}
else {
// some statements;
}
try {
// some statements;
}
catch (ex) {
// some statements;
}
if / else / for / do / while
語句中,即便只有一行,也不得省略塊 {...}
。示例:
// good
if (condition) {
callFunc();
}
// bad
if (condition) callFunc();
if (condition)
callFunc();
示例:
// good
function funcName() {
}
// bad
function funcName() {
};
// 若是是函數表達式,分號是不容許省略的。
var funcName = function () {
};
IIFE
必須在函數表達式外添加 (
,非 IIFE
不得在函數表達式外添加 (
。解釋:
IIFE = Immediately-Invoked Function Expression.
額外的 ( 可以讓代碼在閱讀的一開始就能判斷函數是否當即被調用,進而明白接下來代碼的用途。而不是一直拖到底部才恍然大悟。
示例:
// good
var task = (function () {
// Code
return result;
})();
var func = function () {
};
// bad
var task = function () {
// Code
return result;
}();
var func = (function () {
});
變量
使用 Camel
命名法
。示例:
var loadingModules = {};
常量
使用 所有字母大寫,單詞間下劃線分隔
的命名方式。示例:
var HTML_ENTITY = {};
函數
使用 Camel
命名法
。示例:
function stringFormat(source) {
}
參數
使用 Camel
命名法
。示例:
function hear(theBells) {
}
類
使用 Pascal
命名法
。示例:
function TextNode(options) {
}
方法
/
屬性
使用 Camel
命名法
。示例:
function TextNode(value, engine) {
this.value = value;
this.engine = engine;
}
TextNode.prototype.clone = function () {
return this;
};
枚舉變量
使用 Pascal
命名法
,枚舉的屬性
使用 所有字母大寫,單詞間下劃線分隔
的命名方式。示例:
var TargetState = {
READING: 1,
READED: 2,
APPLIED: 3,
READY: 4
};
命名空間
使用 Camel
命名法
。示例:
equipments.heavyWeapons = {};
示例:
function XMLParser() {
}
function insertHTML(element, html) {
}
var httpRequest = new HTTPRequest();
類名
使用 名詞
。示例:
function Engine(options) {
}
函數名
使用 動賓短語
。示例:
function getStyle(element) {
}
boolean
類型的變量使用 is
或 has
開頭。示例:
var isReady = false;
var hasMoreCommands = false;
Promise
對象
用 動賓短語的進行時
表達。示例:
var loadingData = ajax.get('url');
loadingData.then(callback);
//
後跟一個空格,縮進與下一行被註釋說明的代碼一致。/*...*/
這樣的多行註釋。有多行註釋內容時,使用多個單行註釋。/**...*/
形式的塊註釋中。解釋:
{
開始, 以}
結束。解釋:
經常使用類型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}。
類型不只侷限於內置的類型,也能夠是自定義的類型。好比定義了一個類 Developer,就可使用它來定義一個參數和返回值的類型。
類型定義 |
語法示例 |
解釋 |
String |
{string} |
-- |
Number |
{number} |
-- |
Boolean |
{boolean} |
-- |
Object |
{Object} |
-- |
Function |
{Function} |
-- |
RegExp |
{RegExp} |
-- |
Array |
{Array} |
-- |
Date |
{Date} |
-- |
單一類型集合 |
{Array.<string>} |
string 類型的數組 |
多類型 |
{(number|boolean)} |
多是 number 類型, 也多是 boolean 類型 |
容許爲null |
{?number} |
多是 number, 也多是 null |
不容許爲null |
{!Object} |
Object 類型, 但不是 null |
Function類型 |
{function(number, boolean)} |
函數, 形參類型 |
Function帶返回值 |
{function(number, boolean):string} |
函數, 形參, 返回值類型 |
參數可選 |
@param {string=} name |
可選參數, =爲類型後綴 |
可變參數 |
@param {...number} args |
變長參數, ...爲類型前綴 |
任意類型 |
{*} |
任意類型 |
可選任意類型 |
@param {*=} name |
可選參數,類型不限 |
可變任意類型 |
@param {...*} args |
變長參數,類型不限 |
@file
標識文件說明。示例:
/**
* @file Describe the file
*/
@author
標識開發者信息。解釋:
開發者信息可以體現開發人員對文件的貢獻,而且可以讓遇到問題或但願瞭解相關信息的人找到維護人。一般狀況文件在被建立時標識的是建立者。隨着項目的進展,愈來愈多的人加入,參與這個文件的開發,新的做者應該被加入 @author
標識。
@author
標識具備多人時,原則是按照 責任
進行排序。一般的說就是若是有問題,就是找第一我的應該比找第二我的有效。好比文件的建立者因爲各類緣由,模塊移交給了其餘人或其餘團隊,後來由於新增需求,其餘人在新增代碼時,添加 @author
標識應該把本身的名字添加在建立人的前面。
@author
中的名字不容許被刪除。任何勞動成果都應該被尊重。
業務項目中,一個文件可能被多人頻繁修改,而且每一個人的維護時間均可能不會很長,不建議爲文件增長 @author
標識。經過版本控制系統追蹤變動,按業務邏輯單元肯定模塊的維護責任人,經過文檔與wiki跟蹤和查詢,是更好的責任管理方式。
對於業務邏輯無關的技術型基礎項目,特別是開源的公共項目,應使用 @author
標識。
示例:
/**
* @file Describe the file
* @author author-name(mail-name@domain.com)
* author-name2(mail-name2@domain.com)
*/
@namespace
標識。示例:
/**
* @namespace
*/
var util = {};
@class
標記類或構造函數。解釋:
對於使用對象 constructor
屬性來定義的構造函數,可使用 @constructor
來標記。
示例:
/**
* 描述
*
* @class
*/
function Developer() {
// constructor body
}
@extends
標記類的繼承信息。示例:
/**
* 描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this);
// constructor body
}
util.inherits(Fronteer, Developer);
@lends
進行從新指向。解釋:
沒有 @lends
標記將沒法爲該類生成包含擴展類成員的文檔。
示例:
/**
* 類描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this);
// constructor body
}
util.extend(
Fronteer.prototype,
/** @lends Fronteer.prototype */{
_getLevel: function () {
// TODO
}
}
);
@public
/ @protected
/ @private
中的任意一個,指明可訪問性。解釋:
生成的文檔中將有可訪問性的標記,避免用戶直接使用非 public
的屬性或方法。
示例:
/**
* 類描述
*
* @class
* @extends Developer
*/
var Fronteer = function () {
Developer.call(this);
/**
* 屬性描述
*
* @type {string}
* @private
*/
this._level = 'T12';
// constructor body
};
util.inherits(Fronteer, Developer);
/**
* 方法描述
*
* @private
* @return {string} 返回值描述
*/
Fronteer.prototype._getLevel = function () {
};
@inner
標識。示例:
/**
* 函數描述
*
* @param {string} p1 參數1的說明
* @param {string} p2 參數2的說明,比較長
* 那就換行了.
* @param {number=} p3 參數3的說明(可選)
* @return {Object} 返回值描述
*/
function foo(p1, p2, p3) {
var p3 = p3 || 10;
return {
p1: p1,
p2: p2,
p3: p3
};
}
@param
標識。示例:
/**
* 函數描述
*
* @param {Object} option 參數描述
* @param {string} option.url option項描述
* @param {string=} option.method option項描述,可選參數
*/
function foo(option) {
// TODO
}
@override
標識。若是重寫的形參個數、類型、順序和返回值類型均未發生變化,可省略@param
、@return
,僅用 @override
標識,不然仍應做完整註釋。解釋:
簡而言之,當子類重寫的方法能直接套用父類的方法註釋時可省略對參數與返回值的註釋。
@event
標識事件,事件參數的標識與方法描述的參數標識相同。示例:
/**
* 值變動時觸發
*
* @event
* @param {Object} e e描述
* @param {string} e.before before描述
* @param {string} e.after after描述
*/
onchange: function (e) {
}
@fires
標識廣播的事件,在廣播事件代碼前使用 @event
標識事件。@param
標識,生成文檔時可讀性更好。示例:
/**
* 點擊處理
*
* @fires Select#change
* @private
*/
Select.prototype.clickHandler = function () {
/**
* 值變動時觸發
*
* @event Select#change
* @param {Object} e e描述
* @param {string} e.before before描述
* @param {string} e.after after描述
*/
this.fire(
'change',
{
before: 'foo',
after: 'bar'
}
);
};
@const
標記,幷包含說明和類型信息。示例:
/**
* 常量說明
*
* @const
* @type {string}
*/
var REQUEST_URL = 'myurl.do';
@typedef
標識來定義。示例:
// `namespaceA~` 能夠換成其它 namepaths 前綴,目的是爲了生成文檔中能顯示 `@typedef` 定義的類型和連接。
/**
* 服務器
*
* @typedef {Object} namespaceA~Server
* @property {string} host 主機
* @property {number} port 端口
*/
/**
* 服務器列表
*
* @type {Array.<namespaceA~Server>}
*/
var servers = [
{
host: '1.2.3.4',
port: 8080
},
{
host: '1.2.3.5',
port: 8081
}
];
對於內部實現、不容易理解的邏輯說明、摘要信息等,咱們可能須要編寫細節註釋。
示例:
function foo(p1, p2) {
// 這裏對具體內部邏輯進行說明
// 說明太長鬚要換行
for (...) {
....
}
}
解釋:
var
定義。解釋:
不經過 var 定義變量將致使變量污染全局環境。
示例:
// good
var name = 'MyName';
// bad
name = 'MyName';
var
只能聲明一個變量。解釋:
一個 var 聲明多個變量,容易致使較長的行長度,而且在修改時容易形成逗號和分號的混淆。
示例:
// good
var hangModules = [];
var missModules = [];
var visited = {};
// bad
var hangModules = [],
missModules = [],
visited = {};
即用即聲明
,不得在函數或其它形式的代碼塊起始位置統一聲明全部變量。解釋:
變量聲明與使用的距離越遠,出現的跨度越大,代碼的閱讀與維護成本越高。雖然JavaScript的變量是函數做用域,仍是應該根據編程中的意圖,縮小變量出現的距離空間。
示例:
// good
function kv2List(source) {
var list = [];
for (var key in source) {
if (source.hasOwnProperty(key)) {
var item = {
k: key,
v: source[key]
};
list.push(item);
}
}
return list;
}
// bad
function kv2List(source) {
var list = [];
var key;
var item;
for (key in source) {
if (source.hasOwnProperty(key)) {
item = {
k: key,
v: source[key]
};
list.push(item);
}
}
return list;
}
===
。僅當判斷 null 或 undefined 時,容許使用 == null
。解釋:
使用 === 能夠避免等於判斷中隱式的類型轉換。
示例:
// good
if (age === 30) {
// ......
}
// bad
if (age == 30) {
// ......
}
示例:
// 字符串爲空
// good
if (!name) {
// ......
}
// bad
if (name === '') {
// ......
}
// 字符串非空
// good
if (name) {
// ......
}
// bad
if (name !== '') {
// ......
}
// 數組非空
// good
if (collection.length) {
// ......
}
// bad
if (collection.length > 0) {
// ......
}
// 布爾不成立
// good
if (!notTrue) {
// ......
}
// bad
if (notTrue === false) {
// ......
}
// null 或 undefined
// good
if (noValue == null) {
// ......
}
// bad
if (noValue === null || typeof noValue === 'undefined') {
// ......
}
解釋:
按執行頻率排列分支的順序好處是:
switch
代替 if
。示例:
// good
switch (typeof variable) {
case 'object':
// ......
break;
case 'number':
case 'boolean':
case 'string':
// ......
break;
}
// bad
var type = typeof variable;
if (type === 'object') {
// ......
}
else if (type === 'number' || type === 'boolean' || type === 'string') {
// ......
}
else
塊後沒有任何語句,能夠刪除 else
。示例:
// good
function getName() {
if (name) {
return name;
}
return 'unnamed';
}
// bad
function getName() {
if (name) {
return name;
}
else {
return 'unnamed';
}
}
解釋:
循環體中的函數表達式,運行過程當中會生成循環次數個函數對象。
示例:
// good
function clicker() {
// ......
}
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
addListener(element, 'click', clicker);
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
addListener(element, 'click', function () {});
}
示例:
// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = width;
// ......
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = wrap.offsetWidth + 'px';
// ......
}
length
。解釋:
雖然現代瀏覽器都對數組長度進行了緩存,但對於一些宿主對象和老舊瀏覽器的數組對象,在每次 length 訪問時會動態計算元素個數,此時緩存 length 能有效提升程序性能。
示例:
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
// ......
}
解釋:
逆序遍歷能夠節省變量,代碼比較優化。
示例:
var len = elements.length;
while (len--) {
var element = elements[len];
// ......
}
typeof
。對象類型檢測使用 instanceof
。null
或 undefined
的檢測使用 == null
。示例:
// string
typeof variable === 'string'
// number
typeof variable === 'number'
// boolean
typeof variable === 'boolean'
// Function
typeof variable === 'function'
// Object
typeof variable === 'object'
// RegExp
variable instanceof RegExp
// Array
variable instanceof Array
// null
variable === null
// null or undefined
variable == null
// undefined
typeof variable === 'undefined'
string
時,使用 + ''
。示例:
// good
num + '';
// bad
new String(num);
num.toString();
String(num);
number
時,一般使用 +
。示例:
// good
+str;
// bad
Number(str);
string
轉換成 number
,要轉換的字符串結尾包含非數字並指望忽略時,使用 parseInt
。示例:
var width = '200px';
parseInt(width, 10);
parseInt
時,必須指定進制。示例:
// good
parseInt(str, 10);
// bad
parseInt(str);
boolean
時,使用 !!
。示例:
var num = 3.14;
!!num;
number
去除小數點,使用 Math.floor / Math.round / Math.ceil
,不使用 parseInt
。示例:
// good
var num = 3.14;
Math.ceil(num);
// bad
var num = 3.14;
parseInt(num, 10);
'
。解釋:
示例:
var str = '我是一個字符串';
var html = '<div class="cls">拼接HTML能夠省去雙引號轉義</div>';
數組
或 +
拼接字符串。解釋:
示例:
// 使用數組拼接字符串
var str = [
// 推薦換行開始並縮進開始第一個字符串, 對齊代碼, 方便閱讀.
'<ul>',
'<li>第一項</li>',
'<li>第二項</li>',
'</ul>'
].join('');
// 使用 + 拼接字符串
var str2 = '' // 建議第一個爲空字符串, 第二個換行開始並縮進開始, 對齊代碼, 方便閱讀
+ '<ul>',
+ '<li>第一項</li>',
+ '<li>第二項</li>',
+ '</ul>';
解釋:
使用模板引擎有以下好處:
{}
建立新 Object
。示例:
// good
var obj = {};
// bad
var obj = new Object();
屬性
都可以不添加引號,則全部 屬性
不得添加引號。示例:
var info = {
name: 'someone',
age: 28
};
屬性
須要添加引號,則全部 屬性
必須添加 '
。解釋:
若是屬性不符合 Identifier 和 NumberLiteral 的形式,就須要以 StringLiteral 的形式提供。
示例:
// good
var info = {
'name': 'someone',
'age': 28,
'more-info': '...'
};
// bad
var info = {
name: 'someone',
age: 28,
'more-info': '...'
};
示例:
// 如下行爲絕對禁止
String.prototype.trim = function () {
};
.
。解釋:
屬性名符合 Identifier 的要求,就能夠經過 .
來訪問,不然就只能經過 [expr]
方式訪問。
一般在 JavaScript 中聲明的對象,屬性命名是使用 Camel 命名法,用 .
來訪問更清晰簡潔。部分特殊的屬性(好比來自後端的JSON),可能採用不尋常的命名方式,能夠經過 [expr]
方式訪問。
示例:
info.age;
info['more-info'];
for in
遍歷對象時, 使用 hasOwnProperty
過濾掉原型中的屬性。示例:
var newInfo = {};
for (var key in info) {
if (info.hasOwnProperty(key)) {
newInfo[key] = info[key];
}
}
[]
建立新數組,除非想要建立的是指定長度的數組。示例:
// good
var arr = [];
// bad
var arr = new Array();
for in
。解釋:
數組對象可能存在數字之外的屬性, 這種狀況下 for in 不會獲得正確結果.
示例:
var arr = ['a', 'b', 'c'];
arr.other = 'other things'; // 這裏僅做演示, 實際中應使用Object類型
// 正確的遍歷方式
for (var i = 0, len = arr.length; i < len; i++) {
console.log(i);
}
// 錯誤的遍歷方式
for (i in arr) {
console.log(i);
}
sort
方法。解釋:
本身實現的常規排序算法,在性能上並不優於數組默認的 sort 方法。如下兩種場景能夠本身實現排序:
.length = 0
。50
行之內。解釋:
將過多的邏輯單元混在一個大函數中,易致使難以維護。一個清晰易懂的函數應該完成單一的邏輯單元。複雜的操做應進一步抽取,經過函數的調用來體現流程。
特定算法等不可分割的邏輯容許例外。
示例:
function syncViewStateOnUserAction() {
if (x.checked) {
y.checked = true;
z.value = '';
}
else {
y.checked = false;
}
if (!a.value) {
warning.innerText = 'Please enter it';
submitButton.disabled = true;
}
else {
warning.innerText = '';
submitButton.disabled = false;
}
}
// 直接閱讀該函數會難以明確其主線邏輯,所以下方是一種更合理的表達方式:
function syncViewStateOnUserAction() {
syncXStateToView();
checkAAvailability();
}
function syncXStateToView() {
if (x.checked) {
y.checked = true;
z.value = '';
}
else {
y.checked = false;
}
}
function checkAAvailability() {
if (!a.value) {
displayWarningForAMissing();
}
else {
clearWarnignForA();
}
}
6
個之內。解釋:
除去不定長參數之外,函數具有不一樣邏輯意義的參數建議控制在 6 個之內,過多參數會致使維護難度增大。
options
參數傳遞非數據輸入型參數。解釋:
有些函數的參數並非做爲算法的輸入,而是對算法的某些分支條件判斷之用,此類參數建議經過一個 options 參數傳遞。
以下函數:
/**
* 移除某個元素
*
* @param {Node} element 須要移除的元素
* @param {boolean} removeEventListeners 是否同時將全部註冊在元素上的事件移除
*/
function removeElement(element, removeEventListeners) {
element.parent.removeChild(element);
if (removeEventListeners) {
element.clearEventListeners();
}
}
能夠轉換爲下面的簽名:
/**
* 移除某個元素
*
* @param {Node} element 須要移除的元素
* @param {Object} options 相關的邏輯配置
* @param {boolean} options.removeEventListeners 是否同時將全部註冊在元素上的事件移除
*/
function removeElement(element, options) {
element.parent.removeChild(element);
if (options.removeEventListeners) {
element.clearEventListeners();
}
}
這種模式有幾個顯著的優點:
null
。解釋:
在 JavaScript 中,無需特別的關鍵詞就可使用閉包,一個函數能夠任意訪問在其定義的做用域外的變量。須要注意的是,函數的做用域是靜態的,即在定義時決定,與調用的時機和方式沒有任何關係。
閉包會阻止一些變量的垃圾回收,對於較老舊的JavaScript引擎,可能致使外部全部變量均沒法回收。
首先一個較爲明確的結論是,如下內容會影響到閉包內變量的回收:
Chakra、V8 和 SpiderMonkey 將受以上因素的影響,表現出不盡相同又較爲類似的回收策略,而JScript.dll和Carakan則徹底沒有這方面的優化,會完整保留整個 LexicalEnvironment 中的全部變量綁定,形成必定的內存消耗。
因爲對閉包內變量有回收優化策略的 Chakra、V8 和 SpiderMonkey 引擎的行爲較爲類似,所以能夠總結以下,當返回一個函數 fn 時:
對於Chakra引擎,暫沒法得知是按 V8 的模式仍是按 SpiderMonkey 的模式進行。
若是有 很是龐大 的對象,且預計會在 老舊的引擎 中執行,則使用閉包時,注意將閉包不須要的對象置爲空引用。
IIFE
避免 Lift
效應
。解釋:
在引用函數外部變量時,函數執行時外部變量的值由運行時決定而非定義時,最典型的場景以下:
var tasks = [];
for (var i = 0; i < 5; i++) {
tasks[tasks.length] = function () {
console.log('Current cursor is at ' + i);
};
}
var len = tasks.length;
while (len--) {
tasks[len]();
}
以上代碼對 tasks 中的函數的執行均會輸出 Current cursor is at 5
,每每不符合預期。
此現象稱爲 Lift 效應 。解決的方式是經過額外加上一層閉包函數,將須要的外部變量做爲參數傳遞來解除變量的綁定關係:
var tasks = [];
for (var i = 0; i < 5; i++) {
// 注意有一層額外的閉包
tasks[tasks.length] = (function (i) {
return function () {
console.log('Current cursor is at ' + i);
};
})(i);
}
var len = tasks.length;
while (len--) {
tasks[len]();
}
new Function()
的形式。示例:
var emptyFunction = function () {};
示例:
var EMPTY_FUNCTION = function () {};
function MyClass() {
}
MyClass.prototype.abstractMethod = EMPTY_FUNCTION;
MyClass.prototype.hooks.before = EMPTY_FUNCTION;
MyClass.prototype.hooks.after = EMPTY_FUNCTION;
constructor
。解釋:
一般使用其餘 library 的類繼承方案都會進行 constructor 修正。若是是本身實現的類繼承方案,須要進行 constructor 修正。
示例:
/**
* 構建類之間的繼承關係
*
* @param {Function} subClass 子類函數
* @param {Function} superClass 父類函數
*/
function inherits(subClass, superClass) {
var F = new Function();
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
}
constructor
的正確性。示例:
function Animal(name) {
this.name = name;
}
// 直接prototype等於對象時,須要修正constructor
Animal.prototype = {
constructor: Animal,
jump: function () {
alert('animal ' + this.name + ' jump');
}
};
// 這種方式擴展prototype則無需理會constructor
Animal.prototype.jump = function () {
alert('animal ' + this.name + ' jump');
};
解釋:
原型對象的成員被全部實例共享,能節約內存佔用。因此編碼時咱們應該遵照這樣的原則:原型對象包含程序不會修改的成員,如方法函數或配置項。
function TextNode(value, engine) {
this.value = value;
this.engine = engine;
}
TextNode.prototype.clone = function () {
return this;
};
事件名
必須全小寫。解釋:
在 JavaScript 普遍應用的瀏覽器環境,絕大多數 DOM 事件名稱都是全小寫的。爲了遵循大多數 JavaScript 開發者的習慣,在設計自定義事件時,事件名也應該全小寫。
event
參數。若是事件須要傳遞較多信息,應仔細設計事件對象。解釋:
一個事件對象的好處有:
解釋:
常見禁止默認行爲的方式有兩種:
eval
函數。解釋:
直接 eval,指的是以函數方式調用 eval 的調用方法。直接 eval 調用執行代碼的做用域爲本地做用域,應當避免。
若是有特殊狀況須要使用直接 eval,需在代碼中用詳細的註釋說明爲什麼必須使用直接 eval,不能使用其它動態執行代碼的方式,同時須要其餘資深工程師進行 Code Review。
eval
函數。new Function
執行動態代碼。解釋:
經過 new Function 生成的函數做用域是全局使用域,不會影響噹噹前的本地做用域。若是有動態代碼執行的需求,建議使用 new Function。
示例:
var handler = new Function('x', 'y', 'return x + y;');
var result = handler($('#x').val(), $('#y').val());
with
。解釋:
使用 with 可能會增長代碼的複雜度,不利於閱讀和管理;也會對性能有影響。大多數使用 with 的場景都能使用其餘方式較好的替代。因此,儘可能不要使用 with。
delete
的使用。解釋:
若是沒有特別的需求,減小或避免使用delete
。delete
的使用會破壞部分 JavaScript 引擎的性能優化。
delete
可能產生的異常。解釋:
對於有被遍歷需求,且值 null 被認爲具備業務邏輯意義的值的對象,移除某個屬性必須使用 delete 操做。
在嚴格模式或IE下使用 delete 時,不能被刪除的屬性會拋出異常,所以在不肯定屬性是否能夠刪除的狀況下,建議添加 try-catch 塊。
示例:
try {
delete o.x;
}
catch (deleteError) {
o.x = null;
}
解釋:
JavaScript 因其腳本語言的動態特性,當一個對象未被 seal 或 freeze 時,能夠任意添加、刪除、修改屬性值。
可是隨意地對 非自身控制的對象 進行修改,很容易形成代碼在不可預知的狀況下出現問題。所以,設計良好的組件、函數應該避免對外部傳入的對象的修改。
下面代碼的 selectNode 方法修改了由外部傳入的 datasource 對象。若是 datasource 用在其它場合(如另外一個 Tree 實例)下,會形成狀態的混亂。
function Tree(datasource) {
this.datasource = datasource;
}
Tree.prototype.selectNode = function (id) {
// 從datasource中找出節點對象
var node = this.findNode(id);
if (node) {
node.selected = true;
this.flushView();
}
};
對於此類場景,須要使用額外的對象來維護,使用由自身控制,不與外部產生任何交互的 selectedNodeIndex 對象來維護節點的選中狀態,不對 datasource 做任何修改。
function Tree(datasource) {
this.datasource = datasource;
this.selectedNodeIndex = {};
}
Tree.prototype.selectNode = function (id) {
// 從datasource中找出節點對象
var node = this.findNode(id);
if (node) {
this.selectedNodeIndex[id] = true;
this.flushView();
}
};
除此以外,也能夠經過 deepClone 等手段將自身維護的對象與外部傳入的分離,保證不會相互影響。
解釋:
document.getElementById
獲取,避免使用document.all
。context.getElementsByTagName
獲取。其中 context
能夠爲 document
或其餘元素。指定 tagName
參數爲 *
能夠得到全部子元素。解釋:
原生獲取元素集合的結果並不直接引用 DOM 元素,而是對索引進行讀取,因此 DOM 結構的改變會實時反映到結果中。
示例:
<div></div>
<span></span>
<script>
var elements = document.getElementsByTagName('*');
// 顯示爲 DIV
alert(elements[0].tagName);
var div = elements[0];
var p = document.createElement('p');
docpment.body.insertBefore(p, div);
// 顯示爲 P
alert(elements[0].tagName);
</script>
children
。避免使用childNodes
,除非預期是須要包含文本、註釋和屬性類型的節點。getComputedStyle
或 currentStyle
。解釋:
經過 style 只能得到內聯定義或經過 JavaScript 直接設置的樣式。經過 CSS class 設置的元素樣式沒法直接經過 style 獲取。
解釋:
除了 IE,標準瀏覽器會忽略不規範的屬性值,致使兼容性問題。
DOM
時,儘可能減小頁面 reflow
。解釋:
頁面 reflow 是很是耗時的行爲,很是容易致使性能瓶頸。下面一些場景會觸發瀏覽器的reflow:
DOM
操做。解釋:
DOM 操做也是很是耗時的一種操做,減小 DOM 操做有助於提升性能。舉一個簡單的例子,構建一個列表。咱們能夠用兩種方式:
第一種方法看起來比較標準,可是每次循環都會對 DOM 進行操做,性能極低。在這裏推薦使用第二種方法。
addEventListener / attachEvent
綁定事件,避免直接在 HTML 屬性中或 DOM 的 expando
屬性綁定事件處理。解釋:
expando 屬性綁定事件容易致使互相覆蓋。
addEventListener
時第三個參數使用 false
。解釋:
標準瀏覽器中的 addEventListener 能夠經過第三個參數指定兩種時間觸發模型:冒泡和捕獲。而 IE 的 attachEvent 僅支持冒泡的事件觸發。因此爲了保持一致性,一般 addEventListener 的第三個參數都爲 false。