简体   繁体   English

关于这个/ @在Javascript / Coffeescript中的一个难题

[英]A puzzle about this/@ in Javascript/Coffeescript

I'm working through Trevor Burnham's CoffeeScript book and I've run into a weird puzzle concerning this / @ . 我正在研究Trevor Burnham的CoffeeScript书,我遇到了关于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. 我遇到的主要问题是,我通过不同的REPL和解释器运行相同的代码会得到各种不一致的结果。 I'm testing with (1) the coffee REPL and interpreter, (2) Node's REPL and interpreter and (3) v8's REPL and interpreter. 我正在测试(1) coffee REPL和解释器,(2)Node的REPL和解释器以及(3)v8的REPL和解释器。

Here's the code, first as Coffeescript then as Javascript: 这是代码,首先是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);

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? 因此,我认为真正的问题是:(1)我应该得到什么结果?(2)为什么这些解释器和REPL不能相处? (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.) (我的理论是v8是正确的:在全局上下文namethis.name应该是相同的东西,我会想。但我已经准备好相信我在Javascript中不理解this 。)

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. 编辑 :如果我在调用setName之前添加this.name = null / @name = null (如下面的Pointy所示),那么Coffeescript和Node会给我'Lulu'和'null',但是v8仍然会返回'Lulu'。 (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.) (v8在这里对我来说更有意义。我最初在全局上下文中将name设置为null ,但是setName将它(在全局上下文中)设置为'Lulu'。所以之后,这就是我应该在那里看到的。)

So, first off, there's a bug with the CoffeeScript REPL, issue 1444 , which I reported after Telemachus brought this to my attention. 所以,首先,有一个关于CoffeeScript REPL的问题问题1444 ,我在Telemachus引起我的注意后报告了这个错误。

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 . 但这里更有趣的问题(另外一个是我需要注意我的CoffeeScript的书 )是this在Node.js的模块的最外面的范围不是global -它的那个模块的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. 当您在Node模块中运行该代码时,您会发现两个语句都评估为true 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; 这就是为什么name@name评估不同的东西: name本身总是指向global.name ,除非它在var name声明的范围内; but @name will only point to global.name in a function called in the global context (the default). @name只会指向global上下文中调用的函数中的global.name (默认值)。 In a Node.js module, outside of any function, it'll point to exports.name . 在Node.js模块中,在任何函数之外,它将指向exports.name

I don't know why you get different results, but know that the invocation of a function implicitly involves setting this . 我不知道为什么你得到不同的结果,但知道函数的调用隐含涉及设置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. 因此,该内部函数“的setName()”具有其自己的this值独立的值的this在其中它被定义的外部函数。 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. 因此,通过“.call()”调用设置this的事实对内部“setName()”函数内的this值没有任何影响,因为当调用“setName()”时,不涉及接收器。

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): 我不太了解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);

What is (or should be) happening is effectively this (assuming browser where global is window ): 发生什么(或应该发生)实际上是这个(假设浏览器全局是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). 这在很大程度上取决于他们如何评估代码(假设没有引擎有错误)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM