Не понимаю, почему эта функция JavaScript может быть вызвана одним способом, но не другим

Недавно я начал изучать JavaScript с целью создания игр HTML5, и я столкнулся с поведением, которое мне трудно понять.

В качестве примера у меня есть конструктор, который инициализирует новые спрайты с массивом действий, которые они должны выполнять каждый раз при обновлении игры (например, анимация, перемещение и т.д.). Этот JSFiddle демонстрирует базовую реализацию.

По сути, я в замешательстве, почему это не работает...

Sprite = function () {

    this.actions = [this.animate];
};

Sprite.prototype = {

    animate: function () { /* animate the sprite */ },

    update: function () {

        this.actions[0]();  // doesn't do anything (?)
    }
};

... но это делает

Sprite = function () {

    this.actions = [this.animate];
    this.holder = 'whatever';
};

Sprite.prototype = {

    animate: function () { /* animate the sprite */ },

    update: function () {

        this.holder = this.actions[0];
        this.holder();  // executes animate function as desired
    }
};

Для моих неопытных глаз оба примера кажутся похожими на то, что они должны делать то же самое. Итак, почему ничего не происходит, если я вызываю this.actions[0]() напрямую, но если я назначу this.actions[0] на this.holder, а затем вызовет this.holder(), он отлично работает?

Ответ 1

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

Если вы ничего не сделали для его изменения (например, new, bind(), call(), apply()), это будет объект, на который он вызывается. С foo.bar() this === foo внутри функции bar.

this.actions[0]() делает this равным значению свойства actions

this.holder() делает this равным любому значению this в вызывающей функции.

Ваша функция должна зависеть от значения this, чтобы делать все, что она делает.

Ответ 2

Два способа исправить это:

Sprite = function () {
    this.actions = [this.animate.bind(this)];
};

Или:

update: function () {
    this.actions[0].call(this);
}