JavaScript 高階函式(Heigher-order function)

G_Owen發表於2019-07-21

個人部落格

概念

《javascript設計模式和開發實踐》中定義 函式既可作為引數被傳遞,也可以作為返回值輸出

滿足以下條件:

  1. 接受一個或多個函式作為輸入
  2. 輸出一個函式

高階函式一般是那些函式型包含多於函式。在函數語言程式設計中,返回另一個函式的高階函式被稱為Curry化的函式。

函式作為引數傳遞

將函式作為引數傳遞,我們就可以抽離以部分容易變化的業務邏輯,這樣可以分離業務程式碼中變與不變的部分

回撥函式:

將函式傳進一個方法中,函式不會立即執行,等待出來結果之後在執行。

let func = function (callback){
    if(n === 'owen'){
        callback() //回撥函式
    }
}
 function say (){
     console.log('Hello Word')
 }
 func(say)

Array 物件常用的方法

[1,2,3,4].forEach(iteration)
 function iteration(v){
     console.log(v)
 }

作為返回值輸出

讓函式繼續返回一個可執行的函式,意味著執行過程是可延續的。

判斷資料型別

let type = type =>{
    return obj => Object.prototype.toString.call(obj) === `[object ${type}]`
}
let isArray = type('Array'),isString = type('String'),isNumber = type('Number'),isObject = type('Object');

// or
let Type = (function(){
    let type = {},types = ['Object','Array','Number','String']
    for (let val of  types) {
        (function(str){
            type[`is${str}`] = (obj) => Object.prototype.toString.call( obj ) === `[object ${str}]`
        }(val))
    }
    console.log(type)
    return type
}())
Type.isNumber(2) // true

實現AOP(面向切片程式設計)

AOP 通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。
JAVA 語言中 AOP 將一些跟核心業務邏輯模組無關的功能抽離出來,通常包括日誌統計、安全控制、異常處理燈。再通過“動態織入”的方式摻入業務邏輯中。

好處: 可以保持業務邏輯模組的純淨和高內聚,方便複用日誌統計等功能模組。

JavaScript中實現AOP是指把一個函式“動態織入”到另一個函式之中

具體實現:

Function.prototype.before = function(beforeFn){
    let that = this; // 誰呼叫指向誰 下面是由 func 函式呼叫所以是指向 func

    return function( ...args){
        beforeFn.apply(this,args) // 執行回撥函式 beforeFn
        return that.apply(this,args) // 執行原函式
    }
}

Function.prototype.after = function(afterFn){
    let that = this; // 誰呼叫指向誰 下面是由befor函式呼叫所以是指向 befor
    return function( ...args){
        let ret = that.apply(this,args) // 執行並接收原物件
        afterFn.apply(this,args) //  執行回撥函式 beforeFn
        return ret
    }
}
var func = function (){
    console.log(2)
}
func = func.before(function (){
    console.log(1)
}).after(function (){
    console.log(3)
})
func()
// 1 2 3

函式柯里化 (function currying)

在數學和電腦科學中,柯里化是將多個引數的函式轉換成一系列使用一個引數的函式,且返回接受餘下的引數的新函式

curring 又稱部分求值;一個 curring 函式首先會接收一些引數,該函式並不會立即求值,而是繼續返回另外一個函式,而剛傳入的引數會被儲存在形成的閉包中,待函式真正需要求值的時候,之前的所以引數都會被一次性用於求值

簡單示例:

function add(a,b) {
    return a + b
}
add(1,2) // 3

接下來使用 currying 實現一個幾天之內消費總和的函式

// 普通方法
var cost = (function() {
    var args = [];
    return function(){
        if(!arguments.length){
            let money = 0
            for (let val of args ){
                money += val;
            }
            return money
        }else{
            [].push.apply(args,arguments)
        }
    }
})()
cost(100);
cost(100);
cost(100);
cost(); // 300

cost(100)(100)(100)

// currying
/**
 * 儲存原函式引數返回到新函式中使用
 */

//  func(100,100,100) //300
function count (...args){
    let num = 0;
     if(args.length>1){
         for (let v of args){
             num +=v
         }
         return num
     }else{
         return args[0]
     }
}

var  curry = function(func){
        let args = []
    return function fn(...Args){
        if (Args.length){
            [].push.apply(args,Args)
            return fn
        }else{
            return func.apply(this,args)
        }
    }
}
cost = curry(count);

cost(100);
cost(100);
cost(100);
cost(); // 300

函式節流

JavaScript 中大多數情況都是使用者主動出發函式,除非函式本身的實現不合理,否則一般不會遇到跟效能相關的問題,少數情況下,函式不是由使用者直接觸發控制,可能被頻繁呼叫造成嚴重的效能問題。
比如:

window.addEventListener('resize', function(e) {
   // do something...
});
window.addEventListener('scroll', function(e) {
   // do something...
});
Dom.addEventListener('mousemove', function(e) {
   // do something...
});

// progress
xhr.upload.addEventListener("progress", function(result) {
    // do something...
}, false);

// ...

上述事件1秒種觸發很多次,並且常常操作DOM節點,非常損耗效能,瀏覽器會因此吃不消而卡頓;實際我們不需要觸發如此高的頻率因此我們可以在一段時間內忽略掉一些執行次數

節流原理:

如果持續觸發事件,可每隔一段時間只執行一次。

使用定時器實現節流

將即將被執行的函式用 setTimeout 函式延遲一段時間執行,如果該定時器未執行完成則忽略接下下來的需被執行的函式。

 function throttle(func,wait) {
      let timer, firstFlag = true; //第一次立即執行
      return function(...args) {
          if(timer)  return false; // 如果存在定時器這不執行

          let that = this;
          if(firstFlag){
              firstFlag = false;
             return func.apply(that,args);
          }
          timer = setTimeout(function(){
               clearTimeout(timer);
               timer = null;
               func.apply(that,args);
            },wait)
      }
 }
 window.addEventListener('scroll', throttle(function(e) {
  console.log(e) 
},1000));

函式防抖

和節流一定時間段內只呼叫一次事件處理函式不同,防抖是一定時間段內沒有再觸發事件,事件處理函式才會執行一次,如果設定的時間到來之前,又一次觸發了事件,就重新開始延時。(使用者不再觸發對應事件才執行一次事件)

function debounce(func,wait) {
    let timer;
    return function(...args) {
        let that = this;
        clearTimeout(timer);
        timer = setTimeout(function(){
            func.apply(that,args)
        },wait)
    }
}
 window.addEventListener('scroll', debounce(function(e) {
  console.log(e) 
},1000));

相關文章