Вызов функции в объявлении литеральной нотации объекта javascript - PullRequest
18 голосов
/ 27 января 2011

Я пытаюсь вызвать функцию из созданного мною литерала объекта, используя ключевое слово 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!');

  }
}

Почему возникает ошибка?

Ответы [ 5 ]

16 голосов
/ 27 января 2011

объяснение того, что происходит. Ответ Пойнти хорош, но я хочу объяснить это более обобщенно. Очень хорошее исследование по 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's 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 фактически вызывается обработчиком события, объект this указывает на объект DOM, который вызвал событие, поэтому ваш вызов node.doTheMove, который все еще не существует.

То, что вы хотели сделать, это обернуть его ссылкой на анимацию.

var callback = function() {
    Animation.init();
}

Это функция, выполняющая init в Animation. Когда вы выполняете его на подобном объекте, то внутри this === Animation, как и следовало ожидать.

Подводя итог. Проблема в том, что Animation.init - это просто ссылка на функцию. У него нет информации ни о чем другом, как упоминал Пойнти.

10 голосов
/ 27 января 2011

Вы должны изменить способ настройки:

window.onload = function(){

  var animBtn = document.getElementById('startAnim');

  animBtn.addEventListener('click', function() { Animation.init(); }, false);

}

В JavaScript тот факт, что функция определена как часть литерала объекта, на самом деле не имеет большого значения (на самом деле, на самом деле). Ссылка на Animation.init делает верной вам правильную функцию, но проблема в том, что когда функция вызывается позже (в ответ на фактический «щелчок»), браузер вызывает функцию, но не имеет Идея, что объект "Анимация" должна быть ссылкой this. Опять же, тот факт, что функция была объявлена ​​как часть объекта, здесь не имеет никакого значения. Поэтому, если вы хотите, чтобы this был чем-то конкретным по вашему выбору, то вы должны убедиться, что это установлено явно в коде, которым вы управляете. Приведенное выше решение относится к простейшему способу сделать это: оно обрабатывает события «click» с помощью анонимной функции, которая не делает ничего, кроме вызова функции «init» через явную ссылку через «Animation». Это гарантирует, что this ссылается на объект «Анимация» при запуске «init».

Другой альтернативой может быть использование функции ".bind ()", которую поддерживают некоторые браузеры и фреймворки:

window.onload = function(){

  var animBtn = document.getElementById('startAnim');

  animBtn.addEventListener('click', Animation.init.bind(Animation); }, false);

}

Чистый эффект почти точно такой же: этот вызов ".bind ()" возвращает функцию, которая вызывает функцию, для которой она была вызвана (которая является функцией "init" в объекте "Animation"), и делает это со своим первым аргументом в качестве ссылки this (объект "context"). Это то же самое, что мы получаем из первого примера, или, по сути, то же самое в любом случае.

1 голос
/ 21 июня 2013

Вот еще один хороший подход, я думаю.

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!');

    }
};
0 голосов
/ 02 августа 2017

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

})();
0 голосов
/ 27 января 2011

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

// 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);
}
...