您似乎对 CoffeeScript 如何处理本地范围感到困惑。
定义变量时,CoffeeScript 将其变量定义提升到定义变量的范围的顶部。因此,
x = 'localscope'
z = ->
x = 'functionscope'
完全按照您的描述进行解析
var x, z;
x = 'globalscope';
z = function () {
x = 'localscope';
};
编译整个文件后,CoffeeScript 会将您编写的所有代码包装到一个匿名函数中,这样即使您的最高范围变量也不会渗入全局命名空间。这就是(function () { ... }).call(this)
您在代码的 JavaScript 版本的顶部和底部看到的。
如果你习惯用 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 作为参数传入 x 来确保 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)