-2

I'm creating an object literal and I want to use the reserved word "this". The problem I'm having is that the "this" points to the window object in an object literal. I know the this points to the current object when used in a constructor function. Is there a way to override it so that "this" points to my object literal?

 main = {
     run: function()
     {
         var elements = [];
         var allElements = document.querySelectorAll("*");
         for(var i = 0; i < allElements.length; i++)
         {
             if(allElements[i].nodeType != 3)
             {
                 elements.push(allElements[i]);
             }
         }
         for(var i = 0; i < elements.length; i++)
         {
             // Doesn't work
             // this.parseElement(elements[i]);

             // Works 
             main.parseElement(elements[i]);
         }
     },
     parseElement: function(e)
     {
         // Unimportant code
     } 
 }
 (function()
 {
    main.run();
 })();
  • 1
    Your object literal doesn't exist yet while you're trying to execute that anonymous function. You *cannot* call `parseElement` at this point, because it isn't defined yet. – deceze Jul 13 '16 at 05:58
  • How are you calling the function `main.run()` ? – gurvinder372 Jul 13 '16 at 05:59
  • 1
    The thing you claim works doesn't. – T.J. Crowder Jul 13 '16 at 05:59
  • See [*Self-references in object literal declarations*](http://stackoverflow.com/questions/4616202/self-references-in-object-literal-declarations) – T.J. Crowder Jul 13 '16 at 06:00
  • Thanks @T.J.Crowder I didn't know you could self-reference an object literal in return. – Nathan T Thomas Jul 13 '16 at 06:09
  • *"The problem I'm having is that the "this" points to the window object in an object literal"* - No it doesn't. (Except when it does.) But in any case you aren't using `this` in an object literal, you're using it in a function that happens to be a method of an object created via an object literal. – nnnnnn Jul 13 '16 at 06:12

1 Answers1

2

The thing you claim works in your question doesn't work:

var main = {
     run: (function()
     {
         var elements = [];
         var allElements = document.querySelectorAll("*");
         for(var i = 0; i < allElements.length; i++)
         {
             if(allElements[i].nodeType != 3)
             {
                 elements.push(allElements[i]);
             }
         }
         for(var i = 0; i < elements.length; i++)
         {
             // Doesn't work
             // this.parseElement(elements[i]);

             // Works 
             main.parseElement(elements[i]);
         }
     })(),
     parseElement: function(e)
     {
         // Unimportant code
     } 
 };
<div></div>

Fundamentally, you cannot refer to the object being constructed from within the object initializer. You have to create the object first, because during the processing of the initializer, while the object does exist no reference to it is available to your code yet.

From the name run, it seems like you want run to be a method, which it isn't in your code (you've edited the question now to make it one). Just remove the ()() around the function:

var main = {
  run: function() {
    var elements = [];
    var allElements = document.querySelectorAll("*");
    for (var i = 0; i < allElements.length; i++) {
      if (allElements[i].nodeType != 3) {
        elements.push(allElements[i]);
      }
    }
    for (var i = 0; i < elements.length; i++) {
      this.parseElement(elements[i]);
    }
  },
  parseElement: function(e) {
    console.log("Parsing " + e.tagName);
  }
};
main.run();
<div></div>

Since this is set by how the function is called for normal functions, if you want run to be bound to main so that it doesn't matter how it's called, using main instead of this is the simplest way to do that in that code.

But if you don't want to use main, you could create a bound function:

var main = {
  run: function() {
    var elements = [];
    var allElements = document.querySelectorAll("*");
    for (var i = 0; i < allElements.length; i++) {
      if (allElements[i].nodeType != 3) {
        elements.push(allElements[i]);
      }
    }
    for (var i = 0; i < elements.length; i++) {
      this.parseElement(elements[i]);
    }
  },
  parseElement: function(e) {
    console.log("Parsing " + e.tagName);
  }
};
// Bind run
main.run = main.run.bind(main);

// Use it such that `this` would have been wrong
// if we hadn't bound it:
var f = main.run;
f();
<div></div>

Just as a side note, we can use Array.prototype.filter and Array.prototype.forEach to make that code a bit more concise:

var main = {
  run: function() {
    var allElements = document.querySelectorAll("*");
    var elements = Array.prototype.filter.call(allElements, function(e) {
      return e.nodeType != 3;
    });
    elements.forEach(this.parseElement, this);
  },
  parseElement: function(e) {
    console.log("Parsing " + e.tagName);
  }
};

// Use it
main.run();
<div></div>

That assumes that parseElement only ever looks at the first argument it's given (since forEach will call it with three: the entry we're visiting, its index, and the object we're looping through).

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875