Внедрить карту в javascript, которая поддерживает методы объектов как отображаемые функции?

Недавно я попытался использовать реализацию карты в javascript для создания набора элементов, а затем применить их к методу добавления объектов.

Во-первых, с использованием стандартной реализации болота.

var map = function (fn, a)
{
    for (i = 0; i < a.length; i++)
    {
        a[i] = fn(a[i]);
    }
}

Настройка.

var translateMenu = new Menu;

var languages = [ ['Chinese'   , 'zh-CN']
                , ['German'    , 'de']
                , ['French'    , 'fr']
                , ['Portugese' , 'pt']
                , ['Hindi'     , 'hi']
                ];

И моя функция... (не анонимная, поскольку она позже использовалась при добавлении translateMenu к mainMenu.)

var langItem = function (language, subMenu) 
    { 
       return new MenuItem(language[0], 'http://translate.google.com/translate?u=www.example.com&hl=en&ie=UTF-8&tl=en&sl=' + language[1] , "" , subMenu); 

    }

map ( langItem , languages );

Все это работало нормально, теперь у меня появился массив MenuItems.

Попытка вызова map( Menu.add , languages ) приведет к тому, что внутренние переменные меню будут undefined, а вызов не будет выполнен.
Теперь я уверен, что это связано с объемом метода Menu.add(), поэтому я подумал, что если бы я прошел и в объекте, это могло бы сработать.

Я попытался создать новую функцию карты, которая будет принимать объекты и функции, но имела ту же ошибку undefined.

objMap (fn , obj , a) {
    for (i = 0; i < a.length; i++)
    {
        obj.fn(a);
    }   
}
objMap ( add , translateMenu , languages );   // failed

Я работал над этим, расширяя Menu с помощью addAll(), чтобы взять массив, который отлично работает...

Menu.prototype.addAll = function (items){
    for (i = 0; i < items.length; i++)
    {
        this.add(items[i]);
    }
}

translateMenu.addAll( languages ); // yay! but I want a more elegant solution.

Во всяком случае, мой вопрос: как я могу реализовать карту (или аналогичную общую функцию), чтобы фактически поддерживать методы объектов в качестве моих отображаемых функций?.

Ответы

Ответ 1

Попытка вызова карты (Menu.add, языки)

Здесь ваша проблема почти наверняка связана с отсутствием связанных методов JavaScript.

Установка этого параметра для функции определяется только во время вызова, путем изучения способа получения метода. Если вы скажете одно:

obj.method();
obj['method']();

JavaScript заберет ссылку на "obj и set" this = obj внутри вызова метода. Но если вы скажете:

obj2.method= obj.method;
obj2.method();

Теперь это внутри функции будет obj2, а не obj!

Аналогично, если вы выберете метод с его объекта и обратитесь к нему как к первоклассному объекту:

var method= obj.method;
method();

Не будет объекта для этого, чтобы его установить, поэтому JavaScript устанавливает его в глобальный объект (например, окно для веб-браузеров). Вероятно, это происходит в вашем случае: "Метод Menu.add теряет всякую ссылку на его владельца", поэтому, когда он получает обратный вызов, он, скорее всего, неосознанно пишет членам "оконного объекта" вместо меню.

Это, конечно, очень необычно для языка OO и почти никогда не то, что вы хотите, но эй, что JavaScript рулон. Причинение молчащих, трудно отладочных ошибок является частью обоснования языка.

Чтобы обойти эту проблему, вы можете передать ссылку на объект в свою функцию карты, а затем использовать Function.call()/apply(), чтобы правильно установить эту ссылку:

function mapMethod(fn, obj, sequence) {
    for (var i= 0; i<sequence.length; i++)
        sequence[i]= fn.call(obj, sequence[i]);
}

mapMethod(Menu.add, Menu, languages)

Более общий способ - связать ссылки на функции вручную, используя закрытие:

function bindMethod(fn, obj) {
    return function() {
        fn.apply(obj, arguments)
    };
}

map(bindMethod(Menu.add, Menu), languages)

Эта возможность будет встроена в будущую версию JavaScript:

map(Menu.add.bind(Menu), languages)

И это свойство можно добавить к текущим браузерам, написав в Function.prototype.bind - действительно, некоторые JS-фреймворки уже есть. Однако обратите внимание:

  • ECMAScript 3.1 promises вы также сможете передавать дополнительные аргументы в bind() для выполнения приложения частичной функции, для чего требуется немного больше кода, чем bindMethod() выше;

  • IE любит утечку памяти, когда вы начинаете оставлять ссылки как связанные методы на объектах DOM, таких как обработчики событий.