深入理解javascript函數進階系列第一篇——高階函數

来源:http://www.cnblogs.com/xiaohuochai/archive/2017/12/12/8026038.html
-Advertisement-
Play Games

[1]定義 [2]參數傳遞 [3]返回值輸出 [4]AOP [5]其他應用 ...


前面的話

  前面的函數系列中介紹了函數的基礎用法。從本文開始,將介紹javascript函數進階系列,本文將詳細介紹高階函數

 

定義

  高階函數(higher-order function)指操作函數的函數,一般地,有以下兩種情況

  1、函數可以作為參數被傳遞

  2、函數可以作為返回值輸出

  javascript中的函數顯然滿足高階函數的條件,在實際開發中,無論是將函數當作參數傳遞,還是讓函數的執行結果返回另外一個函數,這兩種情形都有很多應用場景。下麵將對這兩種情況進行詳細介紹

 

參數傳遞

  把函數當作參數傳遞,代表可以抽離出一部分容易變化的業務邏輯,把這部分業務邏輯放在函數參數中,這樣一來可以分離業務代碼中變化與不變的部分。其中一個常見的應用場景就是回調函數

【回調函數】

  在ajax非同步請求的應用中,回調函數的使用非常頻繁。想在ajax請求返回之後做一些事情,但又並不知道請求返回的確切時間時,最常見的方案就是把callback函數當作參數傳入發起ajax請求的方法中,待請求完成之後執行callback函數

var getUserInfo = function( userId, callback ){
  $.ajax( 'http://xx.com/getUserInfo?' + userId, function( data ){
    if ( typeof callback === 'function' ){
      callback( data );
    }
  });
}
getUserInfo( 123, function( data ){ 
  alert ( data.userName );
});

  回調函數的應用不僅只在非同步請求中,當一個函數不適合執行一些請求時,也可以把這些請求封裝成一個函數,並把它作為參數傳遞給另外一個函數,“委托”給另外一個函數來執行

  比如,想在頁面中創建100個div節點,然後把這些div節點都設置為隱藏。下麵是一種編寫代碼的方式:

var appendDiv = function(){
  for ( var i = 0; i < 100; i++ ){
    var div = document.createElement( 'div' );
    div.innerHTML = i;
    document.body.appendChild( div );
    div.style.display = 'none';
  }
};
appendDiv();

  把div.style.display = 'none'的邏輯硬編碼在appendDiv里顯然是不合理的,appendDiv未免有點個性化,成為了一個難以復用的函數,並不是每個人創建了節點之後就希望它們立刻被隱藏

  於是把div.style.display = 'none'這行代碼抽出來,用回調函數的形式傳入appendDiv方法

var appendDiv = function( callback ){
  for ( var i = 0; i < 100; i++ ){
    var div = document.createElement( 'div' ); 
    div.innerHTML = i;
    document.body.appendChild( div );
    if ( typeof callback === 'function' ){
      callback( div );
    }
  }
};
appendDiv(function( node ){ 
  node.style.display = 'none';
});

  可以看到,隱藏節點的請求實際上是由客戶發起的,但是客戶並不知道節點什麼時候會創建好,於是把隱藏節點的邏輯放在回調函數中,“委托”給appendDiv方法。appendDiv方法當然知道節點什麼時候創建好,所以在節點創建好的時候,appendDiv會執行之前客戶傳入的回調函數

【數組排序】

  函數作為參數傳遞的另一個常見場景是數組排序函數sort()。Array.prototype.sort接受一個函數當作參數,這個函數裡面封裝了數組元素的排序方法。目的是對數組進行排序,這是不變的部分;而使用什麼規則去排序,則是可變的部分。把可變的部分封裝在函數參數里,動態傳入Array.prototype.sort,使Array.prototype.sort方法成為了一個非常靈活的方法

// 從小到大排列,輸出: [ 1, 3, 4 ]
[ 1, 4, 3 ].sort( function( a, b ){ 
  return a - b;
});

