Funktionsdeklaration in CoffeeScript

79

Ich stelle fest, dass in CoffeeScript, wenn ich eine Funktion definiere, indem ich:

a = (c) -> c=1

Ich kann nur den Funktionsausdruck bekommen :

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

Ich persönlich verwende jedoch häufig eine Funktionsdeklaration , zum Beispiel:

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

Ich verwende zwar das erste Formular, frage mich jedoch, ob es in CoffeeScript eine Möglichkeit gibt, eine Funktionsdeklaration zu generieren. Wenn es keinen solchen Weg gibt, würde ich gerne wissen, warum CoffeeScript dies vermeidet. Ich glaube nicht, dass JSLint einen Fehler bei der Deklaration auslösen würde, solange die Funktion am oberen Rand des Bereichs deklariert ist.

Grace Shao
quelle
4
Haben Sie einen guten Grund, eine Funktionsdeklaration zu wollen? Wenn Sie Coffeescript verwenden, sollten Sie sich nicht um das Format des kompilierten JS kümmern, es sei denn, es ist defekt / fehlerhaft.
Raynos
3
In den meisten Fällen funktionieren Funktionsdeklaration und Funktionsausdruck auf die gleiche Weise, es besteht jedoch ein geringfügiger Unterschied zwischen beiden. Beispiel: developer.mozilla.org/en/JavaScript/Reference/… In einigen Fällen sind sie also nicht gleich.
Grace Shao
Sie haben mich mit einem Code verknüpft, in dem die Funktionsdeklaration ein undefiniertes Verhalten ist. Möchten Sie Funktionsdeklarationen anstelle von Funktionsausdrücken verwenden, um undefiniertes Verhalten zu missbrauchen ?
Raynos
5
@ Raynos Funktionsdeklarationen können für Stack-Traces und anderes Debugging hilfreich sein, da der Funktion ein Name zugeordnet ist. Deshalb verwendet CoffeeScript sie für classes.
Trevor Burnham
2
@ TrevorBurnham Ich meinte, das ist nur eine geringfügige Verbesserung der Schwierigkeit, kompilierte js zu debuggen. Was Sie eigentlich wollen, ist ein Debugger, der Coffeescript lesen kann.
Raynos

Antworten:

61

CoffeeScript verwendet Funktionsdeklarationen (auch als "benannte Funktionen" bezeichnet) an nur einer Stelle: classDefinitionen. Zum Beispiel,

class Foo

kompiliert zu

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

Der Grund, warum CoffeeScript laut FAQ keine Funktionsdeklarationen an anderer Stelle verwendet :

Beschuldigen Sie Microsoft dafür. Ursprünglich wurde jeder Funktion, für die ein sinnvoller Name abgerufen werden konnte, eine gegeben, aber IE-Versionen 8 und niedriger weisen Gültigkeitsbereichsprobleme auf, bei denen die benannte Funktion sowohl als Deklaration als auch als Ausdruck behandelt wird. Sehen Sie diese für weitere Informationen.

Kurz gesagt: Die unachtsame Verwendung von Funktionsdeklarationen kann zu Inkonsistenzen zwischen dem IE (vor 9) und anderen JS-Umgebungen führen, sodass CoffeeScript diese meidet.

Trevor Burnham
quelle
31
Er spricht über das Problem des IE mit benannten Funktionsausdrücken (z var a = function a() {};. B. ). Funktionsdeklarationen (z. B. function a() {}) weisen keine derartigen browserübergreifenden Inkonsistenzen auf
AngusC
4
Dies wäre für mich sinnvoller, wenn es nicht verrückt wäre, CS überhaupt in einem Browser zu verwenden. Es ist eine Sache, auf eine DOM-Handhabungsbibliothek zu vertrauen, um mit den Variationen und der Verwerfung des Browsers Schritt zu halten. Wenn es sich jedoch um den eigentlichen Quellcode handelt, ist dies wie eine Abhängigkeit von doppelter Gefährdung. Stellen Sie sich vor, Sie haben es 10 Jahre nach dem Austrocknen der CS-Community mit einer alten Codebasis zu tun und sind zum nächsten Phänomen übergegangen, bei dem es darum geht, mehr Schienen für mich zu machen. Wenn alles kaputt geht und Sie herausfinden müssen, was veraltet ist, und herausfinden, was im CS-Parser behoben werden muss.
Erik Reppen
12

Ja, du kannst:

hello()

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

