CoffeeScript 作用域误解

4
我是一名有用的助手,可以翻译文本。
我正在尝试理解Coffeescript中的变量作用域,并且有点困惑,这里有一个例子:
CoffeeScript代码:
  x = "localscope"
  z = () -> 
    x = "functionscope"
    console.log(x)

  console.log(x)

Javascript编译结果:

(function() {
  var x, z;

  x = "localscope";

  z = function() {
    x = "functionscope";
    return console.log(x);
  };

  console.log(x);

}).call(this);

据我所知,在纯 JavaScript 中,所有包含在函数中的变量都在相同的作用域内。因此,我理解在 CoffeeScript 中编写的所有变量都是全局的?

你认为它们为什么是全局的? - Ian
1
变量 x 是在大的 (function() {}).call(this); 作用域中声明的,因此它可以在该作用域内的每个作用域中访问,而不能在外部访问。 - Ian
1
我不确定这是否是您的意思,但是行 x = "functionscope"; 设置的变量与 x = "localscope"; 相同。如果它是 var x = "functionscope";,它就不会影响外部作用域中的 x。 - Shmiddty
我找到了一个类似问题的答案,但我无法完全理解在CoffeeScript中定义全局变量的方式。 - Plugataryov Yura
你想要声明全局变量?就像你发布的链接所说,只需扩展window对象,因为它是全局集合。如果你需要一个全局变量,使用window.VARIABLE_NAME = something;或者window["VARIABLE_NAME"] = something;。明白了吗? - Ian
显示剩余2条评论
3个回答

1
为了在CoffeeScript中影子化变量,您需要执行以下操作:
x = "localscope"
z = ((x) -> () -> 
    x = "functionscope"
    console.log(x)
)(x)
console.log(x)

生成的 JavaScript 代码如下:

var x, z;

x = "localscope";

z = (function(x) {
  return function() {
    x = "functionscope";
    return console.log(x);
  };
})(x);

console.log(x);

在你的例子中,x 不是局部于最内层函数的变量,而是指向你在开头声明的同一个“全局”x。所谓“全局”,是指在同一文件中任何地方都可以访问。

或者 http://coffeescript.org/#try:x%20%3D%20%22localscope%22%0Az%20%3D%20do%20(x)%20-%3E%20()%20-%3E%20%0A%20%20%20%20x%20%3D%20%22functionscope%22%0A%20%20%20%20console.log(x)%0A(x)%0A%0Aconsole.log%20x%0Az() - Andrew Kirkegaard

0

Javascript 默认具有全局作用域。CoffeeScript 通常通过匿名函数调用作为默认作用域的“每个文件一个作用域”。这可以防止 CoffeeScript 脚本文件之间的变量冲突。

我假设您想创建一个全局变量,是吗?为此,您需要将变量附加到“根对象”上。 对于浏览器脚本,它将是 window 对象。所以你只需要:

window.x = 'globalscope'

...完成了。

如果你的意思是相反的,也就是在函数级别上进行变量隔离。它将被创建为这样,但你需要使用不同的名称。请查看来自coffeescript.org的示例(文本搜索“scope”):

CoffeeScript

outer = 1
changeNumbers = ->
  inner = -1
  outer = 10
inner = changeNumbers()

编译成JavaScript

var changeNumbers, inner, outer;

outer = 1;

changeNumbers = function() {
  var inner;
  inner = -1;
  return outer = 10;
};

inner = changeNumbers();

0

看起来你对 CoffeeScript 如何处理本地作用域感到困惑。

当一个变量被定义时,CoffeeScript 会将其变量定义提升到定义变量的作用域顶部。因此,

x = 'localscope'
z = ->
  x = 'functionscope'

被解析的内容与您所描述的完全相同

var x, z;
x = 'globalscope';
z = function () {
  x = 'localscope';
};

当整个文件被编译时,CoffeeScript会将你编写的所有代码包装到一个匿名函数中,以便即使是你最高级别的变量也不会泄漏到全局命名空间中。这就是你在JavaScript版本的代码顶部和底部看到的(function () { ... }).call(this)

如果你习惯于使用Ruby编写代码,那么你的期望应该是

> x = 5
> def z
>   x = 7
>   puts x
> end
> z # logs '7'
> x # still 5

Ruby 会自动将每个变量声明限定在本地作用域,除非变量被定义为实例变量或全局变量。

而 JavaScript 则是在任何时候看到 var 前缀就会限定变量作用域。

var x = 5;
function z () {
  var x = 7;
  console.log(x);
};
z(); // logs '7'
x; // still 5

CoffeeScript假设您可能希望将变量从较高的作用域渗透到较低的作用域中,除了全局变量(可以在window对象上设置),没有办法设置类似于'实例变量'的变量,其作用域在某种程度上是局部的。

正如@Esailija所指出的(尽管解决方案并不完美),您可以通过将x作为参数传递给z函数来确保x在z函数中具有局部作用域,因为JavaScript会自动将参数的作用域限定在接受它们的函数内部:

var x = 5;
function z (x) {
  x = 7;
  console.log(x);
};
z(); // logs '7' -- in JS, it's OK to execute a function without a named argument, which defaults to undefined
x; // still 5

或者用CoffeeScript编写:
x = 5
z = (x) ->
  x = 7
  console.log x
z()
console.log x

顺便提一下,@Esailija的解决方案更符合习惯写法,使用do调用。
x = "localscope"
z = do (x) -> () ->
  x = "functionscope"
  console.log(x)
console.log(x)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接