本文介紹的語法技巧並沒有特別標註上瀏覽器相容

若想使用這些語法跨瀏覽器、瀏覽器版本

在 js 上營運前先經過 Babel 等工具轉換相容性會比較好

Template literals ( Template strings )

  • 直觀寫法

    var url = "https://" + domain + "/" + apiName
    
  • Template 寫法

    var url = `https://${domain}/${apiName}`
    

更多範例可以參考這邊

Three Equal

條件判斷以 === 代替 ==

更多的比較可以在這邊看到

'1' == 1  // true
'1' === 1 // false

避免直接改元素的style,而是靠css class

  1. 維護性比較好(像是要改顏色,只要改css的定義就好)

  2. 一致性比較好,比較不會漏掉特例忘了處理

  3. 當有特例邏輯時較好修改

    (直接修改元素之style,會以元素上設定之style優先套用,但用掉了的話就不能再用了,類似 css 的!important)

  • 直接修改元素 style

    let $el = document.getElementById("id")
    $el.style.color = '#fff'
    
  • 通過套用css修改元素 style

    let $el = document.getElementById("id")
    $el.classList.add('whiteColor');
    $el.classList.remove('whiteColor');
    
    .whiteColor{
        color: #fff;
    }
    

簡單的動畫多利用原生css去達成

  1. 用js去算開發工可能比較多

  2. 用js去算開發動畫效能可能也會比較不好

  • 用 js 實作 button hover時顏色會變化

    $("#btn").on('mouseover', function(){
        $(this)style.color = '#fff'
    })
    
    $("#btn").on('mouseleave', function(){
        $(this)style.color = '#aaa'
    })
    
  • 用 css 實作 button hover時顏色會變化

    #btn{
        color: #fff;
    }
    
    #btn:hover{
        color: #fff;
    }
    

Arrow function return object

  • 不對的寫法

    const square = (n) => { result: n*n }
    console.log( square(2) ) // undefined
    
  • 正確寫法(加上括號)

    const square = (n) => ({ result: n*n })
    console.log( square(2) ) // {result: 4}
    

用 filter 來篩選資料

  • 直觀寫法

    let custom = []
    for( candidate of people ){
        if( candidate.type == 'student' ){
            custom.push(candidate)
        }
    }
    
  • 用 filter

    let custom = people.filter( (candidate) => candidate.type == 'student' )
    

用 map 來轉換資料

  • 直觀寫法

    var people = [ { id:5, name:"AAA" }, { id:2, name:"BBB" }, { id:3, name:"CCC" } ]
    var peopleIds = []
    for( person of people ){
        peopleIds.push(person.id)
    }
    
  • 用 map

    var people = [ { id:5, name:"AAA" }, { id:2, name:"BBB" }, { id:3, name:"CCC" } ]
    var peopleIds = people.map( person => person.id )
    
  • 使用 ts 時,用 map 來轉換 Object.keys() 的型態

    使用 ts 時,若使用 Object.keys() 的指令,一定會拿到 string 陣列

    但可能使用上會想拿到 number 陣列,那就可以這樣寫

    const people = { 
        5: "AAA", 
        2: "BBB", 
        3: "CCC", 
    } as Record<number, string>
    console.log( Object.keys(people) )
    // ["2", "3", "5"] 
    console.log( Object.keys(people).map(Number) )
    // [2, 3, 5] 
    

用 Map 來操作 Ordered Obejcts

  • 取得 Ordered Keys

    若今天資料結構為以下

    var people = { 
        5: "AAA", 
        2: "BBB", 
        3: "CCC", 
    } 
    console.log( Object.keys(people).map(Number) )
    // [2, 3, 5] 
    

    可以看到 Object.keys(people) 產出來的陣列次序跟定義時的次序不一樣

    若希望得到 [ 5, 2, 3 ] 可以這樣寫

    var people = new Map([
        [ 5, "AAA" ],
        [ 2, "BBB" ],
        [ 3, "CCC" ],
    ])
    console.log( people.keys() )
    // MapIterator {5, 2, 3}
    
  • 迴圈

    var people = new Map([
        [ 5, "AAA" ],
        [ 2, "BBB" ],
        [ 3, "CCC" ],
    ])
    
    people.forEach( ( value, key ) => console.log(key, value) )
    // 5 "AAA"
    // 2 "BBB"
    // 3 "CCC"
    
    for( var [ key, value ] of people ){
        console.log(key, value)
    }
    // 5 "AAA"
    // 2 "BBB"
    // 3 "CCC"
    

