2013-04-27 3 views
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. 

Я попробовал 2 подхода:

  • Старинная 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] } 

Здесь я храню ссылки на функцию, а не все определение функции. Но я должен пройти через весь массив, чтобы добавить больше аргументов.

Есть ли лучший подход к поддержанию этой карты? Каковы недостатки вышеупомянутых подходов?

UPDATE

Ответ на вопрос «в какой ситуации я буду нуждаться в этом»:

я поддерживаю карту от аргументов функций. Я обновляю его динамически. Позже в моем коде я хочу создать обратную карту и вызвать функции со всеми своими аргументами.

Для например:

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,...]) 
+0

Какую цель ставите перед собой? Я не могу придумать ситуацию, которая потребует чего-то подобного. –

+2

Не забывайте, что функции могут иметь свойства. Вы всегда можете хранить функции в массиве и присоединять их индекс в массиве к функции как свойство, и искать их таким образом. –

+0

@DaggNabbit, это в основном вариант решения, которое я разместил;) – plalx

ответ

2

Пока не существует встроенного кросс-браузерного решения для размещения объектов в качестве ключей, вы всегда сможете реализовать свое собственное решение. Вот пример того, что вы могли бы сделать. В приведенном ниже коде ObjectMap сохранит сгенерированный ключ как propertyobject, который должен служить key. Имя property, которое используется для хранения key на object, рандомизировано для уменьшения возможных конфликтов. Затем реализация карты может использовать propertyvalue для извлечения key на 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 
+0

+1 Отличный пример. – FacePalm

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

Будет ли конфликт, если 2 функции из разных областей имеют одно и то же имя? – FacePalm

+0

@FacePalm: Да, к сожалению. Если это неприемлемо, вам придется использовать свое решение с массивом объектов. – icktoofay

2

Для использования объектов (или функций) в качестве ключей вам необходимо использовать Harmony (EcmaScript 6) WeakMap или Map. Они оба в настоящее время экспериментальные, и оба доступны в 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 
+0

+1 для вашего второго подхода. В соответствии с тем, что я хочу. – FacePalm

1

Кредиты Дагг Nabbit для предлагая это в комментариях под моим вопросом.

«Не стоит забывать функции могут иметь свойства. Вы всегда можете хранить функции в массиве, и прикрепить их индекс в массиве к функции как Собственость, и искать их таким образом."- Dagg Nabbit

Рассмотрим следующую карту арг-к-функции обратного вызова аргументы:

карта:

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

Цель состоит в том, чтобы построить обратного вызова к арг карта (обратная карта) примерно:

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)

Приветствия!