JavaScript進階系列04,函式引數個數不確定情況下的解決方案

Darren Ji發表於2014-10-04


本篇主要體驗函式引數個數不確定情況下的一個解決方案。先來看一段使用函式作為引數進行計算的例項。

        var calculate = function(x, y, fn) {
            return fn(x, y);
        };
        var sum = function(x, y) {
            return x + y;
        };
        var diff = function(x, y) {
            return x - y;
        };
        var sumResult = calculate(1, 2, sum),
            diffResult = calculate(1, 2, diff);

 

但是,以上有一個問題:彈性不夠好,如果caculate需要多個形參,我們可能這樣寫:

var calculate = function(x, y, z, a, b, c, fn){}

 

這樣,每次需求改變,都要更改這裡的引數,顯得非常不方便。

 

我們知道,不管函式有多少個引數,所有的引數都會被儲存到arguments到這個陣列型別中。試想一下,如果我們可以從argments陣列中拿到最後一個元素,即代表函式的fn,再把其它陣列元素作為fn的引數,執行fn(arguments),這樣,不管方法的引數列表有多長,都可以把所有引數放到argments中再傳遞給函式。


還記得Array有一個原型方法pop,能把陣列的最後一個元素彈出,如果能把這個方法應用到arguments上,不就可以拿到arguments的最後一個元素、fn函式了嗎?而實際上,JavaScript是允許把一個物件的方法應用到另外的物件上的!

 

先來熟悉一下如何使用吧。


□ 把一個物件的方法應用到另外一個物件

※ 不傳遞引數

        var obj = {
            name: "myname",
            doSth: function() {
                alert(this.name);
            }
        };
        var foo = {
            name: "my name is foo"
        };
        var bar = {
            name: "my name is bar"
        };
        obj.doSth();

輸出結果:myname

 

如果我們想把obj物件的doSth方法應用到foo物件,可以使用apply方法,把最後一行改為:

obj.doSth.apply(foo);

輸出結果:my name is foo

 

※ 傳遞引數

如果obj物件的doSth方法帶引數,使用apply方法是否能把doSth方法的引數傳遞給foo或bar物件呢?

        var obj = {
            name: "myname",
            doSth: function(x, y) {
                alert(this.name + " " + x + " " + y);
            }
        };
        var foo = {
            name: "my name is foo"
        };
        var bar = {
            name: "my name is bar"
        };
        obj.doSth.apply(bar, ["hello", "world"]);

輸出結果:my name is bar hello world
可見,可以把doSth的引數以陣列的形式作為apply方法的實參,傳遞給bar物件。

 

※ 使用call方法

call方法也可以實現把一個物件的方法應用到另外一個物件。但用法和apply有所區別。

把最後一行程式碼改為:

obj.doSth.call(foo, "hello", "world");

輸出結果:my name is foo hello world
可見,doSth的引數挨個作為call方法的實參,傳遞給foo物件。

 

□ 程式碼改進

 

        var calculate = function() {
            var fn = Array.prototype.pop.apply(arguments);
            return fn.apply(null, arguments);
        };
        var sum = function(x, y) {
            return x + y;
        };
        var diff = function(x, y) {
            return x - y;
        };
        var sumResult = calculate(1, 2, sum),
            diffResult = calculate(1, 2, diff);
        alert(sumResult);

在calculate方法中,首先把Array的原型方法pop應用到了argments上,這樣就拿到了引數列表中的最後一個函式,然後再把arguments陣列傳遞給fn.apply方法。注意:不能以fn(arguments)呼叫,因為arguments是陣列而不是引數列表。

 

再來修改sum方法和diff方法,讓它們可以處理多個引數。

        var calculate = function() {
            var fn = Array.prototype.pop.apply(arguments);
            return fn.apply(null, arguments);
        };
        var sum = function() {
            var total = 0;
            for (var i = 0, l = arguments.length; i < l; i = i + 1) {
                total = total + arguments[i];
            }
            return total;
        };
        var diff = function () {
            //把Array獲取陣列第一個元素的shift方法應用到argments上
            var total = Array.prototype.shift.apply(arguments);
            for (var i = 0, l = arguments.length; i < l; i = i + 1) {
                total = total - arguments[i];
            }
            return total;
        };
        var sumResult = calculate(1, 2, 3, 4, 5, sum),
            diffResult = calculate(5, 1, 2, diff);
        alert(sumResult);
        alert(diffResult);

輸出結果:
15
2

 

總結:
○ 當一個函式需要處理多個引數,並且最後一個引數是函式,類似someFucntion(引數1, 引數2......引數n, fn),我們可以先取出函式fn,再把引數1, 引數2......引數n以陣列形式傳遞給fn.apply(null,arguments)
○ 在JavaScript中,可以把一個物件的方法應用到另外的物件上

 

“JavaScript進階系列”包括:

JavaScript進階系列01,函式的宣告,函式引數,函式閉包

JavaScript進階系列02,函式作為引數以及在陣列中的應用

JavaScript進階系列03,通過硬編碼、工廠模式、建構函式建立JavaScript物件

JavaScript進階系列04,函式引數個數不確定情況下的解決方案

JavaScript進階系列05,事件的執行時機, 使用addEventListener為元素同時註冊多個事件,事件引數

JavaScript進階系列06,事件委託

JavaScript進階系列07,滑鼠事件

相關文章