用 Set 來操作集合

  • 檢查資料有無重複

    有時若拿到一個陣列,想知道裡面有沒有重複的資料,可以使用 Set

    var people = [
        { id:5, name:"AAA" }, 
        { id:2, name:"BBB" }, 
        { id:3, name:"CCC" }, 
        { id:3, name:"CCC" } ]
    // 3 號是重複的
    
    var idSet = new Set(people.map( person => person.id ))
    console.log( people.length === idSet.size )
    
  • 檢查資料是否一致

    有時資料經過不同篩選後,會想知道最終篩選出來的資料是哪些

    var people = [
        { id:1, name:"AAA", age:20, gender: 0 }, 
        { id:2, name:"BBB", age:23, gender: 0 }, 
        { id:3, name:"CCC", age:22, gender: 1 } ]
    var filterdPeopleByAge = people.filter( person => person.age < 25 )
    var filterdPeopleByGender = people.filter( person => person.gender == 0 )
    
    var ageSet = new Set( filterdPeopleByAge.map( person => person.id ) )
    var genderSet = new Set( filterdPeopleByGender.map( person => person.id ) )
    
    var intersection = new Set([ ...ageSet].filter( x => genderSet.has(x) ))
    var filteredPeople = people.filter( person => intersection.has(person.id) )
    
    console.log( filteredPeople )
    // {id: 1, name: "AAA", age: 20, gender: 0}
    //  { id:2, name:"BBB", age:23, gender: 0 }
    

用 Spread Operator Merge Data

Merge Array

  • 直觀寫法

    var a = [ 1, 2, 3]
    var b = [ 4, 5, 6]
    for( var i=0; i<b.length; i++ ){
        a.push(b[i])
    }
    // a = [ 1, 2, 3, 4, 5, 6 ]
    
  • 用 Spread Operator

    var a = [ 1, 2, 3]
    var b = [ 4, 5, 6]
    a = [ ...a, ...b ]
    // a = [ 1, 2, 3, 4, 5, 6 ]
    

Merge Object

  • 直觀寫法

    var a = { a:1, b:2 }
    var b = { b:20, c:3 }
    Object.keys(b).forEach( key => a[key] = b[key] )
    // a = { a:1, b:20, c:3 }
    
  • 用 Spread Operator

    var a = { a:1, b:2 }
    var b = { b:20, c:3 }
    a = { ...a, ...b }
    // a = { a:1, b:20, c:3 }
    

Optional Chaining

  • 當直接存取變數之 member 報 exception 時

    var a = null
    var b = a.x // throw exception
    
  • 直觀寫法

    var a = null
    var b = a === null ? a.x : null
    // b = null
    
  • 用 Spread Operator

    var a = null
    var b = a?.x
    // b = null
    

用 console debug

console.log

console.log(data)

console.group

for( data in dataList ){
    console.group()
    console.log(data)
    // log more
    console.groupEnd()
}

console.table

console.table(var) // 會log出table

img

Debug Performance

  • 測量執行時間和對執行進行計數

Timer前後端同步

最好是後端給予前端一個end time(要記得使用 GMT/UTC 標準時區時間),並使用setinterval來倒數

可以避免

  1. 時區的問題

  2. 前後端時間不一致問題

  3. 前後端時間驗證不一致問題

  4. Timer暫時停止問題,導致倒數計時時間不正確

    • user使用手機暫時離開瀏覽器時,Timeout跟interval都不會繼續執行

      因此若user回到瀏覽器上時,Timeout倒數計時的時間就會錯誤

function Timer(endDateTime, interval, tasks){
    var timer = setinterval(function(){
        var currentDateTime = new Date()
        var remainingTime = GetUTC(endDateTime) - GetUTC(currentDateTime)
        tasks(remainingTime)

        if( remainingTime <= 0 ){
            clearInterval(timer)
        }
    }, interval)

    return Timer
}

function SomeTasks(remainingTime){
    if(remainingTime <= 0){
        // do something
        console.log("Time Out!")
        return
    }

    // do something
    console.log("Waiting")
}

function GetUTC(date){
    date = new Date(date)
    return new Date(Date.UTC(
        date.getUTCFullYear(), 
        date.getUTCMonth(), 
        date.getUTCDate(),
        date.getUTCHours(), 
        date.getUTCMinutes(), 
        date.getUTCSeconds()));
}

後端給定 GMT/UTC 標準時區時間,以C#為例[1]

var dt = new DateTime(2016, 9, 25, 17, 57, 43);
Console.WriteLine(dt.ToUniversalTime().ToString("s"));
// 2016-09-25T09:57:43

  1. 前端工程研究:關於 JavaScript 中 Date 型別的常見地雷與建議作法 ↩︎