admin管理员组文章数量:1401643
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 piler
(function() {
var setName;
setName = function(name) {
return this.name = name;
};
setName('Lulu');
// console.log for node below - print for v8
// unment 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
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.)
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 piler
(function() {
var setName;
setName = function(name) {
return this.name = name;
};
setName('Lulu');
// console.log for node below - print for v8
// unment 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./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.)
3 Answers
Reset to default 9So, 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.
I don't know much CoffeeScript but a fair bit about JavaScript so I can only attempt to explain this from the point of view of what the piled code does (or should be doing):
(function(){
// In here "this" === [global]
//
// The purpose of this wrapper pattern is that it causes "this" to be
// the global object but all var declared variables will still be
// scoped by the function.
var ctx = this; // let's keep test a ref to current context
var setName;
setName = function(name) {
console.log(this === ctx); // !! true !!
// Because in here "this" is the global context/object
// this is setting [global].name = name
return this.name = name;
};
setName('Lulu'); // It's context will be [global]
console.log(name); // (name === [global].name) == true
console.log(this.name); // (this.name === [global].name) == true
}).call(this);
What is (or should be) happening is effectively this (assuming browser where global is window
):
(function() {
var setName;
setName = function(name) {
return window.name = name;
};
setName('Lulu');
console.log(window.name); // 'Lulu'
console.log(window.name); // 'Lulu'
}).call(this);
So, why doesn't it match up between the engines?
Because the different environments use different means of handing the global object to you and handling scoping. It's hard to say with certainty and every environment may have a separate reason for its behavior. It depends very much on how they evaluate the code (this is assuming none of the engines have bugs).
本文标签: nodejsA puzzle about this in JavascriptCoffeescriptStack Overflow
版权声明:本文标题:node.js - A puzzle about this@ in JavascriptCoffeescript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744251055a2597251.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论