Khai báo hàm trong CoffeeScript


79

Tôi nhận thấy rằng trong CoffeeScript, nếu tôi xác định một hàm bằng cách sử dụng:

a = (c) -> c=1

Tôi chỉ có thể nhận được biểu thức hàm :

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

Tuy nhiên, cá nhân tôi thường sử dụng khai báo hàm , ví dụ:

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

Tôi sử dụng biểu mẫu đầu tiên, nhưng tôi tự hỏi liệu có cách nào trong CoffeeScript tạo ra một khai báo hàm hay không. Nếu không có cách nào như vậy, tôi muốn biết tại sao CoffeeScript tránh làm điều này. Tôi không nghĩ rằng JSLint sẽ gây ra lỗi khi khai báo, miễn là hàm được khai báo ở đầu phạm vi.


4
Bạn có lý do chính đáng nào để muốn khai báo hàm không? Nếu bạn đang sử dụng coffeescript, bạn không nên quan tâm đến định dạng của JS đã biên dịch trừ khi nó bị hỏng / nghe trộm.
Raynos

3
Trong hầu hết các trường hợp, khai báo hàm và biểu thức hàm hoạt động theo cùng một cách, nhưng có một chút khác biệt giữa hai cách. Ví dụ: developer.mozilla.org/en/JavaScript/Reference/… Vì vậy, trong một số trường hợp, chúng không bằng nhau.
Grace Shao

bạn đã liên kết tôi với một đoạn mã trong đó khai báo hàm là hành vi không xác định. Bạn có muốn sử dụng khai báo hàm thay vì biểu thức hàm để bạn có thể lạm dụng hành vi không xác định không?
Raynos

5
@Raynos Khai báo hàm có thể tốt cho dấu vết ngăn xếp và gỡ lỗi khác, vì một tên được gắn vào hàm. Đó là lý do tại sao CoffeeScript sử dụng chúng cho classes.
Trevor Burnham

2
@TrevorBurnham Ý tôi là đó chỉ là một cải tiến nhỏ về độ khó của việc gỡ lỗi js đã biên dịch. Những gì bạn thực sự muốn là một trình gỡ lỗi có thể đọc coffeescript.
Raynos

Câu trả lời:


61

CoffeeScript sử dụng các khai báo hàm (hay còn gọi là "các hàm được đặt tên") chỉ ở một nơi: classđịnh nghĩa. Ví dụ,

class Foo

biên dịch thành

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

Lý do CoffeeScript không sử dụng khai báo hàm ở những nơi khác, theo Câu hỏi thường gặp :

Đổ lỗi cho Microsoft vì điều này. Ban đầu mọi hàm có thể có một tên hợp lý được truy xuất cho nó đều được cung cấp một tên, nhưng IE phiên bản 8 trở xuống có các vấn đề về phạm vi trong đó hàm được đặt tên được coi là cả khai báo và biểu thức. Xem điều này để biết thêm thông tin.

Tóm lại: Sử dụng khai báo hàm một cách bất cẩn có thể dẫn đến sự mâu thuẫn giữa IE (trước 9) và các môi trường JS khác, vì vậy CoffeeScript tránh chúng.


31
Anh ấy đang nói về vấn đề của IE với Biểu thức hàm được đặt tên (ví dụ var a = function a() {};). Khai báo chức năng (ví dụ function a() {}) không có mâu thuẫn duyệt chéo như vậy
AngusC

4
Điều này sẽ có ý nghĩa hơn đối với tôi nếu ngay từ đầu việc sử dụng CS trong trình duyệt không phải là điều điên rồ. Một điều là bạn phải đặt niềm tin vào thư viện xử lý DOM để theo kịp với các biến thể của trình duyệt và việc không dùng nữa nhưng khi điều bạn đang nói đến là chính mã nguồn thực tế, điều đó giống như một sự phụ thuộc nguy hiểm kép. Hãy tưởng tượng đối phó với một cơ sở mã kế thừa 10 năm sau khi cộng đồng CS cạn kiệt và chuyển sang hiện tượng tiếp theo khiến nó trở nên giống với tôi. Khi mọi thứ bắt đầu bị phá vỡ và bạn phải tìm ra những gì đã không còn được sử dụng và tìm ra những gì cần khắc phục trong trình phân tích cú pháp CS.
Erik Reppen

12

Có, bạn có thể:

hello()

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

Bạn thoát JS thuần túy thông qua backtick '

Lưu ý rằng bạn không thể thụt lề trên thân hàm của mình.

Chúc mừng


19
Điều này không cho thấy nó đang được thực hiện trong coffeescript - chỉ rằng coffeescript cho phép thoát sang javascript. Ngoài ra điều này là khó chịu!
Mr Wilde

9
Các định nghĩa trước khi sử dụng là xD khó chịu hơn
Zaid Daghestani

