一道JavaScript編程題的拓展

背景

在下前端小白,近日在刷各類算法/編程題,今天碰到一編程題,考點是apply,雖然說簡單,但在解題時發現了一個挺有意思的東東,特來分享一下。歡迎各位大佬指點~javascript


正文

話很少說,直接上題目:二次封裝函數。前端

已知函數 fn 執行須要 3 個參數。請實現函數 partial,調用以後知足以下條件:
一、返回一個函數 result,該函數接受一個參數
二、執行 result(str3) ,返回的結果與 fn(str1, str2, str3) 一致

哈哈,這題簡單!稍微學過js的朋友就能寫出來:java

function partial(fn,str1,str2) {
    var result = function(str3) {
        return fn(str1,str2,str3);
    }
    return result;
}

這裏用個call可能會顯得有點逼格(笑),固然apply,bind也能達到同樣的效果。面試

function partial(fn,str1,str2) {
    var result = function(str3) {
        return fn.call(null,str1,str2,str3);
    }
    return result;
}

很少停留,咱們來看下一題,一樣是二次封裝函數:算法

實現函數 partialUsingArguments,調用以後知足以下條件:
一、返回一個函數 result
二、調用 result 以後,返回的結果與調用函數 fn 的結果一致
三、fn 的調用參數爲 partialUsingArguments 的第一個參數以後的所有參數以及 result 的調用參數

emmmmm,傳入的參數不固定? 有了! 用arguments~編程

function partialUsingArguments(fn) {
  var _arguments = Array.prototype.slice.call(arguments,1)
  var result = function() {
    var newArguments = _arguments.concat(Array.prototype.slice.call(arguments,0));
    return fn.apply(null,newArguments)
  }
  return result;
}

唔,好像也沒什麼難的,_arguments就是partialUsingArguments第一個參數後的全部參數組成的數組,將它和result的全部參數合併起來,利用apply傳入fn,ok~解決了!數組


誒誒?有意思的東西呢?這特麼一點意思都沒啊。
說出來大家可能不信,其實我看到這題的時候,雖然我想到了用apply,但我並無想到fn.apply(null,newArguments)。那我想到的是什麼呢?瀏覽器

Function.prototype.call.applyapp

什麼鬼?我也不知道是在哪看過這東西(也許沒看過),又好像是less

Function.prototype.call.call

仍是

Function.prototype.apply.apply

仍是

Function.prototype.apply.call

好像都差很少,媽個雞,試試就知道了!

return Function.prototype.call.apply(fn,newArguments)
瀏覽器一跑,好像對了?不對!我傳入的第一個參數怎麼不見了?
Google之~ 噢~ 原來第一個參數被吃了。
原來Function.prototype.call.apply(fn,newArguments)同等於fn.call(a,b,c,...z) // newArguments = [a,b,c,...z],這裏的...z不是ES6中的...rest哦,只是表示省略了中間的參數。

知道了原理(並不知道),那就好辦了,我給newArguments數組的頭部補一個元素上去不就行了~

newArguments.unshift(0);
Function.prototype.call.apply(fn,newArguments)

瀏覽器一跑——沒毛病老鐵!

完了嗎?

並無!

既然.call.apply能夠用,那其餘3個按理來講也能用,何況這裏多了一步對數組的操做,能不能更優雅一點呢?(另外3個怎麼用,觀衆老爺們內心有答案嗎?)
本着 求知 以及 code less,do more 的精神,我對剩下的3種組合進行了深♂入的研究。

分析及結論以下

原理其實很簡單,Function.prototype.apply(call)其實就是一個函數,將視爲一個總體,記做A。原式就能夠轉換成:A.apply(參數)A.call(參數),而後進一步轉換,參數1.A(參數2),什麼參數、參數一、參數2的?
相信你們也看出來了,這裏惟一的難點(?)就是參數。參數怎麼寫呢?

其實很簡單,apply就傳數組,call就傳一個序列。

applycall的區別你們確定知道,我就很少說了。第一個參數確定是fn,這個沒跑了。關鍵就在第二個參數上。

第二個參數,首先它形式上要看後一個apply/call,若是是apply,咱們就傳數組進去,若是是call,就傳一個序列。而後它的內容就要看A了,也就是前一個apply/call,同理,若是是apply,就傳數組參數,若是是call,就傳序列參數

因此咱們能夠得出如下結果:

Function.prototype.apply.apply(fn,[null,newArguments])
Function.prototype.call.call(fn,null,...newArguments])
Function.prototype.apply.call(fn,null,newArguments])
//上面3式能夠實際上等於
fn.apply(null,newArguments)
fn.call(null,...newArguments) //...爲擴展運算符,...[arr] = arr[0],arr[1],...arr[n]
fn.apply(null,newArguments)

繞了一大圈,又回來了~ :)
而上面的Function.prototype.call.apply也能夠改寫成:

Function.prototype.call.apply(fn,[null,...newArguments])

從而減小了對數組newArguments的操做。

寫在最後

第一次寫文章(水貼),十分緊張,刪了改,改完了刪,總以爲寫的很差、十分囉嗦。
可能會有人以爲毫無心義,但我以爲這個卻是能夠做爲一道面試題。

請在填寫空白內容使等式成立:
fn.apply(null,args) = Function.prototype.apply.apply(_____)

若是真的有人遇到,請回來點贊^ ^也但願此文能多少幫助到前端新人,你們一塊兒學習,進步!哪裏要是寫很差能夠直說!幫助我進步。謝謝!不廢話了,完。

相關文章
相關標籤/搜索