2

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

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

4 回答 4

3

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

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

如果我有

var myCounter = makeCounter();

then myCounter.toString()will be 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);
}
于 2013-04-27T02:55:55.317 回答
2

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

如果您的平台支持 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
于 2013-04-27T03:23:38.573 回答
2

在有一个将对象作为键的本机跨浏览器解决方案之前,您始终可以实现自己的解决方案。这是您可以执行的操作的示例。在下面的代码中,ObjectMap将生成的密钥存储为property需要object作为key. property用于存储的名称是随机的keyobject以减少可能的冲突。然后,地图实现可以使用 thispropertyvalue检索keyon the object,然后检索它的关联value

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

function ObjectMap() {
    this.key = 0;
    //you should implement a better unique id algorithm
    this.mapId = '_' + Math.floor(Math.random() * 10000);
    this.data = {};
}

ObjectMap.prototype = {
    set: function (object, value) {
        var key = ++this.key;

        if (object[this.mapId]) {
            return;
        }

        object[this.mapId] = key;
        this.data[key] = value;
    },

    get: function (object) {
        var key = object[this.mapId];

        return key? this.data[key] : null;
    },

    remove: function (object) {
        var key = object[this.mapId];
        if (!key) {
            return;
        }
        delete this.data[key];
        delete object[key];
    }
};

function a() {}

var map = new ObjectMap();

map.set(a, 'test');

console.log(map.get(a)); //test
于 2013-04-27T03:32:19.783 回答
1

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

“不要忘记函数可以有属性。你总是可以将函数存储在数组中,并将它们在数组中的索引作为属性附加到函数中,然后以这种方式查找它们。” - 达格纳比特

考虑以下args 到回调参数的映射:

地图 :

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

目标是构建一个回调到参数映射(反向映射),如下所示:

回调映射:

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 所建议的那样)

干杯!

于 2013-04-27T09:03:23.563 回答