【问题标题】:Function declaration in CoffeeScriptCoffeeScript 中的函数声明
【发布时间】:2011-09-26 18:50:33
【问题描述】:

我注意到在 CoffeeScript 中,如果我使用以下方法定义函数:

a = (c) -> c=1

我只能得到函数表达式

var a;
a = function(c) {
    return c = 1;
};

但是,我个人经常使用函数声明,例如:

function a(c) {
    return c = 1;
}

我确实使用第一种形式,但我想知道 CoffeeScript 中是否有生成函数声明的方法。如果没有这样的方法,我想知道为什么 CoffeeScript 避免这样做。只要函数在作用域的顶部声明,我认为 JSLint 不会抱怨声明错误。

【问题讨论】:

  • 你有什么好的理由想要函数声明吗?如果你使用的是 coffeescript,你不应该关心编译的 JS 的格式,除非它被破坏/错误。
  • 在大多数情况下,函数声明和函数表达式的工作方式相同,但两者之间存在细微差别。例如,developer.mozilla.org/en/JavaScript/Reference/… 所以,在某些情况下,它们是不相等的。
  • 您将我链接到一段代码,其中函数声明是未定义的行为。您想使用函数声明而不是函数表达式以便滥用未定义的行为吗?
  • @Raynos 函数声明可以很好地用于堆栈跟踪和其他调试,因为函数附加了一个名称。这就是 CoffeeScript 将它们用于classes 的原因。
  • @TrevorBurnham 我的意思是这只是对调试已编译 js 的难度的微小改进。你真正想要的是一个可以读取coffeescript的调试器。

标签: javascript coffeescript jslint function-declaration


【解决方案1】:

CoffeeScript 仅在一处使用函数声明(也称为“命名函数”):class 定义。例如,

class Foo

编译成

var Foo;
Foo = (function() {
  function Foo() {}
  return Foo;
})();

根据FAQ,CoffeeScript 不在其他地方使用函数声明的原因:

将此归咎于微软。最初,每个可以为其检索到合理名称的函数都被赋予了一个,但 IE 8 及以下版本存在范围问题,其中命名函数被视为声明和表达式。请参阅this 了解更多信息。

简而言之:粗心地使用函数声明会导致 IE(9 之前)和其他 JS 环境之间的不一致,因此 CoffeeScript 避开了它们。

【讨论】:

  • 他在谈论 IE 的命名函数表达式问题(例如var a = function a() {};)。函数声明(例如function a() {})没有这样的跨浏览器不一致
  • 如果一开始在浏览器中使用 CS 不是很疯狂的话,这对我来说会更有意义。相信 DOM 处理库以跟上浏览器的变化和弃用是一回事,但是当您谈论的是实际的源代码本身时,这就像一个双重危险的依赖关系。想象一下在 CS 社区枯竭并转向下一个 make-it-more-rails-like-for-me 现象 10 年后处理遗留代码库。当一切都开始崩溃时,您需要找出弃用的内容并找出在 CS 解析器中要修复的内容。
【解决方案2】:

是的,你可以:

hello()

`function hello() {`
console.log 'hello'
dothings()
`}`

