Javascript/Coffeescript 中关于 this/@ 的谜题
我正在阅读 Trevor Burnham 的 CoffeeScript 书,并且遇到了一个关于 this
/@
的奇怪难题。这个谜题有几个部分(我可能很困惑),所以我会尽力把它说清楚。
我遇到的主要问题是,通过不同的 REPL 和解释器运行相同代码会得到不同且不一致的结果。我正在使用 (1) Coffee REPL 和解释器、(2) Node 的 REPL 和解释器以及 (3) v8 的 REPL 和解释器进行测试。
这是代码,首先是 Coffeescript,然后是 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
。)
编辑:如果我添加 this.name = null
/@name = null
在调用 setName
之前(正如 Pointy 所建议的那样)下面)然后 Coffeescript 和 Node 给我返回“Lulu”和“null”,但 v8 仍然返回“Lulu”。 (v8 在这里对我来说仍然更有意义。我最初在全局上下文中将 name
设置为 null
,但随后 setName
设置它(在全局上下文)到“Lulu”,所以之后,这就是我应该在那里看到的。)
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.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因此,首先,CoffeeScript REPL 存在一个错误,问题 1444,在特勒马科斯引起我注意后我报告了这一情况。
但这里更有趣的问题(也是我需要在 我的 CoffeeScript 书 中注意的问题)是Node.js 模块最外层作用域中的
this
不是全局
,而是该模块的导出
。尝试一下:当您在 Node 模块中运行该代码时,您会发现这两个语句的计算结果均为
true
。这就是为什么name
和@name
计算结果不同:name
本身总是指向global.name
,除非它在 var name
声明的范围内;但@name只会指向在global上下文(默认)中调用的函数中的global.name。在 Node.js 模块中,在任何函数之外,它将指向exports.name
。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'tglobal
—it's that module'sexports
. Try this out:You'll find that both statements evaluate to
true
when you run that code in a Node module. That's whyname
and@name
evaluate to different things:name
by itself will always point toglobal.name
, unless it's in the scope of avar name
declaration; but@name
will only point toglobal.name
in a function called in theglobal
context (the default). In a Node.js module, outside of any function, it'll point toexports.name
.我不知道为什么你会得到不同的结果,但我知道函数的调用隐式涉及设置
this
。因此,内部函数“setName()”具有其自己的this
值,独立于定义它的外部函数中的this
值。因此,通过“.call()”调用设置this
对内部“setName()”函数内的this
值没有任何影响,因为当调用“setName()”时,不涉及接收者。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 ownthis
value independent of the value ofthis
in the outer function in which it is defined. Thus, the fact that you setthis
via that ".call()" invocation has no effect whatsoever on thethis
value inside the inner "setName()" function, because when "setName()" is called there's no receiver involved.我对 CoffeeScript 不太了解,但对 JavaScript 知之甚少,所以我只能尝试从编译代码做什么(或应该做什么)的角度来解释这一点:
正在(或应该)发生的事情实际上是这样的(假设全局的浏览器是
window
):那么,为什么它在引擎之间不匹配呢?
因为不同的环境使用不同的方式将全局对象传递给您并处理作用域。很难肯定地说,每个环境的行为可能都有不同的原因。这在很大程度上取决于他们如何评估代码(假设所有引擎都没有错误)。
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 compiled code does (or should be doing):
What is (or should be) happening is effectively this (assuming browser where global is
window
):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).