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

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

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

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 ) вызовет приведет к тому, что внутренние переменные Menu будут неопределенными, а вызов не удастся.
Теперь я уверен, что это связано с областью действия Menu.add() метода, поэтому я подумал, что если я также передам объект, это может сработать.

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

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, languages)

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

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

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

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

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

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

Точно так же, если вы выберете метод из его объекта и назовете его первоклассным объектом:

var method= obj.method;
method();

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

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

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

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 обещает, что вы также сможете передавать дополнительные аргументы в bind () для выполнения частичной функции приложения, для чего требуется немного больше кода, чем для bindMethod () выше;

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