【问题标题】:Force missing parameters in JavaScript在 JavaScript 中强制缺少参数
【发布时间】:2012-06-28 21:28:05
【问题描述】:

当你在 JavaScript 中调用一个函数并且你错过了传递一些参数时,什么都不会发生。

这使得代码更难调试,所以我想改变这种行为。

我见过 How best to determine if an argument is not sent to the JavaScript function 但我想要一个输入代码行数恒定的解决方案;不要为每个函数输入额外的代码。

我曾考虑通过修改 ("first-class") Function 对象的构造函数来自动为所有函数的代码添加前缀。

灵感来自 Changing constructor in JavaScript 我首先测试了是否可以更改 Function 对象的构造函数,如下所示:

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

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

但它会提醒“未定义”而不是“白色”,所以它不起作用,所以我没有进一步探索这种技术。

您知道任何级别的此问题的解决方案吗?破解 JavaScript 的内脏是可以的,但任何其他关于如何查找缺失参数的实用技巧也可以。

【问题讨论】:

  • 这种方法听起来是一种绝妙的方法,可以破坏所有依赖 JS 中这种行为的第三方代码。
  • 您正试图改变语言的一个基本方面。这是一种误入歧途的努力。

标签: javascript


【解决方案1】:

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

扩展 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
};

【讨论】:

    【解决方案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
    

    第二种方式:

    现在有另一种方法可以做到这一点,我不记得确切的细节,基本上你覆盖Function.prototype.call。但正如this question 中所说,这涉及到一个无限循环。所以你需要一个未污染的函数对象来调用,这是通过将变量转换为字符串然后使用eval 在未污染的上下文中调用函数来完成的!从网络的早期开始,有一个非常棒的 sn-p 向您展示如何,但可惜我现在找不到它。正确传递变量需要一个技巧,我认为您实际上可能会丢失上下文,所以它非常脆弱。

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

    【讨论】:

      【解决方案3】:

      你可以模仿 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) {
          // ...
      });
      

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

      【讨论】:

      • 装饰器模式不是 Python 特有的。
      • 谢谢大家。我想我会单独使用 Vasily 的提议,或者按照 mattmanser 的建议在循环中使用,但是按照所有人的建议,通过我自己的代码进行迭代(不破坏第三方代码)。
      • 对 Vasily 的回答:每次我对我的代码进行一些适度的重新设计时,我都会遇到几个函数调用缺少参数。这些情况通常需要几分钟的超无聊调试。这是我试图避免的。当然,在重构时更加专注也会对我有所帮助:)
      【解决方案4】:

      您可以使用装饰器模式。以下装饰器允许您指定需要传递的最小和最大参数数量以及可选的错误处理程序。

      /* 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).
      

      【讨论】:

        猜你喜欢
        • 2022-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-01
        • 1970-01-01
        • 2011-02-02
        • 1970-01-01
        相关资源
        最近更新 更多