CoffeeScript中的函数声明


79

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

a = (c) -> c=1

我只能得到函数表达式

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

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

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

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


4
您是否有充分的理由要进行函数声明?如果您使用的是coffeescript ,则除非已损坏或有问题,否则您不必关心已编译JS的格式。
雷诺斯

3
在大多数情况下,函数声明和函数表达式的工作方式相同,但是两者之间略有不同。例如,developer.mozilla.org / en / JavaScript / Reference /…因此,在某些情况下,它们并不相等。

您将我链接到一段函数声明未定义的代码。是否要使用函数声明而不是函数表达式来滥用未定义的行为?
雷诺斯2011年

5
@Raynos函数声明对于堆栈跟踪和其他调试非常有用,因为函数名称已附加。这就是为什么CoffeeScript将它们用于classes的原因。
特雷弗·伯纳姆

2
@TrevorBurnham我的意思是,这仅是调试已编译js的难度的一个小改进。您真正想要的是一个可以读取coffeescript的调试器。
雷诺斯2011年

Answers:


61

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

class Foo

编译为

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

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

怪微软这个。最初,每个可能具有合理名称的函数都被赋予了一个名称,但是IE 8及更低版本具有范围问题,其中将命名函数同时视为声明和表达式。请参阅以获取更多信息。

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


31
他在谈论IE的命名函数表达式(例如var a = function a() {};)问题。函数声明(例如function a() {})没有这种跨浏览器不一致的情况
AngusC 2011年

4
如果首先在浏览器中使用CS并不疯狂,这对我来说将更有意义。相信DOM处理库来跟上浏览器的变化和不赞成使用是一回事,但是当您谈论的是实际的源代码本身时,这就像是双重危险的依赖关系。想象一下,在CS社区枯竭并进入下一个使我变得更像轨道的现象之后的10年,如何处理遗留代码库。当一切开始崩溃时,由您来查找后弃用的内容,并找出在CS解析器中要解决的问题。
埃里克·雷彭

12

是的你可以:

hello()

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

您可以通过反引号`

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

干杯


19
这并不表明它是在coffeescript中完成的-只是coffeescript允许转义为javascript。这也很讨厌!
王尔德先生

9
使用之前的定义更加讨厌xD
Zaid Daghestani 2014年

1
另外,在更高版本的v8中,函数声明似乎已得到了优化
James M. Lay 2014年

注意你可以写function updateSettings() {; do-> dothings()}以允许缩进。github.com/jashkenas/coffeescript/issues/...
avalanche1

6

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); }`

1

不,您不能在coffee脚本中定义一个函数,而让它在coffee脚本中生成一个函数声明

即使你写

-> 123

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

(function() {
  return 123;
});

我的猜测是,这是因为函数声明“悬挂”在了封闭范围的顶部,这将破坏coffeescript源的逻辑流程。


11
提升正是我要使用函数声明的原因!
ivanreese

1
从某种意义上说,CoffeeScript已经“提升”了,因为它在范围的顶部预先声明了带有var的变量。因此,函数可以相互引用,顺序无关紧要。
埃文·莫兰

15
@EvanMoran确实,CoffeeScript预先声明了变量,但是并未提升函数,因为直到函数表达式之前,变量一直未定义。因此,在定义功能之前,不能使用这些功能。
jasonkarns

1

虽然这是一篇较旧的文章,但我想在对话中添加一些内容,以便将来的Google员工使用。

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

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

这是一个附加到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()相同的操作


1

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

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

8
函数声明没有什么坏处。您只需要了解JS中如何悬挂变量和函数声明。可变吊装功能吊装的
Ben Harold

函数定义比函数声明更直观。
Tomasz Jakub Rup

0

尝试这个:

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)

我实际上并不使用此功能,但有时确实希望咖啡功能具有自省名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.