1

I'm reading the "You don't know JS" book series and I tried to run the snippet:

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

function doFoo(fn) {
  // `fn` is just another reference to `foo`

  fn(); // <-- call-site!
}

var obj = {
  a: 2,
  foo: foo
};

var a = "oops, global"; // `a` also property on global object

doFoo( obj.foo ); // "oops, global"

(You can find it in the 2nd chapter of the 3d book: 'this' All make sense now)

If I save this into 'foo.js' and run it with node foo.js (v 8.11.1) then I get undefined. While if I start node REPL and type in the same code I get:

> function foo() { console.log(this.a); }
undefined
> function doFoo(fn) { fn(); }
undefined
> var obj = { a:2, foo:foo };
undefined
> var a = "oops, global";
undefined
> doFoo(obj.foo);
oops, global
undefined

As expected from the book. Same result on Firefox dev console.

If I remove the declaration and leave only the assignment a = "oops, global" then it run as expected both on REPL and Node.js. This make more sense to me because in this way I'm setting a property on the global object, while in the "original" way I'm just declaring a variable.

Can anyone explain to me this behaviour? Thank you all.

EDIT: I think I'm close to the solution, I noticed that if I make a script foo.js that contains only:

var x = 42;
console.log(this);

I get {}, so x is not attached to the global object. While if I start the Node.js REPL and type in the same code I get a big object with x attached to it:

{
   ...
   x: 42
}

So I think the difference it depends on "who is the global object?" in REPL and in Node.js.

  • I arrived at the same conclusion as your edit, you may want to post it as a answer to your own question. – DrakaSAN Apr 16 '18 at 08:45

1 Answers1

1

When you run the file in Node.js, your var a isn't actually in global scope, but function scope -- a module function scope (more on this here). Basically all of the contents of the file is run as if inside a function. So var a is actually in that function scope.

Whereas in REPL it's truly in global scope.

laggingreflex
  • 32,948
  • 35
  • 141
  • 196
  • Sorry if I insist but I would try to understand well: Is this the same reason why if feeding Node.js REPL with `Object.prototype;` and `Array.prototype` he reply with `{ }` and `[ ]`, while with the same input the browser engine (e.g. Chrome v8) replies with `{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}` and `[constructor: ƒ, concat: ƒ, find: ƒ, findIndex: ƒ, pop: ƒ, …]` ? **Thank you for your time.** – Daniele Paolini Apr 18 '18 at 07:35
  • Not the same reason (i.e. scope) at all. This has more to do with how the engine decides to show them. `.prototype` is an interesting object which you should read about separately. But it basically shares some common properties, like `constructor`, `hasOwnProperty`, etc. Chrome showing `{constructor ...}` for `Object.prototype`, and `[constructor ...]` for `Array.prototype` is just its stylistic choice. Firefox displays it differently. Node doesn't display those inner properties at all, but you'll still be able to access them if you specified them by name, i.e. `Object.prototype.constructor`. – laggingreflex Apr 18 '18 at 11:32