确定函数对象的参数类型

时间:2013-02-19 22:15:55

标签: javascript

我正在尝试找到一种确定函数对象参数的方法,以便我可以确定它们各自的类型。

Params.prototype.getParams = function (fn) {
   var a = fn.length;
   console.log("Args: " + a);

   for (var i = 0; i < a; i++) {
      // This is where I want to print the type
      console.log("Arg[" + i + "] type is: " + typeof(fn.arguments[i]));
   }
}

然后,如果我定义了一些函数(可以是任何JS函数),例如:

function callback1("one") {}
function callback2("one", 2) {}
function callback3(1, "two", []) {}

如果我以后再调用每个:

var params = new Params();

params.getParams(callback1);
params.getParams(callback2);
params.getParams(callback3);

我想弄清楚每个函数实例的每个param类型。我已谷歌了,在搜索它,但找不到任何有用的东西。如果它知道'长度',我不明白为什么它也不知道参数?

更新 - 2013.02.19 17:41

是的,我知道上面的代码中存在潜在的错误,我为了简化阅读而剥离了它。

这是我想要完成的事情:

我正在阅读这篇(有趣的)文章:JavaScript Function Overloading

来自Java并且是一个纯粹主义者,我想开始尝试方法重载(该死的我无法相信原始的JS是多么)。

我希望通过将“函数计数”方法替换为使用参数类型的方法来“增强”本文中的示例,而不是“解析”应该调用哪个重载函数。看起来这只是通过在我的例子中实际知道'fn'的参数类型来实现的。

5 个答案:

答案 0 :(得分:4)

函数参数没有类型。

function fun(a,b,c) {... }

在JavaScript中,任何变量或函数参数都可以包含任何类型的对象或基元。

唯一的方法是在运行时使用arguments

进行检查
function callback3(a,b,c) {
    if (typeof a != 'string') {
      throw new Error('callback3 expects a string for its first argument');
    }
}

因为看起来你想要键入检查,你可以用

之类的东西来抽象它
function checkParams(args, types) {
    if (args.length != types.length) {
        throw new Error('Invalid number of arguments passed');
    }
    for (var i=0; i < args.length; i ++) {
        if (typeof args[i] != types[i]) {
            throw new Error('Argument number '+ i +' has wrong type'); 
        }
    }
}

function callback3(a,b,c) {
    checkParams(arguments, ['string', 'number', 'object') );
    // rest of the code
} 

答案 1 :(得分:1)

函数参数没有类型,如果你想要一些类似重载的东西,你需要自己实现它。

使用它的最简单方法是重载getter,像jQuery这样的setter方法用于val()和许多其他函数。如果你没有传递任何东西,它会检索该值,如果你传递了某些东西,它将设置一个值。我写的东西与John Resig建议的略有不同。这样,你将有两个例子可以看。

重要的是你无法检索参数类型,只能检索函数定义中声明的参数数量。

function overload(/*fun1, fun2, fun3... */) {
    var overloadedFuns = arguments;
    return function() {
        var argCount = arguments.length;
        for (var i=0; i < overloadedFuns.length; i++) {
           if (overloadedFuns[i].length === argCount) {
               return overloadedFuns[i].apply(this, arguments);
               break;
           }
        }
    }
}


var obj = {
  _val = 0;
  val: overload(
      // This will get called when called with no arguments
      function() {
          return this._val;
      },
      // This will get called when called with one arguments
      function(val){
          this._val = val;
      }
  )
}

console.log( obj.val() ); // Outputs 0
obj.val(5); // Sets it to 5
console.log( obj.val() ); // Outputs 5

可以引入类似的概念来添加类型。但由于您无法从函数定义中推断出类型,因此您需要自己添加该信息

示例: http://jsfiddle.net/UPuwE/1/

/**
 * @param {object[]} fnDefs, Each object should contain a paramList and a fn
 *  {paramMap: ["string"], fn: function() {myString}}
 *
 */
function overload(fnDefs) {

    function matches(arr1, arr2) {
        if (arr1.length !== arr2.length) {
            return false;
        }
        for (var i=0; i < arr1.length; i++) {
            if (arr1[i] !== arr2[i]) {
                return false;
            }
        }
        return true;
    }

    return function() {
        var types = [];
        for (var i =0; i < arguments.length; i++) {
            types.push(typeof arguments[i]);
        }
        for (var i=0; i < fnDefs.length; i++) {
            if (matches(types, fnDefs[i].paramMap)) {
                return fnDefs[i].fn.apply(this, arguments);
            }
        }
    }
}


var obj = {
    _val: 0,
    val: overload([
        {
            paramMap: ['string'],
            fn: function(str) {
                this._val = str;
            }
        },

        {
            paramMap: ['object'],
            // If an object is passed in, grab the string from its 
            // str property or by calling the toString() method;
            fn: function(obj) {
                this._val = obj.str || obj.toString();
            }
        },
        {
            paramMap: [],
            // Getter
            fn: function(obj) {
                return this._val;
            }
        }
    ])
};

obj.val('34');
console.log( obj.val() );

obj.val({str: '35'});
console.log( obj.val() );

obj.val( {toString: function(){return '36';} } );
console.log( obj.val() );

我希望这个例子向您展示运行时有很多开销,这就是大多数库不使用它的原因

