为什么 Coffeescript 认为阴影是一个坏主意
我想切换到 Coffeescript 有一段时间了,昨天我以为我终于被卖掉了,但后来我偶然发现了 Armin Ronachers 文章关于 Coffeescript 中的阴影.
I've wanted to switch to Coffeescript for a while now and yesterday I thought I'm finally sold but then I stumbled across Armin Ronachers article on shadowing in Coffeescript.
Coffeescript 现在确实放弃了阴影,如果您对嵌套循环使用相同的迭代器,该问题的一个示例就是.
Coffeescript indeed now abandoned shadowing, an example of that problem would be if you use the same iterator for nested loops.
var arr, hab, i;
arr = [[1, 2], [1, 2, 3], [1, 2, 3]];
for(var i = 0; i < arr.length; i++){
var subArr = arr[i];
(function(){
for(var i = 0; i < subArr.length; i++){
console.log(subArr[i]);
}
})();
}
因为 cs 仅在我无法在咖啡脚本中执行此操作时才声明变量
Because cs only declares variables once I wouldn't be able to do this within coffeescript
阴影已被有意移除,我想了解为什么 cs 的作者想要摆脱这样的功能?
Shadowing has been intentionally removed and I'd like to understand why the cs-authors would want to get rid of such a feature?
更新:这里有一个更好的例子说明为什么影子很重要,源自关于这个问题的问题在github
Update: Here is a better example of why Shadowing is important, derived from an issue regarding this problem on github
PS:我不是在寻找可以告诉我可以插入带有反引号的纯 Javascript 的答案.
PS: I'm not looking for an answer that tells me that I can just insert plain Javascript with backticks.
推荐答案
如果你阅读了关于 这张票,你可以看到 CoffeeScript 的创建者 Jeremy Ashkenas 解释了禁止显式阴影之间的一些推理:
If you read the discussion on this ticket, you can see Jeremy Ashkenas, the creator of CoffeeScript, explaining some of the reasoning between forbidding explicit shadowing:
我们都知道动态作用域与词法作用域相比是不好的,因为它很难推断变量的值.使用动态作用域,您无法通过阅读周围的源代码来确定变量的值,因为该值完全取决于调用函数时的环境.如果允许并鼓励变量隐藏,则如果不向后跟踪源中最接近的 var 变量,则无法确定变量的值,因为完全相同的局部变量标识符在相邻范围内可能具有完全不同的值.在所有情况下,当您想要隐藏变量时,只需选择一个更合适的名称即可完成相同的操作.如果一个局部变量名在整个词法范围内只有一个值,那么你的代码就更容易推理了,并且禁止隐藏.
We all know that dynamic scope is bad, compared to lexical scope, because it makes it difficult to reason about the value of your variables. With dynamic scope, you can't determine the value of a variable by reading the surrounding source code, because the value depends entirely on the environment at the time the function is called. If variable shadowing is allowed and encouraged, you can't determine the value of a variable without tracking backwards in the source to the closest var variable, because the exact same identifier for a local variable can have completely different values in adjacent scopes. In all cases, when you want to shadow a variable, you can accomplish the same thing by simply choosing a more appropriate name. It's much easier to reason about your code if a local variable name has a single value within the entire lexical scope, and shadowing is forbidden.
所以对于 CoffeeScript 来说,用一块石头杀死两只鸟是一个非常慎重的选择——通过删除var"来简化语言.概念,并禁止阴影变量作为自然结果.
So it's a very deliberate choice for CoffeeScript to kill two birds with one stone -- simplifying the language by removing the "var" concept, and forbidding shadowed variables as the natural consequence.
如果您搜索范围";或遮蔽"在 CoffeeScript 问题中,您可以看到这一直出现.我不会在这里发表意见,但要点是 CoffeeScript 创建者相信它会导致更简单的代码更不容易出错.
If you search "scope" or "shadowing" in the CoffeeScript issues, you can see that this comes up all the time. I will not opine here, but the gist is that the CoffeeScript Creators believe it leads to simpler code that is less error-prone.
好的,我会说一点:阴影并不重要.你可以想出一些人为的例子来说明为什么这两种方法都更好.事实是,无论是否有阴影,您都需要搜索向上".作用域链来了解变量的生命周期.如果您显式声明变量 ala JavaScript,您可能能够更快地短路.但这没关系.如果您不确定给定函数的范围内有哪些变量,那您就错了.
Okay, I will opine for a little bit: shadowing doesn't matter. You can come up with contrived examples that show why either approach is better. The fact is that, with shadowing or not, you need to search "up" the scope chain to understand the life of a variable. If you explicitly declare your variables ala JavaScript, you might be able to short-circuit sooner. But it doesn't matter. If you're ever unsure of what variables are in scope in a given function, you're doing it wrong.
Shadowing is 在 CoffeeScript 中是可能的,不包括 JavaScript.如果你实际上需要一个你知道是本地范围的变量,你可以得到它:
Shadowing is possible in CoffeeScript, without including JavaScript. If you ever actually need a variable that you know is locally scoped, you can get it:
x = 15
do (x = 10) ->
console.log x
console.log x
因此,如果在实践中出现这种情况的可能性很小,那么有一个相当简单的解决方法.
So on the off-chance that this comes up in practice, there's a fairly simple workaround.
就个人而言,我更喜欢显式声明每个变量的方法,并将提供以下内容作为我的论据":
Personally, I prefer the explicitly-declare-every-variable approach, and will offer the following as my "argument":
doSomething = ->
...
someCallback = ->
...
whatever = ->
...
x = 10
...
这很好用.然后突然一个实习生出现并添加了这行:
This works great. Then all of a sudden an intern comes along and adds this line:
x = 20
doSomething = ->
...
someCallback = ->
...
whatever = ->
...
x = 10
...
然后 bam,代码已损坏,但损坏直到很久以后才会出现.哎呀!使用 var
,就不会发生这种情况.但是如果使用通常是隐式范围,除非您另有说明",它就会有.所以.无论如何.
And bam, the code is broken, but the breakage doesn't show up until way later. Whoops! With var
, that wouldn't have happened. But with "usually implicit scoping unless you specify otherwise", it would have. So. Anyway.
我在一家在客户端和服务器上使用 CoffeeScript 的公司工作,我从未听说过这种情况在实践中发生.我认为不必在任何地方都输入单词 var
所节省的时间比定义范围错误(永远不会出现)所浪费的时间要多.
I work at a company that uses CoffeeScript on the client and server, and I have never heard of this happening in practice. I think the amount of time saved in not having to type the word var
everywhere is greater than the amount of time lost to scoping bugs (that never come up).
自从写下这个答案以来,我已经看到这个错误在实际代码中发生了两次.每次发生这种情况,都非常烦人且难以调试.我的感觉已经改变,认为 CoffeeScript 的选择是糟糕的时期.
Since writing this answer, I have seen this bug happen two times in actual code. Each time it's happened, it's been extremely annoying and difficult to debug. My feelings have changed to think that CoffeeScript's choice is bad times all around.
一些类似 CoffeeScript 的 JS 替代品,例如 LiveScript 和 coco,为此使用了两种不同的赋值运算符:=
来声明变量,:=
来修改外部变量范围.这似乎是一个比仅仅保留 var
关键字更复杂的解决方案,而且一旦 let
被广泛使用,它也不会很好地支持.
Some CoffeeScript-like JS alternatives, such as LiveScript and coco, use two different assignment operators for this: =
to declare variables and :=
to modify variables in outer scopes. This seems like a more-complicated solution than just preserving the var
keyword, and something that also won't hold up well once let
is widely usable.
相关文章