1. 程式人生 > >Function.apply.bind()與Function.apply.bind()

Function.apply.bind()與Function.apply.bind()

1.Function.apply.bind(…)

我在學習promise部分的時候遇到了這樣的程式碼:

Promise.resolve([10,20]).then(Function.apply.bind(function(x, y){
   console.log(x, y);
}, null));     // 10,20

看到這裡我已經蒙圈了,Function.apply.bind(…)是個什麼操作?可能很多人和我一樣之前只接觸過Function.bind.apply(…)。

於是查了一下文件,大致明白了其中的含義。先撇開Promise不談,直接來看Function.apply.bind(…):

var sum = function(x, y) {
   console.log(x, y);
}
var foo = Function.apply.bind(sum, null);
foo([10, 20]);   // 10, 20

這裡我們有一個函式sum,通過Function.apply.bind(sum, null)我們建立了一個新的函式foo(…)。

我們一步步分析Function.apply.bind(sum, null)這段程式碼。 
sum.apply(null, [10, 20])這句程式碼將第一個引數置為null,第二個引數是一個數組,用於拆開後作為sum的最終引數。 
熟悉sum.apply(…)方法的朋友一定知道,如果將sum.apply(…)的第一個引數設定為null,那麼就意味著我們並不關心sum在執行時其內部的this指向誰。而Function.apply.bind(sum, null)目的就是將sum.apply(…)的第一個引數固定為null(其中,Function.apply.bind(sum, null)等價於sum.apply.bind(sum, null))。

所以最終我們得到的foo函式就是sum.apply(null, [10, 20]); [10,20]會拆開成10和20傳遞給sum(…)。

那麼我們再回到最開始的那個Promise的例子,傳遞給.then()的Promise決議值就是陣列[10,20],.then函式的第一個引數(通常我們稱之為fulfilled(…)函式)就相當於我們剛才建立的foo(…),執行foo([10, 20])輸出結果就是10,20。

2.Function.bind.apply(…) 
那麼類似的問題就還剩Function.bind.apply(…)。

我第一次見到這樣的程式碼是在《你不知道的JS》中卷的2.4小節。講回撥的時候。針對回撥的呼叫過早的問題,有經驗的開發者們給出了這樣的解決方式(當然ES6之後解決回撥函式呼叫過早的問題還是傾向於藉助Promise機制):

function asyncify(fn) {
    var orig_fn = fn,
        intv = setTimeout( function(){
            intv = null;
            if (fn) fn();
        }, 0 )
    ;

    fn = null;

    return function() {
        // 觸發太快,在定時器intv觸發指示非同步轉換髮生之前?
        if (intv) {
            fn = orig_fn.bind.apply(
                orig_fn,
                // 將包裝函式的`this`加入`bind(..)`呼叫的
                // 引數,同時currying其他所有的傳入引數
                [this].concat( [].slice.call( arguments ) )
            );
        }
        // 說明沒有過早觸發,這裡已經是非同步
        else {
            // 呼叫原來的函式
            orig_fn.apply( this, arguments );
        }
    };
}

和前面類似,我們將orig_fn.bind.apply(orig_fn, args)拆成兩部分來看:函式orig_fn.bind(…)和.apply(orig_fn, args)。根據.apply(…)的定義,orig_fn.bind.apply(orig_fn, args)其實就意味著我們將orig_fn.bind(…)函式的this指向orig_fn,然後.apply(orig_fn, args)的第二個引數會將剩下的引數傳遞給orig_fn.bind(…)函式。

那麼我們現在分析一下剩下的引數([this].concat( [].slice.call( arguments ))都是什麼吧,首先arguments是外界傳入的其餘引數(return function(…)這個函式傳入的引數),接下來我們藉助[].slice.call( arguments )將其轉化為一個引數陣列,備用。由於.bind(…)的第一個引數為在 origin_fn 呼叫中用到的 this (我們在前一段就已經提到過,這個this其實就指向orig_fn),所以使用 [this] 將構造的引數陣列中的第一個引數設定為 this 。[this]再與我們前面的備用陣列拼接起來,一同傳遞給.bind(…)。

此時,.bind(…)的第一個引數就是this,剩餘引數就是外界傳入的引數。所以,除了傳遞給orig_fn.bind(…)的第一個引數this,其餘的引數都會作為柯里化引數(預設值)。

在這裡的關鍵點是:.bind(…) 函式是通過 .apply(…) 呼叫的,所以 .bind(…) 自身所需要的 this 物件是一個函式(函式也是物件,在這裡即 origin_fn)。
--------------------- 
作者:Typhoonnnnn 
來源:CSDN 
原文:https://blog.csdn.net/weixin_37787381/article/details/81509361 
版權宣告:本文為博主原創文章,轉載請附上博文連結!