// 從大到小排列,輸出: [ 4, 3, 1 ]
[ 1, 4, 3 ].sort( function( a, b ){ 
  return b - a;
});

 

返回值輸出

  相比把函數當作參數傳遞,函數當作返回值輸出的應用場景也有很多。讓函數繼續返回一個可執行的函數,意味著運算過程是可延續的

  下麵是使用Object,prototype.toString方法判斷數據類型的一系列的isType函數

var isString = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object String]';
};
var isArray = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object Array]';
};
var isNumber = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object Number]';
};

  實際上,這些函數的大部分實現都是相同的,不同的只是Object.prototype.toString.call(obj)返回的字元串。為了避免多餘的代碼,可以把這些字元串作為參數提前傳入isType函數。代碼如下:

var isType = function( type ){ 
  return function( obj ){
    return Object.prototype.toString.call( obj ) === '[object '+ type +']';
  }
};

var isString = isType( 'String' ); 
var isArray = isType( 'Array' ); 
var isNumber = isType( 'Number' );

console.log( isArray( [ 1, 2, 3 ] ) );    // 輸出:true

  當然,還可以用迴圈語句,來批量註冊這些 isType 函數:

var Type = {};
for ( var i = 0, type; type = [ 'String', 'Array', 'Number' ][ i++ ]; ){ 
  (function( type ){
    Type[ 'is' + type ] = function( obj ){
      return Object.prototype.toString.call( obj ) === '[object '+ type +']';
    }
  })( type )
};
Type.isArray( [] );    // 輸出:true 
Type.isString( "str" ); // 輸出:true

 

AOP

  AOP(面向切麵編程)的主要作用是把一些跟核心業務邏輯模塊無關的功能抽離出來,這些跟業務邏輯無關的功能通常包括日誌統計、安全控制、異常處理等。把這些功能抽離出來之後,再通過“動態織入”的方式摻入業務邏輯模塊中。這樣做的好處首先是可以保持業務邏輯模塊的純凈和高內聚性,其次是可以很方便地復用日誌統計等功能模塊

  通常,在javascript中實現AOP,都是指把一個函數“動態織入”到另外一個函數之中。下麵通過擴展Function.prototype來實現

  Function.prototype.before = function (beforefn) {
    var _this = this;    // 保存原函數的引用
    return function () {    // 返回包含了原函數和新函數的"代理"函數 
      beforefn.apply(this, arguments);    // 先執行新函數,修正this 
      return _this.apply(this, arguments);    // 再執行原函數
    }
  };
  Function.prototype.after = function (afterfn) {
    var _this = this;
    return function () {
      var ret = _this.apply(this, arguments); //先執行原函數
      afterfn.apply(this, arguments); //再執行新函數
      return ret;
    }
  };

  var func = function () {
    console.log(2);
  };

  func = func.before(function () {
    console.log(1);
  }).after(function () {
    console.log(3);
  });

  func();

  把負責列印數字1和列印數字3的兩個函數通過AOP的方式動態植入func函數。通過執行上面的代碼,控制台順利地返回了執行結果1、2、3

//1
//2
//3

 

其他應用

【not】

  下麵的not函數用於返回參數的返回值的邏輯非

  function not(f) {
    return function () {
      return !(f.apply(this, arguments));
    };
  }
  //偶數時,返回true;奇數時,返回false
  var even = function (x) {
    return x % 2 === 0;
  }
  //偶數時,返回false;奇數時,返回true
  var odd = not(even);
  [1, 1, 3, 5, 5].every(odd);//true

【mapper】

  下麵的mapper()函數,返回的新函數將一個數組映射到另一個使用這個函數的數組上

//所返回的函數的參數應當是一個實參數組,並對每個數組元素執行函數f(),並返回所有計算結果組成的數組
function mapper(f){
    return function(a){
        return Array.prototype.map.call(a,f);
    }
}
var increment = function(x){
    return x+1;
}
var incrementer = mapper(increment);
increment([1,2,3]);//[2,3,4]

【squareofsum】

  下麵的函數接收兩個函數f()和g(),並返回一個新函數用以計算f(g())

