0

I am trying to understand the this keyword in javascript, i have read various articles about but it seems I have still some confusion.

For example let's consider the following code:

/*
 * script.js
 */

var data = 'global data';

var myObj = {

  data: 'object data',

  scope: function() {
      console.log(this.data);
  }

};


myObj.scope();    // print "object data"

So in this case the invocation context of the scope() function is the object, and this is bound to the object itself.

Now let's add some html..

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>

<input type="button" value="clickme" id="button">

<script src="script.js"></script>

</body>
</html>

.. and a click event listener on the button to trigger the scope() function

document.getElementById("button").addEventListener('click', myObj.scope, false);

// click on the button => print "undefined"

On the click event this refers to the button object that doesn't have a data property, so it correctly prints undefined. Now i want to fix this issue at the declaration time using the bind method (instead of call or apply). Thus I write:

/*
 * script.js
 */

var data = 'global data';

var myObj = {

  data: 'object data',

  scope: function() {
      console.log(this.data);
  }.bind(this)

};

document.getElementById("button").addEventListener('click', myObj.scope, false);


myObj.scope();    // print "global data"

// click on the button => print "global data"

It seems that I am bounding to the global object (window) instead of the myObj. Why? Which is the difference between the this keyword used in the line console.log(this.data) in the first example and the one used in the line bind(this) in the last example? Should not both refer to the myObj? I am sure I have some confusion about this point, so thanks in advance for any explanation :)

revy
  • 3,945
  • 7
  • 40
  • 85
  • You need to pass `myObj.scope.bind(myObj)` to `addEventListener`. You cannot bind the function inside the object literal. – Bergi May 05 '16 at 15:57
  • It's because your passing in *just* the function `myObj.scope` by itself - think about what that function looks like if you pluck it out of `myObj` and stick it in as a function by itself; how will it know the scope you're trying to access? Does your data need to be structured this way? You should instead have a function that takes a parameter of what scope to reference – Nick Zuber May 05 '16 at 15:58
  • possible duplicate of [How does the “this” keyword in Javascript act within an object literal?](http://stackoverflow.com/q/13441307/1048572) – Bergi May 05 '16 at 15:59
  • I know i can fix the issue using call (or apply) like this: document.getElementById("button").addEventListener('click', function() {myObj.scope.call(myObj)}, false); Just wondering if I can fix it at declaration time (using bind) instead at calling time – revy May 05 '16 at 16:08

3 Answers3

0

Here's the solution, you need to bind the eventHandler to the context you want. Look at the example below to be clear:

var data = 'global data';

var myObj = {
  data: 'object data',
  scope: function() {
      console.log(this.data);
  }
};

document.getElementById("button").addEventListener('click', myObj.scope.bind(myObj), false);

If you don't want to bother with bind, see the example on ES6 using Fat arrows (you can use it today, just read about Babeljs). See the example:

var data = 'global data';

var myObj = {
  data: 'object data',
  scope: function() {
      console.log(this.data);
  }
};

document.getElementById("button").addEventListener('click', () => myObj.scope(), false);

To learn more please read the article ( https://john-dugan.com/this-in-javascript/ ) that explains this in 4 easy steps

punkbit
  • 7,347
  • 10
  • 55
  • 89
  • To elaborate on this answer: when a function is called without the dot syntax (so `fn()` instead of `object.fn()`), the scope of `this` is set to `null`, which means that it will default to the global scope (in a browser, it's always `window`). You can override this behavior by `bind()`ing the function, as described in this answer. No matter how the function is called, `this` will refer to whatever you pass into `bind()` as the first argument. – Klemen Slavič May 05 '16 at 16:27
  • Thanks for the answer. I'll mark this as accepted, but still i don't understand why in the third example bind(this) refers to the window object and not myObj. Any clue? – revy May 05 '16 at 19:22
  • Yeah @KlemenSlavič explained it very well! revy check the article ( To learn more please read the article ( https://john-dugan.com/this-in-javascript/ ) that explains this in 4 easy steps ) – punkbit May 06 '16 at 12:11
  • @revy In the third example you're binding to `this`, which is `window` in your case. An object declaration does not automatically assume its lexical scope, which is by design. You must use `.bind(myObj)` as an explicit reference. – Klemen Slavič May 07 '16 at 14:02
  • yep, i got it. Thanks! – revy May 08 '16 at 16:26
0

It seems that is not possible to bind the function to the object itself directly inside the object, but augmenting the object outside of the object declaration seems to work too, and you have no to worry about to bind the context each time you want to call the function:

var myObj = {
  data: 'object data',
};

myObj.scope = function() {
  console.log(this.data);
}.bind(myObj);

What do you think?

revy
  • 3,945
  • 7
  • 40
  • 85
0

Why? Which is the difference between the this keyword used in the line console.log(this.data) in the first example and the one used in the line bind(this) in the last example? Should not both refer to the myObj? I am sure I have some confusion about this point, so thanks in advance for any explanation :)

In your last code snippet:

var myObj = {

  data: 'object data',

  scope: function() {
      console.log(this.data);
  }bind(this)

};
  1. first of all, you're missing a dot before bind, but I think it's just typo
  2. bind(this), here this doesn't point to the myObj object. this points to the global object. Why? because the context which the code is running in is the global context( the code outside of any function )
  3. If you really want to bind the context of myObj.scope to myObj, you still can't do like below, at runtime, you'll have can't read property of undefined error:

-

var myObj = {

  data: 'object data',

  scope: function() {
      console.log(this.data);
  }.bind(myObj)

};

Why? Because above code snippet is one javascript statement, when the javascript engine trying to interpret the statement, myObj hasn't been initialized yet. It's during initializing, so when the function bind(myObj), myObj is undefined. You need to do it with 2 statements:

var myObj = {

  data: 'object data',

  scope: function() {
      console.log(this.data);
  }
};
myObj.scope = myObj.scope.bind(myObj)
Aaron Shen
  • 8,124
  • 10
  • 44
  • 86
  • Just as a side note; I usually like to construct objects using the constructor method, so I do something like this: function MyObject() { this.scope = this.scope.bind(this); this.data = 'object data'; } MyObject.prototype.scope = function() { console.log(this.data); }; var myObj = new MyObject(); This way, I know that any constructed instance will have that bound function without having to bind it every time I construct an object just by using the `new` keyword. – Klemen Slavič May 07 '16 at 14:03