Вызов функции в объявлении нотации литерала объекта javascript
Я пытаюсь вызвать функцию внутри литерала объекта, который я создал, используя ключевое слово this
. Но появляется сообщение об ошибке: this.doTheMove()
не является функцией:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
}
var Animation = {
init: function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
},
doTheMove: function(){
alert('Animation!');
}
}
Почему возникает ошибка?
Ответы
Ответ 1
Объяснение того, что происходит. Настойный ответ хорош, но я хочу объяснить его более общее. Очень хорошее исследование this
можно найти здесь
Обработчик событий - это просто обратный вызов. Вы передаете ему функцию и событие для прослушивания. В целом все, что он будет делать, это вызов этой функции.
Animation.init - это просто получатель этой функции. Подумайте об этом так:
var callback = Animation.init
animBtn.addEventListener('click', callback, false);
...
// internal browser event handler
handler() {
// internal handler does stuff
...
// Oh click event happened. Let call that callback
callback();
}
Итак, все, что вы сделали, передано в
var callback = function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
}
По умолчанию в javascript this === window
. Это будет относиться к глобальному объекту, если он не настроен на что-то. Чистый эффект заключается в том, что вызывается window.doTheMove
. И эта функция не существует.
В этом случае, поскольку callback
actaully вызывает обработчик события, объект this
указывает на объект DOM, который вызвал событие, поэтому ваш вызов node.doTheMove
, который все еще не существует.
Что вы хотели сделать, это обернуть его ссылкой на анимацию.
var callback = function() {
Animation.init();
}
Это выполнение функции и выполняется init
на Animation
. Когда вы выполняете его на таком объекте, а затем внутренне this === Animation
, как и следовало ожидать.
Подводя итог. Проблема здесь в том, что Animation.init
- это просто ссылка на функцию. У него нет никакой информации о чем-либо еще, как упоминается Уолли.
Ответ 2
Вы должны изменить способ установки:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', function() { Animation.init(); }, false);
}
В JavaScript факт, что функция, по-видимому, определяется как часть литерала объекта, на самом деле не означает очень много (если вообще что-то). Ссылка на Animation.init
приведет вас к правильной функции, но проблема в том, что когда функция будет вызываться позже (в ответ на фактический "click"), браузер вызывает функцию, но не имеет понятия, что объект "Анимация" msgstr "должно быть ссылкой this
. Опять же, тот факт, что функция была объявлена как часть объекта, вообще не имеет значения. Поэтому, если вы хотите, чтобы this
был чем-то конкретным по своему усмотрению, вы должны убедиться, что он явно установлен в управляемом вами коде. Вышеупомянутое решение касается простейшего способа сделать это: оно обрабатывает события "click" с анонимной функцией, которая ничего не делает, кроме вызова функции" init" через явную ссылку через "Анимация". Это гарантирует, что this
относится к объекту "Анимация", когда выполняется "init".
Другой альтернативой может быть использование функции ".bind()", которую поддерживают некоторые браузеры и фреймворки:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init.bind(Animation); }, false);
}
Сетевой эффект почти то же самое: вызов в .bind() возвращает функцию, вызывающую функцию, на которую он был вызван (являющийся функцией "init" в объекте "Анимация" ), и делает это с его первым аргументом как ссылка this
(объект "контекст" ). Это то же самое, что мы получаем из первого примера, или фактически все равно.
Ответ 3
Вот еще один хороший подход, я думаю.
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
};
var Animation = {
init: function(){
Animation.doTheMove(); // This will work, but your IDE may complain...
},
doTheMove: function(){
alert('Animation!');
}
};
Ответ 4
Возможно, вы захотите использовать базовый подход к каталогу:
// generate a prototype object which can be instantiated
var Animation = function() { this.doTheMove(); }
Animation.prototype.doTheMove = function() {
// if the object has only one method, the whole code could be moved to
// var Animation = function() {...} above
alert('Animation!');
}
Animation.prototype.otherMethod = function(param1, param2) {
// ...
}
// run the code onload
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', new Animation(), false);
}
Ответ 5
Шесть с половиной лет спустя, но я надеюсь, что мой ответ также может дать некоторое представление о нынешних и будущих разработчиках.
Я склонен кодировать, используя литеральные объекты внутри самостоятельно определенных функций, и исходный вопрос, который вы опубликовали, отлично работает, если добавлена еще одна функция самоисполнения вместе с инструкцией try и catch.
Очень важно отметить, что все это касается области и контекста.
Просьба исправить любые недостатки или предоставить более эффективные рекомендации по использованию этого метода.
(function() {
console.log(this); // window object
var animation = {
init: function() {
this.doTheMove();
},
doTheMove: function() {
alert("Animation");
console.log(animation); // animation object
}
};
(function() {
try {
console.log("animation.init"); // animation.init function
animation.init();
} catch(e) {
console.log("Error is: " + e);
}
})();
})();