Deklaracja funkcji w CoffeeScript

79

Zauważyłem, że w CoffeeScript, jeśli zdefiniuję funkcję za pomocą:

a = (c) -> c=1

Mogę tylko uzyskać wyrażenie funkcyjne :

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

Ale osobiście często używam deklaracji funkcji , na przykład:

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

Używam pierwszego formularza, ale zastanawiam się, czy w CoffeeScript jest sposób na wygenerowanie deklaracji funkcji. Jeśli nie ma takiego sposobu, chciałbym wiedzieć, dlaczego CoffeeScript tego unika. Nie sądzę, by JSLint wykrzykiwał błąd dla deklaracji, o ile funkcja jest zadeklarowana na początku zakresu.

Grace Shao
źródło
4
Czy masz dobry powód, aby chcieć deklaracji funkcji? Jeśli używasz coffeescript, nie powinieneś przejmować się formatem skompilowanego JS, chyba że jest uszkodzony / zawiera błędy.
Raynos
3
W większości przypadków deklaracja funkcji i wyrażenie funkcji działają w ten sam sposób, ale jest między nimi niewielka różnica. Na przykład developer.mozilla.org/en/JavaScript/Reference/… Więc w niektórych przypadkach nie są sobie równe.
Grace Shao
połączyłeś mnie z fragmentem kodu, w którym deklaracja funkcji jest niezdefiniowanym zachowaniem. Czy chcesz używać deklaracji funkcji zamiast wyrażeń funkcji, aby nadużywać niezdefiniowanego zachowania?
Raynos
5
@Raynos Deklaracje funkcji mogą być przydatne w przypadku śledzenia stosu i innych metod debugowania, ponieważ nazwa jest dołączona do funkcji. Dlatego CoffeeScript używa ich do classes.
Trevor Burnham
2
@TrevorBurnham Miałem na myśli, że to tylko niewielka poprawa trudności w debugowaniu skompilowanego js. To, czego naprawdę chcesz, to debugger, który może czytać coffeescript.
Raynos

Odpowiedzi:

61

CoffeeScript używa deklaracji funkcji (zwanych też „nazwanymi funkcjami”) tylko w jednym miejscu: w classdefinicjach. Na przykład,

class Foo

kompiluje się do

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

Według FAQ, powód, dla którego CoffeeScript nie używa deklaracji funkcji w innym miejscu, zgodnie z FAQ :

Winę za to Microsoft. Oryginalnie każda funkcja, która mogłaby mieć sensowną nazwę dla niej, otrzymała jedną, ale wersje IE 8 i starsze mają problemy z zakresem, w których nazwana funkcja jest traktowana zarówno jako deklaracja, jak i wyrażenie. Zobacz to, aby uzyskać więcej informacji.

Krótko mówiąc: nieostrożne używanie deklaracji funkcji może prowadzić do niespójności między IE (przed 9) a innymi środowiskami JS, więc CoffeeScript ich unika.

Trevor Burnham
źródło
31
Mówi o problemie IE z nazwanymi wyrażeniami funkcyjnymi (np var a = function a() {};.). Deklaracje funkcji (np. function a() {}) Nie mają takich niespójności między przeglądarkami
AngusC
4
Miałoby to dla mnie większy sens, gdyby używanie CS w przeglądarce nie było szaleństwem. Jedną rzeczą jest zaufanie do biblioteki obsługującej DOM, aby nadążyć za odmianami i przestarzałymi przeglądarkami, ale kiedy to, o czym mówisz, to sam kod źródłowy, jest to podwójnie niebezpieczna zależność. Wyobraź sobie, że masz do czynienia ze starszą bazą kodu 10 lat po tym, jak społeczność CS wyschła i przeszła do następnego zjawiska „make-it-more-rails-like-for-me”. Kiedy wszystko zacznie się zepsuć i od Ciebie zależy, co zostało wycofane z użycia i co należy naprawić w parserze CS.
Erik Reppen
12

Tak, możesz:

hello()

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

