2010-11-01 41 views
32

Như đã thảo luận here, các định nghĩa chức năng có thể được sử dụng trước khi chúng được xác định. Nhưng ngay sau khi một phần của mã được bao bọc trong một khối thử, điều này không còn là trường hợp.Tại sao tôi không thể sử dụng hàm Javascript trước định nghĩa của nó bên trong khối thử?

này sẽ hiển thị "Hello world":

hello(); 
function hello() { alert("Hello world"); } 

Nhưng màn hình này "ReferenceError: hello không được định nghĩa":

try { 
    hello(); 
    function hello() { alert("Hello world"); } 
} catch (err) { 
    alert(err); 
} 

Vì vậy, rõ ràng là một cái gì đó "đặc biệt" về một khối try với liên quan đến khai báo hàm. Có cách nào để giải quyết hành vi này không?

+1

trình duyệt này thuộc loại nào? –

+0

Đồng ý; nó không hoạt động (Firefox), nhưng câu hỏi tôi muốn hỏi là tại sao bạn cần điều này? Tại sao bạn cần đặt các khai báo hàm bên trong một khối try/catch? – Spudley

+1

Firefox. Bây giờ tôi thấy rằng cả IE và Chrome đều hiển thị Hello world ... –

Trả lời

5

Cho rằng khối chức năng thiết lập phạm vi cục bộ với tham chiếu hàm chuyển tiếp, gói nội dung của khối thử trong một hàm ngay lập tức có vẻ khôi phục hành vi đó.

này hoạt động trong Firefox, IE, Chrome:

try { 
    (function(){ 
    hello(); 
    function hello() { alert("Hello world"); } 
    }()) 
} catch (err) { 
    alert(err); 
} 

Trong số chức năng khóa học và các biến được định nghĩa trong thử chức năng không còn được nhìn thấy trong khối catch, vì họ sẽ không có chức năng bao bọc ngay lập tức. Nhưng đây là một giải pháp có thể có để thử/bắt gói kịch bản.

26

Firefox diễn giải các câu lệnh hàm khác nhau và rõ ràng là chúng đã phá vỡ khai báo cho khai báo chức năng. (A good read about named functions/declaration vs expression)

Tại sao Firefox giải thích các báo cáo khác nhau là do đoạn mã sau:

if (true) { 
    function test(){alert("YAY");} 
} else { 
    function test(){alert("FAIL");} 
} 
test(); // should alert FAIL 

Do cẩu khai, chức năng test nên luôn luôn cảnh giác "thất bại", nhưng không phải trong Firefox. Các mã trên thực sự cảnh báo "YAY" trong Firefox và tôi nghi ngờ mã mà làm cho điều đó xảy ra cuối cùng đã phá vỡ tuyên bố cẩu hoàn toàn.

Tôi giả định Firefox chuyển các khai báo hàm thành các khai báo var khi chúng được đặt trong câu lệnh if/else hoặc try/catch. Cũng giống như vậy:

// firefox interpretted code 
var test; // hoisted 
if (true) { 
    test = function(){alert("yay")} 
} else { 
    test = function(){alert("fail")} 
} 

Sau một cuộc tranh luận ngắn với Šime Vidas, tôi phải nói rằng giao dịch của Firefox với tờ khai chức năng là phi tiêu chuẩn, vì:

The production SourceElement : Statement is processed for function declarations by taking no action.
The production SourceElement : Statement is evaluated as follows:

  1. Evaluate Statement.
  2. Return Result(1).

Cả FunctionDeclaration và Tuyên bố là SourceElements, ergo, không nên có FunctionDeclarations bên trong một câu lệnh (nếu/else, try/catch). Cho Šime Vidas một brownie!

Thử/nắm bắt về cơ bản là một dạng khác của if/else và có thể sử dụng cùng một mã ngoại lệ.

+0

@BGerrissen Ý của bạn là gì theo "cách nào là tiêu chuẩn"? Các câu lệnh chức năng không phải là một phần của tiêu chuẩn ECMAScript chính thức. Đó là thứ mà Mozilla đã giới thiệu. –

+0

@Sime http://bclary.com/2004/11/07/#a-13 (tuyên bố) Tùy chọn được đề cập ở trên cũng có ở đâu đó, nhưng quá mơ hồ để tìm thấy atm. Tôi đã tìm thấy nó khá lâu rồi ... – BGerrissen

+0

Tuyên bố, khai báo, chọn một;) – BGerrissen

1

Bạn luôn có thể làm điều đó theo cách này và tận dụng tốt nhất của cả hai thế giới:

function hello() { 
    alert("Hello world"); 
} 

try { 
    hello(); 
} 
catch (err) { 
    alert(err); 
} 

Bạn vẫn sẽ nhận được ngoại lệ của bạn trong khối catch, nhưng các chức năng sẽ có sẵn. Nó cũng phải dễ dàng hơn để duy trì, và không có lợi ích chức năng cho các chức năng cẩu.

Edit:

Để chứng minh rằng đây chỉ là bền như bao trùm toàn bộ mã trong một catch, tôi đang cung cấp một ví dụ chi tiết hơn.

function hello(str) { 
    alert("Hello, " + str); 
} 

function greet() { 
    asdf 
} 

try { 
    var user = "Bob"; 
    hello(user); 
    greet(); 
    asdf 
} 
catch (e) { 
    alert(e); 
} 

Điều này sẽ hoạt động như mong đợi, không có vấn đề phân tích cú pháp. Các vị trí duy nhất mà nó có thể thất bại tại thời điểm tải nằm ngoài các hàm defs và try catch. Bạn cũng sẽ nhận được ngoại lệ về bất kỳ rác bên trong của chức năng defs.

Tôi đoán đó là tùy chọn kiểu, nhưng có vẻ dễ đọc và dễ bảo trì hơn so với các tùy chọn khác.

+0

Điều này sẽ không bắt lỗi thời gian tải tập lệnh. Trừ khi bạn có ý định di chuyển toàn bộ nội dung tập lệnh vào hàm hello(), và sau đó chúng ta có cùng một phương thức như phương pháp trình bao bọc hàm thử. –

+0

Bạn có ý nghĩa gì với lỗi thời gian tải? Nếu bạn đặt bất kỳ tham chiếu không xác định nào bên trong định nghĩa hàm, nó sẽ hoạt động như mong đợi. Nó chỉ kiểm tra tên của hàm trên tải ban đầu, và khi được gọi tạo ra một ngoại lệ được xử lý bởi try-catch. – sworoc

+0

Ví dụ, vô tình gõ một ký tự rác trên đường giữa hàm và thử. –

Các vấn đề liên quan