CoffeeScript 中的函数声明

发布于 2024-11-18 05:13:24 字数 426 浏览 2 评论 0原文

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

a = (c) -> c=1

我只能得到函数表达式

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

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

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

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

I notice that in CoffeeScript, if I define a function using:

a = (c) -> c=1

I can only get the function expression:

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

But, personally I often use function declaration,for example:

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

I do use the first form, but I'm wondering if there is a way in CoffeeScript generating a function declaration. If there is no such way, I would like to know why CoffeeScript avoid doing this. I don't think JSLint would holler an error for declaration, as long as the function is declared at the top of the scope.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

执妄 2024-11-25 05:13:24

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

class Foo

,编译为

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

根据常见问题解答< “CoffeeScript 在其他地方不使用函数声明的原因” /a>:

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

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

CoffeeScript uses function declarations (aka "named functions") in just one place: class definitions. For instance,

class Foo

compiles to

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

The reason CoffeeScript doesn't use function declarations elsewhere, according to the FAQ:

Blame Microsoft for this one. Originally every function that could have a sensible name retrieved for it was given one, but IE versions 8 and down have scoping issues where the named function is treated as both a declaration and an expression. See this for more information.

In short: Using function declarations carelessly can lead to inconsistencies between IE (pre-9) and other JS environments, so CoffeeScript eschews them.

扎心 2024-11-25 05:13:24

是的,你可以:

hello()

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

你可以通过反引号`转义纯JS

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

干杯

Yes you can:

hello()

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

You escape pure JS via the backtick `

Note that you can't indent on your function body.

Cheers

懒猫 2024-11-25 05:13:24

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

One thing to keep in mind with CoffeeScript is that you can always kick back to JavaScript. While CoffeeScript doesn't support named function declarations, you can always drop back to JavaScript to do it.

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

You can also write a big fat function in CoffeeScript and then just use the backticks trick to have JavaScript call the other function:

# 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); }`
抚你发端 2024-11-25 05:13:24

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

即使你只是编写

-> 123

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

(function() {
  return 123;
});

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

No, you can't define a function in coffee script and have it generate a function declaration in coffee script

Even if you just write

-> 123

the generated JS will be wrapped in parens, thus making it a function expression

(function() {
  return 123;
});

My guess is that this is because function declarations get "hoisted" to the top of the enclosing scope which would break up the logical flow of the coffeescript source.

故事和酒 2024-11-25 05:13:24

虽然这是一篇较旧的帖子,但我想为未来的 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 Places 返回地址信息以自动填充表单。

因此,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() 相同的事情

While this is an older post, I wanted to add something to the conversation for future Googlers.

OP is correct in that we cannot declare functions in pure CoffeeScript (excluding the idea of using back-ticks to escape pure JS inside the CoffeeScript file).

But what we can do is bind the function to the window and essentially end up with something we can call as though it was a named function. I am not stating this is a named function, I'm providing a way to do what I imagine OP wants to actually do (call a function like foo(param) somewhere in the code) using pure CoffeeScript.

Here is an example of a function attached to the window in 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

This is using Google Places to return address information to auto-populate a form.

So we have a partial in a Rails app which is being loaded into a page. This means the DOM is already created, and if we call the function above on initial page load (before the ajax call renders the partial), jQuery won't see the $('#property_street_address_1') element (trust me - it didn't).

So we need to delay the google.maps.places.Autocomplete() until after the element is present on the page.

We can do this via the Ajax callback on successful load of the partial:

            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

So here, essentially, we're doing the same thing as calling foo()

恋你朝朝暮暮 2024-11-25 05:13:24

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

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

Why? Because function declaration is evil. Look at this code

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

What will be on the output?

b
b

If we use the function definition

var a = function() {
        return 'a';
}

console.log(a());

a = function() {
        return 'b';
}

console.log(a());

the output is:

a
b
埋情葬爱 2024-11-25 05:13:24

试试这个:

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)

我实际上并没有使用这个,但有时确实希望咖啡函数有用于内省的名称。

Try this:

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

Now the following will print "true":

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

I don't actually use this, but do sometimes wish coffee functions had names for introspection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文