this и его область видимости

В конструкторе World есть вызов forEach. Хочу отметить, что внутри функции, передаваемой в forEach, мы уже не находимся непосредственно в области видимости конструктора. Каждый вызов функции получает своё пространство имён, поэтому this внутри неё уже не ссылается на создаваемый объект, на который ссылается this снаружи функции. И вообще, если функция вызывается не как метод, this будет относиться к глобальному объекту.

Значит, мы не можем писать this.grid для доступа к сетке изнутри цикла. Вместо этого внешняя функция создаёт локальную переменную grid, через которую внутренняя функция получает доступ к сетке.

Это промах в дизайне JavaScript. К счастью, в следующей версии есть решение этой проблемы. А пока есть пути обхода. Обычно пишут var self = this и после этого работают с переменной self.

Другое решение – использовать метод bind, который позволяет привязаться к конкретному объекту this.

var test = {

  prop: 10,

  addPropTo: function(array) {

    return array.map(function(elt) {

      return this.prop + elt;

    }.bind(this));

  }

};

console.log(test.addPropTo([5]));

// ? [15]

Функция, передаваемая в map – результат привязки вызова, и посему её this привязан к первому аргументу, переданному в bind, то есть переменной this внешней функции (в которой содержится объект test).

Большинство стандартных методов высшего порядка у массивов, таких как forEach и map, принимают необязательный второй аргумент, который тоже можно использовать для передачи this при вызовах итерационной функции. Вы могли бы написать предыдущий пример чуть проще:

var test = {

  prop: 10,

  addPropTo: function(array) {

    return array.map(function(elt) {

      return this.prop + elt;

    }, this); // ? без bind

  }

};

console.log(test.addPropTo([5]));

// ? [15]

Это работает только с теми функциями высшего порядка, у которых есть такой контекстный параметр. Если нет – приходится использовать другие упомянутые подходы.

В нашей собственной функции высшего порядка мы можем включить поддержку контекстного параметра, используя метод call для вызова функции, переданной в качестве аргумента. К примеру, вот вам метод forEach для нашего типа Grid, вызывающий заданную функцию для каждого элемента сетки, который не равен null или undefined:

Grid.prototype.forEach = function(f, context) {

  for (var y = 0; y < this.height; y++) {

    for (var x = 0; x < this.width; x++) {

      var value = this.space[x + y * this.width];

      if (value != null)

        f.call(context, value, new Vector(x, y));

    }

  }

};

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК