數(shù)組扁平化
數(shù)組扁平化:使用遞歸實(shí)現(xiàn)
function flattenDepth(array, depth=1) {
let result = [];
array.forEach (item => {
let d = depth;
if(Array.isArray(item) && d > 0){
result.push(...(flattenDepth(item, --d)))
} else {
result.push(item);
}
})
return result;
}
console.log(flattenDepth([1,[2,[3,[4]],5]]))
console.log(flattenDepth([1,[2,[3,[4]],5]],2))
console.log(flattenDepth([1,[2,[3,[4]],5]],3))
將每一項(xiàng)遍歷,如果某一項(xiàng)為數(shù)組,則讓該項(xiàng)繼續(xù)調(diào)用,這里指定了depth作為扁平化的深度,因?yàn)檫@個(gè)參數(shù)對(duì)數(shù)組的每一項(xiàng)都要起作用。
柯里化
參數(shù)夠了就執(zhí)行,參數(shù)不夠就返回一個(gè)函數(shù),之前的參數(shù)存起來,直到夠了為止。
function curry(func) {
var l = func.length;
return function curried() {
var args = [].slice.call(arguments);
if(args.length < l) {
return function() {
var argsInner = [].slice.call(arguments)
return curried.apply(this, args.concat(argsInner))
}
} else {
return func.apply(this, args)
}
}
}
var f = function(a,b,c) {
return console.log([a,b,c])
}
var curried = curry(f);
curried(1)(2)(3)
節(jié)流和防抖
函數(shù)節(jié)流和函數(shù)防抖都是對(duì)大量頻繁調(diào)用代碼的一種優(yōu)化。
防抖
不管你觸發(fā)了多少次,都等到你最后觸發(fā)后過一段你指定的時(shí)間才觸發(fā)。簡(jiǎn)單地說,即函數(shù)在特定的時(shí)間內(nèi)不被再調(diào)用后執(zhí)行。
- 實(shí)際應(yīng)用場(chǎng)景:監(jiān)聽窗口大小重繪的操作。
在用戶拖拽窗口時(shí),一直在改變窗口的大小,如果我們?cè)?resize 事件中進(jìn)行一些操作,消耗將是巨大的。而且大多數(shù)可能是無意義的執(zhí)行,因?yàn)橛脩暨€處于拖拽的過程中。可以使用 函數(shù)防抖 來優(yōu)化相關(guān)的處理。
// 普通方案
window.addEventListener('resize', () => {
console.log('trigger');
})
//函數(shù)防抖方案
let debounceIdentify = 0;
window.addEventListener('resize', () => {
debounceIdentify && clearTimeout(debounceIdentity)
debounceIdentity = setTimeout(() => {
console.log('trigger')
}, 300)
})
我們?cè)?resize 事件中添加了一個(gè) 300 ms 的延遲執(zhí)行邏輯。
并且每次事件觸發(fā)時(shí),都會(huì)重新計(jì)時(shí),這樣保證,函數(shù)的執(zhí)行肯定是在距離上次 resize 事件被觸發(fā)的 300 ms 后。兩次 resize 事件間隔小于 300 ms 的都被忽略了,這樣就會(huì)節(jié)省很多無意義的事件觸發(fā)。
- 輸入框的聯(lián)想
幾乎所有的搜索引擎都會(huì)對(duì)你輸入的文字進(jìn)行預(yù)判,并在下方推薦相關(guān)的結(jié)果。但是這個(gè)聯(lián)想意味著我們需要將當(dāng)前用戶所輸入的文本傳遞到后端,并獲取返回?cái)?shù)據(jù),展示在頁面中。如果遇到打字速度快的人,在一小段時(shí)間內(nèi),會(huì)連續(xù)發(fā)送大量的 ajax 請(qǐng)求到后端。并且當(dāng)前的數(shù)據(jù)返回過來后,其實(shí)已經(jīng)失去了展示的意義,因?yàn)橛脩艨赡軓?you 輸入到了 young ,這兩個(gè)單詞的相關(guān)結(jié)果肯定不一樣的。所以我們就在監(jiān)聽用戶輸入的事件那里做函數(shù)防抖處理,在 XXX 秒后發(fā)送聯(lián)想搜索的 ajax 請(qǐng)求。
/**
* 函數(shù)防抖的實(shí)現(xiàn)
* @param {Function} func 要實(shí)現(xiàn)函數(shù)節(jié)流的原函數(shù)
* @param {Number} delay 結(jié)束的延遲時(shí)間
* @return {Function} 添加節(jié)流功能的函數(shù)
*/
function debounce (func, delay) {
let debounceIdentify = 0
return (...args) => {
debounceIdentify && clearTimeout(debounceIdentify)
debounceIdentify = setTimeout(() => {
debounceIdentify = 0
func.apply(this, args)
}, delay)
}
}
-
基本版的:
function debounce(func, wait){ var timer; return function(){ var context = this; var args = arguments; clearTimeout(timer); timer = setTimeout(function(){ func.apply(context, args) }, wait) } } function debounce(func, wait, leading, trailing) { var timer, lastCall = 0, flag = true return function() { var context = this var args = arguments var now = + new Date() if (now - lastCall < wait) { flag = false lastCall = now } else { flag = true } if (leading && flag) { lastCall = now return func.apply(context, args) } if (trailing) { clearTimeout(timer) flag = true func.apply(context, args) }, wait) } } }
類似函數(shù)防抖操作
在一些與用戶的交互上,比如提交表單后,一般都會(huì)顯示一個(gè)loading框來提示用戶,他提交的表單正在處理中。但是發(fā)送表單請(qǐng)求后就顯示loading是一件很不友好的事情,因?yàn)檎?qǐng)求可能在幾十毫秒內(nèi)就會(huì)得到響應(yīng)。
這樣在用戶看來就是頁面中閃過一團(tuán)黑色,所以可以在提交表單后添加一個(gè)延遲函數(shù),在XXX秒后再顯示loading框。這樣在快速響應(yīng)的場(chǎng)景下,用戶是不會(huì)看到一閃而過的loading框,當(dāng)然,一定要記得在接收到數(shù)據(jù)后去clearTimeout.
let identify = setTimeout(showLoadingModal, 500)
fetch('XXX').then(res => {
// doing something
// clear timer
clearTimeout(identify)
})
- 節(jié)流
不管怎么觸發(fā),都是按照指定的時(shí)間間隔來執(zhí)行。簡(jiǎn)單地說,就是限制函數(shù)在一定時(shí)間內(nèi)調(diào)用的次數(shù)。在程序中,可以通過限制函數(shù)的調(diào)用頻率,來抑制資源的消耗。需要實(shí)現(xiàn)一個(gè)元素拖拽的效果,可以在每次 move 事件中進(jìn)行重繪 DOM,但是這樣做,程序的開銷是非常大的。所以這里用到函數(shù)節(jié)流的方法,來減少重繪的次數(shù)。
//普通方案
$dragable.addEventListener('mousemove', () => {
console.log('trigger')
})
// 函數(shù)節(jié)流的實(shí)現(xiàn)方案
let throttleIndentify = 0;
$dragable.addEventListener('mousemove', () => {
if(throttleIndentify) return;
throttleIndentify = setTimeout(() => throttleIdentify = 0, 500);
console.log('trigger');
})
這樣做的效果是,在拖拽的過程中,能保證 500 ms 內(nèi),只能重繪一次 DOM。 在同時(shí)監(jiān)聽了 mousemove 后,兩者最終的效果是一致的,但是在拖拽的過程中,函數(shù)節(jié)流 版觸發(fā)事件的次數(shù)會(huì)減少很多,資源相應(yīng)地會(huì)消耗更少。
通用的函數(shù)節(jié)流實(shí)現(xiàn)
// ES6 版
function throttle (func, interval) {
let identify = 0;
return (...args) => {
if (identify) return;
identify = setTimeout(() => identify = 0, interval);
func.apply(this, args)
}
}
function throttle(func, wait){
var timer;
return function() {
var context = this;
var args = arguments;
if(!timer) {
timer = setTimeout(function() {
timer = null;
func.apply(context, args)
},wait)
}
}
}
function throttle(func, wait, leading, trailing) {
var timer, lastCall = 0, flag = true
return function() {
var context = this
var args = arguments
var now = + new Date()
flag = now - lastCall > wait
if (leading && flag) {
lastCall = now
return func.apply(context, args)
}
if (!timer && trailing && !(flag && leading)) {
timer = setTimeout(function () {
timer = null
lastCall = + new Date()
func.apply(context, args)
}, wait)
} else {
lastCall = now
}
}
}
- 類似函數(shù)節(jié)流的操作
平時(shí)開發(fā)中經(jīng)常會(huì)做的 ajax 請(qǐng)求獲取數(shù)據(jù),這里可以用到類似函數(shù)節(jié)流的操作。在我們發(fā)送一個(gè)請(qǐng)求到后臺(tái)時(shí),當(dāng)返回的數(shù)據(jù)還沒有接收到,我們會(huì)添加一個(gè)標(biāo)識(shí),來表明當(dāng)前有一個(gè)請(qǐng)求正在被處理,如果這時(shí)用戶再觸發(fā) ajax 請(qǐng)求,則會(huì)直接跳過本次函數(shù)的執(zhí)行。同樣的還有滑動(dòng)加載更多數(shù)據(jù),如果不添加類似的限制,可能會(huì)導(dǎo)致發(fā)送更多條請(qǐng)求,渲染重復(fù)數(shù)據(jù)。
對(duì)象拷貝
-
對(duì)象拷貝分為深拷貝和淺拷貝
JSON.parse(JSON.stringify(obj)) function clone(value, isDeep) { if(value === null) return null; if(typeof value !== 'object') return value if(Array.isArray(value)) { if(isDeep) { return value.map(item => clone(item, true)) } return [].concat(value) } else { if(isDeep) { var obj = {}; Object.keys(value).forEach(item => { obj[item] = clone(value[item], true) }) return obj; } return {...value} } } var objects = { c: { 'a': 1, e: [1, {f: 2}] }, d: { 'b': 2 } } var shallow = clone(objects, true) console.log(shallow.c.e[1]) // { f: 2 } console.log(shallow.c === objects.c) // false console.log(shallow.d === objects.d) // false console.log(shallow === objects) // false