洪雋倫 已發佈 2019-10-15

讓初學者能更優美編撰 JavaScript 的小撇步

「我要努力向上,不付諸君期望」 整人專家-胡真

就前端初學者的思維,大多會把「寫出可以運行的 code 」當作是練習或是實作的終極目標,而美化 code 內容或是使其運行效率更快什麼的,等到實力累積到一定程度之後在來處理就好。(好吧也有可能只有我是這樣想 QQ )
但其實世界是美好的~有些優化的關鍵其實只是透過一些基礎概念的運用便可達成,不需要多艱深的知識就能使用自如,本篇就以 JavaScript 中常會使用的判斷式與迴圈為例,透過一些使用上的小訣竅讓 code 更加美化~

一. if 與 switch 的使用時機

身為 JavaScript 判斷式的兩本柱,switch 真的是存在感渺小的可憐,因為 if 的自由度跟彈性真的是比 switch 來得強大許多,但其實 switch 也沒那麼不堪,有些時候派他上場反而能使 code 更加美觀而且不會妨礙運行效力喔!
舉個例子,當今天有一個欄位要請使用者填入 1~n,然後依照填入的各種數字有對應的功效,使用 if 的話應該會寫成:

var input_number = 輸入數字 
if(input_number === '1'){
    console.log('子瑜我婆');
}else if(input_number === '2'){
    console.log('初音我婆');
}else if(input_number === '3'){
        ......
        ......
}else{
    console.log('全都是我婆');
}

當填入的數字越多,要鍵入的 input_number 次數就越多,雖然有複製貼上但也是挺麻煩的,而且畫面也因為夾雜許多不同種類的括弧而有些雜亂,此時只要使用被遺忘的 switch :

var input_number = 輸入數字 
switch(input_number){
    case '1':
        console.log('子瑜我婆');
        break;
    case '2':
        console.log('初音我婆');
        break;
        ......
        ......
    default:
        console.log('全都是我婆');
}

比較一下兩邊的內容,switch 版本是不是比較漂亮一些呢~這邊補充一個微知識,switch 是使用全等模式判斷(就是 if 中的三個等號),所以在上面的範例,千萬不能寫成 case 1: 這樣,不然就會直接走 default 分支喔!

二. 三目運算子(三元運算子/條件運算子)

相信有不少新手在自學時,上網拜讀各個大神的文章時,時常會看到 code 中判斷式夾雜著 ? 與 : 的語句,看到的當下肯定是滿腦子問號,其實這就是大名鼎鼎的三目運算子,公式如下:

//使用 if 判斷式
if(target){
    //當目標為 True 時的答案
}else{
    //當目標為 False 時的答案
}

