5

When you call a function in JavaScript and you miss to pass some parameter, nothing happens.

This makes the code harder to debug, so I would like to change that behavior.

I've seen How best to determine if an argument is not sent to the JavaScript function but I want a solution with a constant number of typed lines of code; not typing extra code for each function.

I've thought about automatically prefixing the code of all functions with that code, by modifying the constructor of the ("first-class") Function object.

Inspired by Changing constructor in JavaScript I've first tested whether I can change the constructor of the Function object, like this:

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

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

But it alerts "undefined" instead of "white", so it is not working, so I've don't further explored this technique.

Do you know any solution for this problem at any level? Hacking the guts of JavaScript would be OK but any other practical tip on how to find missing arguments would be OK as well.

4

4 回答 4

10

如果您的函数需要传递某些参数,您应该专门检查这些参数作为函数验证的一部分。

扩展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
};
于 2012-06-27T12:47:54.043 回答
2

正如其他人所说,有很多理由不这样做,但我知道几种方法,所以我会告诉你如何!为了科学!

这是第一个,从Gaby那里偷来的,给他点个赞吧!以下是其工作原理的粗略概述:

//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。但正如它在这个问题中所说,这涉及到一个无限循环。所以你需要一个未污染的函数对象来调用,这是通过将变量转换为字符串然后使用eval在未污染的上下文中调用函数来完成的!从网络的早期开始,有一个非常棒的片段向您展示了如何,但可惜我现在找不到它。正确传递变量需要一个技巧,我认为您实际上可能会丢失上下文,因此它非常脆弱。

尽管如此,如前所述,不要试图强迫 javascript 做一些违背其本质的事情,要么信任你的程序员同事,要么提供默认值,就像所有其他答案一样。

于 2012-06-27T13:38:11.293 回答
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).
于 2012-06-27T12:48:26.450 回答
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) {
    // ...
});

一般来说,这听起来是个坏主意,因为你基本上是在搞乱语言。你真的忘记经常传递参数吗?

于 2012-06-27T12:48:35.270 回答