我正在阅读Trevor Burnham的CoffeeScript书,遇到了一个关于this
/@
的奇怪谜题。这个谜题有几个部分(我可能只是很困惑(,所以我会尽量把它弄清楚。
我遇到的主要问题是,通过不同的REPL和解释器运行相同的代码会得到不同且不一致的结果。我正在测试(1(coffee
的REPL和解释器,(2(Node的REPL与解释器,以及(3(v8的REPL及解释器。
这是代码,首先是Coffeescapet,然后是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
。(
编辑:如果我在调用setName
之前添加this.name = null
/@name = null
(正如Pointy在下面建议的那样(,则Coffeescapet和Node会返回'Lulu'和'null',但v8仍然会返回两者的'Lulu'。(v8在这里对我来说仍然更有意义。我最初在全局上下文中将name
设置为null
,但随后setName
将其(在全局上下文中(设置为"Lulu"。所以之后,这就是我应该在那里看到的。(
因此,首先,CoffeeScript REPL第1444期有一个错误,我在Telemachus引起我的注意后报告了这个错误。
但这里更有趣的问题(我需要在CoffeeScript书中注意的一个问题(是,Node.js模块最外层的this
不是global
,而是该模块的exports
。试试这个:
console.log this is exports
console.log do -> this is global
当您在Node模块中运行该代码时,您会发现这两个语句的求值结果都为true
。这就是name
和@name
对不同事物求值的原因:name
本身总是指向global.name
,除非它在var name
声明的范围内;但CCD_ 22将仅指向在CCD_ 24上下文中调用的函数中的CCD_。在Node.js模块中,在任何函数之外,它都指向exports.name
。
我不知道为什么会得到不同的结果,但我知道函数的调用隐含地涉及设置this
。内部功能";setName(("因此具有其自身的CCD_ 27值,该CCD_。因此,事实上,您通过"设置this
;。call(("调用对内部"0"内的CCD_ 30值没有任何影响;setName(("函数,因为当";setName(("被呼叫时没有接收器。
我对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);
那么,为什么发动机之间不匹配呢?
因为不同的环境使用不同的方法将全局对象交给您并处理范围。很难说确切,每个环境的行为都可能有单独的原因。这在很大程度上取决于他们如何评估代码(这是假设没有一个引擎有错误(。