Nodejs + Coffeescript的变量范围

时间:2014-07-08 21:27:48

标签: node.js express coffeescript mongoose momentjs

我很难跟踪这个。

...

for offset in [1..3]
  queryDate = moment().subtract(offset, 'days')
  console.log offset    # gives 1, 2, 3

  # check if a row already exists for this day
  Datum.findOne { date: { $lte: queryDate.toDate() } }, (err, datum) ->
    console.log offset # gives 4, 4, 4

...

对变量作用域的正确方法是什么,以便它们可以在回调中使用?

2 个答案:

答案 0 :(得分:1)

这实际上是一个闭包问题,而不是范围问题。当你在javascript中定义一个函数时(当然也是coffeescript),该函数会记住它创建的上下文,包括父作用域中的所有变量。但是有什么不是变量的副本,而是那些变量的引用。

Datum.findOne循环完成迭代后,将调用来自for的回调。这意味着offset变量已经增加。您可以轻松地阻止这包围Datum.findOne周围的匿名函数,如下所示:

for offset in [1..3]
  do ( offset = offset ) ->  
    Datum.findOne { date: { $lte: queryDate.toDate() } }, (err, datum) ->
      console.log offset

将变量作为参数传递给函数将创建它的副本。

我相信定义循环内部的函数对于可读性更好,但是如果函数很大,或者循环有很多迭代,那么实际上更好地定义它。

//编辑:实际上,coffeescript会在循环外定义它。

您可能需要参考此question来阅读闭包。

答案 1 :(得分:0)

使用包装功能应该有效。

# this is a wrapper to setTimeout
# it's used as a substitute to Datum.findOne in the question
delay = (ms, func) -> setTimeout func, ms

myRange = 4

myWrapper = (offset, range) ->             # range could be queryDate
  delay 0, ->                              # delay could be Datum.findOne
    console.log offset + ' ' + range

for offset in [1..myRange]
  myWrapper offset, myRange

delay是setTimeout的别名(种类),如建议的here