Javascript/Coffeescript 中关于 this/@ 的谜题
我正在阅读 Trevor Burnham 的 CoffeeScript 书,遇到了一个关于 this
/@
的奇怪谜题.这个谜题有几个部分(我可能只是很困惑),所以我会尽量把它说清楚.
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.
我遇到的主要问题是,通过不同的 REPL 和解释器运行相同的代码,我得到了不同且不一致的结果.我正在测试 (1) coffee
REPL 和解释器,(2) Node 的 REPL 和解释器以及 (3) v8 的 REPL 和解释器.
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.
这是代码,首先是 Coffeescript,然后是 Javascript:
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);
结果如下:
$ 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
所以我想,真正的问题是 (1) 我应该期待什么结果以及 (2) 为什么这些解释器和 REPL 不能相处?(我的理论是 v8 是正确的:在全局上下文中,name
和 this.name
应该是同一件事,我想.但我已经准备好了相信我不懂 Javascript 中的 this
.)
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.)
编辑:如果我在调用 setName
之前添加 this.name = null
/@name = null
(正如下面 Pointy 建议的那样)然后 Coffeescript 和 Node 给我 'Lulu' 和 'null' 但 v8 仍然为两者返回 'Lulu'.(v8 在这里对我来说仍然更有意义.我最初在全局上下文中将 name
设置为 null
,但随后 setName
设置它(在全局上下文)到Lulu".所以之后,这就是我应该在那里看到的.)
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.)
推荐答案
首先,CoffeeScript REPL 有一个错误,issue 1444,这是在 Telemachus 引起我注意后我报告的.
So, first off, there's a bug with the CoffeeScript REPL, issue 1444, which I reported after Telemachus brought this to my attention.
但这里更有趣的问题(我需要在 我的 CoffeeScript 书) 是 Node.js 模块最外层范围内的 this
不是 global
— 它是该模块的 exports
.试试这个:
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
当您在 Node 模块中运行该代码时,您会发现这两个语句的计算结果都为 true
.这就是为什么 name
和 @name
评估不同的东西的原因:name
本身总是指向 global.name
,除非它在 var name
声明的范围内;但 @name
只会在 global
上下文中调用的函数中指向 global.name
(默认值).在 Node.js 模块中,在任何函数之外,它都会指向 exports.name
.
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
.
相关文章