Declaración de función en CoffeeScript

79

Noto que en CoffeeScript, si defino una función usando:

a = (c) -> c=1

Solo puedo obtener la expresión de la función :

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

Pero, personalmente, a menudo uso la declaración de función , por ejemplo:

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

Utilizo el primer formulario, pero me pregunto si hay alguna forma en CoffeeScript de generar una declaración de función. Si no existe tal manera, me gustaría saber por qué CoffeeScript evita hacer esto. No creo que JSLint grite un error para la declaración, siempre que la función se declare en la parte superior del alcance.

Grace Shao
fuente
4
¿Tiene alguna buena razón para querer la declaración de función? Si usa coffeescript, no debería preocuparse por el formato del JS compilado a menos que esté roto / con errores.
Raynos
3
En la mayoría de los casos, la declaración de función y la expresión de función funcionan de la misma manera, pero hay una ligera diferencia entre las dos. Por ejemplo, developer.mozilla.org/en/JavaScript/Reference/… Entonces, en algunos casos, no son iguales.
Grace Shao
me vinculó a un fragmento de código donde la declaración de función es un comportamiento indefinido. ¿Desea utilizar declaraciones de funciones en lugar de expresiones de funciones para poder abusar del comportamiento indefinido?
Raynos
5
@Raynos Las declaraciones de función pueden ser útiles para seguimientos de pila y otras depuraciones, ya que se adjunta un nombre a la función. Es por eso que CoffeeScript los usa para classes.
Trevor Burnham
2
@TrevorBurnham Quiero decir que es solo una pequeña mejora en la dificultad de depurar js compilados. Lo que realmente desea es un depurador que pueda leer coffeescript.
Raynos

Respuestas:

61

CoffeeScript usa declaraciones de funciones (también conocidas como "funciones con nombre") en un solo lugar: classdefiniciones. Por ejemplo,

class Foo

compila a

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

La razón por la que CoffeeScript no usa declaraciones de funciones en otros lugares, según las preguntas frecuentes :

Culpa a Microsoft por este. Originalmente, a todas las funciones que podían tener un nombre sensato recuperado se les asignaba uno, pero las versiones de IE 8 y anteriores tienen problemas de alcance en los que la función nombrada se trata como una declaración y una expresión. Consulte esto para obtener más información.

En resumen: el uso descuidado de las declaraciones de funciones puede generar inconsistencias entre IE (pre-9) y otros entornos JS, por lo que CoffeeScript las evita.

Trevor Burnham
fuente
31
Está hablando del problema de IE con las expresiones de función con nombre (por ejemplo var a = function a() {};). Las declaraciones de funciones (por ejemplo function a() {}) no tienen tales inconsistencias entre navegadores
AngusC
4
Esto tendría más sentido para mí si no fuera una locura usar CS en un navegador en primer lugar. Una cosa es confiar en una biblioteca de manejo de DOM para mantenerse al día con las variaciones y el abandono del navegador, pero cuando de lo que estás hablando es del código fuente real, eso es como una dependencia de doble peligro. Imagínese lidiar con una base de código heredada 10 años después de que la comunidad de CS se secó y pasó al siguiente fenómeno de hacer más rieles como para mí. Cuando todo comience a fallar y dependa de usted descubrir qué quedó en desuso y qué corregir en el analizador CS.
Erik Reppen
12

Sí tu puedes:

hello()

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

Escapas de JS puro a través de la comilla invertida `

Tenga en cuenta que no puede aplicar sangría al cuerpo de su función.

Salud

Zaid Daghestani
fuente
19
Esto no muestra que se esté haciendo en coffeescript, solo que coffeescript permite escapar a javascript. ¡También esto es naaaasty!
Mr Wilde
9
Las definiciones antes del uso son más desagradables xD
Zaid Daghestani
1
Además, las declaraciones de funciones parecen estar muy optimizadas en versiones posteriores de v8.
James M. Lay
tenga en cuenta que puede escribir function updateSettings() {; hacer -> dothings () }para permitir la sangría. github.com/jashkenas/coffeescript/issues/…
avalanche1
6

Una cosa a tener en cuenta con CoffeeScript es que siempre puede volver a JavaScript. Si bien CoffeeScript no admite declaraciones de funciones con nombre, siempre puede volver a JavaScript para hacerlo.

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

También puede escribir una función grande en CoffeeScript y luego usar el truco de las comillas invertidas para que JavaScript llame a la otra función:

# 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
fuente
1

No, no puede definir una función en el script de café y hacer que genere una declaración de función en el script de café

Incluso si solo escribes

-> 123

el JS generado se envolverá en parens, convirtiéndolo en una expresión de función

(function() {
  return 123;
});

Supongo que esto se debe a que las declaraciones de funciones se "elevan" a la parte superior del alcance adjunto, lo que rompería el flujo lógico de la fuente coffeescript.

AngusC
fuente
11
¡La elevación es exactamente la razón por la que quiero usar declaraciones de funciones!
ivanreese
1
CoffeeScript en cierto sentido ya "eleva" porque predeclara variables con var en la parte superior del alcance. Por lo tanto, las funciones pueden referirse entre sí y el orden no importa.
Evan Moran
15
@EvanMoran Es cierto que CoffeeScript pre-declara las variables, pero las funciones no son elevadas porque la variable permanece indefinida hasta la expresión de la función. Por lo tanto, no puede utilizar las funciones hasta que estén definidas.
Jasonkarns
1

Si bien esta es una publicación anterior, quería agregar algo a la conversación para futuros empleados de Google.

OP tiene razón en que no podemos declarar funciones en CoffeeScript puro (excluyendo la idea de usar ticks inversos para escapar JS puro dentro del archivo CoffeeScript).

Pero lo que podemos hacer es vincular la función a la ventana y esencialmente terminar con algo que podemos llamar como si fuera una función con nombre. No estoy diciendo que esto sea una función con nombre, estoy proporcionando una forma de hacer lo que imagino que OP quiere hacer (llamar a una función como foo (param) en algún lugar del código) usando CoffeeScript puro.

Aquí hay un ejemplo de una función adjunta a la ventana 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

Esto está utilizando Google Places para devolver la información de la dirección para completar automáticamente un formulario.

Así que tenemos un parcial en una aplicación Rails que se está cargando en una página. Esto significa que el DOM ya está creado, y si llamamos a la función anterior en la carga de la página inicial (antes de que la llamada ajax muestre el parcial), jQuery no verá el elemento $ ('# property_street_address_1') (confía en mí, no lo hizo) t).

Por lo tanto, debemos retrasar google.maps.places.Autocomplete () hasta que el elemento esté presente en la página.

Podemos hacer esto a través de la devolución de llamada Ajax en la carga exitosa del parcial:

            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

Entonces, aquí, esencialmente, estamos haciendo lo mismo que llamar a foo ()

notaceo
fuente
1

¿Por qué? Porque la declaración de función es mala. Mira este código

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

¿Qué habrá en la salida?

b
b

Si usamos la definición de función

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

console.log(a());

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

console.log(a());

la salida es:

a
b
Tomasz Jakub Rup
fuente
8
No hay nada de malo en las declaraciones de funciones. Solo necesita comprender cómo se elevan las declaraciones de variables y funciones en JS. Lectura adicional sobre elevación variable y elevación funcional
Ben Harold
La definición de función es más intuitiva que la declaración de función.
Tomasz Jakub Rup
0

Prueba esto:

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

Ahora lo siguiente imprimirá "verdadero":

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

En realidad, no uso esto, pero a veces desearía que las funciones de café tuvieran nombres para la introspección.

Shaunc
fuente