Sie entkommen reinem JS über das Backtick `

Beachten Sie, dass Sie Ihren Funktionskörper nicht einrücken können.

Prost

Zaid Daghestani
quelle
19
Dies zeigt nicht, dass dies in Coffeescript erfolgt - nur, dass Coffeescript die Flucht nach Javascript ermöglicht. Auch das ist naaaasty!
Herr Wilde
9
Definitionen vor dem Gebrauch ist böser xD
Zaid Daghestani
1
Außerdem scheinen Funktionsdeklarationen in späteren Versionen von Version 8 stark optimiert zu sein .
James M. Lay
Beachten Sie, dass Sie schreiben können function updateSettings() {; do -> dothings () }, um Einrückungen zu ermöglichen. github.com/jashkenas/coffeescript/issues/…
avalanche1
6

Eine Sache, die Sie bei CoffeeScript beachten sollten, ist, dass Sie jederzeit auf JavaScript zurückgreifen können. Während CoffeeScript benannte Funktionsdeklarationen nicht unterstützt, können Sie dazu jederzeit auf JavaScript zurückgreifen.

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

Sie können auch eine große Fettfunktion in CoffeeScript schreiben und dann einfach den Backticks-Trick verwenden, damit JavaScript die andere Funktion aufruft:

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

Nein, Sie können eine Funktion im Kaffeeskript nicht definieren und eine Funktionsdeklaration im Kaffeeskript generieren lassen

Auch wenn Sie nur schreiben

-> 123

Das generierte JS wird in Parens eingeschlossen, wodurch es zu einem Funktionsausdruck wird

(function() {
  return 123;
});

Ich vermute, dass dies daran liegt, dass Funktionsdeklarationen an die Spitze des umschließenden Bereichs "gehisst" werden, was den logischen Fluss der Coffeescript-Quelle unterbrechen würde.

AngusC
quelle
11
Das Heben ist genau der Grund, warum ich Funktionsdeklarationen verwenden möchte!
Iwanreese
1
CoffeeScript "hebt" in gewisser Weise bereits, weil es Variablen mit var am oberen Rand des Bereichs vorab deklariert. Funktionen können sich also aufeinander beziehen und die Reihenfolge spielt keine Rolle.
Evan Moran
15
@EvanMoran Es ist wahr, dass CoffeeScript die Variablen vordeklariert, aber die Funktionen werden nicht angehoben, da die Variable bis zum Funktionsausdruck undefiniert bleibt. Daher können Sie die Funktionen erst verwenden, nachdem sie definiert wurden.
Jason Karns
1

Während dies ein älterer Beitrag ist, wollte ich der Konversation für zukünftige Googler etwas hinzufügen.

OP ist insofern richtig, als wir keine Funktionen in reinem CoffeeScript deklarieren können (mit Ausnahme der Idee, Back-Ticks zu verwenden, um reines JS in der CoffeeScript-Datei zu umgehen).

Was wir aber tun können, ist, die Funktion an das Fenster zu binden und im Wesentlichen etwas zu erhalten, das wir aufrufen können, als wäre es eine benannte Funktion. Ich sage dies nicht ist eine benannte Funktion, ich bin ein Weg , um zu tun , was ich OP vorstellen will tatsächlich tun (nennen wir eine Funktion wie foo (param) irgendwo im Code) unter Verwendung von reinem Coffeescript .

Hier ist ein Beispiel für eine Funktion, die in Coffeescript an das Fenster angehängt ist:

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

Hierbei werden Google Places verwendet, um Adressinformationen zurückzugeben und ein Formular automatisch auszufüllen.

Wir haben also einen Teil in einer Rails-App, der auf eine Seite geladen wird. Dies bedeutet, dass das DOM bereits erstellt wurde. Wenn wir die obige Funktion beim ersten Laden der Seite aufrufen (bevor der Ajax-Aufruf den Teil rendert), sieht jQuery das Element $ ('# property_street_address_1') nicht (vertrau mir - es hat nicht funktioniert). t).

Daher müssen wir google.maps.places.Autocomplete () verzögern, bis das Element auf der Seite vorhanden ist.

Wir können dies über den Ajax-Rückruf bei erfolgreichem Laden des Teils tun:

            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

Hier machen wir also im Wesentlichen dasselbe wie beim Aufrufen von foo ()

notaceo
quelle
1

Warum? Weil Funktionsdeklaration böse ist. Schauen Sie sich diesen Code an

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

Was wird auf der Ausgabe sein?

b
b

Wenn wir die Funktionsdefinition verwenden

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

console.log(a());

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

console.log(a());

Die Ausgabe ist:

a
b
Tomasz Jakub Rup
quelle
8
Funktionsdeklarationen sind nichts Böses. Sie müssen nur verstehen, wie Variablen- und Funktionsdeklarationen in JS gehisst werden. Weiterführende Literatur zu variablem Heben und Funktionsheben
Ben Harold
Die Funktionsdefinition ist intuitiver als die Funktionsdeklaration.
Tomasz Jakub Rup
0

Versuche dies:

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

Jetzt wird Folgendes "true" ausgegeben:

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

Ich benutze das eigentlich nicht, aber manchmal wünschte ich mir, Kaffee-Funktionen hätten Namen für die Selbstbeobachtung.

shaunc
quelle