Объявление функции в CoffeeScript

79

Я замечаю, что в CoffeeScript, если я определяю функцию, используя:

a = (c) -> c=1

Я могу получить только выражение функции :

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

Но лично я часто использую объявление функции , например:

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

Я использую первую форму, но мне интересно, есть ли в CoffeeScript способ создания объявления функции. Если такого способа нет, я хотел бы знать, почему CoffeeScript этого не делает. Я не думаю, что JSLint выдаст ошибку при объявлении, если функция объявлена ​​в верхней части области видимости.

Грейс Шао
источник
4
У вас есть веские причины требовать объявления функции? Если вы используете coffeescript, вам не нужно заботиться о формате скомпилированного JS, если он не сломан / не содержит ошибок.
Raynos 01
3
В большинстве случаев объявление функции и выражение функции работают одинаково, но между ними есть небольшая разница. Например, developer.mozilla.org/en/JavaScript/Reference/… Итак, в некоторых случаях они не равны.
Грейс Шао
вы связали меня с фрагментом кода, где объявление функции имеет неопределенное поведение. Вы хотите использовать объявления функций вместо выражений функций, чтобы можно было злоупотреблять неопределенным поведением?
Raynos 01
5
Объявления функций @Raynos могут быть полезны для трассировки стека и другой отладки, поскольку к функции добавляется имя. Вот почему CoffeeScript использует их для classes.
Trevor Burnham
2
@TrevorBurnham Я имел в виду, что это лишь незначительное улучшение сложности отладки скомпилированных js. На самом деле вам нужен отладчик, который может читать coffeescript.
Raynos 01

Ответы:

61

CoffeeScript использует объявления функций (также известные как «именованные функции») только в одном месте: classопределениях. Например,

class Foo

компилируется в

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

Причина, по которой CoffeeScript не использует объявления функций где-либо еще, согласно FAQ :

Винить в этом Microsoft. Первоначально каждой функции, для которой можно было получить разумное имя, было дано одно, но IE версии 8 и ниже имеют проблемы с областью видимости, когда именованная функция обрабатывается и как объявление, и как выражение. См. Это для получения дополнительной информации.

Вкратце: неосторожное использование объявлений функций может привести к несогласованности между IE (до 9) и другими средами JS, поэтому CoffeeScript избегает их.

Тревор Бернхэм
источник
31
Он говорит о проблеме IE с выражениями именованных функций (например var a = function a() {};). Объявления функций (например function a() {}) не имеют таких межбраузерных несоответствий
AngusC
4
Для меня это имело бы больший смысл, если бы не было безумием использовать CS в браузере. Одно дело - верить в библиотеку обработки DOM, чтобы не отставать от вариантов и устаревания браузера, но когда речь идет о самом исходном коде, это похоже на двойную зависимость. Представьте себе, что вы имеете дело с устаревшей базой кода через 10 лет после того, как сообщество CS иссякло и перешло к следующему феномену «сделай больше рельсов, как для меня». Когда все начинает ломаться, и вы должны выяснить, что стало устаревшим, и выяснить, что исправить в парсере CS.
Эрик Реппен
12

Да, ты можешь:

hello()

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

Вы избегаете чистого JS с помощью обратной кавычки `

Обратите внимание, что вы не можете делать отступ в теле функции.

Ура

Заид Дагестанский
источник
19
Это не показывает, что это делается в coffeescript - только то, что coffeescript позволяет уйти в javascript. Также это наааастично!
Mr Wilde
9
Определения перед использованием более неприятны xD
Заид Дагестани
1
Кроме того, объявления функций, похоже, сильно оптимизированы в более поздних версиях v8.
Джеймс М. Лэй
обратите внимание, что вы можете писать function updateSettings() {; do -> dothings (), }чтобы разрешить отступ. github.com/jashkenas/coffeescript/issues/…
avalanche1
6

При работе с 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); }`
mattmc3
источник
1

Нет, вы не можете определить функцию в сценарии кофе и заставить ее генерировать объявление функции в сценарии кофе.

Даже если вы просто напишете

-> 123

сгенерированный JS будет заключен в скобки, что сделает его функциональным выражением

(function() {
  return 123;
});

Я предполагаю, что это происходит из-за того, что объявления функций «поднимаются» наверх охватывающей области видимости, что нарушает логический поток исходного кода coffeescript.

AngusC
источник
11
Подъем - это именно то, почему я хочу использовать объявления функций!
ivanreese
1
CoffeeScript в некотором смысле уже «поднимается», потому что он заранее объявляет переменные с var в верхней части области видимости. Таким образом, функции могут ссылаться друг на друга, и порядок не имеет значения.
Эван Моран
15
@EvanMoran Это правда, что CoffeeScript предварительно объявляет переменные, но функции не поднимаются, потому что переменная остается неопределенной до выражения функции. Следовательно, вы не можете использовать функции до тех пор, пока они не будут определены.
jasonkarns
1

Хотя это старый пост, я хотел добавить кое-что к разговору для будущих гуглеров.

OP верен в том, что мы не можем объявлять функции в чистом CoffeeScript (за исключением идеи использования обратных тиков для выхода из чистого JS внутри файла CoffeeScript).

Но что мы можем сделать, так это привязать функцию к окну и, по сути, получить то, что мы можем вызвать, как если бы это была именованная функция. Я не о том , это будет именованная функция, я обеспечиваю способ сделать то , что я полагаю , ОП хочет на самом деле сделать (вызвать функцию как Foo (пары) где - то в коде) с использованием чистого CoffeeScript.

Вот пример функции, прикрепленной к окну в 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 Адреса для возврата адресной информации для автоматического заполнения формы.

Итак, у нас есть партиал в приложении Rails, который загружается на страницу. Это означает, что модель DOM уже создана, и если мы вызовем указанную выше функцию при начальной загрузке страницы (до того, как вызов ajax отобразит фрагмент), jQuery не увидит элемент $ ('# property_street_address_1') (поверьте мне, это не было) т).

Поэтому нам нужно отложить 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 ()

notaceo
источник
1

Почему? Потому что объявление функции - зло. Посмотри на этот код

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
Томаш Якуб Руп
источник
8
В объявлениях функций нет ничего плохого. Вам просто нужно понять, как объявления переменных и функций поднимаются в JS. Дальнейшее чтение по переменной грузоподъемного и функции подъема
Бен Harold
Определение функции более интуитивно, чем объявление функции.
Tomasz Jakub Rup
0

Попробуй это:

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)

На самом деле я этим не пользуюсь, но иногда мне хочется, чтобы кофейные функции имели имена для самоанализа.

Shaunc
источник