1

I'm currently learning javascript and trying to understand 'this'. In the following code, why am I not able to access this.ul within my render method? (It says it is undefined). I was led to believe that the cacheDOM method would bind this.ul to the people object which could then be accessed by the rest of the Object.

(function(){

  var people = {
    people: ['Tom', 'Sean'],

    init: function() {
      this.cacheDOM();
      this.render();
    },

    cacheDOM: function() {
      this.input = window.document.querySelector('.input');
      this.button = window.document.querySelector('.button');
      this.ul = window.document.querySelector('.ul');
    },

    render: function() {
      var data = this.people;

      data.map(function(person){
        var li = document.createElement('li');
        li.textContent = person;
        this.ul.appendChild(li);
      });
    }
  };

  people.init();

})();

Fixed. Added var ul = this.ul into the top of my render function which then allowed the map function proper access!

  • 1
    [This](http://stackoverflow.com/questions/133973/how-does-this-keyword-work-within-a-javascript-object-literal) (no pun intended), may help shine some light on your situation. – Matt Burland Jan 14 '16 at 15:15
  • Ah, all fixed :) thank you! – Thomas_Hoadley Jan 14 '16 at 15:17
  • 2
    @Thomas_Hoadley How was it fixed? – hudsond7 Jan 14 '16 at 15:17
  • 2
    defined `var ul = this.ul` at the top of my render function... which allowed the inner map function proper access! @hudsond7 – Thomas_Hoadley Jan 14 '16 at 15:19
  • `map` is creating a closure so `this` inside `map` will be `undefined` or in some implementations (jQuery) refer to `data` and not your outer object. Some implementation such as Lodash's `map` allow passing `this` into `map` as appended argument. – bendulum Jan 14 '16 at 15:21

4 Answers4

1

The value of this parameter is determined by the invocation pattern.

There are four patterns of invocation in JavaScript: the method invocation pattern, the function invocation pattern, the constructor invocation pattern, and the apply invocation pattern. Check this link to understand these patterns.

Check the following implementation of render function;

render: function() {
  var data = this.people;
  var that = this;
  data.map(function(person){
    var li = document.createElement('li');
    li.textContent = person;
    that.ul.appendChild(li);
  });
}

or you can pass the value of this as a an argument to the map() function :

  render: function() {
  var data = this.people;
  data.map(function(person){
    var li = document.createElement('li');
    li.textContent = person;
    this.ul.appendChild(li);
  },this);
}
0

Array.prototype.map method creates its own closure so that this won't refer people object here. You need to bind "this" to function as:

render: function() {
  var data = this.people;

  data.map(function(person){
    var li = document.createElement('li');
    li.textContent = person;
    this.ul.appendChild(li);
  }.bind(this));
}
Cem Ekici
  • 364
  • 1
  • 8
0

You can bind the function you are passing into map to the context of you object literal:

data.map(function(person){
  var li = document.createElement('li');
  li.textContent = person;
  this.ul.appendChild(li);
}.bind(this));

Or more transparently:

var iteratee = function(person){
  var li = document.createElement('li');
  li.textContent = person;
  this.ul.appendChild(li);
}
data.map(iteratee.bind(this));
bendulum
  • 1,777
  • 1
  • 13
  • 18
0

Inside the anonymous function this does not reference the object literal. this is a reference to the owner of the scope where it is being referenced. In an anonymous function I am pretty sure this is a reference to the global/window object.

To access the ul member of the object you can use the closure feature of JavaScript something like this.

render: function() {
  var self = this; // here this points to the object literal
  var data = this.people;

  data.map(function(person){
    var li = document.createElement('li');
    li.textContent = person;
    self.ul.appendChild(li);
  });
}
Mike Hanson
  • 266
  • 1
  • 8