0

As far as I know, "this" should hold the properties and functions of the object which actually triggered the function with "this". But in the following case it also contains the properties of the constructor "foo", why?

function foo(id)
{
   var self = this;
   this.foo = "foo";
   this.element = document.getElementById(id);
   this.element.addEventListener("keyup", function(){self.bar();}, false);
}

foo.prototype.bar = function()
{
   console.log(this.element.value);
};
new foo("anyInputElement");
salacis
  • 205
  • 1
  • 3
  • 11
  • 4
    Because prototyping inherits the properties. – adeneo Aug 06 '14 at 22:25
  • bar doesn't exist in that context. it would need to be self.bar() – Travis J Aug 06 '14 at 22:26
  • @TravisJ - but `self.bar.validate()` doesn't exist either. – adeneo Aug 06 '14 at 22:26
  • When I remove the anonymous function for the eventHandler and just write self.bar, it doesnt contain the properties of the constructor – salacis Aug 06 '14 at 22:27
  • 3
    What _exactly_ are you asking? What property in that code is showing up in what place you don't expect? – Alex Wayne Aug 06 '14 at 22:27
  • "this" contains the properties of the anyInputElement and the constructor of foo, thougth "this" should only provide the properties of the anyInputElement – salacis Aug 06 '14 at 22:29
  • 1
    _"this" contains the properties of the anyInputElement_. Why do you think this is true? We are all confused about why you are confused. Help us help you. **Be explicit.** – Alex Wayne Aug 06 '14 at 22:31
  • 1
    @adeneo - The context changed when the OP edited the question. – Travis J Aug 06 '14 at 22:31
  • Did you try doing `this.element.addEventListener("keyup", this.bar, false);` just for fun – adeneo Aug 06 '14 at 22:34
  • "But in the following case it also contains the properties of the constructor "foo", why?" - I should have thought that was a clear enough question, downvoters. – Mitya Aug 06 '14 at 22:34
  • @Utkanos - A better question would be, why wouldn't it ? – adeneo Aug 06 '14 at 22:35
  • 1
    Well there are many questions where, if one had more knowledge, a different question would be better placed, and more likely to yield more quickly a suitable answer. It doesn't negate that the OP asked a clear question. His choice of question is informed by his situation and knowledge. – Mitya Aug 06 '14 at 22:37
  • @Utkanos - my point being, the OP already answered that question, *`"this" should hold the properties and functions of the object which actually triggered the function`*, he then calls `self.bar` where `self` is `this`, or more correctly `foo`, so why wouldn't the bar function have access to the properties in the foo object, it does exactly what the OP says it should do, so a better question would be, why wouldn't it ? – adeneo Aug 06 '14 at 22:40
  • That code works as is. So I think the question is "why does this work?". And what's not clear is why the OP thinks it should not work. – Alex Wayne Aug 06 '14 at 22:56

2 Answers2

2

In JavaScript it's all about whether you instantiate or merely invoke a function. It's a constructor only in cases of the former. The difference, as you probably know already, is whether the new keyword is used before the function reference.

It's this that controls what those properties inside your constructor are properties of.

So in your case, they are not properties of the constructor (this is an unhelpful way of thinking of them); rather, they are properties of your instance.

Invoked functions are executed in the window scope, and so if you merely invoke your function, this.foo etc would be setting properties on window.

If you instantiate the function, however, they become properties on the instance, since an instance is implicitly returned from a function (constructor) that is instantiated rather than invoked. So:

Invocation: properties are set on default context, window:

function static_func() { this.foo = 'bar'; }
static_func();
window.foo; //'bar'

Instantiation: properties are set on returned instance

function constructor(val) { this.foo = val; }
var instance = new constructor('bar');
window.foo; //undefined
instance.foo; //'bar'
Mitya
  • 33,629
  • 9
  • 60
  • 107
0

So you are confused why when you save this in a variable, it isn't "this" later on? There are several reasons why self isn't "this" later on in the keyup event.

When a keyup event is called as a result of <input id="anyInputElement" />, the keyup event's callback function's this is going to be that input. This is because a new execution context has been created resulting in this being assigned to the element.

So why then isn't self, which you assigned to this, not the input element?

Execution contexts are stack based. So when the parent function foo is instantiated with new it creates its own execution context. This is the parent context to the execution context used in the keyup callback because it comes lower in the stack. Each execution context also contains a lexical and variable environment. These environments contain the variables available (naming collision results in variable overwriting lexical).

function foo(id)
{
 var self = this;  //parent context
 this.element.addEventListener("keyup", function(){
  self.bar();  //child context
 }, false);
}

A lexical environment contains variables from the parent environment all the way up the stack (this is why window is accessible from everywhere for example). So the child execution context here (inside of the keyup callback) contains self in its lexical environment.

self contains a copy of this from the parent execution context.

As a result of that, when self is used that is the execution context (including lexical and variable environments) that is accessed. This is where the properties from foo come from. Note that because the keyup callback created its own execution context, it also has a newly created this (which means that self != this inside of the keyup event listener).

Related: Save access to this scope

Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273