-1

I'm trying to find a way to determine a function objects parameters so that I can determine their respective types.

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]));
   }
}

Then if I define a few functions (could be any JS functions), such as:

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

If I later invoke each:

var params = new Params();

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

I would like to figure out each param type for each function instance. I've Google'd it, searched it on SO but can't find anything helpful. If it knows the 'length' I don't see why it wouldn't know the params also?

Update - 2013.02.19 17:41

Yes, I do know there are potential errors in the code above, I stripped it for simplicity of reading.

Here is what I was trying to accomplish:

I was reading this (interesting) article: JavaScript Function Overloading

Coming from Java and being a purist, I wanted to start experiment with method overloading (damn I can't believe how primitive JS is).

I wanted to 'enhance' the example in the article by replacing the 'function count' methodology with one where the parameter types are used instead to 'resolve' which overloaded function should be invoked. It seems this is pretty much only doable by actually knowing the parameter types of 'fn' in my example.

4

5 回答 5

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
} 
于 2013-02-19T22:20:05.303 回答
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() );

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

于 2013-02-20T01:08:26.603 回答
0

Are you looking for the arguments variable?

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

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

It seems like if you had the arguments, you could just loop over them and use typeOf() or something similar to do type checking.

于 2013-02-19T22:17:04.983 回答
0

我有一个小时的空闲时间,所以我想我会尝试实施我从“胡安门德斯”和“系统”收到的各种建议。

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

如果有人认为这有什么问题,请提供建设性的批评!

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 

所以它肯定会按照我的预期/想要的那样运行!但是它将获得什么样的基准。接下来我将这样做,看看我是否在引用文章的基准范围内(即在剥离所有日志记录之后)。一旦我有一个大致的想法,我会发布结果。

于 2013-02-20T22:28:46.420 回答
0

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

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

  //try this instead
  console.log("Arg[" + i + "] type is: " + typeof(arguments[i]));
于 2013-02-19T22:20:24.570 回答