2012-06-27 76 views
4

當您在JavaScript中調用函數時,如果您錯過了傳遞某個參數的操作,則不會發生任何操作。在JavaScript中強制缺少參數

這使得代碼更難調試,所以我想改變這種行爲。

我見過 How best to determine if an argument is not sent to the JavaScript function 但我想要一個具有固定數量的代碼行類型的解決方案;不要爲每個函數輸入額外的代碼。

我想過通過修改(「first-class」)函數對象的構造函數,用代碼自動爲所有函數的代碼加前綴。

靈感來自 Changing constructor in JavaScript 我第一次測試我是否可以改變函數對象的構造函數,像這樣:

function Function2() { 
    this.color = "white"; 
} 

Function.prototype = new Function2(); 
f = new Function(); 
alert(f.color); 

但它提醒「不確定」,而不是「白」,所以它是不工作,所以我沒有進一步探索這種技術。

你知道任何級別的這個問題的解決方案嗎?黑客的JavaScript的膽量是好的,但如何找到缺失的參數的任何其他實用技巧也可以。

+2

這種方法聽起來像是一種絕妙的方式來打破依賴於JS中的這種行爲的所有第三方代碼。 – millimoose

+0

您試圖改變該語言的基本方面。這是一個誤導性的努力。 – Pointy

回答

9

如果你的函數需要傳遞某些參數,你應該特別檢查那些參數作爲函數驗證的一部分。

擴展Function對象並不是最好的想法,因爲許多庫依賴於未傳遞的默認參數的行爲(例如jQuery不會將任何東西傳遞給它的範圍undefined變量)。

兩種方法我傾向於使用:

1)參數是必需的功能工作

var foo = function (requiredParam) { 
    if (typeof requiredParam === 'undefined') { 
     throw new Error('You must pass requiredParam to function Foo!'); 
    } 

    // solve world hunger here 
}; 

2)參數沒有通過,但可以默認的東西(用了jQuery)

var foo = function (argumentObject) { 
    argumentObject = $.extend({ 
     someArgument1: 'defaultValue1', 
     someArgument2: 'defaultValue2' 
    }, argumentObject || {}); 

    // save the world from alien invaders here 
}; 
1

您可以使用裝飾模式。以下裝飾器允許您指定需要傳遞的參數的最小和最大數量以及可選的錯誤處理程序。

/* Wrap the function *f*, so that *error_callback* is called when the number 
    of passed arguments is not with range *nmin* to *nmax*. *error_callback* 
    may be ommited to make the wrapper just throw an error message. 
    The wrapped function is returned. */ 
function require_arguments(f, nmin, nmax, error_callback) { 
    if (!error_callback) { 
     error_callback = function(n, nmin, nmax) { 
      throw 'Expected arguments from ' + nmin + ' to ' + nmax + ' (' + 
        n + ' passed).'; 
     } 
    } 
    function wrapper() { 
     var n_args = arguments.length; 
     console.log(n_args, nmin, nmax); 
     console.log((nmin <= 0) && (0 <= nmax)); 
     if ((nmin <= n_args) && (n_args <= nmax)) { 
      return f.apply(this, arguments); 
     } 
     return error_callback(n_args, nmin, nmax); 
    } 
    for (e in f) { 
     wrapper[e] = f[e]; 
    } 
    return wrapper; 
} 


var foo = require_arguments(function(a, b, c) { 
    /* .. */ 
}, 1, 3); 
foo(1); 
foo(1, 2); 
foo(1, 2, 3); 
foo(1, 2, 3, 4); // uncaught exception: Expected arguments from 1 to 3 (4 passed). 
foo(); // uncaught exception: Expected arguments from 1 to 3 (0 passed). 
1

你可以模仿Python的裝飾器。這確實需要每個功能的額外輸入,但不需要額外的行。

function force(inner) { 
    return function() { 
     if (arguments.length === inner.length) { 
      return inner.apply(this, arguments); 
     } else { 
      throw "expected " + inner.length + 
       " arguments, got " + arguments.length; 
     } 
    } 
} 

var myFunc = force(function(foo, bar, baz) { 
    // ... 
}); 

一般來說,這聽起來像一個壞主意,因爲你基本上搞不懂這個語言。你真的忘記了經常傳遞的論點嗎?

+0

裝飾器模式不是Python特定的。 –

+0

感謝所有。我想我會單獨使用瓦西里的提議,或者在mattmanser建議的循環內部使用,但是這隻會通過我自己的代碼迭代(不會破壞第三方代碼)。 – user1485520

+0

回答瓦西里:每次我對代碼進行一些適度的重新設計時,我最終會遇到幾個缺少參數的函數調用。這些情況通常需要幾分鐘的超級調試。這是我試圖避免的。當然,重構時更集中也會幫助我:) – user1485520

2

正如其他人所說,有很多原因不這樣做,但我知道有幾種方法,所以我會告訴你如何!爲了科學!

這是第一個,從Gaby被盜,給他一個upvote!下面是它如何工作的一個粗略地:

//example function 
function thing(a, b, c) { 

} 

var functionPool = {} // create a variable to hold the original versions of the functions 

for(var func in window) // scan all items in window scope 
{ 
    if (typeof(window[func]) === 'function') // if item is a function 
    { 
    functionPool[func] = window[func]; // store the original to our global pool 
    (function(){ // create an closure to maintain function name 
     var functionName = func; 
     window[functionName] = function(){ // overwrite the function with our own version 
     var args = [].splice.call(arguments,0); // convert arguments to array 
     // do the logging before callling the method 
     if(functionPool[functionName].length > args.length) 
       throw "Not enough arguments for function " + functionName + " expected " + functionPool[functionName].length + " got " + args.length;      
     // call the original method but in the window scope, and return the results 
     return functionPool[functionName].apply(window, args); 
     // additional logging could take place here if we stored the return value .. 
     } 
     })(); 
    } 
} 

thing(1,2 ,3); //fine 
thing(1,2); //throws error 

第二種方式:

現在有另一種方式做到這一點,我不記得細節準確,基本上你overrride Function.prototype.call。但正如它在this question中所述,這涉及無限循環。所以你需要一個無名的函數對象來調用,這是通過把變量變成一個字符串然後用eval來調用這個函數在一個無用的上下文中完成的!從網頁的早期階段就可以看到一個非常棒的片段,但是我現在找不到它。有必要的黑客正確傳遞變量,我認爲你可能實際上失去了上下文,所以它非常脆弱。

如上所述,儘管如此,不要試圖強迫javascript做一些違背其本質的事情,要麼相信你的同事或者提供默認值,就像所有其他答案一樣。