//使用三目運算子
(target) ? (//當目標為 True 時的答案) : (//當目標為 False 時的答案)

上面可以看到,使用三目運算子可以將五行的 code 縮減成一行結束,真的簡單有力又省時~以下舉幾個例子:

//如果考試前有拜春哥及關帝則可上榜,否則落榜
拜春哥||拜關帝 ? 上榜 : 落榜;

//三目運算子也可以運用到 jquery 上
//當 target 為 True 時使用 addClass 方法,False 時則使用 removeClass 
target ? $('body').addClass('子瑜') : $('body').removeClass('子瑜');

//將上面的 code 昇華一下
$('body')[target ? 'addClass' : 'removeClass']('子瑜');

//套用在function上
function a(){....}
function b(){....}
target ? a() : b();

三. return 的美妙之處

在判斷式中,return 常用來返回達成目標對應之值,但其實 return 同時也會終止函式的執行,活用 return 則可以減少 if else 的使用,使 code 更加美觀,像是下面的例子:

//當搜尋欄按下enter時才會進行動作,當搜尋欄為空值則 alert('欄位不得空白')
//一般寫法
searchbar.addEventListener('keyup', function(e){
    if(e.keyCode === '13'){
        if(this.value === ''){
            alert('欄位不得空白');
        }else{
            //搜尋動作
        }
    }
}  
//活用return的寫法
searchbar.addEventListener('keyup', function(e){
    if(!e.keyCode === '13'){
        return;
    }
    if(this.value === ''){
        alert('欄位不得空白');
        return;
    }
    //搜尋動作
}
//更簡潔的寫法
searchbar.addEventListener('keyup', function(e){
    if(!e.keyCode === '13') return;
    if(this.value === '') return alert('欄位不得空白');
    //搜尋動作
}

上面的例子可以看出使用 return 可以減少巢狀的寫法,使 code 簡潔有力,真的是個神奇的小東西~

四. for 陳述式/for in/forEach/for of 的效能比較

講完了判斷式的優化撇步,也來談談迴圈吧!迴圈最常被用到的就是 for-loop 了,而 for 迴圈也有百百中,各種都有不同的美妙跟運行能力,關於這些用法執行的效率測試估狗大神已經滿多都有實作的,本篇就直接破題統整各種 for-loop 的效能與用途(想看效能實測的人可以看這篇)

  1. for 陳述式 : 效能最高,其實一般使用都建議用此方法,但寫法有些繁瑣(參考連結)
  2. for in : 效能最低(需要歷遍對象的所有屬性),運行耗時最久,類似 forEach 但可作用在 object 跟 array 上(參考連結)
  3. forEach : 效能一般,目標為 array 時推薦使用,寫法簡單清楚速度又不算慢( 參考連結)
  4. for of : 效能中上(略高於 forEach),屬於 ES6 的語法,其寫法簡潔有力,兼具 for in 的優點又避開其所有缺陷,與 forEach 不同的是,此語法可響應 break、return、continue 等語句(參考連結)

五. 讓 for-loop 更加有效率吧!

除了透過活用上面各種 for-loop 的語法外,也可以透過一些小技巧來使迴圈運行更有效率喔!
1. 減少迭代的工作量
我們拿兩個例子來做比較

//Case A
for(var i = 0; i < target.length; i++){  
    //迭代內容;  
}  

//Case B
for(var i = 0,len = target.length; i < len; i++){  
    //迭代內容;   
}  

上面兩個案例都可以達到相同的目標,但 CaseA 每迭代一次就需要計算一次 target 的長度,累加下來會比 CaseB 耗費更多的時間,所以如果迴圈中有已知的參數,盡量在迴圈外就將其定義,則可減少迴圈迭代的工作量。
這邊補充一個微知識,如果無關迭代的運算也別放在迴圈內喔,不然也會增加迴圈工作量(雖然能減少 code 的行數,好像是有達到美觀效果)。
2. 降低迭代的次數
就算是一個再有效率的 code ,當迭代次數過多時,還是會受其影響而使 運行效能降低,如果能在迴圈中使用展開技術,減少迭代的次數,就可以提高效能。上面的敘述相信很多人看到都會有所共鳴,沒錯!這就是傳說中的廚具,阿不是,是傳說中的達夫裝置(關於達夫裝置的定義可以看wiki),簡單來說達夫裝置是一個迴圈體展開技術,使得一次迭代實際執行了多次迭代的操作,最初是用在 C 語言中,後面則出現 JavaScript 的應用版本,其 code 如下:

var iterations = Math.floor(items.length/8),  
    startAt = items.length%8,  
    i = 0;  
do{  
    switch(startAt){  
        case 0 : process(items[i++]);  
        case 7 : process(items[i++]);  
        case 6 : process(items[i++]);  
        case 5 : process(items[i++]);  
        case 4 : process(items[i++]);  
        case 3 : process(items[i++]);  
        case 2 : process(items[i++]);  
        case 1 : process(items[i++]);  
    }  
    startAt =  0;  
 }while(--iterations);

但根據網路大大們實測,當迭代量少時,使用達夫裝置反而會適得其反,所以使用上還需自行評估喔>Q^

六. 迴圈中的邊緣人- while

判斷式有其邊緣人 switch,迴圈當然也有!就是 while-loop~~
while-loop 使用上與 for-loop 用途一樣,用來重覆執行 while(for) 區塊內的語句,但是,就是這個但是,使用 while-loop 時,如果一不小心忘記給他一個計算值,使其有辦法跳出迴圈,就很容易產生可怕的無限循環~再加上使用上的美觀程度跟效能也不會比 for 迴圈來得高,所以活該被遺忘較少人使用。
但是其實 while 迴圈有一個鮮為人知的用法,就是 do...while 迴圈,它的語法如下:

do {
  //欲進行的動作
} while (判斷目標);

這個方法與一般的 while-loop 類似,比較特別的是他會先執行一次動作才開始判斷,當遇到需要先執行後判斷的狀況時,就可以活用 do...while 迴圈達到目的,並且能讓 while-loop 增加就業機會~真滴佛心!

以上就是本篇彙整個幾個優化小撇步,之後如果有遇到或學到更多撇步再來補充囉~上述內容如果有任何問題煩請告知,小弟會立即修正!!!也希望如果有其他優化的小技巧,大家能不吝分享~~讓世界更加美好!

關於筆者

暱稱:洪雋倫

文章列表 文章列表