如何使用函式來優化效能

Ilion發表於2019-03-01

一、節流函式

1. 使用場景

DOM.onclick()事件,我們給一個DOM節點繫結了點選事件,當點選該元素時觸發事件函式的執行,但是當我們頻繁點選該元素時,就會不斷觸發該點選事件,如果該點選事件觸發的事件函式是DOM元素的,就會造成很高的效能消耗,可能會造成頁面的卡頓。

所以此時我們應該限制該事件的觸發頻率,減少頁面的開銷。

2. 原理

連續觸發事件,但是事件函式只在在規定的週期之內只執行一次。

3. 程式碼實現

function throttle(fn, wait = 500) {
    let lastTime = 0 // 初始化上一次呼叫的事件
    return function () {
    	let args = [].slice.call(arguments) // 將類陣列轉化為陣列
        let nowTime = new Date().getTime() // 獲取當前時間
        if(nowTime - lastTime > wait) { 
            fn.apply(this, args)
            lastTime = nowTime // 把上一次呼叫時間重新賦值
        }
    }
}

// 使用
let btn = document.getElementById('btn')
let fn = function () {
    console.log(1)
}
btn.onclick = throttle(fn, 1000)
複製程式碼

在給按鈕加上點選事件後,即使一直不停的點選按鈕,也只會每隔1000ms執行一次事件處理函式。

二、防抖函式

1.使用場景

例如我們在百度搜尋的輸入框輸入我們想要搜尋的內容,在我們停止輸入後一小段時間(delay)後就會得你輸入框得內容然後進行搜尋,如果你在輸入後暫停的時間小於規定的時間(delay),就會重新計算該時間。

2.原理

所謂防抖,就是指觸發事件後在 n 秒內函式只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函式執行時間。

3. 程式碼實現

function debounce(fn, delay) {
    let timer = null
    return function () {
        let _self = this
        let args = [].slice.call(arguments)
        clearTimout(timer)
        timer = setTimout(function () {
            fn.apply(_self, args)
        }, delay)
    }
} 

// 使用
let inp = document.getElementById('inp')
function handler() {
    console.log(this.value)
}
inp.oninput = debounce(handler, 500)
複製程式碼

在使用節流函式後,我們在暫停輸入的500ms後就會輸入輸入框內的值,暫停時間小於500ms,則不會輸出,將重新計算函式執行時間。

三、分時函式

比如我們在將一個很大的資料渲染成列表的時候,我們要求必須將所有資料渲染完成,不能使用懶載入,所以這樣當我們在短時間內往頁面新增大量的DOM節點的時候,顯然會造成瀏覽器的卡頓。

let arr = []
for(let a = 0; a < 1000; a++) {
    arr.push(a)
}
function render(data) {
    for(let i = 0; i < arr.length; i++) {
        let div = document.createElement('div')
        div.innerHTML = arr[i]
        document.body.appenChild(div)
    }
}
render(arr)
複製程式碼

所以我們我們建立一個函式,然節點的新增分時進行,比如把在1s新增1000個節點改為每隔200ms新增20個節點。

let timeChunk = function (data, fn, count = 20, delay = 200) {
    let obj,timer
    let start = function () {
        for(let i = 0; i < Math.min(count, data.length); i++) {
            let obj = data.shift()
            fn(obj)
        }
    }
    return function () {
        timer = setInterval(function () {
            if(data.length === 0) {
                return clearInterval(timer)
            }
            start()
        }, delay)
    }
}
複製程式碼

使用分時函式

let arr = []
for (let a = 0; a < 1000; a++) {
	arr.push(a)
}

function render(data) {
    let div = document.createElement('div')
    div.innerText = data
    document.body.appendChild(div)
}
let renderlist = timeChunk(arr, render, 20, 200)
renderlist()
複製程式碼

這樣在呼叫分時函式後每隔200ms建立20個節點。

四、惰性函式

在前端開發中,因為瀏覽器的差異,一些嗅探工作是不可避免的,比如要實現一個在各個瀏覽器中都通用的新增事件函式。常見寫法:

let addEvent = function (element, type, handler) {
    if(window.addEventListener) {
        return element.addEventLisenter(type, handler, false)
    } else if (window.attachEvent) {
        return element.attachEvent('on'+type, handler)
    }
}
複製程式碼

但是我們每次執行函式的時候都要進行分支判斷,然後當我們在確定了在哪一種瀏覽器中執行該函式的時候,我們只需要在第一次判斷,後面的使用都不用判斷,因為我們是在同一個瀏覽器執行該函式。

所以我們可以使用惰性載入函式,在函式體內往往都會有一些分支判斷,但是在第一次進入分支條件後,在函式內部會重寫這個函式,重寫之後就是我們所期望的函式,在下一次再進入函式的時候不用在進行分支判斷。

let addEvent = function (element, type, handler) {
    if(window.addEventListener) {
        addEvemt = function(element, type, handler) {
            element.addEventLisenter(type, handler, false)
        }
    } else if (window.attachEvent) {
        addEvent = function(element, type, handler) {
            element.attachEvent('on'+type, handler)
        }
    }
    addEvent(element, type, handler)
}
複製程式碼

參考文獻

相關文章