Coffeescript空白、参数和函数作用域

Coffeescript空白、参数和函数作用域,coffeescript,nested,indentation,Coffeescript,Nested,Indentation,我用咖啡脚本已经有一段时间了。我发现它总体上是一种很好的语言,当然比普通的JS要好,但我发现它的缩进规则仍然让我感到困惑。举个例子: Bacon.mergeAll( @searchButton.asEventStream('click') @searchInput.asEventStream('keyup') .filter (e) => e.keyCode is 13 ) .map => @searchInput.val() .flatMap

我用咖啡脚本已经有一段时间了。我发现它总体上是一种很好的语言,当然比普通的JS要好,但我发现它的缩进规则仍然让我感到困惑。举个例子:

Bacon.mergeAll(
    @searchButton.asEventStream('click')
    @searchInput.asEventStream('keyup')
        .filter (e) => e.keyCode is 13
)
.map =>
    @searchInput.val()
.flatMapLatest (query) =>
    Bacon.fromPromise $.ajax
        url: @searchURL + encodeURI query
        dataType: 'jsonp'
(代码是基于,顺便说一句)但我花了大量的尝试和错误,使其正确

为什么
mergeAll
asEventStream
的参数周围需要括号?为什么缩进不足以确定参数列表的开始和结束位置?OTOH,为什么缩进对于
map
flatMapLatest
足够?为什么挂起方法之前的空格,例如
.filter
(其缩进级别)不足以确定它绑定到什么?这似乎被完全忽视了


这种语言的缩进规则有明确的指南吗?我对Python语法一目了然,即使是非常复杂的嵌套,也从来没有遇到过问题,因此基于缩进的语法本身并不存在问题。

CoffeeScript中的缩进通常定义块,而参数列表(不一定)是块。类似地,链式函数调用不是块;CoffeeScript只看到一行以
开头,并将其连接到类似或更低缩进的前一行

因此,
asEventStream
需要括号,因为CoffeeScript会看到:

 @searchInput.asEventStream 'keyup'.filter (e) => e.keyCode is 13
它将在
'keyup'
字符串上调用
filter
,并且该函数是
filter
的参数还是
@searchInput.asEventStream('keyup.filter)()
的参数仍然不明确。最后一点显然没有多大意义,但CoffeeScript不是静态分析器,所以它不知道这一点

同时,函数是块,因此
.map()
的函数参数不带括号,因为它由缩进清楚地分隔。即,函数后面的行具有较少的缩进

就我个人而言,我可能会写信

Bacon.mergeAll(
  @searchButton.asEventStream('click'), # explicit comma
  @searchInput.asEventStream('keyup').filter (e) -> e.keyCode is 13 # no need for =>
)
.map(=> @searchInput.val()) # maybe not as pretty, but clearer
.flatMapLatest (query) =>
  Bacon.fromPromise $.ajax
    url: @searchURL + encodeURI query
    dataType: 'jsonp'
事实上,我可能会把它分解成不同的表达式,使它更清晰。在链接东西的同时坚持使用语法糖确实会让CoffeeScript感到困惑,但请记住,你没有义务使用它。就像你不必总是避免括号一样;如果他们让事情变得更清楚,一定要用他们

如果代码更容易编写,阅读起来不那么模棱两可,维护起来也更简单,而不需要复杂的链接/语法(在本例中,所有这些似乎都是正确的),那么我建议跳过它


最后,CoffeeScript中的缩进语法组合可能使您或编译器绊倒。不过,大多数情况下,如果您查看某个内容,发现它很简单,编译器可能也会这么认为。如果您有疑问,编译器也可能有疑问,或者它会以意外的方式解释它。这是我所能提供的最好的“权威指南”(不知道有没有书面指南)。

您看过这段代码生成的Javascript了吗?省略
()
时会发生什么

尝试Coffeescript
中,我发现:

 @searchButton.asEventStream 'click'
没关系。第二个
asEventStream
编译为:

this.searchInput.asEventStream('keyup').filter(function(e) {
但省略
()
会将其更改为:

this.searchInput.asEventStream('keyup'.filter(function(e) {
filter
现在是
'keyup'
的一个属性。将
asEventStream
('keyup')
分开放置一个空格也是这样做的

@searchInput.asEventStream ('keyup')
正如所写的
.mergeAll()
生成:

Bacon.mergeAll(...).map(...).flatMapLatest(...);
省略
()

给出错误,因为编译器无法知道
mergeAll
是一个接受参数的函数。它没有理由期望出现缩进块


来自Python,我倾向于继续使用
(),[],{}
标记参数、数组和对象等结构,除非没有它们,代码更清晰。它们经常帮助我阅读代码,即使编译器不需要它们。Coffeescript在使用缩进表示代码块方面也类似于Python(与Javascript和其他
C
样式化语言中使用的
{}
相反)。

仅仅因为可以使用匿名函数并不意味着必须这样做。如果你破坏了你的函数并给它们命名,那么你就可以用简单易读的东西,比如
o.map(把它们弄乱)、filter(把垃圾过滤掉)。我只是想确保我理解语法的所有细微差别,以免落入陷阱。谢谢,这很有意义。CS确实将一条以点开始的线连接到“类似或更低缩进的前一行”,但并非所有内容都被视为块<代码>如果
和函数定义是,而参数列表不是。关于fat arrow的使用
=>
我在这里发布了一篇评论,如果你感兴趣的话:@Tobia Heh,好评论。就我个人而言,对于我经常编写的代码,胖箭头是一个“特例”。没有必要经常使用它来证明总是使用它是正确的。也就是说,我通常不需要一个绑定函数,所以当我需要绑定函数时,我喜欢显式地描述它。也就是说,你的论点并非毫无价值;如果我的项目不同,我会考虑的。
Bacon.mergeAll
    @searchButton.asEventStream('click') 
    @searchInput.asEventStream('keyup')