为参数创建函数映射的最有效方法是什么?

时间:2013-04-27 02:43:31

标签: javascript

我想为其参数创建一个函数映射。此地图将动态更新。最后,将使用相应的参数调用所有函数。

function foo1(x) { 
    //do something with x
}

function foo2(x) { 
    //do something else with x
}

var map = {};
map[foo1] = [1,2,3];  //this array is updated dynamically in my code
map[foo2] = [4,5,6];

// I want to call foo1 and foo2 with their [1,2,3] and [4,5,6] arguments respectively.

我尝试了两种方法:

  • foo1转换为字符串(使用toString()方法)作为地图的关键字。然后我使用Function构造函数从该字符串返回函数。但我担心这会影响表现。
// This works. But concerned about the performance

map[foo1.toString()] = [1,2,3];

for(i in map){
    var fn = Function( 'return '+ i)();
    fn(map[i]);
}
  • 存储包装函数的对象及其各自的参数,如:
   { fn : foo1 , args : [1,2,3] }
   { fn : foo2 , args : [4,5,6] }

这里我存储对函数的引用而不是整个函数定义。但我必须遍历整个数组才能添加更多参数。

有没有更好的方法来维护这张地图?上述方法有哪些缺点?

更新

回答“在什么情况下我需要这个”的问题:

我正在维护从参数到函数的映射。我动态更新它。 稍后在我的代码中,我想创建一个反向映射并使用其所有参数调用函数。

例如:

1 -> foo1
2 -> foo2
3 -> foo1,foo2
4 -> foo1
... and so on.

然后我想创建一个这样的反向地图:

foo1 -> [1,3,4...]
foo2 -> [2,3,...]

最后致电:

foo1( [1,3,4...])

foo2( [2,3,...])

4 个答案:

答案 0 :(得分:3)

JavaScript中的对象只能将字符串作为键,因此使用map[foo1]几乎与map[foo1.toString()]相同。这些都有你没有注意到的问题:它们丢弃了封闭变量,例如:

function makeCounter() {
    var counter = 0;
    return function() { return ++counter; }
}

如果我有

var myCounter = makeCounter();

然后myCounter.toString()将为function() { return ++counter; },尝试使用Function构造函数重新构建它将导致错误的counter引用。

真的,最好的选择可能是使用函数的名称作为属性,作为值,使用像你建议的对象:

var map = {};
map['foo1'] = { fn: foo1, args: [1, 2, 3] };

然后,如果你想稍后添加更多参数,那很明显:

map['foo1'].args.push(4);

要打电话给他们,你可以使用这样的东西:

for(var functionName in map) {
    if(!Object.prototype.hasOwnProperty.call(map, functionName)) {
        continue;
    }
    map[functionName].fn.apply(null, map[functionName].args);
}

答案 1 :(得分:2)

为了使用对象(或函数)作为键,您需要使用Harmony(EcmaScript 6)WeakMapMap。它们目前都是实验性的,都可以在Firefox中使用。我相信Chrome中也可以使用WeakMap(使用正确的标记设置?)。

如果您的平台支持WeakMap,并且您选择合并它们,那么它们的使用非常简单:

var myWeakMap=new WeakMap();
myWeakMap.get(key [, defaultValue]);
myWeakMap.set(key, value);
myWeakMap.has(key);
myWeakMap.delete(key);
myWeakMap.clear();

更多信息(请注意,MDN引用似乎不公开):

另外:您也可以使用函数数组,然后使用indexOf获取函数的索引,然后使用该索引访问另一个数组中的参数。

function a(){}
function b(){}
var x=[a,b].indexOf(b);  //x=1

答案 2 :(得分:2)

除非有将对象作为键的本机跨浏览器解决方案,否则您始终可以实现自己的解决方案。这是你可以做的一个例子。在下面的代码中,ObjectMap会将生成的密钥存储为需要作为property的{​​{1}}的{​​{1}}。用于在object上存储key的{​​{1}}名称会随机化,以减少可能的冲突。然后,地图实现可以使用此property的{​​{1}}来检索key上的object,然后检索其关联的property

JSPERF:http://jsperf.com/object-map

value

答案 3 :(得分:1)

致Dagg Nabbit在我的问题评论中提出这一建议。

  

“不要忘记功能可以有属性。你可以随时存储   函数在一个数组中,并将它们的索引附加到数组中   作为一个属性,并以这种方式看待它们。“ - Dagg Nabbit

考虑以下 args-to-callback 参数的映射:

地图:

1 -> foo1
2 -> foo1,foo2
3 -> foo2

目标是构建一个 callback-to-args 地图(反向地图),如下所示:

callbackMap:

foo1 -> [1,2]
foo2 -> [2,3]

方法:

var allArgsPossible = [1,2,3]

// contains the list of callbacks to be called
var callbackArray = [];  

//maps the callback index in callbackArray to the callback's arguments
//callbackMap[index] = args means callbackArray[index] will be called with parameter "args" 
var callbackMap = {};

for( i in allArgsPossible)
{
    var item = allArgsPossible[i];
    var callbacks =  map[ item  ];
    for(j in callbacks)
    {
        var callback = callbacks[j];

        if(callback.index == undefined)
        {
            var index = callbackArray.length;
            // adding a new property "index" to the callback
            callback.index = index;
            callbackMap[index] = [item];
            //create a new entry in callbackArray
            callbackArray.push(callback);
        }
        else
        {
            callbackMap[callback.index].push(item);
        }
    }
}

console.log(JSON.stringify(callbackMap));

for( i in callbackArray)
{
    var callback = callbackArray[i];
    //get arguments from our reverse map
    var args = callbackMap[callback.index];
    // Bingo ! 
    callback(args);
}

您可以在此处了解全局:http://jsfiddle.net/kyvUA/2/

这里要注意的一点是,回调函数可能已经有一个“索引”属性用于其他目的。如果这是一个问题,您可以生成一个随机字符串并将此属性存储在回调中,并将索引作为值。 (由@plalx建议)

干杯!