天天看點

JavaScript進階之高階函數篇

JavaScript進階之高階函數篇

   簡介:歡迎大家來到woo爺說前端;今天給你們帶來的是JavaScript進階的知識,接下來的系列都是圍繞着JavaScript進階進行闡述;首先我們第一篇講的是高階函數。

  高階函數定義:高階函數是指操作函數的函數;一般情況在項目開發過程中都會分兩種情況  

  函數可以作為參數傳遞到另外一個函數執行

  函數可以作為傳回值輸出被執行

 讓我們來用一張圖描述一下高階函數

    

以上是高階函數的要求。我們在開發項目使用到的JavaScript的函數明顯滿足高階函數的要求;是以我們再寫代碼過程中可以利用高階函數對基本函數已經業務邏輯進行再次封裝,或者作為回調函數。接下來我們開始見識一下平時業務中怎麼使用高階函數;如何去封裝高階函數?、

    第一種模式:作為參數傳遞

在業務代碼中經常遇到兩個基本函數邏輯相同但業務邏輯不用的情況下;我們可以把這兩個相同的邏輯封裝成一個高階函數,進而把不同的邏輯寫在函數裡面作為參數傳遞到封裝好的函數裡面。這樣可以實作業務邏輯一些變化一些不變的場景,這種是我們最常見的場景;簡稱為回調函數。

接下來讓我們來舉例子說明一下

例子1:

1 //兩個不同的函數,但是其中一部分邏輯是相同的一部分是可變的

2 function a(){

3 console.log("我是一個函數");

4 console.log("我是a函數");

5 }

6

7

8 function b(){

9 console.log("我是一個函數");

10 console.log("我是b函數")

11 }

12

13 /*

14 以上就是我們兩個基本得函數,我們分别執行這兩個函數

15 */

16

17 a();

18 b()

這就是我們上面執行的結果,可以發現兩個函數存在着相同點。那麼我們接下來對兩個函數進行下一步的處理

function c(fn){

console.log("我是一個函數")

fn()

}

//把相同的邏輯封裝成一個c函數,不同邏輯的作為fn參數函數傳遞進去執行

c(function(){

console.log("我是a函數")

})

console.log("我是b函數")

//由此可見我們實作我們想要的呈現方式,接下來我們看一下執行的結果是怎麼樣的

這是我們最後執行的結果,跟上面的執行結果是一樣的,可見高階函數可以讓代碼更加多變性,更加簡潔明了、易懂;這是我們平時常見的場景。

例子2:

其實我們還有一種場景更加經常在項目裡面遇到。我們經常會在項目中使用ajax請求或者使用axios請求等等一些異步請求;一般往往我們不關心請求過程(請求過程是相同的)、隻想要請求的結果處理不同業務邏輯。我們可以利用高階函數對請求統一封裝,也叫請求攔截

