理解thenBy.js从JavaScript到CoffeeScript的翻译时遇到的问题

理解thenBy.js从JavaScript到CoffeeScript的翻译时遇到的问题,javascript,coffeescript,Javascript,Coffeescript,我需要在CoffeeScript中进行一些高级数组排序,我发现了完全符合我需要的微库。它是用JavaScript编写的,所以我把它翻译成了CoffeeScript,这样我就可以在我的.coffee文件中内联使用它,在翻译过程中遇到了一些问题。这不起作用: firstBy = -> # mixin for the `thenBy` property extend = (f) -> f.thenBy = tb return f # adds a secon

我需要在CoffeeScript中进行一些高级数组排序,我发现了完全符合我需要的微库。它是用JavaScript编写的,所以我把它翻译成了CoffeeScript,这样我就可以在我的.coffee文件中内联使用它,在翻译过程中遇到了一些问题。这不起作用:

firstBy = ->

  # mixin for the `thenBy` property
  extend = (f) ->
    f.thenBy = tb
    return f

  # adds a secondary compare function to the target function (`this` context)
  #which is applied in case the first one returns 0 (equal)
  #returns a new compare function, which has a `thenBy` method as well
  tb = (y) ->
    x = this
    return extend((a, b) ->
      return x(a, b) or y(a, b)
    )
  return extend
但是,如果我使用parens包装并使用尾部parens,它确实有效:

### Notice the starting paren
firstBy = (->

  # mixin for the `thenBy` property
  extend = (f) ->
    f.thenBy = tb
    return f

  # adds a secondary compare function to the target function (`this` context)
  #which is applied in case the first one returns 0 (equal)
  #returns a new compare function, which has a `thenBy` method as well
  tb = (y) ->
    x = this
    return extend((a, b) ->
      return x(a, b) or y(a, b)
    )
  return extend
)() ### <- Notice the ending parens
####注意起始参数
firstBy=(->
#为'thenBy'属性混音
扩展=(f)->
f、 thenBy=tb
返回f
#将辅助比较函数添加到目标函数(`this`context)
#如果第一个返回0(相等),则应用该值
#返回一个新的比较函数,该函数也有一个'thenBy'方法
tb=(y)->
x=这个
返回扩展((a,b)->
返回x(a,b)或y(a,b)
)
回程延伸

)()####后面的参数正在调用函数“大”函数(在第一个示例中定义为firstBy的函数),有效地将变量设置为函数的返回值:extend函数。换言之:

# Let firstBy1 be equivalent to your first definition, and
# firstBy2 equivalent to your second. Then
# firstBy1() is functionally equivalent to firstBy2

console.log firstBy2
### logs:
#   function (f) {
#     f.thenBy = tb
#     return f
### }

# So:
func = (x, y) ->
  # ... some comparison of x and y...

foo = firstBy2(func) # or `foo = firstBy1()(func)`
console.log foo.thenBy
### logs:
#   function (y) {
#     x = this
#     return extend(function (a, b) {
#       return x(a, b) or y(a, b)
#     })
### }
这可能最好用一个例子来说明:

# For brevity/clarity...
firstBy = firstBy2

randomNums =
  { attr1: 1, attr2: 2 },
  { attr1: 2, attr2: 8 },
  { attr1: 4, attr2: 2 },
  { attr1: 5, attr2: 2 },
  { attr1: 5, attr2: 3 },
  { attr1: 6, attr2: 1 },
  { attr1: 3, attr2: 1 },
  { attr1: 2, attr2: 4 }

func1 = (x, y) ->
  if x.attr1 == y.attr1
    return 0
  if x.attr1 > y.attr1 then 1 else -1

func2 = (x, y) ->
  if x.attr2 == y.attr2
    return 0
  if x.attr2 > y.attr2 then 1 else -1
当我们打电话

randomNums.sort(firstBy(func1).thenBy(func2))
…首先计算firstBy(func1)。它返回带有thenBy属性的func1:

func1 = (x, y) -> 
  if x.attr1 == y.attr1
    return 0
  if x.attr1 > y.attr1 then 1 else -1

func1.thenBy = (y) ->
  x = this
  return extend((a, b) ->
    return x(a, b) or y(a, b)
  )
然后,调用firstBy(func1)。thenBy(func2)调用新添加的thenBy 参数附加到func1,产生:

func1.thenBy(func2) =
  extend((a, b) ->
    func1(a, b) or func2(a, b)
  )
然后,extend函数将另一个(未调用的)thenBy属性应用于该匿名函数,生成sort用于排序randomnum的最终函数。它基本上是对数组中的每对数字调用func1,如果func1返回0(当每个对象的attr1相等时),则对同一对数字求值func2。这可以通过对thenBy的更多调用进行无休止的推断。最后,我们的函数调用返回:

[
  { attr1: 1, attr2: 2 },
  { attr1: 2, attr2: 4 },
  { attr1: 2, attr2: 8 },
  { attr1: 3, attr2: 1 },
  { attr1: 4, attr2: 2 },
  { attr1: 5, attr2: 2 },
  { attr1: 5, attr2: 3 },
  { attr1: 6, attr2: 1 }
]

希望有帮助

你能用这两个版本编译成JS吗?能。它们都编译得很好,paren是输出JS和输入JS中两者之间的唯一区别。这真的只是我不理解这个聪明的小片段实际上是如何工作的问题。为什么在定义它时必须调用它才能使其工作?在第一个示例中,您可以尝试使用
=>
而不是
->
来扩展
tb
,并让我知道这是否适用于您吗?啊,这就是启用此fluent API机制的原因,即
首先通过(???)。然后通过(???)。然后通过(???).etc
?是的,因为firstBy和thenBy都返回内部扩展函数的实例。这段代码是令人沮丧的递归代码,为了清晰起见,我将在文章中提供一个示例。还值得注意的是,使用
do
关键字可以获得相同的功能!您可以将其更改为
firstBy=do->
,而不是在开始时使用
),在结束时使用
)()
。。。这将调用函数。您也不需要明确地告诉CoffeeScript返回任何内容。