【问题标题】:Coffeescript whitespace, arguments and function scopesCoffeescript 空格、参数和函数范围
【发布时间】:2013-12-25 13:11:14
【问题描述】:

我使用 CoffeeScript 已经有一段时间了。我发现它总体上是一门很好的语言,肯定比普通的 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'

This does what it should(代码基于this tutorial,顺便说一句)但我花了很多时间试错才把它弄好。

为什么mergeAllasEventStream 需要在它们的参数周围加上括号?为什么缩进不足以确定参数列表的开始和结束位置? OTOH,为什么mapflatMapLatest 的缩进就足够了?为什么挂起方法之前的空格,例如.filter(它的缩进级别)不足以确定它绑定到什么?它似乎被完全忽略了。

是否有该语言的缩进规则的权威指南?即使是非常复杂的嵌套,我也从来没有一眼理解 Python 语法的问题,因此基于缩进的语法本身不是问题。

【问题讨论】:

  • 可以使用匿名函数并不意味着必须这样做。如果你把你的函数弄出来并给它们命名,那么你可以用简单易读的东西来转换混乱的心理解析和猜测,比如o.map(mangle_them).filter(out_the_garbage)...
  • 是的,我肯定会在生产代码中这样做。我只是确保我理解语法的所有细微差别,以免陷入陷阱。

标签: coffeescript nested indentation


【解决方案1】:

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

Try 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(...);

省略()

Bacon.mergeAll
    @searchButton.asEventStream('click') 
    @searchInput.asEventStream('keyup')

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

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

【讨论】:

    【解决方案2】:

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

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

     @searchInput.asEventStream 'keyup'.filter (e) => e.keyCode is 13
    

    哪个会在'keyup' 字符串上调用filter,并且该函数是filter 的参数还是@searchInput.asEventStream('keyup'.filter)() 的参数仍然是模棱两可的。最后一点显然没有多大意义,但 CoffeeScript 不是静态分析器,所以它不知道。

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

    就个人而言,我可能会写

    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 中的缩进语法组合可能会让您或编译器出错。但是,大多数情况下,如果您查看某些内容并发现它很简单,编译器可能也会这么认为。如果你有疑问,编译器可能也是,或者它会以意想不到的方式解释它。这是我能提供的最好的“权威指南”(不知道书面指南)。

    【讨论】:

    • 谢谢,有道理。 CS 确实将一条以点开头的行“连接到类似或更低缩进的前一行”,但并非所有内容都被视为一个块。 ifwhile 和函数定义是,而参数列表不是。
    • 关于胖箭头=>的使用我已经在这里发了一个咆哮,如果你有兴趣的话:stackoverflow.com/questions/13682986/…
    • @Tobia Heh,好咆哮。就个人而言,对于我经常编写的代码,粗箭头是“特例”。没有必要经常证明总是使用它是合理的。 IE。我通常不需要 需要绑定函数,所以当我这样做时,我喜欢明确说明它。也就是说,您的论点并非没有道理。如果我的项目不同,我会考虑的。
    猜你喜欢
    • 2011-08-21
    • 1970-01-01
    • 2017-12-28
    • 1970-01-01
    • 2018-06-28
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    • 2013-11-04
    相关资源
    最近更新 更多