I'm working through Trevor Burnham's CoffeeScript book and I've run into a weird puzzle concerning this
/@
. The puzzle has a few parts (and I may be just very confused), so I'll try to make this as clear as I can.
The main problem I'm having is that I get varied and inconsistent results running the same code through different REPLs and interpreters. I'm testing with (1) the coffee
REPL and interpreter, (2) Node's REPL and interpreter and (3) v8's REPL and interpreter.
Here's the code, first as Coffeescript then as Javascript:
// coffeescript
setName = (name) -> @name = name
setName 'Lulu'
console.log name
console.log @name
// Javascript via the coffee compiler
(function() {
var setName;
setName = function(name) {
return this.name = name;
};
setName('Lulu');
// console.log for node below - print for v8
// uncomment one or the other depending on what you're trying
// console.log(name);
// console.log(this.name);
// print(name);
// print(this.name);
}).call(this);
Here are the results:
$ coffee setName.coffee
Lulu
undefined
# coffee REPL
# This appears to be a bug in the REPL
# See https://github.com/jashkenas/coffee-script/issues/1444
coffee> setName = (name) -> @name = name
[Function]
coffee> setName 'Lulu'
'Lulu'
coffee> console.log name
ReferenceError: name is not defined
at repl:2:1
at Object.eval (/Users/telemachus/local/node-v0.4.8/lib/node_modules/coffee-script/lib/coffee-script.js:89:15)
at Interface.<anonymous> (/Users/telemachus/local/node-v0.4.8/lib/node_modules/coffee-script/lib/repl.js:39:28)
at Interface.emit (events.js:64:17)
at Interface._onLine (readline.js:153:10)
at Interface._line (readline.js:408:8)
at Interface._ttyWrite (readline.js:585:14)
at ReadStream.<anonymous> (readline.js:73:12)
at ReadStream.emit (events.js:81:20)
at ReadStream._emitKey (tty_posix.js:307:10)
coffee> console.log @name
undefined
$ v8 setName.js
Lulu
Lulu
# v8 REPL
>> (function(){var setName; setName=function(name){return this.name=name;};setName('Lulu');print(name);print(this.name);}).call(this);
Lulu
Lulu
# Switch print to console.log or require puts from sys
$ node setName.js
Lulu
undefined
# node REPL
> (function() {
... var setName;
... setName = function(name) {
... return this.name = name;
... };
... setName('Lulu');
... console.log(name);
... console.log(this.name);
... }).call(this);
Lulu
Lulu
So the real questions, I suppose, are (1) what results should I expect and (2) why can't these interpreters and REPLs get along? (My going theory is that v8 is right: in the global context name
and this.name
should be the same thing, I would have thought. But I'm very ready to believe that I don't understand this
in Javascript.)
Edit: If I add this.name = null
/@name = null
before calling setName
(as Pointy suggests below) then Coffeescript and Node give me 'Lulu' and 'null' back but v8 still returns 'Lulu' for both. (v8 still makes more sense to me here. I set name
to null
initially in the global context, but then setName
sets it (in the global context) to 'Lulu'. So afterwards, this is what I should see there.)
So, first off, there's a bug with the CoffeeScript REPL, issue 1444, which I reported after Telemachus brought this to my attention.
But the more interesting issue here (and one that I need to note in my CoffeeScript book) is that this
in the outermost scope of a Node.js module isn't global
—it's that module's exports
. Try this out:
console.log this is exports
console.log do -> this is global
You'll find that both statements evaluate to true
when you run that code in a Node module. That's why name
and @name
evaluate to different things: name
by itself will always point to global.name
, unless it's in the scope of a var name
declaration; but @name
will only point to global.name
in a function called in the global
context (the default). In a Node.js module, outside of any function, it'll point to exports.name
.
I don't know why you get different results, but I know that the invocation of a function implicitly involves setting this
. The inner function "setName()" therefore has its very own this
value independent of the value of this
in the outer function in which it is defined. Thus, the fact that you set this
via that ".call()" invocation has no effect whatsoever on the this
value inside the inner "setName()" function, because when "setName()" is called there's no receiver involved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With