Выразительный JavaScript (Хавербеке) - страница 74

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

>    }

>  }

>};

Оживляем мир

Следующий шаг – создание метода >turn (ход) для мирового объекта, дающего существам возможность действовать. Он будет обходить сетку методом >forEach, и искать объекты, у которых есть метод >act. Найдя объект, >turn вызывает этот метод, получая объект >action и производит это действие, если оно допустимо. Пока мы понимаем только действие >“move”.

Есть одна возможная проблема. Догадаетесь, какая? Если мы позволим существам двигаться по мере того, как мы их перебираем, они могут перейти на клетку, которую мы ещё не обработали, и тогда мы позволим им сдвинуться ещё раз, когда очередь дойдёт до этой клетки. Таким образом, нам надо хранить массив существ, которые уже сделали свой шаг, и игнорировать их при повторном проходе.

>World.prototype.turn = function() {

>  var acted = [];

>  this.grid.forEach(function(critter, vector) {

>    if (critter.act && acted.indexOf(critter) == -1) {

>      acted.push(critter);

>      this.letAct(critter, vector);

>    }

>  }, this);

>};

Второй параметр метода >forEach используется для доступа к правильной переменной >this во внутренней функции. Метод >letAct содержит логику, которая позволяет существам двигаться.

>World.prototype.letAct = function(critter, vector) {

>  var action = critter.act(new View(this, vector));

>  if (action && action.type == "move") {

>    var dest = this.checkDestination(action, vector);

>    if (dest && this.grid.get(dest) == null) {

>      this.grid.set(vector, null);

>      this.grid.set(dest, critter);

>    }

>  }

>};


>World.prototype.checkDestination = function(action, vector) {

>  if (directions.hasOwnProperty(action.direction)) {

>    var dest = vector.plus(directions[action.direction]);

>    if (this.grid.isInside(dest))