2012-06-27 72 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做一些违背其本质的事情,要么相信你的同事或者提供默认值,就像所有其他答案一样。