0

Простой цикл в конструкторе «класса»:

function TRoute(attributes) {
  var attributes = attributes || {};

  this.waypoints = [];

  attributes.waypoints.forEach(function(waypoint_text, i){
    this.waypoints.push({text: waypoint_text})
  });
};

После этого при вызове конструктора получаю ошибку

TypeError: 'undefined' is not an object (evaluating 'this.waypoints.push')

Дебагером выяснил, что внутри цикла forEach переменная this указывает на глобальный объект (в случае с браузером — Window). Вопрос — почему?

К слову, мне нужно, чтобы this указывал на объект TRoute (как в строчке)

this.waypoints = [];
installero
  • 1,076

2 Answers2

2

Как известно, в JS this не привязывается к объекту, а зависит от контекста вызова.

Если функция вызывается как свойство объекта, this в ней будет указывать на объект, вызвавший её, в отличных случаях this == window, а если указан строгий режим ("use strict"), то и вовсе this === undefined.

Так как внутре array.prototype.forEach() функция вызывается независимо от объекта, то и получается, что у Вас контекст в ней равен, так как не установлен строгий режим, window.

Распространенной практикой является сохранение нужного контекста в отдельной переменной:

var me = this;
var self = this;
var that = this;

Бывает удобно использовать его именно так, ведь тогда можно делать с этим контекстом что угодно и где угодно, не задумываясь, что же будет значить this в этой ситуации (надо будет только подумать в самый первый раз, выполнив это присвоение).

Тогда в Вашем случае можно написать так:

function TRoute(attributes) {
    var me = this;

    me.waypoints = [];

    attributes.waypoints.forEach(function(waypoint_text, i){
        me.waypoints.push({text: waypoint_text})
    });
};

Однако, если у функции есть возможность передать контекст вызова, а у Array.prototype.forEach() она есть, то надо использовать её:

attributes.waypoints.forEach(
    function(waypoint_text, i){
        this.waypoints.push({text: waypoint_text})
    },
    me
);
Rolandius
  • 3,612
  • надо использовать её... Так в результате и сделал. А не подскажите, почему это лучше, чем с me?

    – installero Oct 23 '15 at 11:14
  • @installero, на самом деле просто универсальней (предчувствие говорит, что в производительности разницы не будет), Вы можете использовать одну и туже конструкцию для разных случаев, просто меняя второй аргумент. Я считаю, что если что-то предоставляет удобный API, и ты используешь это что-то, то надо использовать его по полной. – Rolandius Oct 23 '15 at 11:27
0

Выяснилось, что чтобы передать нужный контекст у forEach есть второй параметр:

attributes.waypoints.forEach(function(waypoint_text, i){
  this.waypoints.push({text: waypoint_text})
}, this);

Но, может, у кого есть лучше идеи?

installero
  • 1,076