Javascript/Coffeescript 中关于 this/@ 的谜题

发布于 2024-11-16 05:19:24 字数 2806 浏览 7 评论 0原文

我正在阅读 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 是正确的:在全局上下文中 namethis.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我是男神闪亮亮 2024-11-23 05:19:24

因此,首先,CoffeeScript REPL 存在一个错误,问题 1444,在特勒马科斯引起我注意后我报告了这一情况。

但这里更有趣的问题(也是我需要在 我的 CoffeeScript 书 中注意的问题)是Node.js 模块最外层作用域中的 this 不是全局,而是该模块的导出。尝试一下:

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

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.

难如初 2024-11-23 05:19:24

我不知道为什么你会得到不同的结果,但我知道函数的调用隐式涉及设置 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 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.

南七夏 2024-11-23 05:19:24

我对 CoffeeScript 不太了解,但对 JavaScript 知之甚少,所以我只能尝试从编译代码做什么(或应该做什么)的角度来解释这一点:

(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);

正在(或应该)发生的事情实际上是这样的(假设全局的浏览器是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);

那么,为什么它在引擎之间不匹配呢?

因为不同的环境使用不同的方式将全局对象传递给您并处理作用域。很难肯定地说,每个环境的行为可能都有不同的原因。这在很大程度上取决于他们如何评估代码(假设所有引擎都没有错误)。

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):

(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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文