2008-10-24 52 views
12

Hôm nay tôi đã có một cuộc thảo luận với một đồng nghiệp về chức năng lồng nhau trong Javascript:Phạm vi của hàm trong Javascript/ECMAScript là gì?

function a() { 
    function b() { 
     alert('boo') 
    } 
    var c = 'Bound to local call object.' 
    d = 'Bound to global object.' 
} 

Trong ví dụ này, thử nghiệm chỉ ra rằng b là không thể truy cập bên ngoài cơ thể của một, giống như c là. Tuy nhiên, d là - sau khi thực hiện một(). Tìm kiếm định nghĩa chính xác của hành vi này trong ECMAScript v.3 standard, tôi không tìm thấy từ ngữ chính xác mà tôi đang tìm kiếm; những gì Sec.13 p.71 không nói, là đối tượng mà đối tượng hàm được tạo ra bởi câu lệnh khai báo hàm được ràng buộc. Tui bỏ lỡ điều gì vậy?

Trả lời

21

Đây là Phạm vi tĩnh. Các câu lệnh trong một hàm được đặt trong phạm vi hàm đó.

Javascript có một hành vi kỳ quặc, tuy nhiên, đó là nếu không có sự var từ khóa, bạn đã ngụ ý một biến toàn cầu. Đó là những gì bạn nhìn thấy trong bài kiểm tra của bạn. Biến "d" của bạn có sẵn bởi vì nó là một hàm toàn cục ngụ ý, mặc dù được viết bên trong phần thân của hàm.

Ngoài ra, để trả lời phần thứ hai của câu hỏi: Một hàm tồn tại ở bất kỳ phạm vi nào được khai báo, giống như một biến.

Sidenote: Có thể bạn không muốn biến toàn cục, đặc biệt là không có biến thể ngụ ý. Bạn nên luôn sử dụng từ khóa var, để tránh nhầm lẫn và giữ mọi thứ sạch sẽ.

Sidenote: Tiêu chuẩn ECMA có lẽ không phải là nơi hữu ích nhất để tìm câu trả lời về Javascript, mặc dù nó chắc chắn không phải là tài nguyên xấu. Hãy nhớ rằng javascript trong trình duyệt của bạn chỉ là một thực hiện tiêu chuẩn đó, do đó, các tài liệu tiêu chuẩn sẽ được cung cấp cho bạn các quy tắc được (chủ yếu) tiếp theo là những người triển khai khi động cơ javascript đã được xây dựng. Nó không thể cung cấp thông tin cụ thể về các triển khai mà bạn quan tâm, cụ thể là các trình duyệt chính. Có một vài cuốn sách nói riêng sẽ cung cấp cho bạn thông tin rất trực tiếp về cách triển khai javascript trong các trình duyệt chính hoạt động. Để minh họa sự khác biệt, tôi sẽ bao gồm các đoạn trích bên dưới từ cả đặc tả ECMAScript và một cuốn sách trên Javascript. Tôi nghĩ bạn sẽ đồng ý rằng cuốn sách đưa ra câu trả lời trực tiếp hơn.

Dưới đây là từ ECMAScript Ngôn ngữ Đặc điểm kỹ thuật:

10,2 Bước vào một bối cảnh Execution

Mỗi chức năng và xây dựng cuộc gọi vào một bối cảnh thực hiện mới, thậm chí nếu một hàm được tự xưng đệ quy. Mỗi lần thoát đều thoát khỏi ngữ cảnh thực thi . Một ngoại lệ bị ném, nếu không bị bắt, cũng có thể thoát ra một hoặc ngữ cảnh thực thi khác.

Khi kiểm soát vào một bối cảnh thực hiện, chuỗi phạm vi được tạo ra và khởi tạo, biến instantiation được thực hiện, và giá trị này được xác định.

Các khởi động của chuỗi phạm vi, biến instantiation, và xác định giá trị này phụ thuộc vào loại mã đang được nhập vào.

