What is the most efficient way to create map of functions to arguments?

前端 未结 4 1969
我寻月下人不归
我寻月下人不归 2021-01-07 04:32

I want to create a map of functions to its argument. This map will be updated dynamically. Finally all the functions will be called with their corresponding arguments.

相关标签:
4条回答
  • 2021-01-07 04:51

    Credits to Dagg Nabbit for suggesting this in the comments under my question.

    "Don't forget functions can have properties. You could always store the functions in an array, and attach their index in the array to the function as a propery, and look them up that way." - Dagg Nabbit

    Consider the following map of args-to-callback arguments :

    map :

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

    The objective is to construct a callback-to-args map (reverse map) like this :

    callbackMap:

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

    Approach :

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

    You can get the whole picture here : http://jsfiddle.net/kyvUA/2/

    One point to note here is that the callback function may already have an "index" property for some other purpose. If that is a concern, you can generate a random string and store this property on the callback with the index as the value. ( as suggested by @plalx )

    Cheers !

    0 讨论(0)
  • 2021-01-07 04:52

    Objects in JavaScript can only have strings as keys, so using map[foo1] is practically identical to map[foo1.toString()]. These both have problems that you haven't noticed: they discard closed-over variables, e.g.:

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

    If I have

    var myCounter = makeCounter();
    

    then myCounter.toString() will be function() { return ++counter; }, and trying to reconstitute that with the Function constructor will result in having the wrong counter reference.

    Really, the best option might be to use the function's name as the property and as a value, use an object like you suggested:

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

    Then, if you want to add more arguments later, it's pretty obvious:

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

    And to call them all, you might use something like this:

    for(var functionName in map) {
        if(!Object.prototype.hasOwnProperty.call(map, functionName)) {
            continue;
        }
        map[functionName].fn.apply(null, map[functionName].args);
    }
    
    0 讨论(0)
  • 2021-01-07 05:01

    Until there's a native cross-browser solution for having objects as keys, you could always implement your own solution. Here's an example of what you could do. In the code below, the ObjectMap will store a generated key as a property of the object that needs to serve as a key. The property name that is used to store the key on the object is randomized to reduce possible conflicts. The map implementation can then use this property's value to retrieve the key on the object and then retrieve it's associated 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
    
    0 讨论(0)
  • 2021-01-07 05:03

    In order to use objects (or functions) as keys you'll need to use Harmony (EcmaScript 6) WeakMap or Map. They're both currently experimental and both are available in Firefox. I believe WeakMap might also be available in Chrome (with the proper flag settings?).

    If your platform supports WeakMap, and you choose to incorporate them, then their usage is quite straightforward:

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

    More information (note the MDN references appear to be unlisted):

    • https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap
    • https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Map
    • http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps

    Also: Alternatively you can use an array of functions, then use indexOf to get the index of the function, then access the parameters in an another array with that index.

    function a(){}
    function b(){}
    var x=[a,b].indexOf(b);  //x=1
    
    0 讨论(0)
提交回复
热议问题