//返回一個新的可以計算f(g(...))的函數
//返回的函數h()將它所有的實參傳入g(),然後將g()的返回值傳入f()
//調用f()和g()時的this值和調用h()時的this值是同一個this
function compose(f,g){
    return function(){
        //需要給f()傳入一個參數,所以使用f()的call()方法
        //需要給g()傳入很多參數,所以使用g()的apply()方法
        return f.call(this,g.apply(this,arguments));
    };
}
var square = function(x){
    return x*x;
}
var sum = function(x,y){
    return x + y;
}
var squareofsum = compose(square,sum);
squareofsum(2,3);//25

  上面代碼中,首先執行compose(square,sum)。square傳給f,sum傳給g。然後執行f(g())。g作為f函數的參數,首先執行。即先執行sum(2,3),結果為5。再執行square(5),最終結果為25

 

最後

  本文介紹了高階函數的基礎使用,主要包括參數傳遞和返回值輸出兩種形式。其中,高階函數的一個重要應用是函數柯里化(currying),將在下篇博文中詳細介紹

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先自我介紹一下,本人鳥窩,現在就職於xx共用汽車,擔任主程,目前用的技術棧是.net core+angular。 今天我講的是關於ReactNative從零基礎開發,希望可以對入門的新手,起到一個指導作用。 目前學習React Native跨平臺開發的人員比較多,乾ReactNative開發的程式 ...
  • 這裡將為你詳細介紹占位符的使用,將其學以致用,可以達到簡化佈局文件,減少字元串資源量。 1、在資源文件中的使用。 打開資源文件中的strings.xml文件,進行編輯。如下圖所示: 圖 1.0 2、獲取字元串資源文件的使用說明。 方式一: 輸出的結果是:13.0得分:12.22 方式二: 輸出的結果 ...
  • 近日,Google在12月發佈的安卓系統安全公告中披露了一個名為“Janus”安卓漏洞(漏洞編號:CVE-2017-13156)。該漏洞可以讓攻擊者繞過安卓系統的signature scheme V1簽名機制,進而直接對App進行篡改。而且由於安卓系統的其他安全機制也是建立在簽名和校驗基礎之上,該漏 ...
  • xamarin上常用的崩潰分析工具有TestFlight,HockeyApp, Crashlytics等。TestFlight沒用過,Crashlytics註冊需要訪問Google,不好弄,HockeyApp走通了,步驟記錄如下: 1.配置HockeyApp 進入官網https://hockeyap ...
  • 在前面已經初步封裝了一個MVP的網路請求框架,那隻是個雛形,還有很多功能不完善,現在進一步進行封裝。添加了網路請求時的等待框,retrofit中添加了日誌列印攔截器,添加了token攔截器,並且對DataManager類進行了擴展,真正體現它的作用,並且對大量的重覆代碼做了一定封裝,減少代碼的冗餘。 ...
  • 25條提高iOS App性能的技巧和訣竅 當我們開發iOS應用時,好的性能對我們的App來說是很重要的。你的用戶也希望如此,但是如果你的app表現的反應遲鈍或者很慢也會傷害到你的審核。 然而,由於IOS設備的限制有時很難工作得很正確。我們開發時有很多需要我們記住這些容易忘記的決定對性能的影響。 這是 ...
  • 一旦攻擊者將植入惡意代碼的仿冒的App投放到安卓商店等第三方應用市場,就可替代原有的App,進行公開下載、更新。網友安裝這些仿冒App後,不僅會泄露個人賬號、密碼、照片、文件等隱私信息,手機更可能被植入木馬病毒,進而或導致手機被ROOT,甚至被遠程操控。 ...
  • dispatch_sync 線程同步、dispatch_async線程非同步 比如 這些代碼輸出的結果是 1 2 3 4 依次輸出、無論你運行多少次都會是這一種結果 但是來看下麵的非同步呢? 輸出的結果是 1 2 3 4 隨機輸出、有時 2 1 3 4 有時 1 3 2 4 等 在iOS中是無法使用 d ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...