所謂無規矩不成方圓,前端時間在團隊 code-review 中發現,不一樣時期不一樣開發人員寫的代碼可謂五花八門。所以咱們提出了一些相關代碼方面的規範,但願往後能造成團隊的編碼規範。javascript
本文主要針對一些 JavaScript 進行優化,使之更加健壯,可讀性更強,更以維護。html
gitthub地址:github.com/Michael-lzg…前端
上一篇:code-review以前端代碼規範vue
JavaScript 條件語句在咱們平時的開發中是不可避免要用到的,可是不少時候咱們的代碼寫的並很差,一連串的 if-else
或者多重嵌套判斷都會使得代碼很臃腫,下面舉例進行優化。java
需求:如今有 4 個產品,分別是手機、電腦、電視機、遊戲機,固然每一個產品顯示的價格不同。node
一、最簡單的方法:if 判斷webpack
let commodity = {
phone: '手機',
computer: '電腦',
television: '電視',
gameBoy: '遊戲機',
}
function price(name) {
if (name === commodity.phone) {
console.log(1999)
} else if (name === commodity.computer) {
console.log(9999)
} else if (name === commodity.television) {
console.log(2999)
} else if (name === commodity.gameBoy) {
console.log(3999)
}
}
price('手機') // 9999
複製代碼
缺點:代碼太長了,維護和閱讀都很不友好git
二、好一點的方法:Switch
github
let commodity = {
phone: '手機',
computer: '電腦',
television: '電視',
gameBoy: '遊戲機',
}
const price = (name) => {
switch (name) {
case commodity.phone:
console.log(1999)
break
case commodity.computer:
console.log(9999)
break
case commodity.television:
console.log(2999)
break
case commodity.gameBoy:
console.log(3999)
break
}
}
price('手機') // 9999
複製代碼
三、更優的方法: 策略模式web
策略模式利用組合、委託和多態等技術和思想,能夠有效地避免多重條件選擇語句。它提供了對開放—封閉原則的完美支持,將算法封裝在獨立的 strategy 中,使得它們易於切換,易於理解,易於擴展。
const commodity = new Map([
['phone', 1999],
['computer', 9999],
['television', 2999],
['gameBoy', 3999],
])
const price = (name) => {
return commodity.get(name)
}
price('phone') // 1999
複製代碼
includes
是 ES7 新增的 API,與 indexOf
不一樣的是 includes
直接返回的是 Boolean
值,indexOf
則 返回的索引值, 數組和字符串都有 includes
方法。
需求:咱們來實現一個身份認證方法,經過傳入身份 Id 返回對應的驗證結果
傳統方法
function verifyIdentity(identityId) {
if (identityId == 1 || identityId == 2 || identityId == 3 || identityId == 4) {
return '你的身份合法,請通行!'
} else {
return '你的身份不合法'
}
}
複製代碼
includes
優化
function verifyIdentity(identityId) {
if ([1, 2, 3, 4].includes(identityId)) {
return '你的身份合法,請通行!'
} else {
return '你的身份不合法'
}
}
複製代碼
在 JavaScript 中,咱們可使用 for()
, while()
, for(in)
,for(of)
幾種循環,事實上,這三種循環中 for(in)
的效率極差,由於他須要查詢散列鍵,因此應該儘可能少用。
for 循環是最傳統的語句,它以變量 i 做爲索引,以跟蹤訪問的位置,對數組進行操做。
var arr = ['a', 'b', 'c']
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]) //結果依次a,b,c
}
複製代碼
以上的方法有一個問題:就是當數組的長度到達百萬級時,arr.length
就要計算一百萬次,這是至關耗性能的。因此能夠採用如下方法就行改良。
var arr = ['a', 'b', 'c']
for (var i = 0, length = arr.length; i < length; i++) {
console.log(arr[i]) //結果依次a,b,c
}
複製代碼
此時 arr.length
只須要計算一次,優化了性能。
for-in
通常用來來遍歷對象的屬性的,不過屬性須要 enumerable
(可枚舉)才能被讀取到。同時 for-in
也能夠遍歷數組,遍歷數組的時候遍歷的是數組的下標值。
var obj = { 0: 'a', 1: 'b', 2: 'c' }
for (var key in obj) {
console.log(key) //結果爲依次爲0,1,2
}
var arr = ['a', 'b', 'c']
for (var key in a) {
console.log(key) //結果爲依次爲0,1,2
}
複製代碼
for-of
語句看着有點像 for-in
語句,可是和 for-of
語句不一樣的是它不能夠循環對象,只能循環數組。
var arr = ['a', 'b', 'c']
for (var value of arr) {
console.log(value) // 結果依次爲a,b,c
}
複製代碼
for-of
比 for-in
循環遍歷數組更好。for-of
只要具備 Iterator
接口的數據結構,均可以使用它迭代成員。它直接讀取的是鍵值。for-in
須要窮舉對象的全部屬性,包括自定義的添加的屬性也能遍歷到。且 for-in
的 key
是 String
類型,有轉換過程,開銷比較大。
因此在開發過程當中循環數組儘可能避免使用 for-in
。
數組去重是實際開發處理數據中常常遇到的,方法有不少,這裏就不一一例舉了。
一、最傳統的方法:利用數組的 indexOf
下標屬性來查詢。
function unique4(arr) {
var newArr = []
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4]))
// [1, 2, 3, 5, 6, 7, 4]
複製代碼
二、優化:利用 ES6 的 Set
方法。
Set
自己是一個構造函數,用來生成 Set
數據結構。Set
函數能夠接受一個數組(或者具備 iterable 接口的其餘數據結構)做爲參數,用來初始化。Set
對象容許你存儲任何類型的值,不管是原始值或者是對象引用。它相似於數組,可是成員的值都是惟一的,沒有重複的值。
function unique4(arr) {
return Array.from(new Set(arr)) // 利用Array.from將Set結構轉換成數組
}
console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4]))
// [1, 2, 3, 5, 6, 7, 4]
複製代碼
箭頭函數表達式的語法比函數表達式更簡潔。因此在開發中更推薦使用箭頭函數。特別是在 vue
項目中,使用箭頭函數不須要在更 this
從新賦一個變量。
// 使用functions
var arr = [5, 3, 2, 9, 1]
var arrFunc = arr.map(function (x) {
return x * x
})
console.log(arrFunc)
// 使用箭頭函數
var arr = [5, 3, 2, 9, 1]
var arrFunc = arr.map((x) => x * x)
複製代碼
要注意的是,箭頭函數不綁定 arguments
,取而代之用 rest
參數…解決。
// 不能使用 arguments
let fun1 = (b) => {
console.log(arguments)
}
fun1(2, 92, 32, 32) // Uncaught ReferenceError: arguments is not defined
// 使用rest 參數
let fun2 = (...c) => {
console.log(c)
}
fun2(3, 82, 32, 11323) // [3, 82, 32, 11323]
複製代碼
建立多個 dom 元素時,先將元素 append
到 DocumentFragment
中,最後統一將 DocumentFragment
添加到頁面。
常規方法;
for (var i = 0; i < 1000; i++) {
var el = document.createElement('p')
el.innerHTML = i
document.body.appendChild(el)
}
複製代碼
使用 DocumentFragment
優化屢次 append
var frag = document.createDocumentFragment()
for (var i = 0; i < 1000; i++) {
var el = document.createElement('p')
el.innerHTML = i
frag.appendChild(el)
}
document.body.appendChild(frag)
複製代碼
更優的方法:使用一次 innerHTML
賦值代替構建 dom 元素
var html = []
for (var i = 0; i < 1000; i++) {
html.push('<p>' + i + '</p>')
}
document.body.innerHTML = html.join('')
複製代碼
系統進程再也不用到的內存,沒有及時釋放,就叫作內存泄漏(memory leak)。當內存佔用愈來愈高,輕則影響系統性能,重則致使進程崩潰。
引發內存泄漏的緣由
一、未聲明變量或者使用 this
建立的變量(this
的指向是 window
)都會引發內存泄漏
function fn() {
a = "Actually, I'm a global variable"
}
fn()
function fn() {
this.a = "Actually, I'm a global variable"
}
fn()
複製代碼
解決方法:
use strict
。二、在 vue 單頁面應用,聲明的全局變量在切換頁面的時候沒有清空
<template>
<div id="home">
這裏是首頁
</div>
</template>
<script>
export default {
mounted() {
window.test = {
// 此處在全局window對象中引用了本頁面的dom對象
name: 'home',
node: document.getElementById('home')
}
}
}
</script>
複製代碼
解決方案: 在頁面卸載的時候順便處理掉該引用。
destroyed () {
window.test = null // 頁面卸載的時候解除引用
}
複製代碼
閉包引發的內存泄漏緣由:閉包能夠維持函數內局部變量,使其得不到釋放。
function fn() {
var a = "I'm a"
return function () {
console.log(a)
}
}
複製代碼
解決:將事件處理函數定義在外部,解除閉包,或者在定義事件處理函數的外部函數中,刪除對 dom 的引用。
因爲項目中有些頁面不免會碰到須要定時器或者事件監聽。可是在離開當前頁面的時候,定時器若是不及時合理地清除,會形成業務邏輯混亂甚至應用卡死的狀況,這個時就須要清除定時器事件監聽,即在頁面卸載(關閉)的生命週期函數裏,清除定時器。
methods:{
resizeFun () {
this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128
},
setTimer() {
this.timer = setInterval(() => { })
},
clearTimer() {//清除定時器
clearInterval(this.timer)
this.timer = null
}
},
mounted() {
this.setTimer()
window.addEventListener('resize', this.resizeFun)
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeFun)
this.clearTimer()
}
複製代碼
在前端開發的過程當中,咱們常常會須要綁定一些持續觸發的事件,如 resize
、scroll
、mousemove
等等,但有些時候咱們並不但願在事件持續觸發的過程當中那麼頻繁地去執行函數。這時候就用到防抖與節流。
案例 1:遠程搜索時須要經過接口動態的獲取數據,如果每次用戶輸入都接口請求,是浪費帶寬和性能的。
<Select :remote-method="remoteMethod">
<Option v-for="item in temoteList" :value="item.value" :key="item.id">{{item.label}}</Option>
</Select>
<script> function debounce(fn, wait) { let timeout = null return function () { if (timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } export default { methods:{ remoteMethod:debounce(function (query) { // to do ... }, 200), } } <script> 複製代碼
案例 2:持續觸發 scroll
事件時,並不當即執行 handle
函數,當 1000 毫秒內沒有觸發 scroll
事件時,纔會延時觸發一次 handle
函數。
function debounce(fn, wait) {
let timeout = null
return function () {
if (timeout !== null) clearTimeout(timeout)
timeout = setTimeout(fn, wait)
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', debounce(handle, 1000))
複製代碼
默認狀況下,瀏覽器是同步加載 js 腳本,解析 html 過程當中,遇到 <script>
標籤就會停下來,等腳本下載、解析、執行完後,再繼續向下解析渲染。
若是 js 文件體積比較大,下載時間就會很長,容易形成瀏覽器堵塞,瀏覽器頁面會呈現出「白屏」效果,用戶會感受瀏覽器「卡死了」,沒有響應。此時,咱們可讓 js 腳本異步加載、執行。
<script src="path/to/home.js" defer></script>
<script src="path/to/home.js" async></script>
複製代碼
上面代碼中,<script>
標籤分別有 defer
和 async
屬性,瀏覽器識別到這 2 個屬性時 js 就會異步加載。也就是說,瀏覽器不會等待這個腳本下載、執行完畢後再向後執行,而是直接繼續向後執行
defer 與 async 區別:
defer
腳本時,會按照頁面出現的順序依次加載、執行。async
腳本時,不能保證按照頁面出現順序加載、執行