答案 2 :(得分:0)

您在寻找arguments变量吗?

function sayHi(name) {console.log(arguments)}

    sayHi('hackNightly')
>>> ['hackNightly']

好像你有参数,你可以循环遍历它们并使用typeOf()或类似的东西进行类型检查。

答案 3 :(得分:0)

函数对象不再存储其参数,可以使用var arguments直接在函数调用中使用它们。

http://www.sitepoint.com/arguments-a-javascript-oddity/

  //try this instead
  console.log("Arg[" + i + "] type is: " + typeof(arguments[i]));

答案 4 :(得分:0)

我有一个小时的闲暇时间,所以我想我会尝试实施我从'Juan Mendes'和'系统'中收到的各种建议。

以下只是一个工作示例(未优化,未进行基准测试,未清理),因此请考虑它正在进行中。在我做任何其他事情之前,我会想要运行一些基准来看看它最终会提供什么样的“膨胀”。

如果有人对此有任何不妥之处,请提出建设性的批评!

function Overload() {
    // Empty for now!
}

Overload.prototype.link = function (object, method) {   
    console.log("Creating dispatch method '" + method + "()'");

    object.prototype[method] = function () {
        var target = method + "_";

        console.log("Invoked dispatch method '" + method + "()'...");

        for (var i=0; i < arguments.length; i++) {
            target += (typeof arguments[i]).substring(0, 1);
        }

        console.log("Resolved target as '" + target + "'");

        if (typeof object.prototype._overloaded[target] !== "undefined") {
            console.log("Dispatching to overloaded method: '" + target + "'");
            return object.prototype._overloaded[target].apply(object, arguments);
        } else {
            console.log("Method not found: '" + method + "('" + target + "')'");
            //throw "Exception ...";
        }
    }
}

以下函数将用于重载对象上的所有其他函数:

Overload.prototype.overload = function (object, method, fn, params) {
    var target = method + "_" + params;

    console.log("Overloading method '" + method + "()' to '" + method + "('" + params + "')'");

    if (typeof object === "undefined") {
        console.log("Object doesn't exist!");
    }

    if (typeof object.prototype[method] === "undefined") {
        this.link(object, method);
    }

    if (typeof object.prototype._overloaded === "undefined") {
        console.log("Creating '[obj].prototype._overloaded' property");
        object.prototype._overloaded = {};
    }

    if (typeof object.prototype._overloaded[target] === "undefined") {
        //console.log("Assigning overload function as target '" + method + "('" + params + "')'");
        object.prototype._overloaded[target] = fn;
    } else {
        console.log("Definition for '" + method + "('" + params + "')' already eixsts!");
    }

    return fn;
}

现在定义一些基本上“模拟”重载函数的示例函数:

function fn1(one) {
    console.log("Invoked function 1: " + one);
}

function fn2(one, two) {
    console.log("Invoked function 2: " + one + ", " + two);
}

function fn3(one, two, three) {
    console.log("Invoked function 3: " + one + ", " + two + ", " + three);
}

function fn4(one, two, three) {
    console.log("Invoked function 4: " + one + ", " + two);
}

function fn5(one, two, three) {
    console.log("Invoked function 5: " + one + ", " + two);
}

使用它来运行我们的测试:

function testMethodOverloading() {
    console.log("Testing method overloading!");

    var ov = new Overload();

    function OBJ() {}

    console.log("--");

    ov.overload(OBJ, "name", fn1, 's');
    ov.overload(OBJ, "name", fn2, 'sn');
    ov.overload(OBJ, "name", fn3, 'sns');
    ov.overload(OBJ, "name", fn4, 'ss');
    ov.overload(OBJ, "name", fn5, 'nn');

    console.log("--");

    var obj = new OBJ();

    obj.name("one");
    obj.name("two", 1);
    obj.name("three", 2, "four");
    obj.name("five", "six");
    obj.name(3, 4);
}

这是我从上面的测试中获得的输出:

Overloading method 'name()' to 'name('s')'
Creating dispatch method 'name()'
Creating '[obj].prototype._overloaded' property
Overloading method 'name()' to 'name('sn')'
Overloading method 'name()' to 'name('sns')'
Overloading method 'name()' to 'name('ss')'
Overloading method 'name()' to 'name('nn')'
--
Invoked dispatch method 'name()'...
Resolved target as 'name_s'
Dispatching to overloaded method: 'name_s'
Invoked function 1: one
Invoked dispatch method 'name()'...
Resolved target as 'name_sn'
Dispatching to overloaded method: 'name_sn'
Invoked function 2: two, 1
Invoked dispatch method 'name()'...
Resolved target as 'name_sns'
Dispatching to overloaded method: 'name_sns'
Invoked function 3: three, 2, four
Invoked dispatch method 'name()'...
Resolved target as 'name_ss'
Dispatching to overloaded method: 'name_ss'
Invoked function 4: five, six
Invoked dispatch method 'name()'...
Resolved target as 'name_nn'
Dispatching to overloaded method: 'name_nn'
Invoked function 5: 3, 4 

所以它绝对按照我的预期/想要的方式运行!但它会得到什么样的基准。我将在接下来做这个,看看我是否在所引用文章的基准测试范围内的任何地方(在剥离所有日志记录之后)。一旦我有了一个大致的想法,我会发布结果。