常用JS函式-陣列扁平化,快取函式,柯里化函式,防抖和節流函式

蔣鵬飛發表於2020-02-09

flat函式 - 陣列扁平化

考慮我們有如下一個陣列

const arr = [1, 2, [3, 4], [5, 6, [7, 8]]]
複製程式碼

這個陣列有很多層,我們現在需要將它變成一層的應該怎麼做呢?結合我們前面講過的reduce和遞迴我們很容易寫出這個方法:

const flat = (arr, initVal) => {
  const startVal = initVal || [];
  return arr.reduce((prevRes, item) => {
    // 如果裡層還是陣列,遞迴呼叫自身
    if(Array.isArray(item)){
      return flat(item, prevRes);
    }else{
      return prevRes.concat(item);
    }
  }, startVal)
}

const arr = [1, 2, [3, 4], [5, 6, [7, 8]]];
const flatArr = flat(arr);

console.log(flatArr); // [1, 2, 3, 4, 5, 6, 7, 8]
複製程式碼

如果我們想對遞迴的層數進行限制,我們可以再加一個引數來進行控制:

const flat = (arr, depth, initVal) => {
  const startVal = initVal || [];
  return arr.reduce((prevRes, item) => {
    // 如果裡層還是陣列,遞迴呼叫自身
    if(Array.isArray(item) && depth > 1){
      return flat(item, depth - 1, prevRes);
    }else{
      return prevRes.concat(item);
    }
  }, startVal)
}

const arr = [1, 2, [3, 4], [5, 6, [7, 8]]];
const flatArr = flat(arr, 1); // 只扁平化一層

console.log(flatArr);
複製程式碼

快取函式

有時候一個複雜的計算函式需要反覆執行,如果每次都對他進行計算,會浪費大量效能,我們可以用一個記憶函式來快取計算過的值,比較典型的就是斐波拉契數列:

const fibonacci = (x) => {
  if(x === 1 || x === 2){
    return 1;
  }

  return fibonacci(x - 1) + fibonacci(x - 2);
}
複製程式碼

我們看下計算第40個數需要的時間:

const startTime = new Date().getTime();
fibonacci(40);
const needTime = new Date().getTime() - startTime;

console.log(needTime); // 959毫秒
複製程式碼

由於每次調fibonacci的計算過程都是一樣的,所以每次用時也是一樣,但算過一次後,其中很多數字我們已經計算過一次了,沒有必要進行重複計算,我們可以用一個記憶方法來記住以前的結果,下次需要用的時候直接取出結果就好了:

// 第一個引數是需要快取的函式,第二個引數是用來生成快取key的方法,如果不傳就用第一個引數做key
const memo = function(fn, hasher) {
  const memoFun = function(){
    const cache = memoFun.cache;
    const args = [].slice.apply(arguments);
    const hashKey = hasher ? hasher.apply(this, arguments) : args[0];
    if(!cache[hashKey]){
      cache[hashKey] = fn.apply(this, arguments);
    }

    return cache[hashKey];
  }

  memoFun.cache = {};
  return memoFun;
}
複製程式碼

然後我們用memo方法包裝一下fibonacci,讓他具有快取功能:

const cachedfFibonacci = memo(fibonacci);

// 然後看下效果
let startTime = new Date().getTime();
cachedfFibonacci(40);
let needTime = new Date().getTime() - startTime;

console.log(needTime); // 第一次執行時間還是959毫秒

// 再調一次
startTime = new Date().getTime();
cachedfFibonacci(40);
needTime = new Date().getTime() - startTime;

console.log(needTime); // 時間直接變為0了,直接取快取,快到1毫秒都不要
複製程式碼

柯里化函式

柯里化就是將一個接收多個引數的函式轉化為一系列使用一個引數的函式的技術。實現的效果就是

const fun = (a, b, c) => {return [a, b, c]};

//上述函式經過科裡化後就是
const curriedFun = curry(fun);
// curriedFun的呼叫變為 curriedFun(a)(b)(c)
複製程式碼

下面我們來看看curry函式應該怎麼實現

// 觀察上訴柯里化呼叫發現,它其實就是把引數都蒐集起來了,每次呼叫搜集幾個引數
// 當蒐集的引數足夠時執行主方法
const curry = (fn) => {
  // 先記錄主方法原始的引數個數,fn.length就是函式接收的引數個數
  const parmasLength = fn.length;

  return executeFun = (...args) => {
    // 如果接收引數夠了,執行主方法
    if(args.length >= parmasLength) {
      return fn(...args);
    } else {
      // 如果引數不夠,繼續接收引數
      return (...args2) => {
        // 注意executeFun接收的引數是平鋪的,需要將陣列解構
        return executeFun(...args.concat(args2));
      }
    }
  }
}

// 現在看下結果
curriedFun(1)(2)(3); // [1, 2, 3]
curriedFun(1, 2)(3); // [1, 2, 3]
curriedFun(1, 2, 3); // [1, 2, 3]
複製程式碼

防抖函式

我們有一個需求:實現一個搜尋框,當使用者連續輸入的時候不發請求去搜尋,只有當使用者輸入暫停超過500毫秒才發請求。實現這個需求就需要我們的防抖函式了,因為是等待500毫秒才發起請求,我們很容易就想到了setTimeout,如果timer存在,又觸發了這個方法,就把timer清了繼續等,知道方法不再觸發,timer執行

// 發起請求的函式
const sendRequest = () => {};

// 防抖函式
const debounse = (fn, waitTime) => {
  let timer = null;

  return function() {
    const self = this;
    const args = [].slice.apply(arguments);
    if(timer){
      clearTimeout(timer);
    } else {
      setTimeout(() => {
        fn.apply(self, args);
      }, waitTime);
    }
  }
}

const debounsedSendRequest = debounse(sendRequest, 500);
複製程式碼

節流函式

節流函式和防抖函式很像,但是針對的需求不一樣,比如onScorll方法可能會觸發的很頻繁,我們不能每次觸發的時候都去調回撥,會浪費大量效能,我們可能需要每50ms呼叫一次,那就需要節流函式了:

const scrollHandler = () => {};

const throttle = (fn, waitTime) => {
  let isRunnig = false;
  return (...args) => {
    if(!isRunnig) {
      isRunnig = true;
      setTimeout(() => {
        fn(...args);
        isRunnig = false;
      }, waitTime)
    }
  }
}

const throttledScrollHandler = throttle(scrollHandler, 50);複製程式碼


原創不易,每篇文章都耗費了作者大量的時間和心血,如果本文對你有幫助,請點贊支援作者,也讓更多人看到本文~~

更多文章請看我的掘金文章彙總



相關文章