var httpsAjax = function(obj,callback){

var {url,data,type} = obj;
$.ajax({
    url:url,
    type:type || 'POST' ,
    data:dara || {},
    success:function(res){
      //利用typeof 判斷資料類型,如果是傳進來的是函數,我們就執行回調函數
      if(typeof callback === 'function'){
         callback(res)
      }
    }              
httpsAjax({
    url:"xxx/get/user",
    type:"GET",
    data:{}
},function(res){
  //操作一定的業務邏輯
  console.log(res)           

第一種模式總結:以上就是我們最常見的基本高階函數的使用,一般我們會用函數作為參數傳遞到另外一個參數裡面,然後另外一個參數執行傳遞進去的函數,進而形成了回調函數(高階函數)

    第二種模式:作為傳回值輸出

相比把函數當作參數傳遞,函數當作傳回值輸出的應用場景也有很多。讓函數繼續傳回一個可執行的函數,這樣意味着運算過程是可延續的,就比如我們經常用到數組排序Array.sort()方法

下面是使用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){

rturn object.prototype.toString.call(obj) === '[object Number]'

isString("我是一個數組串");//true

isArray(["1","2"]);//true

isNumber(1)//true

注意:其實我們會發現上面的三個方法有大部分相同的邏輯object.prototype.toString.call(obj),不同的是處理邏輯傳回的字元串結果,為了避免備援的代碼,我們将其封裝成一個函數is_type();

var is_type = function(obj,type){

return object.prototype.toString.call(obj) == '[object ' + type + ']'

console.log(is_type(11,'Number'));//true

console.log(is_type(['a','b'],"Array");//true

注意:上面就是我們進行封裝的方法,可以發現我們提取出來之後,代碼量少了很多,更加明确,但是我們會發現我們需要傳遞兩個參數,而且兩個參數的含義要一一對上,不然我們在業務代碼上一旦寫錯沒對應上,那我們寫的邏輯就會出現bug。是以我們将這個方法再次封裝一下,把type先區分類型作為參數傳遞進去,利用高階函數擁有儲存變量的作用,傳回一個函數,分析我們傳遞的obj時到底是什麼類型。

var isType = function(type){

//先傳遞一個type參數作為資料類型,利用高階函數擁有儲存變量的特性,傳回一個可持續執行的函數
return function(obj){
      return object.prototype.toStirng.call(obj) === '[object '+ type +']'      
}           

//先細分到每種類型,這樣我們就可以明确知道具體類型調用什麼方法

var isString = isType("String");

var isArray = isType("Array");

var isNumber = isType("Number");

console.log(isArray([1,2,3]))//true

第二種模式總結:以上就是我們第二種模式的例子。顯然而見我們可以利用這種模式讓一個函數傳回另外一個函數,讓函數的運算繼續延續下去,這就是第二種模式作為傳回值輸出

以上就是高階函數兩種模式的基本了解與示範。相信對你在平時開發項目中有很多幫助;同時也要注意平時開發項目不是每個方法都要使用高階函數,高階函數使用場景就是以上兩種。不然會導緻大材小用。接下來我們來講一下JavaScript經常用到的高階函數map()/reduce()、filter()、sort()四個方法。

map()方法

定義:map()方法周遊原有數組傳回一個新數組,數組中的元素為原始數組元素調用函數處理後的值,按照原始數組元素順序依次處理元素。

注意:不會對空數組進行檢測、傳回的是新數組不會改變原始的數組

文法格式 :

newArray.map(function(item){

//周遊newArray item是newArray元素;類似于newArray[i]           

  return item

  //必須有return,反正不會産生新的數組

map()方法定義在JavaScript得Array中,我們調用Array得map方法,傳入我麼自己想要得函數,就等到一個新得array作為結果:

例子:

function pow(x){

return x*x

var arr = [1,2,3,4,5];

console.log(arr.map(pow));//[1,4,9,16,25]

這就是一個簡單得map方式調用,傳遞一個pow自定義函數,對原數組每個元素進行乘幂,得到一個新的數組

再比如我們平時經常會遇到這種,需要取數組對象中某個屬性用來做一組數組

var arr = [

{
    name:"張三",
    type:2
},
{
     name:"李四",
     type:3
}
           

]

//要求拿到type屬性組成新的數組

//以往的寫法for循環

var new_arr = []

for(var i = 0 ; i < arr.length ; i++){

var obj = arr[i]
for(var key in obj){
   if(key == 'type'){
       new_arr.push(obj[key])
    }
}
           

//map的寫法

var new_arr = arr.map(function(item){

return item.type
           

從上面可以看到一個寫法是for循環的寫法,一個是map的寫法;雖然兩個得到的結果都是一樣的[2,3];但是從代碼量來說,for循環過于備援,map簡單友善。是以以後遇到這種場景可以使用map方法更加友善

2.reduce()方法

    1、定義:接收一個函數作為累加器,數組中的每一個值(從左到右)開始周遊,最終計算為一個值

2、注意:對空數組是不會執行回調函數的

3、文法格式 : new Array.reduce(callback,initialValue)

   對文法格式的了解:

reduce(callback,initialValue)會傳入兩個變量,第一個參數是回調函數(callback)和第二個初始值(initialValue)。

第一個參數回調函數(callback)有四個傳入參數,prev和next,index和array。prev和next是必傳的參數。

第一個參數初始值(initialValue)決定回調函數的第一個參數prev的取值結果,當reduce傳入initialValue時,prev的預設值就是initialValue,當reduce沒有傳入initialValue時,那麼prev的預設值就是原數值的第一個元素值。

下面解釋一下

var arr = ["apple","orange"];

//第一種沒有傳遞initialValue的情況

function getValue(){

return arr.reduce(function(prev,next){

console.log("prev",prev);
    console.log("next",next)
    return prev;
})           

console.log("getValue",getValue())

運作結果可以看出來我們沒有傳遞initialValue的情況,prev取得是arr得第一個元素值開始周遊

接下來我們看一下傳遞initialValue得情況

var arr = ["a","b"];

//傳遞initialValue情況時

return arr.reduce(function(prev,next){
   console.log("prev",prev);
   console.log("next",next);
   prev[next] =1;
   return prev;   
},{})
//initialValue傳遞一個空對象           

console.log("getValue",getValue());

可以看到我們運作得結果,當傳遞initialValue得時候,prev得預設值就是你傳遞的initialValue;而我們就可以利用傳遞的預設值進行一系列業務邏輯處理。達到我們想要的效果

接下來我們來看一下經常業務中是怎麼使用reduce()的。

案例1:計算數組總和

var numArray = [1,2,3,4,5];

//用for循環來計算

var num = 0;

for(var i = 0 ; i < numArray.length ; i++){

num = num + numArray[i]

console.log(num);//15

//利用reduce方法

var res = numArray.reduce(function(prev,next){

return prev + next           

},0)

console.log(res) ;//15

利用for循環我們要先聲明一個全局變量作為預設值。我們知道開發項目過程中,盡量不要使用全局變量,而使用reduce我們可以完全避過,而且更加直接。這種是簡單的使用reduce()。

案例2:合并二維數組

var arr = [[0,1],[2,3],[4,5]];

var res = arr.reduce(function(prev,next){

return prev.concat(next)           

},[])

console.log(res);//[0,1,2,3,4,5];

我們可以傳遞一個空數組作為預設值,對原始的數組元素進行合并成一個新的數組。

    案例3:統計一個數組中的單詞重複有幾個

var arr = ["apple","orange","apple","orange","pear","orange"];

//不用reduce時

function getWorad(){

var obj = {};

for(var i = 0 ; i < arr.length ; i++){

var item = arr[i];
if(obj[item]){
  obj[item] = obj[item] + 1
}else{
  obj[item] = 1
}           

return obj

console.log(getWorad());

function getWorad2(){

prev[next] = (prev[next] + 1 ) || 1;
return prev;           

},{})

console.log(getWorad2())

最後兩個的結果都是一樣的,其實我們使用reduce()方法會讓我們的邏輯變得簡單易處理。進而抛棄我們備援的代碼,讓代碼看起來更加明了。相信你們再平時的業務中也會遇到這種業務邏輯的。

3、filter()方法

      1、定義:filter()也是一個常用的操作,它用于把Array的某些元素過濾調,然後傳回剩下的元素,和map方法類似,Array的filter也接收一個函數,和map()不同的是;filter()把傳入的函數依次作用于每個元素,然後根據傳回值是true還是false決定保留還是丢失該元素

案列1:例如,在一個Array中,删掉偶數,隻保留奇數,可以這麼寫:

var r = arr.filter(function(item){

return item%2 !== 0 ;

console.log(r)

可以看到,我們利用filter對元素的過濾,隻要元素取餘不等于零,就傳回來,等于零就抛棄,最後組成一個新的數組。

    案例2:把一個arr中的空字元串去掉,可以這麼寫:

var arr = ['a','b','','c']

return item && item.trim();

我們可以利用filter過濾數組中空的字元串,傳回一個沒有空字元串的數組。友善簡單,接下來我們來解析一下filter方法回調函數所帶的參數有哪些

//先看一下filter的文法格式

var r = Array.filter(function(ele,index,_this){

console.log(ele);
console.log(index);
console.log(_this);
return ture;           

/*

可見用filter()這個高階函數,關鍵在于正确實作一個篩選函數

回調函數:filter()接收的回調函數,其實可以有多個參數。通常我們僅使用第一個參數,表示Array的某個元素,回調函數還可以接收另外兩個參數,表示元素的位置,和數組本身。

*/

業務中最經常用到的還是用filter來去除數組中重複的元素

var r,arr = [1,1,3,4,5,3,4];

r= arr.filter(function(ele,index,self){

return self.indexOf(ele) === index;

這樣我們就很快速等到一個沒有重複元素的數組。這也是我們經常遇到的去重,也是一種經常面試問到的。

4、sort()方法

1、定義:sort()  方法用于對數組的元素進行排序,并傳回數組。

2、文法格式:arrayObject.sort(sortby);

3、注意:參數sortby  可選,用來規定排序的順序,但必須是函數。

//接下來我們就來看一下到底如何排序

//從小到大的排序,輸出:[1,2,3,9,56,87]

var arr = [9,87,56,1,3,2];

arr.sort(function(x,y){

if(x < y){

return -1           

if(x > y){

return 1           
return 0;           

//如果我們想要倒序排序;我們可以把大的放在前面[5,4,3,2,1]

arr.sort(function(x , y){

if(x < y ){
    return 1
 }
 if(x > y){
    return -1
 }
  return 0           

//在比如我們想要對字元串排序也可以實作,以字元串的首個字元,按照ASCII的大小

//進行比較的,忽略大小寫字母 ['good','apple','moide']

var arr = ['good','apple','moide'];

arr.sort(function(s1,s2){

var x1 = s1.toUpperCase();//轉成大寫
var x2 = s2.toUpperCase();
if(x1 < x2){
   return -1
 }
 if(x1 > x2){
    return 1
 }
 return 0           

//忽略大小寫其實是把值轉成大寫或者全部小寫,再去做比較

//注意:sort()方法會直接對原有的Array數組進行修改,他傳回的結果仍是目前的Array

//還有往往我們會在代碼裡見到很多數組對象,相對數組對象的某個屬性做排序那要怎麼實作呢

//下面就是對數組對象的排序例子

{

Name:'zopp',
 Age:10           

},

{

Name:'god',
  Age:1,
},
{
   Name:'ytt',
   Age:18
 }           

];

//簡單的寫法;預設的升序

function compare(prototype){

return function(a,b){

var value1 = a[prototype];
    Var value2 = b[prototype];
    return value1 - value2           

//一般我們往往會把參數函數單獨寫一個函數;達到清晰明确,多變性

console.log(arr.sort(compare(“Age”)));

/*最後我們寫了這麼多例子,也發現作為可變的部分參數函數其實無非就是兩種;一種是升序一種是降序。其實我們還可以把這可變的參數函數封裝成一個方法,利用傳參的方式來說明我們是要升序還是降序

/**數組根據數組對象中的某個屬性值進行排序的方法

* 使用例子:newArray.sort(sortBy(type,rev,[,key])) 
 * @param type 代表newArray的類型,’number’表示數值數組[1,2],’string’表示字元串數組[‘abhs’,’as’],’obj’表示對象數組[{},{}],如果是obj的話第三個參數必填
 * @param rev true表示升序排列,false降序排序
 * @param key 非必填的第三個函數,是作為如果type是obj的話作為屬性傳遞
 * */
           

var sortBy = function(type,rev,key){

//第二個參數不傳預設就是升序排列
           

if(!rev){

rev = 1           

}else{

rev = rev ? 1 : -1;
}           

return function(a,b){

//如果是string的話我們就要處理一下統一大寫還是小寫

if(type == 'string'){

a = a.toUpperCase();
 b = b.toUpperCase();           

//如果是obj的話我們就是取對應的屬性值

if(type == 'obj'){

a = a[key];
 b = b[key];           

if(a < b){

return rev * -1;           

if(a > b){

return rev * 1;           
return 0;
           

//這就是我們最後想要的統一封裝,大家也可以拿着自己去修改一下,這種封裝是最後傳回一個處理好的匿名函數,也是高階函數中的一種,後面我們也會提到

    總結:

如果要得到自己想要的結果,不管是升序還是降序,就需要提供比較函數了。該函數比較兩個值的大小,然後傳回一個用于說明這兩個值的相對順序的數字。

比較函數應該具有兩個參數 a 和 b,其傳回值如下:

若 a 小于 b,即 a - b 小于零,則傳回一個小于零的值,數組将按照升序排列。

若 a 等于 b,則傳回 0。

若 a 大于 b, 即 a - b 大于零,則傳回一個大于零的值,數組将按照降序排列。

原文位址

https://www.cnblogs.com/MyYTT/p/12539268.html