Uciekasz przed czystym JS przez lewy przycisk `

Zwróć uwagę, że nie możesz tworzyć wcięć w treści funkcji.

Twoje zdrowie

Zaid Daghestani
źródło
19
To nie pokazuje, że jest to zrobione w skrypcie coffeescript - tylko, że coffeescript pozwala na ucieczkę do javascript. To też jest naaaasty!
Pan Wilde,
9
Definicje przed użyciem są bardziej nieprzyjemne xD
Zaid Daghestani
1
Ponadto deklaracje funkcji wydają się być mocno zoptymalizowane w późniejszych wersjach v8.
James M. Lay
pamiętaj, że możesz pisać function updateSettings() {; do -> dothings (), }aby umożliwić wcięcie. github.com/jashkenas/coffeescript/issues/…
avalanche1
6

Jedną rzeczą, o której należy pamiętać w przypadku CoffeeScript, jest to, że zawsze możesz wrócić do JavaScript. Chociaż CoffeeScript nie obsługuje nazwanych deklaracji funkcji, zawsze możesz wrócić do JavaScript, aby to zrobić.

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

Możesz także napisać dużą, grubą funkcję w CoffeeScript, a następnie użyć sztuczki z lewymi znakami, aby JavaScript wywołał drugą funkcję:

# 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
źródło
1

Nie, nie możesz zdefiniować funkcji w skrypcie coffee i sprawić, by wygenerowała deklarację funkcji w skrypcie coffee

Nawet jeśli po prostu piszesz

-> 123

wygenerowany JS zostanie opakowany w pareny, czyniąc go tym samym wyrażeniem funkcji

(function() {
  return 123;
});

Domyślam się, że dzieje się tak dlatego, że deklaracje funkcji są „podnoszone” na szczyt obejmującego zakresu, co spowodowałoby przerwanie logicznego przepływu źródła coffeescript.

AngusC
źródło
11
Właśnie podnoszenie jest powodem, dla którego chcę używać deklaracji funkcji!
ivanreese
1
CoffeeScript w pewnym sensie już „podnosi”, ponieważ wstępnie deklaruje zmienne z var na początku zakresu. Funkcje mogą więc odnosić się do siebie, a kolejność nie ma znaczenia.
Evan Moran,
15
@EvanMoran Prawdą jest, że CoffeeScript wstępnie deklaruje zmienne, ale funkcje nie są podnoszone, ponieważ zmienna pozostaje niezdefiniowana aż do wyrażenia funkcji. Dlatego nie możesz używać funkcji, dopóki nie zostaną zdefiniowane.
jasonkarns
1

Chociaż jest to starszy post, chciałem dodać coś do rozmowy dla przyszłych pracowników Google.

OP ma rację, ponieważ nie możemy deklarować funkcji w czystym CoffeeScript (pomijając pomysł użycia back-ticków do ucieczki czystego JS w pliku CoffeeScript).

Ale to, co możemy zrobić, to powiązać funkcję z oknem i zasadniczo otrzymać coś, co możemy nazwać tak, jakby to była nazwana funkcja. Nie twierdzę, że jest to nazwana funkcja, zapewniam sposób zrobienia tego, co wyobrażam sobie, że OP chce faktycznie zrobić (wywołać funkcję taką jak foo (param) gdzieś w kodzie) przy użyciu czystego CoffeeScript.

Oto przykład funkcji dołączonej do okna w skrypcie 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

To jest używanie Miejsc Google do zwracania informacji adresowych w celu automatycznego wypełnienia formularza.

Mamy więc fragment w aplikacji Rails, który jest ładowany na stronę. Oznacza to, że DOM jest już utworzony, a jeśli wywołamy powyższą funkcję przy początkowym załadowaniu strony (zanim wywołanie AJAX wyrenderuje częściową część), jQuery nie zobaczy elementu $ ('# property_street_address_1') (zaufaj mi - nie udało się) t).

Musimy więc opóźnić google.maps.places.Autocomplete () do momentu pojawienia się elementu na stronie.

Możemy to zrobić za pomocą wywołania zwrotnego Ajax po pomyślnym załadowaniu częściowej:

            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

Więc tutaj robimy to samo, co wywołanie foo ()

notaceo
źródło
1

Czemu? Ponieważ deklaracja funkcji jest zła. Spójrz na ten kod

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

Co będzie na wyjściu?

b
b

Jeśli użyjemy definicji funkcji

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

console.log(a());

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

console.log(a());

wynik to:

a
b
Tomasz Jakub Rup
źródło
8
Nie ma nic złego w deklaracjach funkcji. Musisz tylko zrozumieć, jak deklaracje zmiennych i funkcji są podnoszone w JS. Dalsze czytanie o zmiennym podnoszeniu i podnoszeniu funkcyjnym
Ben Harold
definicja funkcji jest bardziej intuicyjna niż deklaracja funkcji.
Tomasz Jakub Rup
0

Spróbuj tego:

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

Teraz zostanie wydrukowane „prawda”:

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

Właściwie tego nie używam, ale czasami chciałbym, żeby funkcje kawy miały nazwy dla introspekcji.

shaunc
źródło