五、變量類型透明化
因為 JS 是一個弱類型語言,在定義變量的時候,不會限制數(shù)據(jù)類型
但是我們在給變量賦值的時候,也要盡可能的做到數(shù)據(jù)類型統(tǒng)一
當你需要定義一些變量,在后期操作中進行賦值的時候
盡可能在定義的時候,給一個初始值表示一下你變量將來要存儲的數(shù)據(jù)類型
比如:
var count = 0;
var name = '';
var boo = false;
var person = null;
var todoList = [ ];
如果你實在不想給一個初始值
也可以使用注釋的形式表明一下你定義的變量, 將來存儲的是什么類型的數(shù)據(jù)
var count /* Number */;
var name /* String */;
var boo /* Boolean */;
六、代碼書寫習慣
我們要保證一個良好的代碼書寫習慣
七、鏈式編程的習慣
我們來看一下下面這個代碼
[ ... ].map(function () {
// code ...
}).filter(function () {
// code ...
}).reduce(function () {
// code ...
})
其實沒啥問題, 而且也挺好的
更甚至當代碼簡單一些的時候有人把它寫成一行
[ ... ].map(function () { ... }).filter(function () { ... }).reduce(function () { ... })
但是到了后期修改的時候,問題就會逐步顯示,一旦修改了第一個,那么后面的都有可能會出現(xiàn)問題
而且當代碼量過大的時候,很難保證你不修改串行了
· 我們可以把上面的代碼換成下面的方式
[ ... ]
.map(function () {
// code ...
})
.filter(function () {
// code ...
})
.reduce(function () {
// code ...
})
這樣的話,看起來會舒服的多
而且可以利用編輯器的代碼折疊,一個函數(shù)一個函數(shù)的來書寫
八、書寫運算符的習慣
很多人喜歡相對緊湊的書寫結(jié)構(gòu)
比如下面的代碼
if (year%4===0&&year%100!==0||year%400===0) { ... }
很簡單的一個判斷閏年的代碼
但是當你的運算符很緊湊的時候,那么看起來就會比較費眼睛
相對來說,我更喜歡在運算符兩邊都加上空格
讓結(jié)構(gòu)相對松散一些,看起來可能也容易一些
我們也不用擔心這些空格,后期處理都會幫我們處理掉的
if ( year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { ... }
還有一種寫法
if (
year % 4 === 0 &&
year % 100 !== 0 ||
year % 400 === 0
) { ... }
這個適用于條件比較長的時候使用
看起來會更加清晰一些
九、函數(shù)調(diào)用傳遞參數(shù)
· 當調(diào)用一個函數(shù),需要傳遞一個函數(shù)作為參數(shù)的時候
· 我們通常都會直接書寫一個匿名函數(shù)或者箭頭函數(shù)在參數(shù)位置
· 或者說傳遞一個復(fù)雜數(shù)據(jù)類型作為參數(shù)的時候,都會直接講對應(yīng)的數(shù)組或者對象寫在參數(shù)位置
· 比如下面這段代碼
$.get('/xxx', {
a: 100,
b: 200
}, function (res) {
// code ...
}, 'json')
代碼沒有問題,但是一旦對象中數(shù)據(jù)過多
或者函數(shù)中代碼過多的時候
后期看起來就會很復(fù)雜
我會建議把這些內(nèi)容單獨書寫出來
var params = {
a: 100,
b: 200
}
function success(res) {
// code ...
}
$.get('/xxx', params, success, 'json')
這樣一來, 不管是修改, 還是增加一些內(nèi)容, 都會比較方便了
十、功能性函數(shù)的單獨封裝
把我們自定義的一些功能性函數(shù)進行單獨的封裝,放在一個單獨的 JS 文件中進行引入或者導(dǎo)入使用,其實就是模塊化的概念
十一、松散耦合
對于比較難以閱讀的代碼來說,強耦合的代碼是最難閱讀的,JS 代碼本身層面上的耦合我們就不說了,大家都應(yīng)該了解面向?qū)ο缶幊毯湍K化編程
十二、HTML 和 JavaScript 的耦合
在前端開發(fā)中,我們經(jīng)常會見到有些人寫代碼會把一些簡單的事件直接寫到 html 結(jié)構(gòu)上
<button onclick="doSomething()" ></button>
從代碼層面上來說完全沒有問題
但是實際上,這個是 HTML 和 JavaScript 的強耦合現(xiàn)象
第一: 每次對于代碼進行的修改,都要從 HTML 和 JavaScript 兩個位置去進行修改
第二: 代碼引入位置不可變,一定要保證在用戶點擊之前就已經(jīng)有函數(shù)存在了,不然一定會報錯的
比較好的方法就是進行 HTML 和 JavaScript 的分離
在 .js 文件中獲取 DOM 元素
通過事件綁定的形式來完成操作
var btn = document.querySelector('button')
btn.addEventListener('click', doSomething)
還有一種情況更常見, 就是在 JS 代碼中為了渲染頁面而進行字符串拼接
container.innerHTML = `
<div>
...
<p> ... </p>
<span> ... </span>
</div>
這個代碼也是完全沒有問題的,而且大部分同學都會這樣書寫代碼,因為省時省力
但是這樣的情況,一旦渲染到頁面上,出現(xiàn)樣式問題需要調(diào)整的時候
我們在 HTML 結(jié)構(gòu)中很難找到內(nèi)容來修改,必須要到 JavaScript 代碼里面去修改
如果我們的字符串拼接是在循環(huán)里面完成的話,那么有可能你添加一個或者刪除一個標簽的時候,導(dǎo)致整個頁面崩潰
比較好的做法
使用一些第三方小腳本或者模板引擎來進行渲染:
比如:art-template / e.js / ...
真的需要這樣渲染的時候,那么在原始 html 結(jié)構(gòu)中以注釋的形式留下一部分渲染內(nèi)容
<div class="container">
<!-- 商品詳情信息渲染結(jié)構(gòu)
<div>
...
<p> ... </p>
<span> ... </span>
</div>
-->
</div>
· 當 HTML 和 JavaScript 解耦以后
· 可以大量節(jié)省我們的排錯時間, 和錯誤的準確定位
十三、CSS 和 JavaScript 的耦合
在前端的開發(fā)中,使用 JS 來操作一些元素的樣式,是在常見不過的事情了
比如我們經(jīng)常會寫
ele.style.color = 'red' ;
ele.style.display = 'none' ;
這樣書寫代碼其實沒有大問題
對于渲染也不會造成很大的困擾
但是,一旦我們需要修改樣式的時候,那么就比較麻煩了
因為有的樣式可能需要在 .css 文件內(nèi)修改,有的樣式需要在 .js 文件內(nèi)修改
· 比較好的做法是, 把我們需要修改的樣式寫成一個單獨類名下
· 放在 .css 文件內(nèi)
· 我們在代碼里面通過操作元素的類名來進行修改
ele.classList.add('active')
ele.classList.remove('active')
這樣做保證了樣式和行為的分離,我們在調(diào)整頁面樣式的時候,不需要 JS,直接在 CSS 中修改就可以
十四、事件處理 和 應(yīng)用邏輯 的耦合
在開發(fā)過程中, 我們經(jīng)常要處理一些事件,并且在事件里面要進行一些邏輯的運算
比如:我們在點擊登錄的時候,要對用戶填寫的內(nèi)容進行一個正則的驗證,然后提交到服務(wù)器
ele.addEventListener('submit', function () {
let username = xxx.value
let password = xxx.value
// 正則驗證
if ( ... ) { ... }
if ( ... ) { ... }
// 提交到服務(wù)器
var xhr = new XMLHttpRequest()
xhr.open( ... )
xhr.send( ... )
xhr.onload = function () { ... }
})
這是一段合法的代碼
但是函數(shù)里面包含了太多的內(nèi)容
有事件處理
有邏輯處理
有請求發(fā)送
這樣就相當于在一個函數(shù)里面做了太多的事情
這個代碼的邏輯運算還是比較少的,但是一旦邏輯運算多了以后,那么后期閱讀的時候就很麻煩了
我們可以把里面的邏輯運算和請求發(fā)送都單獨提取出來,變成下面這個形式:
function validateValue(val) {
// 正則驗證
if ( ... ) { ... }
if ( ... ) { ... }
// 將驗證結(jié)果返回
return true // or false
}
function sendAjax() {
// 發(fā)送請求的業(yè)務(wù)邏輯
}
ele.addEventListener('submit', function () {
let username = xxx.value
let password = xxx.value
// 正則驗證
if (!validateValue( xxx )) return
// 提交到服務(wù)器
sendAjax()
})
這樣一來,只要我們給函數(shù)寫好注釋,那么后期的時候,哪里出現(xiàn)問題,我們可以快速準確的定位問題所在位置
十五、尊重對象所有權(quán)
· JavaScript 的動態(tài)天性決定了沒有什么是不能修改的
· 從代碼層面出發(fā),我們可以修改任何內(nèi)容,包括向 Object 的 prototype 上擴展一些方法,,向 Array 的 prototype 上擴展一些方法
· 但是在真實的企業(yè)級開發(fā)過程中,我們要絕對的尊重每一個對象的所有權(quán)
不要修改任何不屬于你的代碼,如果某一個對象不是由你負責創(chuàng)建或者維護,那么你也不要修改他的構(gòu)造函數(shù)
在好久好久以前:
我接觸過一個叫做 prototype 的第三方庫
它里面向 document 對象上擴展了一個叫做 getElementsByClassName 的方法
是不是看起來很無聊,但是在沒有 getElementsByClassName 的年代,確實很好用
并且,擴展的這個 getElementsByClassName 方法的返回值是一個 Array 并不是我們后來使用的 NodeList
而且還在實例身上擴展了一個叫做 each() 的方法,專門用來遍歷
我們用起來的時候就會很方便
document.getElementsByClassName('item').each()
這個很好,而且對代碼開發(fā)進行了簡化
但是,一旦瀏覽器廠商開始支持這個方法了,那么你的方法就會出現(xiàn)問題了
后來,在所有瀏覽器廠商都支持了 getElementsByClassName 以后
當在使用這個方法的時候,因為和原生的重名了
會出現(xiàn)代碼的大面積報錯
這個就是尊重代碼所有權(quán)
因為你不知道瀏覽器廠商什么時候會 告知 或 不告知 的更新一些內(nèi)容,或者修改一些 API
所以,不要修改任何不屬于你的內(nèi)容
十六、盡量不聲明全局變量
和尊重對象所有權(quán)有密切關(guān)系的就是盡可能少的聲明全局變量
拋開變量污染的層面不說,我們的每一個全局變量其實都是在向 window 上添加成員
var name = 'Jack'
function getInfo() { ... }
這都是全局變量,用起來也沒什么問題
但是也確實是在 window 上掛載了兩個名字
我們在開發(fā)自己的代碼的時候, 盡可能的在全局制作一個命名空間,然后把我們所有需要的內(nèi)容全部放在里面
var myApp = {
name: 'jack',
getInfo () { ... }
}
這樣一來, 我們只是向 window 上掛載了一個 myApp
剩下的所有東西都在我自己的命名空間里面
一旦出現(xiàn)問題,你能準確的知道是你自己定義的變量或者方法出現(xiàn)了問題,還是原生的變量或者方法出現(xiàn)了問題
這個也是前端從沒有模塊化到模塊化開發(fā)的演變過程的原始階段:
o 獨立命名空間
o IIFE
o AMD / CMD
o CommonJS
o ES6 模塊化
十七、習慣使用常量
我們在開發(fā)的過程中,經(jīng)常要使用一些變量來操作某些內(nèi)容
o 任何出現(xiàn)一次以上的內(nèi)容,都應(yīng)該提取出來變成一個常量的定義
o 任何一個需要顯示給用戶看到的文本內(nèi)容,都應(yīng)該提取出來變成一個常量
o 任何一個變量,在定義的時候都要考慮,將來會不會發(fā)生變化,如果不發(fā)生變化,那么就直接定義成常量
o 包括我們在操作一些類名的時候,應(yīng)該把這些類名提取出來做成常量,然后統(tǒng)一操作
這樣一來,我們可以避免因為不小心修改變量而導(dǎo)致出現(xiàn)的問題,也可以在代碼的各個部分保證代碼數(shù)據(jù)的統(tǒng)一,避免一個東西這里修改了,那里沒有修改的問題