Dưới đây là từ O'Reilly Javascript: The Definitive Guide (5th Edition):

8.8.1 Phạm vi từ vựng

Chức năng trong JavaScript là lexically chứ không phải là động scoped. Điều này có nghĩa là chúng chạy trong phạm vi mà chúng được xác định, không phải phạm vi mà từ đó chúng được thực thi. Khi một hàm được xác định, phạm vi hiện tại chuỗi được lưu và trở thành một phần của trạng thái nội bộ của hàm . ...

Rất khuyến khích cho bao gồm các loại câu hỏi là Douglas Crockford của cuốn sách:

JavaScript, The Good Parts http://oreilly.com/catalog/covers/9780596517748_cat.gif

Javascript, The Good Parts, cũng từ O'Reilly.

+2

Chính xác - nói rộng 'hàm a() {}' tương đương với 'var a = function() {}' (có một số khác biệt ngữ nghĩa nhỏ, nhưng không có gì quá đáng kể). – olliej

+0

cảm ơn bạn, nhưng nơi nào tôi tìm thấy các xác nhận sau trong tiêu chuẩn ECMAScript (hoặc nó bị thiếu?): "Các câu lệnh trong một hàm được đặt trong phạm vi hàm đó." Trong ấn tượng của tôi, ngữ nghĩa của "var a = function() {}" được xác định rõ, nhưng không phải là câu lệnh "function a() {...}". –

+0

Tiêu chuẩn ECMAScript sẽ chỉ cung cấp cho bạn các nguyên tắc để triển khai ECMAScript, trong đó Javascript chỉ là một thành viên gia đình. Vì vậy, bạn sẽ không tìm thấy tuyên bố chính xác đó, nhưng có những tài nguyên khác có thể giúp bạn. Xem ghi chú bổ sung của tôi ở trên. – keparo

4

Theo tôi được biết, đây là những tương đương như xa như Phạm vi là có liên quan:

function a() { ... } 

var a = function() { ... } 
0

...

function a() { 
    function b() { 
     alert('boo') 
    } 
    var c = 'Bound to local call object.' 
    d = 'Bound to global object.' 
} 

mà không bị trước bởi var, d là toàn cầu. Thực hiện việc này để thực hiện riêng tư:

function a() { 
    function b() { 
     alert('boo') 
    } 
    var c = 'Bound to local call object.' 
    var d = 'Bound to local object.' 
} 
2

Điều quan trọng cần lưu ý là khi d được tạo thành "toàn cầu", thực tế nó được tạo ra như là một thuộc tính của đối tượng cửa sổ. Điều này có nghĩa là bạn vô tình có thể ghi đè điều gì đó đã tồn tại trên đối tượng cửa sổ hoặc biến của bạn có thể thực sự không được tạo ra chút nào. Vì vậy:

function a() { 
    d = 'Hello World'; 
} 
alert(window.d); // shows 'Hello World' 

Nhưng bạn không thể làm:

function a() { 
    document = 'something'; 
} 

vì bạn không thể ghi đè lên các đối tượng window.document.

Đối với tất cả các mục đích thực tế, bạn có thể chụp ảnh rằng tất cả mã của bạn đang chạy trong khối with(window) khổng lồ.

+0

Có, hành vi này được xác định trong Sec. 10.1.9 của tiêu chuẩn ECMAScript. –

1

Javascript có hai phạm vi. Toàn cầu, và chức năng. Nếu bạn khai báo một biến bên trong một hàm bằng từ khóa "var", nó sẽ là cục bộ cho hàm đó và bất kỳ hàm bên trong nào. Nếu bạn khai báo một biến bên ngoài một hàm, nó có phạm vi toàn cục.

Cuối cùng, nếu bạn bỏ qua từ khóa var khi lần đầu tiên khai báo biến, javascript giả sử bạn muốn biến toàn cục, bất kể bạn khai báo ở đâu.

Vì vậy, bạn đang gọi hàm a và hàm a đang khai báo biến toàn cầu d.

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