Ответ 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, таких как обработчики событий.