web-dev-qa-db-fra.com

Déclaration de fonction dans CoffeeScript

Je remarque que dans CoffeeScript, si je définis une fonction en utilisant:

a = (c) -> c=1

Je ne peux obtenir que expression de fonction:

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

Mais personnellement, j'utilise souvent déclaration de fonction, par exemple:

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

J'utilise le premier formulaire, mais je me demande s'il existe un moyen dans CoffeeScript de générer une déclaration de fonction. S'il n'y a pas une telle façon, je voudrais savoir pourquoi CoffeeScript évite de faire ça. Je ne pense pas que JSLint crierait une erreur de déclaration, tant que la fonction est déclarée en haut de la portée.

78
Grace Shao

CoffeeScript utilise des déclarations de fonctions (alias "fonctions nommées") en un seul endroit: class définitions. Par exemple,

class Foo

compile en

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

La raison pour laquelle CoffeeScript n'utilise pas de déclarations de fonction ailleurs, selon la FAQ :

Blâmez Microsoft pour celui-ci. A l'origine, chaque fonction qui pouvait avoir un nom sensible récupéré en avait un, mais IE versions 8 et antérieures ont des problèmes de portée où la fonction nommée est traitée à la fois comme une déclaration et une expression. Voir - this pour plus d'informations.

En bref: l'utilisation imprudente des déclarations de fonctions peut entraîner des incohérences entre IE (pré-9) et d'autres environnements JS, donc CoffeeScript les évite.

60
Trevor Burnham

Oui, vous pouvez:

hello()

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

Vous échappez pur JS via le backtick `

Notez que vous ne pouvez pas indenter votre corps de fonction.

À votre santé

12
Zaid Daghestani

Une chose à garder à l'esprit avec CoffeeScript est que vous pouvez toujours revenir à JavaScript. Bien que CoffeeScript ne prenne pas en charge les déclarations de fonctions nommées, vous pouvez toujours revenir à JavaScript pour le faire.

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

Vous pouvez également écrire une grosse fonction grasse dans CoffeeScript, puis utiliser simplement l'astuce pour que JavaScript appelle l'autre fonction:

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

Bien qu'il s'agisse d'un article plus ancien, je voulais ajouter quelque chose à la conversation pour les futurs Googlers.

OP est correct dans la mesure où nous ne pouvons pas déclarer de fonctions en pur CoffeeScript (à l'exception de l'idée d'utiliser des back-ticks pour échapper au JS pur à l'intérieur du fichier CoffeeScript).

Mais ce que nous pouvons faire, c'est lier la fonction à la fenêtre et nous retrouver essentiellement avec quelque chose que nous pouvons appeler comme s'il s'agissait d'une fonction nommée. Je ne dis pas ceci est une fonction nommée, je fournis un moyen de faire ce que j'imagine que OP veut réellement faire (appeler une fonction comme foo (param) quelque part dans le code) en utilisant du pur CoffeeScript .

Voici un exemple de fonction attachée à la fenêtre en 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

Cela utilise Google Adresses pour renvoyer les informations d'adresse pour remplir automatiquement un formulaire.

Nous avons donc un partiel dans une application Rails qui est en cours de chargement dans une page. Cela signifie que le DOM est déjà créé, et si nous appelons la fonction ci-dessus lors du chargement initial de la page (avant l'appel ajax rend le partiel), jQuery ne verra pas l'élément $ ('# property_street_address_1') (croyez-moi, ce n'est pas le cas).

Nous devons donc retarder google.maps.places.Autocomplete () jusqu'à ce que l'élément soit présent sur la page.

Nous pouvons le faire via le rappel Ajax en cas de chargement réussi du partiel:

            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

Donc, ici, nous faisons essentiellement la même chose que d'appeler foo ()

1
notaceo

Pourquoi? Parce que déclaration de fonction est mauvais. Regardez ce code

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

Quel sera le résultat?

b
b

Si nous utilisons la définition de la fonction

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

console.log(a());

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

console.log(a());

la sortie est:

a
b
1
Tomasz Jakub Rup

Non, vous ne pouvez pas définir une fonction dans le script café et lui faire générer une déclaration de fonction dans le script café

Même si vous écrivez

-> 123

le JS généré sera enveloppé dans des parens, ce qui en fera une expression de fonction

(function() {
  return 123;
});

Je suppose que cela est dû au fait que les déclarations de fonctions sont "hissées" en haut de la portée englobante, ce qui briserait le flux logique de la source coffeescript.

1
AngusC

Essaye ça:

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

Maintenant, ce qui suit imprimera "vrai":

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

Je ne l'utilise pas réellement, mais je souhaite parfois que les fonctions café aient un nom pour l'introspection.

0
shaunc