1. 風格
一千個讀者有一千個哈姆雷特
,每個人都有自己的code style。我也曾為了要不要加分號給同事鬧個臉紅脖子粗,實際上有必要嗎? 其實JavaScript已經有了比較流行的幾個風格
我自己使用的是JavaScript Standard Style
, 我之所以使用這個,是因為它有一些工具。可以讓你寫完程式碼後,一旦儲存,就自動幫你把你的風格的程式碼修正成標準分割,而不是死記硬背應該怎麼寫。看完這個頁面,你就應該立馬愛上JavaScript Standard Style , 如果你用vscode, 恰好你有寫vue, 你想在.vue檔案中使用standard風格,那麼你需要看看這篇文章
2. 可維護性
很多時候,我們不是從零開始,開發新程式碼。而是去維護別人的程式碼,以他人的工作成果為基礎。確保自己的程式碼可維護,是贈人玫瑰,手留餘香的好事。一方面讓別人看的舒服,另一方面也防止自己長時間沒看過自己的程式碼,自己都難以理解。
2.1. 什麼是可維護程式碼
可維護的程式碼的一些特徵
-
可理解
易於理解程式碼的用途 -
可適應
資料的變化,不需要完全重寫程式碼 -
可擴充套件
要考慮未來對核心功能的擴充套件 -
可除錯
給出足夠的資訊,讓除錯的時候,確定問題所在 -
不可分割
函式的功能要單一,功能粒度不可分割,可複用性增強
2.2. 程式碼約定
2.2.1. 可讀性
- 統一的縮排方式
- 註釋
- 空白行
2.2.1.1. 縮排:
- 一般使用4個空格
- 不用製表符的原因是它在不同編輯器裡顯示效果不同
2.2.1.2. 註釋:哪些地方需要註釋?
- 函式和方法
- 大段程式碼
- 複雜的演算法
- hack
2.2.1.3. 空白行:哪些地方需要空白行?
- 方法之間
- 方法裡的區域性變數和第一個語句之間
- 單行或者多行註釋
- 方法內衣個邏輯單元之間
// Good
if (wl && wl.length) {
for (i = 0, l = wl.length; i < l; ++i) {
p = wl[i];
type = Y.Lang.type(r[p]);
if (s.hasOwnProperty(p)) {
if (merge && type == `object`) {
Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
r[p] = s[p];
}
}
}
}
2.2.2. 變數名和函式名
There are only two hard problem in Computer Science cache invalidation and naming things.—Phil Karlton
- 駝峰式命名
- 變數名以名詞開頭
- 方法名以動詞開頭
- 常量全部大寫
- 建構函式以大寫字母開頭
- jQuery物件以”$”符號開頭
- 自定義事件處理函式以“on”開頭
// Good
var count = 10;
var myName = "wdd";
var found = true;
// Bad: Easily confused with functions
var getCount = 10;
var isFound = true;
// Good
function getName() {
return myName;
}
// Bad: Easily confused with variable
function theName() {
return myName;
}
// Bad:
var btnOfSubmit = $(`#submit`);
// Good:
var $btnOfSubmit = $(`#submit`);
// Bad:給App新增一個處理聊天事件的函式,一般都是和websocket服務端推送訊息相關
App.addMethod(`createChat`,function(res){
App.log(res);
});
// Bad: 此處呼叫,這裡很容易誤以為這個函式是處理建立聊天的邏輯函式
App.createChat();
// Good:
App.addMethod(`onCreateChat`,function(res){
App.log(res);
});
// Good:此處呼叫
App.onCreateChat();
變數命名不僅僅是一種科學,更是一種藝術。總之,要短小精悍,見名知意。有些名詞可以反應出變數的型別。
2.2.2.1. 變數名
名詞 | 資料型別含義 |
---|---|
count, length,size | 數值 |
name, title,message | 字串 |
i, j, k | 用來迴圈 |
car,person,student,user | 物件 |
success,fail | 布林值 |
payload | post資料的請求體 |
method | 請求方式 |
2.2.2.2. 函式名
動詞 | 含義 |
---|---|
can | Function returns a boolean |
has | Function returns a boolean |
is | Function returns a boolean |
get | Function returns a nonboolean |
set | Function is used to save a value |
2.2.2.3. 一些與函式名搭配的常用動詞
動詞 | 用法 |
---|---|
send | 傳送 |
resend | 重發 |
validate | 驗證 |
query | 查詢 |
create | 建立 |
add | 新增 |
delete | 刪除 |
remove | 移除 |
insert | 插入 |
update | 更新,編輯 |
copy | 複製 |
render | 渲染 |
close | 關閉 |
open | 開啟 |
clear | 清除 |
edit | 編輯 |
query | 查詢 |
on | 當事件發生 |
list | 渲染一個列表,如使用者列表renderUsersList() |
content | 渲染內容,如使用者詳情的頁面 renderUserContent() |
2.2.2.4. 介面常用的動詞
對於http請求的最常用的四種方法,get,post,put,delete,有一些常用的名詞與其對應
含義 | 請求方法 | 詞語 | 栗子 |
---|---|---|---|
增加 | post | create | createUser,createCall |
刪除 | delete | delete | deleteUser |
修改 | put | update | updateUser,updateProfile |
查詢 | get | get,query | getUser,queryUser(無條件查詢使用get,有條件查詢使用query) |
2.2.2.5. 學會使用單複數命名函式
函式名 | 含義 |
---|---|
getUser() | 獲取一個使用者,一般是通過唯一的id來獲取 |
getUsers() | 獲取一組使用者,一般是通過一些條件來獲取 |
createUser() | 建立一個使用者 |
createUsers() | 建立一組使用者 |
2.2.2.6. 常量
var MAX_COUNT = 10;
var URL = "http://www.nczonline.net/";
2.2.2.7. 建構函式
// Good
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
alert(this.name);
};
var me = new Person("wdd");
2.2.2.8. 底層http請求介面函式
- 建議使用“_”開頭,例如App._getUsers();而對於介面函式的封裝,例如App.getUsers(),內部邏輯呼叫App._getUsers();
2.2.3. 檔名
- 全部使用小寫字母
- 單詞之間的間隔使用“-”
eg:
app-main.js
app-event.js
app-user-manger.js
2.2.4. 檔案歸類
自己寫的js檔案最好和引用的一些第三方js分別放置在不同的資料夾下。
2.2.5. 千萬別用alert
alert的缺點
- 如果你用alert來顯示提醒訊息,那麼使用者除了點選alert上的的確定按鈕外,就只能點選上面的關閉,或者選擇禁止再選擇對話方塊,除此以外什麼都不能操作。
- 有些瀏覽器如果禁止了alert的選項,那麼你的alert是不會顯示的
- 如果你在try catch語句裡使用alert,那麼console裡將不會輸出錯誤資訊,你都沒辦法檢視錯誤的詳細原因,以及儲出錯的位置。
更優雅的提醒方式
- console.log() 普通提示訊息
- console.error() 錯誤提示訊息
- console.info() 資訊提示訊息
- console.warn() 警告提示訊息
2.3. 鬆散耦合
- html檔案中儘可能避免寫js語句
- 儘量避免在js更改某個css類的屬性,而使用更改類的方法
- 不要在css中寫js的表示式
- 解耦應用邏輯和事件處理程式
2.3.1. 將應用邏輯和事件處理程式的解耦
//一般事件訂閱的寫法,以jQuery的寫法為栗子
$(document).on(`click`,`#btn-get-users`,function(event){
event.stopPropagation();
//下面的省略號表示執行獲取所有用於並顯示在頁面上的邏輯
// Bad
...
...
...
//
});
如果增加了需求,當點選另外一個按鈕的時候,也要執行獲取所有使用者並顯示在頁面上,那麼上面省略的程式碼又要複製一份。如果介面有改動,那麼需要在兩個不同的地方都要修改。
所以,應該這樣。
$(document).on(`click`,`#btn-get-users`,function(event){
event.stopPropagation();
//將應用邏輯分離在其他個函式中
// Good
App.getUsers();
App.renderUsers();
});
2.3.2. 鬆散解耦規則
- 不要將event物件傳給其他方法,只傳遞來自event物件中的某些資料
- 任何事件處理程式都應該只處理事件,然後把處理轉交給應用邏輯。
2.3.3. 將非同步請求和資料處理解耦
// Bad
ReqApi.tenant.queryUsers({},function(res){
if(!res.success){
console.error(res);
return;
}
//對資料的處理
...
...
...
});
上面程式碼對資料的處理直接寫死在非同步請求裡面,如果換了一個請求,但是資料處理方式是一樣的,那麼又要複製一遍資料處理的程式碼。最好的方式是將資料處理模組化成為一個函式。
// Good
ReqApi.tenant.queryUsers({},function(res){
if(!res.success){
console.error(res);
return;
}
//對資料的處理
App.renderUsers(res.data);
});
非同步請求只處理請求,不處理資料。函式的功能要專一,功能粒度不可分割。
2.3.4. 不要將某個變數寫死在函式中,儘量使用引數傳遞進來
如果你需要一個函式去驗證輸入框是否是空,如下。這種方式就會繫結死了這個只能驗證id為test的輸入框,換成其他的就不行
// bad
function checkInputIsEmpty(){
var value = $(`#test`).val();
if(value){
return true;
}
else{
return false;
}
}
// good
function isEmptyInput(id){
var value = $(`#`+id).val();
if(value){
return true;
}
else{
return false;
}
}
2.4. 程式設計實踐
2.4.1. 尊總物件所有權
javascript動態性質是的幾乎任何東西在任何時間都能更改,這樣就很容易覆寫了一些預設的方法。導致一些災難性的後果。如果你不負責或者維護某個物件,那麼你就不能對它進行修改。
- 不要為例項或原型新增屬性
- 不要為例項或者原型新增方法
- 不要重定義存已存在的方法
2.4.2. 避免全域性變數
// Bad 兩個全域性變數
var name = "wdd";
funtion getName(){
console.log(name);
}
// Good 一個全域性變數
var App = {
name:"wdd",
sayName:funtion(){
console.log(this.name);//如果這個函式當做回撥數使用,這個this可能指向window,
}
};
單一的全域性變數便是名稱空間的概念,例如雅虎的YUI,jQuery的$等。
2.4.3. 避免與null進行比較
funtion sortArray(values){
// 避免
if(values != null){
values.sort(comparator);
}
}
function sortArray(values){
// 推薦
if(values instanceof Array){
values.sort(compartor);
}
}
2.4.3.1. 與null進行比較的程式碼,可以用以下技術進行替換
- 如果值是一個應用型別,使用instanceof操作符,檢查其建構函式
- 如果值是基本型別,使用typeof檢查其型別
- 如果是希望物件包含某個特定的方法名,則只用typeof操作符確保指定名字的方法存在於物件上。
程式碼中與null比較越少,就越容易確定程式碼的目的,消除不必要的錯誤。
2.4.4. 從程式碼中分離配置檔案
配置資料是一些硬程式碼(hardcoded),看下面的栗子
function validate(value){
if(!value){
alert(`Invalid value`);
location.href = `/errors/invalid.php`;
}
}
上面程式碼裡有兩個配置資料,一個是UI字串(`Invalid value`),另一個是一個Url(`/error/invalid.php`)。如果你把他們寫死在程式碼裡,那麼如果當你需要修改這些地方的時候,那麼你必須一處一處的檢查並修改,而且還可能會遺漏。
2.4.4.1. 所以第一步是要區分,哪些程式碼應該寫成配置檔案的形式?
- 顯示在UI元素中的字串
- URL
- 一些重複的唯一值
- 一些設定變數
- 任何可能改變的值
2.4.4.2. 一些例子
var Config = {
"MSG_INVALID_VALUE":"Invalid value",
"URL_INVALID":"/errors/invalid.php"
}
2.4.5. 除錯資訊開關
在開發過程中,可能隨處留下幾個console.log,或者alert語句,這些語句在開發過程中是很有價值的。但是專案一旦進入生產環境,過多的console.log可能影響到瀏覽器的執行效率,過多的alert會降低程式的使用者體驗。而我們最好不要在進入生產環境前,一處一處像掃雷一樣刪除或者註釋掉這些除錯語句。
最好的方式是設定一個開關。
//全域性命令空間
var App = {
debug:true,
log:function(msg){
if(debug){
console.log(msg);
}
},
alert:function(msg){
if(debug){
alert(msg);
}
}
};
//使用
App.log(`獲取使用者資訊成功`);
App.alert(`密碼不匹配`);
//關閉日誌輸出與alert
App.debug = false;
2.4.6. 使用jQuery Promise
沒使用promise之前的回撥函式寫法
// bad:沒使用promise之前的回撥函式寫法
function sendRequest(req,successCallback,errorCallback){
var inputData = req.data || {};
inputData = JSON.stringify(inputData);
$.ajax({
url:req.base+req.destination,
type:req.type || "get",
headers:{
sessionId:session.id
},
data:inputData,
dataType:"json",
contentType : `application/json; charset=UTF-8`,
success:function(data){
successCallback(data);
},
error:function(data){
console.error(data);
errorCallback(data);
}
});
}
//呼叫
sendRequest(req,function(res){
...
},function(res){
...
});
使用promise之後
function sendRequest(req){
var dfd = $.Deferred();
var inputData = req.data || {};
inputData = JSON.stringify(inputData);
$.ajax({
url:req.base+req.destination,
type:req.type || "get",
headers:{
sessionId:session.id
},
data:inputData,
dataType:"json",
contentType : `application/json; charset=UTF-8`,
success:function(data){
dfd.resolve(data);
},
error:function(data){
dfd.reject(data);
}
});
return dfd.promise();
}
//呼叫
sendRequest(req)
.done(function(){
//請求成功
...
})
.fail(function(){
//請求失敗
...
});
2.4.7. 顯示錯誤提醒,不要給後端介面背鍋
假如前端要去介面獲取使用者資訊並顯示出來,如果你的請求格式是正確的,但是介面返回400以上的錯誤,你必須通過提醒來告知測試,這個錯誤是介面的返回錯誤,而不是前端的邏輯錯誤。
2.4.8. REST化介面請求
對資源的操作包括獲取、建立、修改和刪除資源,這些操作正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。
對應方式
請求型別 | 介面字首 |
---|---|
GET | .get, |
POST | .create 或者 .get |
PUT | .update |
DELETE | .delete |
說明
- 有些介面雖然是獲取某一個資源,但是它使用的卻是POST請求,所以建議使用.get比較好
示例:
// 與使用者相關的介面
App.api.user = {};
// 獲取一個使用者: 一般來說是一個指定的Id,例如userId
App.api.user.getUser = function(){
...
};
// 獲取一組使用者: 一般來說是一些條件,獲取條件下的使用者,篩選符合條件的使用者
App.api.user.getUsers = function(){
...
};
// 建立一個使用者
App.api.user.createUser = function(){
};
// 建立一組使用者
App.api.user.createUsers = function(){
};
// 更新一個使用者
App.api.user.updateUser = function(){
};
// 更新一組使用者
App.api.user.updateUsers = function(){
};
// 更新一個使用者
App.api.user.updateUser = function(){
};
// 更新一組使用者
App.api.user.updateUsers = function(){
};
// 刪除一個使用者
App.api.user.deleteUser = function(){
};
// 刪除一組使用者
App.api.user.deleteUsers = function(){
};
3. 效能
3.1. 注意作用域
- 避免全域性查詢
- 避免with語句
3.2. 選擇正確的方法
-
優化迴圈
-
減值迭代
:從最大值開始,在迴圈中不斷減值的迭代器更加高效 -
簡化終止條件
:由於每次迴圈過程都會計算終止條件,所以必須保證它儘可能快。也就是避免其他屬性查詢 -
簡化迴圈體
:由於迴圈體是執行最多的,所以要確保其最大限度地優化。
-
- 展開迴圈
- 避免雙重解釋:
// **Bad** 某些程式碼求值
eval("alert(`hello`)");
// **Bad** 建立新函式
var sayHi = new Function("alert(`hello`)");
// **Bad** 設定超時
setTimeout("alert(`hello`)");
-
效能的其他注意事項
- 原生方法較快
- switch語句較快:可以適當的替換ifelse語句
case 的分支不要超過128條
- 位運算子較快
3.3. 最小化語句數
3.3.1. 多個變數宣告(廢棄
)
// 方式1:Bad
var count = 5;
var name = `wdd`;
var sex = `male`;
var age = 10;
// 方式2:Good
var count = 5,
name = `wdd`,
sex = `male`,
age = 10;
2017-03-07 理論上方式2可能要比方式1效能高一點。但是我在實際使用中,這個快一點幾乎是沒什麼感受的。就像你無法感受到小草的生長一樣。反而可讀性更為重要。所以,每行最好只定義一個變數,並且每行都有一個var,並用分號結尾。
3.3.2. 插入迭代值
// Good
var name = values[i++];
3.3.3. 使用陣列和物件字面量
// Good
var values = [`a`,`b`,`c`];
var person = {
name:`wdd`,
age:10
};
只要有可能,儘量使用陣列和物件字面量的表示式來消除不必要的語句
3.4. 優化DOM互動
在JavaScript各個方面中,DOM無疑是最慢的一部分。DOM操作與互動要消耗大量的時間。因為他們往往需要重新渲染整個頁面或者某一部分。進一步說,看似細微的操作也可能花很久來執行。因為DOM要處理非常多的資訊。理解如何優化與DOM的互動可以極大的提高指令碼完成的速度。
- 使用dom快取技術
- 最小化現場更新
- 使用innerHTML插入大段html
- 使用事件代理
3.4.1. Dom快取技術
呼叫頻率非常高的dom查詢,可以將DOM快取在於一個變數中
// 最簡單的dom快取
var domCache = {};
function myGetElement(tag){
return domCache[tag] = domCache[tag] || $(tag);
}
3.5. 避免過長的屬性查詢,設定一個快捷方式
// 先看下面的極端情況
app.user.mother.parent.home.name = `wdd`
app.user.mother.parent.home.adderess = `上海`
app.user.mother.parent.home.weather = `晴天`
// 更優雅的方式
var home = app.user.mother.parent.home;
home.name = `wdd`;
home.address = `上海`,
home.weather = `晴天`
注意
使用上面的方式是有前提的,必須保證app.user.mather.parent.home是一個物件,因為物件是傳遞的引用。如果他的型別是一個基本型別,例如:number,string,boolean,那麼複製操作僅僅是值傳遞,新定義的home的改變,並不會影響到app.user.mather.parent.home的改變。
4. 快捷方式
4.1. 字串轉數字
+`4.1` === 4.1
4.2. 數字轉字元
4.1+`` === `4.1`
4.3. 字串取整
`4.99` | 0 === 4
5. 通用編碼原則
建議讀者自行擴充套件
DRY(dont`t repeat yoursele: 不要重複你自己)
高內聚低耦合
開放閉合
最小意外
單一職責(single responsibility)
6. 高階技巧
6.1. 安全型別檢測
- javascript內建型別檢測並不可靠
- safari某些版本(<4)typeof正規表示式返回為function
建議使用Object.prototype.toString.call()方法檢測資料型別
function isArray(value){
return Object.prototype.toString.call(value) === "[object Array]";
}
function isFunction(value){
return Object.prototype.toString.call(value) === "[object Function]";
}
function isRegExp(value){
return Object.prototype.toString.call(value) === "[object RegExp]";
}
function isNativeJSON(){
return window.JSON && Object.prototype.toString.call(JSON) === "[object JSON]";
}
對於ie中一COM物件形式實現的任何函式,isFunction都返回false,因為他們並非原生的javascript函式。
在web開發中,能夠區分原生與非原生的物件非常重要。只有這樣才能確切知道某個物件是否有哪些功能
以上所有的正確性的前提是:Object.prototype.toString沒有被修改過
6.2. 作用域安全的建構函式
function Person(name){
this.name = name;
}
//使用new來建立一個物件
var one = new Person(`wdd`);
//直接呼叫建構函式
Person();
由於this是執行時分配的,如果你使用new來操作,this指向的就是one。如果直接呼叫建構函式,那麼this會指向全域性物件window,然後你的程式碼就會覆蓋window的原生name。如果有其他地方使用過window.name, 那麼你的函式將會埋下一個深藏的bug。
那麼,如何才能建立一個作用域安全的建構函式?
function Person(name){
if(this instanceof Person){
this.name = name;
}
else{
return new Person(name);
}
}
6.3. 惰性載入函式
假設有一個方法X,在A類瀏覽器裡叫A,在b類瀏覽器裡叫B,有些瀏覽器並沒有這個方法,你想實現一個跨瀏覽器的方法。
惰性載入函式的思想是:在函式內部改變函式自身的執行邏輯
function X(){
if(A){
return new A();
}
else{
if(B){
return new B();
}
else{
throw new Error(`no A or B`);
}
}
}
換一種寫法
function X(){
if(A){
X = function(){
return new A();
};
}
else{
if(B){
X = function(){
return new B();
};
}
else{
throw new Error(`no A or B`);
}
}
return new X();
}
6.4. 防篡改物件
6.4.1. 不可擴充套件物件 Object.preventExtensions
// 下面程式碼在谷歌瀏覽器中執行
> var person = {name: `wdd`};
undefined
> Object.preventExtensions(person);
Object {name: "wdd"}
> person.age = 10
10
> person
Object {name: "wdd"}
> Object.isExtensible(person)
false
6.4.2. 密封物件Object.seal
密封物件不可擴充套件,並且不能刪除物件的屬性或者方法。但是屬性值可以修改。
> var one = {name: `hihi`}
undefined
> Object.seal(one)
Object {name: "hihi"}
> one.age = 12
12
> one
Object {name: "hihi"}
> delete one.name
false
> one
Object {name: "hihi"}
6.4.3. 凍結物件 Object.freeze
最嚴格的防篡改就是凍結物件。物件不可擴充套件,而且密封,不能修改。只能訪問。
6.5. 高階定時器
6.5.1. 函式節流
函式節流的思想是:某些程式碼不可以沒有間斷的連續重複執行
var processor = {
timeoutId: null,
// 實際進行處理的方法
performProcessing: function(){
...
},
// 初始化呼叫方法
process: function(){
clearTimeout(this.timeoutId);
var that = this;
this.timeoutId = setTimeout(function(){
that.performProcessing();
}, 100);
}
}
// 嘗試開始執行
processor.process();
6.5.2. 中央定時器
頁面如果有十個區域要動態顯示當前時間,一般來說,可以用10個定時來實現。其實一箇中央定時器就可以搞定。
中央定時器動畫 demo地址:http://wangduanduan.coding.me…
var timers = {
timerId: 0,
timers: [],
add: function(fn){
this.timers.push(fn);
},
start: function(){
if(this.timerId){
return;
}
(function runNext(){
if(timers.timers.length > 0){
for(var i=0; i < timers.timers.length ; i++){
if(timers.timers[i]() === false){
timers.timers.splice(i, 1);
i--;
}
}
timers.timerId = setTimeout(runNext, 16);
}
})();
},
stop: function(){
clearTimeout(timers.timerId);
this.timerId = 0;
}
};
7. 函數語言程式設計
推薦閱讀:JS函數語言程式設計中文版
8. HTML的告誡
- 使用input的時候,一定要加上maxlength屬性。(你以為只需要輸入一個名字的地方,使用者可能複製一篇文章放進去。)
- 從input取值的時候,最好去除一下首尾空格
9. ajax的告誡
ajax在使用的時候,例如點選按鈕,獲取某個列表。需要注意以下方面
- ajax請求還沒有結束時,按鈕一定要disabled,防止多次點選。請求結束時,才去掉按鈕的disabled屬性。
- 請求沒結束的時候,一定要顯示一個gif的動畫,告訴使用者請求還在loading。不要讓使用者以為這垃圾程式又卡死了。
- 請求的結果如果是空的,一定要告訴使用者: 很抱歉,暫時沒有查詢到相關記錄之類的話語。不要給一個空白頁面給使用者。
- 最好考慮到請求報錯的情況,給出友好的錯誤提醒。
10. 程式碼整潔之道
10.1. 函式整潔
儘量將所有程式碼封裝在函式中,不要暴露全域性變數
每個函式的函式體中,程式碼行越少越好,最好一個函式中就一句程式碼
11. 工程化與模組化
11.1. 前端構建工具必不可少
11.1.1. webpack
11.1.2. rollup
11.1.3. parcel
12. 協議 TCP IP HTTP
如果你認為前端不需要關於協議的知識,那麼你就是大錯特錯了。其實不僅僅是前端,所有的開發者都應該學習底層的協議。因為他們是網際網路通訊的基石。
推薦三本必讀的書籍
或者你一也可以看看關於協議方面的一些問題,以及如果你遇到過,你是否知道如何解決:
13. 推薦深度閱讀
13.1. 推薦閱讀技術書籍
- 編寫可讀程式碼的藝術
- 編寫可維護的JavaScript
- JavaScript忍者祕籍(第2版)
- JavaScript語言精粹
- HTTP權威指南
- 圖解TCP/IP : 第5版
- 圖解HTTP
- 程式碼整潔之道
13.2. 推薦閱讀線上文章
- Writing Fast, Memory-Efficient JavaScript
- JavaScript 祕密花園
- You-Dont-Know-JS
- 關於快取,你應該連結的一切
- JS函數語言程式設計中文版
13.3. 技術之外
14. 參考文獻
- JavaScript高階程式設計(第3版) 【美】尼古拉斯·澤卡斯
- Maintainable JavaScript (英文版) Nicholas C. Zakas(其實和上邊那本書應該是同一個人)
- JavaScript忍者祕籍 John Resig / Bear Bibeault (John Resig 大名鼎鼎jQuery的創造者)
- 百度前端研發部 文件與原始碼編寫風格
- js函數語言程式設計指南
- JavaScript SDK Design Guide: JavaScript-sdk設計指南