1
Ngoài ra, các khai báo hàm dường như được tối ưu hóa nhiều trong các phiên bản v8 mới hơn.
James M. Lay

lưu ý rằng bạn có thể viết function updateSettings() {; do -> dothings () }để cho phép thụt lề. github.com/jashkenas/coffeescript/issues/…
avalanche1

6

Một điều cần ghi nhớ với CoffeeScript là bạn luôn có thể quay trở lại JavaScript. Mặc dù CoffeeScript không hỗ trợ khai báo hàm đã đặt tên, nhưng bạn luôn có thể quay lại JavaScript để thực hiện.

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

Bạn cũng có thể viết một hàm big fat trong CoffeeScript và sau đó chỉ cần sử dụng thủ thuật backticks để JavaScript gọi hàm khác:

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

1

Không, bạn không thể xác định một hàm trong tập lệnh cà phê và để nó tạo ra một khai báo hàm trong tập lệnh cà phê

Ngay cả khi bạn chỉ viết

-> 123

JS được tạo sẽ được bao bọc trong parens, do đó làm cho nó trở thành một biểu thức hàm

(function() {
  return 123;
});

Tôi đoán rằng điều này là do các khai báo hàm được "nâng" lên đầu phạm vi bao quanh, điều này sẽ phá vỡ luồng logic của nguồn coffeescript.


11
Việc nâng cấp chính xác là lý do tại sao tôi muốn sử dụng các khai báo hàm!
ivanreese

1
CoffeeScript theo một nghĩa nào đó đã "hoists" vì nó báo trước các biến với var ở đầu phạm vi. Vì vậy, các chức năng có thể tham chiếu đến nhau và thứ tự không quan trọng.
Evan Moran

15
@EvanMoran Đúng là CoffeeScript khai báo trước các biến, nhưng các hàm không được lưu vào vì biến vẫn chưa được xác định cho đến khi biểu thức hàm. Do đó, bạn không thể sử dụng các hàm cho đến khi chúng được xác định.
jasonkarns

1

Mặc dù đây là một bài đăng cũ hơn, nhưng tôi muốn thêm điều gì đó vào cuộc trò chuyện dành cho các nhân viên Google trong tương lai.

OP đúng ở chỗ chúng ta không thể khai báo các hàm trong CoffeeScript thuần túy (loại trừ ý tưởng sử dụng dấu tích để thoát JS thuần túy bên trong tệp CoffeeScript).

Nhưng những gì chúng ta có thể làm là liên kết hàm với cửa sổ và về cơ bản kết thúc với một thứ mà chúng ta có thể gọi như thể nó là một hàm được đặt tên. Tôi không nói rõ đây một hàm được đặt tên, tôi đang cung cấp một cách để thực hiện những gì tôi tưởng tượng OP muốn thực sự làm (gọi một hàm như foo (param) ở đâu đó trong mã) bằng cách sử dụng CoffeeScript thuần túy.

Đây là một ví dụ về một hàm được đính kèm vào cửa sổ trong 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

Điều này đang sử dụng Google Địa điểm để trả lại thông tin địa chỉ nhằm tự động điền vào biểu mẫu.

Vì vậy, chúng tôi có một phần trong ứng dụng Rails đang được tải vào một trang. Điều này có nghĩa là DOM đã được tạo và nếu chúng ta gọi hàm ở trên khi tải trang ban đầu (trước khi lệnh gọi ajax hiển thị một phần), jQuery sẽ không nhìn thấy phần tử $ ('# property_street_address_1') (tin tôi đi - nó không có " t).

Vì vậy, chúng ta cần trì hoãn google.maps.places.Autocomplete () cho đến sau khi phần tử hiện diện trên trang.

Chúng tôi có thể thực hiện việc này thông qua lệnh gọi lại Ajax khi tải thành công một phần:

            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

Vì vậy, ở đây, về cơ bản, chúng tôi đang làm điều tương tự như gọi foo ()


1

Tại sao? Vì khai báo hàm là ác. Nhìn vào mã này

function a() {
        return 'a';
}

console.log(a());

function a() {
        return 'b';
}

console.log(a());

Những gì sẽ được trên đầu ra?

b
b

Nếu chúng ta sử dụng định nghĩa hàm

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

console.log(a());

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

console.log(a());

đầu ra là:

a
b

8
Không có gì xấu về khai báo hàm. Bạn chỉ cần hiểu cách khai báo biến và hàm được lưu trong JS. Đọc thêm về cẩu biếncẩu chức năng
Ben Harold

định nghĩa hàm phức tạp hơn khai báo hàm.
Tomasz Jakub Rup

0

Thử cái này:

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

Bây giờ phần sau sẽ in ra "true":

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

Tôi không thực sự sử dụng điều này, nhưng đôi khi ước các chức năng của cà phê có tên để xem xét nội tâm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.