Реализовать карту в javascript, который поддерживает методы объекта как сопоставленные функции? - PullRequest
2 голосов
/ 25 февраля 2009

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

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

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

Setup.

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.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.

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

1 Ответ

11 голосов
/ 25 февраля 2009

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

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

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

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

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

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

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

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

var method= obj.method;
method();

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

Это, конечно, весьма необычно для языка ОО, и почти никогда не того, чего вы хотите, но, эй, вот как 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, такие как обработчики событий.

...