javascript函數語言程式設計 : call 和 apply

莊文達發表於2017-10-18

買一送一:本系列可以讓你掌握函數語言程式設計,並且附贈 underscore 技能


日常BB

面對日新月異的程式語言都展開了函數語言程式設計大戰,靈活古老的javascript怎麼不參戰?javascript可是天然支援函式基礎的元老人物。想要成為一名高逼格的程式設計師不管你是前端,還是後臺,亦或是全棧,不管你開發webhybrid,怎麼能不掌握呢?筆者主要是java從業者,物件導向思想根深蒂固,讓我們以javascript為基石,破而後立,重新學習javascript,進行函式程式設計,感受非同一般的程式設計樂趣。


要說javascript函式程式設計 Jquery可以少,但是underscore是必不可少的,不管你是否掌握underscore,後續系列文章可能會大量使用此庫。

不得不說的第一課

  • call()apply()

掌握call()apply()不是學好函式程式設計的關鍵,而是基石,先簡單講下: 其實callapply的存在目的只有一個:改變函式整體內部this的指向,this 這裡就不老生常談了,完全浪費大家時間,聽說不舉例子都是耍流氓,我舉還不行嘛...

/**
 * Created by Venda-GM on 2017/10/18.
 */

function Peaple() {}
Peaple.prototype = {
    name:'小明',
    say:function(){
        alert(this.name);
    }
}

var Peaple1 = {
    name:'小強'
}

var peaple = new Peaple();
peaple.say.call(Peaple1); //小強
peaple.say.apply(Peaple1);//小強   
//******************知識點0.0***************
Fn1並沒有say這個方法,但是fn原型有,那麼fn.call...
複製程式碼

可以看出來: 其中的this被指向了,name並不是原來Peaple中的小明

  • 結論:

不管是call 還是 apply 都改變了函式的 this 物件

那兩個函式總有差距,具體差距呢 --接受引數不一樣--

call()方法中的[其餘的]引數必須直接傳給函式

apply()接收兩個引數:一個引數是執行時的作用域,
另一個是引數陣列、或arguments等
複製程式碼
  • arguments是什麼?

call,appply一樣,都是每個function內建的方法,arguments是屬性,可以獲取到傳遞到這個方法的全部變數。一般在庫中極為常見問底註解①_.toArray就用了。

  • 瞭解了之後我們組合起來做一個例子:

我們製造一個函式:它 接受一個函式,返回一個函式,並用apply執行返回來的函式。

        function splat(fun){ 
            return function(array){
                return fun.apply(console,array);
            }
        }
        var addArray = splat(function(x,y){
            this.log(x,y)
            return x+y;
        })
        addArray([1,2]); //3
        
        ```
        
先自己想1分鐘,然後我來解析一下發生了啥?

![](https://user-gold-cdn.xitu.io/2017/10/19/3147f656dc04af503316778871056470)

我們呼叫`addArray`的時候`addArray`呼叫了`splat()`函式並向他傳遞了一個`函式`(我們簡稱`解決方案`吧),而他也沒幹啥好事,最終`splat()`返回的函式說:`“我也解決不了,你的方案不錯,就用你的做吧。”` 說完大筆一揮, `fun.apply() `[同意!] 並且把你提交的`[1,2]`,按照你的解決方案執行了後還給你。

- 並且發現

最終`addArray`內部的`this`物件由`window`轉變為了`console`。有人問這有個吊用?下面舉例

複製程式碼
    function splat(fun){ 
        return function(array){
            var math_π =[1,4,1,5,9,2,6,5];
            return fun.apply(math_π,array);
        }
    }
    var addArray = splat(function(x,y){
        this.push(x+y)
        console.log(this); //[1,4,1,5,9,2,6,5,3]
        return x+y;
    })
    addArray([1,2]); //3
    
    ```
複製程式碼

我們有一個私有屬性math_π,並不想設為全域性,並且在執行addArray的匿名方法是還想讓他對math_π搞事情,那麼我們可以吧指標通過apply指向它,處理一些事情

我以前講的物件導向程式設計說過,私有的物件導向處理方法是,製造原型鏈設定get/set方法,在new一個物件,get到即可,物件導向是容易理解,但是是不是有點向java一樣繁瑣了呢。


剛才例子講了啥?健忘症又犯了! 講了我們實現了一個函式它接受了一個函式,並且返回了一個函式,返回的函式執行了接受的函式,並且改變了作用域。

我們可以做一個相反的,若有這樣一個需求:

我有個方法只接受陣列,但是現在有個不可抗力讓我只能傳字串,傳多少個我也不知道,我該怎麼辦?

//原始方法
var F = function(array){
            return array.join(' ')
        }
複製程式碼

想傳的資料

1,2,3,4,5,7,7,zzz,www,ddd
複製程式碼

我們首先想到arguments,那怎麼原封不動F實現需求呢?用call!完全吻合

//做一個轉換器
var ParamsConvertor  =  function(fun) {
    return function(){ //返回一個匿名函式
        fun.call(this,_.toArray(arguments));①
    }
}
複製程式碼

我們呼叫下:

ParamsConvertor(F)(1,2,3,4,5,7,7,'zzz','www','ddd');
複製程式碼

列印結果:

1 2 3 4 5 7 7 zzz www ddd
複製程式碼

完美!

這還只是摸到了函式式的一些邊緣就已經很興奮了,正式開始進行程式設計會怎麼樣呢?

今天太晚了就先寫到這裡

ps:①_.toArrayunderscore的一個函式,

toArray_.toArray(list) 
把list(任何可以迭代的物件)轉換成一個陣列,在轉換 arguments 物件時非常有用。

(function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
=> [2, 3, 4]

複製程式碼

我們看到其實他也是運用了arguments物件。


資料:underscore中文文件underscore可以配合使用的還有lodash中文文件 目前先掌握underscore即可。

程式碼地址

以後部落格將首發到這個git庫中,並寫一個列表,感興趣可以點下star,點star不迷路,github有歸檔。


接下來會正式踏足函數語言程式設計,準備好了麼,另外設計模式也會盡力持續更新,本來打算一個系列一個系列更新,但是根本按耐不住想寫其他的,其實我最近更想寫的是java,還想用Electron封裝一個elasticsearch客戶端、繼續維護爬蟲框架、想做的事情很多,慢慢來吧。

javascript函數語言程式設計 : call 和 apply

相關文章