1

After watching Kyle Simpson's Advanced JavaScript course on Pluralsight, I created a simple code snippet to try out this binding. I usually work in SublimeText editor and have node build engine configured in it and occasionally I run the same code in browser as well. I have noticed one difference in the program output when execute code in Node and in Browser [Chrome].

Following is the code snippet to try out this binding.

function foo() {
    console.log(this.bar);
}

var bar = "bar1";
var obj = {bar : "bar2"};

foo();
foo.call(obj);

When I execute it using Sublime, it returns undefined and bar2. However when I execute same snippet in a browser it returns bar1 and bar2, which I think is correct since variable bar is defined at global scope. I am not able to understand why Node environment is returning it as undefined. Kindly help.

Prasad Honrao
  • 192
  • 1
  • 14
  • 1
    This is why all Javascript code should be run in strict mode. Then, wrong code won't accidentally work sometimes, but not other times. The invocation of `foo()` and the subsequent reference to `this.bar` inside of `foo()` is simply just bad code that accidentally works sometimes in a browser because you coincidentally have a global variable with the same name as the property you're trying to reference and `this` is set to `window` in a browser not running in strict mode and globals are properties of the `window` object. Run in strict mode and this error will be immediately shown to you. – jfriend00 Jul 07 '15 at 06:19
  • 1
    possible duplicate of [Meaning of "this" in node.js module](http://stackoverflow.com/questions/22770299/meaning-of-this-in-node-js-module) – Alexander O'Mara Jul 07 '15 at 06:20
  • 1
    Basically, in the browser `this === window`, but in Node `this !== global` and var outside a closure in a Node module does not create a global variable anyway. – Alexander O'Mara Jul 07 '15 at 06:21
  • Thanks a lot @jfriend00 for quick response and clarifying the doubt. – Prasad Honrao Jul 07 '15 at 06:39
  • Thanks @AlexanderO'Mara for clarification. – Prasad Honrao Jul 07 '15 at 06:40
  • @PrasadHonrao - I expanded my comment into an answer which explains in detail what is happening in the node.js case. – jfriend00 Jul 07 '15 at 07:09
  • possible duplicate of [what's the difference between Browsers and Node?](http://stackoverflow.com/q/8624913/1048572) – Bergi Jun 16 '16 at 07:33

2 Answers2

2

In node.js, your bar variable is a module variable which is local to that module. It is not a global variable. Globals in node must be explicitly assigned to the global object. Top level declarations in a node.js module are not automatically globals like they are in a browser JS file. Technically, they exist inside a function scope that is created by the module loader so they are local variables to the module's function scope.

In node.js, the value of this in a plain function call like foo() is the global object.

But, in node.js, your bar variable is NOT a property of the global object. So, when you try to reference this.bar, that's global.bar and there is no property on the global object by that name.

So, in node.js, you're essentially doing this:

// create a function which is like the module scope
function myModuleFunc() {
    function foo() {
        // the value of this in a plain function call in node.js is the
        // global object
        console.log(global.bar);
    }

    // this isn't a global
    var bar = "bar1";

    foo();
}

// execute that module scope function
myModuleFunction();

And, hopefully you can see why there is no global.bar property, thus you get undefined.


This all accidentally works in a browser because var bar is a global and globals are on the window object and this === window so it accidentally works. Two wrongs make a right that works only sometimes.

As I said in my comment, it is highly recommended to run your code in strict mode and things will not accidentally work sometimes, but not others - this issue will all be highly consistent and you will get an immediate error when you have written the wrong sort of code.


Though you said you already understand what is happening in the browser environment, since your question asks why the two are different, I will describe the browser situation just to cover all the bases.

In the browser, the value of this in a plain function call (when not in strict mode) is the window object.

In the browser, your bar variable is a global variable and thus automatically becomes a property of the window object.

So, in the browser, you are essentially doing this:

function foo() {
    console.log(window.bar);
}

window.bar = "bar1";

foo();

And, thus you can see why it happily creates a property on the window object and then references it.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • This is absolutely the correct answer and is exactly how I would have answered it. :) In addition, I'd say that there are other cases where Node has inconsistency with browsers on `this` behavior, such as with `setTimeout(..)`. The browser standard says timers should always fire in global (regardless of strict mode), but in Node, they violate this and always fire timers in the context of a proprietary (node-only) timer object. – Kyle Simpson Jul 07 '15 at 12:35
  • @jfriend00 I cant thank you enough for such a detailed explanation. – Prasad Honrao Jul 07 '15 at 16:41
0

That is because of the variable declaration var bar = "bar1";, That is declared in the global scope. All global variables are available as properties of the window object.

When you call foo() without any context the default context of the method is the window object(in non strict mode), so your code execution becomes console.log(window.bar);, as we have seen above your bar variable has attached itself to the window object as a property, so its prints the value bar1

function foo() {
  snippet.log('is window:' + (this instanceof Window))
  snippet.log('bar value: ' + this.bar);
}

var bar = "bar1";
snippet.log('window.bar: ' + window.bar)

var obj = {
  bar: "bar2"
};

foo();
foo.call(obj);
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Arun P Johny
  • 365,836
  • 60
  • 503
  • 504
  • 1
    I am not confused about browser output, which as I mentioned seems to be correct. The Node environment returns it as `undefined` and I don't know why, and so the question. – Prasad Honrao Jul 07 '15 at 06:23