你通过反引号转义纯 JS `

请注意,您不能在函数体上缩进。

干杯

【讨论】:

  • 这并不表明它是在咖啡脚本中完成的——只有咖啡脚本允许转义到 javascript。这也太恶心了!
  • 使用前的定义更讨厌xD
  • 另外,在更高版本的 v8 中,函数声明似乎很重 optimized
  • 注意可以写function updateSettings() {; do -> dothings() } 允许缩进。 github.com/jashkenas/coffeescript/issues/…
【解决方案3】:

使用 CoffeeScript 时要记住的一件事是,您总是可以回到 JavaScript。虽然 CoffeeScript 不支持命名函数声明,但您始终可以退回到 JavaScript 来执行此操作。

http://jsbin.com/iSUFazA/11/edit

# http://jsbin.com/iSUFazA/11/edit
# You cannot call a variable function prior to declaring it!
# alert csAddNumbers(2,3) # bad!

# CoffeeScript function
csAddNumbers = (x,y) -> x+y

# You can call a named function prior to
# delcaring it
alert "Calling jsMultiplyNumbers: " + jsMultiplyNumbers(2,3) # ok!

# JavaScript named function
# Backticks FTW!
`function jsMultiplyNumbers(x,y) { return x * y; }`

您也可以在 CoffeeScript 中编写一个大的胖函数,然后使用反引号技巧让 JavaScript 调用另一个函数:

# Coffeescript big function
csSomeBigFunction = (x,y) ->
   z = x + y
   z = z * x * y
   # do other stuff
   # keep doing other stuff

# Javascript named function wrapper
`function jsSomeBigFunction(x,y) { return csSomeBigFunction(x,y); }`

【讨论】:

    【解决方案4】:

    不,你不能在咖啡脚本中定义一个函数并让它在咖啡脚本中生成一个函数声明

    即使你只是写

    -> 123
    

    生成的 JS 将被包裹在括号中,从而使其成为函数表达式

    (function() {
      return 123;
    });
    

    我的猜测是,这是因为函数声明被“提升”到封闭范围的顶部,这会破坏咖啡脚本源的逻辑流程。

    【讨论】:

    • 吊装正是我要使用函数声明的原因!
    • CoffeeScript 在某种意义上已经“提升”了,因为它在范围顶部使用 var 预先声明了变量。所以函数可以相互引用,顺序无关紧要。
    • @EvanMoran 确实,CoffeeScript 预先声明了变量,但是函数没有被提升,因为在函数表达式之前变量保持未定义。因此,在定义函数之前,您不能使用这些函数。
    【解决方案5】:

    虽然这是一篇较早的帖子,但我想为未来的 Google 员工添加一些内容。

    OP 是正确的,因为我们不能在纯 CoffeeScript 中声明函数(不包括在 CoffeeScript 文件中使用反引号转义纯 JS 的想法)。

    但是我们可以做的是将函数绑定到窗口,最终得到我们可以调用的东西,就好像它是一个命名函数一样。我不是说这个 is 一个命名函数,我提供了一种方法来做我想象的 OP 想要实际做的事情(在代码中的某处调用像 foo(param) 这样的函数)使用纯 CoffeeScript .

    这是一个在coffeescript中附加到窗口的函数示例:

    window.autocomplete_form = (e) ->
        autocomplete = undefined
        street_address_1 = $('#property_street_address_1')
        autocomplete = new google.maps.places.Autocomplete(street_address_1[0], {})
        google.maps.event.addListener autocomplete, "place_changed", ->
            place = autocomplete.getPlace()
    
            i = 0
    
            while i < place.address_components.length
                addr = place.address_components[i]
                st_num = addr.long_name if addr.types[0] is "street_number"
                st_name = addr.long_name if addr.types[0] is "route"
    
                $("#property_city").val addr.long_name if addr.types[0] is "locality"
                $("#property_state").val addr.short_name if addr.types[0] is "administrative_area_level_1"
                $("#property_county").val (addr.long_name).replace(new RegExp("\\bcounty\\b", "gi"), "").trim() if addr.types[0] is "administrative_area_level_2"
                $("#property_zip_code").val addr.long_name if addr.types[0] is "postal_code"
                i++
    
            if st_num isnt "" and (st_num?) and st_num isnt "undefined"
                street1 = st_num + " " + st_name
            else
                street1 = st_name
    
            street_address_1.blur()
            setTimeout (->
                street_address_1.val("").val street1
                return
                ), 10
            street_address_1.val street1
            return
    

    这是使用 Google 地方信息返回地址信息以自动填充表单。

    所以我们在 Rails 应用程序中有一个部分正在加载到页面中。这意味着 DOM 已经创建,如果我们在初始页面加载时调用上面的函数(在 ajax 调用呈现部分之前),jQuery 将看不到 $('#property_street_address_1') 元素(相信我 - 它没有' t)。

    所以我们需要延迟 google.maps.places.Autocomplete() 直到元素出现在页面上之后。

    我们可以通过成功加载部分的 Ajax 回调来做到这一点:

                url = "/proposal/"+property_id+"/getSectionProperty"
                $("#targ-"+target).load url, (response, status, xhr) ->
                    if status is 'success'
                        console.log('Loading the autocomplete form...')
                        window.autocomplete_form()
                        return
    
                window.isSectionDirty = false
    

    所以在这里,本质上,我们做的事情和调用 foo() 是一样的

    【讨论】:

      【解决方案6】:

      为什么?因为函数声明是邪恶的。看这段代码

      function a() {
              return 'a';
      }
      
      console.log(a());
      
      function a() {
              return 'b';
      }
      
      console.log(a());
      

      输出会是什么?

      b
      b
      

      如果我们使用函数定义

      var a = function() {
              return 'a';
      }
      
      console.log(a());
      
      a = function() {
              return 'b';
      }
      
      console.log(a());
      

      输出是:

      a
      b
      

      【讨论】:

      • 函数声明并没有什么坏处。你只需要了解变量和函数声明在 JS 中是如何提升的。进一步阅读 variable hoistingfunction hoisting
      • 函数定义比函数声明更直观。
      【解决方案7】:

      试试这个:

      defineFct = (name, fct)->
        eval("var x = function #{name}() { return fct.call(this, arguments); }")
        return x
      

      现在以下将打印“true”:

      foo = defineFct('foo', ()->'foo')
      console.log(foo() == foo.name)
      

      我实际上并没有使用它,但有时希望咖啡函数具有自省的名称。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多