2009-02-26 24 views
331

Trong javascript, khi bạn muốn sử dụng này:Mục đích của chức năng tự thực hiện trong javascript là gì?

(function(){ 
    //Bunch of code... 
})(); 

trên này:

//Bunch of code... 
+3

Ngoài ra, hãy xem [(kỹ thuật] (http://stackoverflow.com/q/4212149/1048572)) [giải thích] (http://stackoverflow.com/q/8228281/1048572) và [tại đây ] (http://stackoverflow.com/a/441498/1048572). Đối với cú pháp, hãy xem [lý do tại sao dấu ngoặc đơn là cần thiết] (http://stackoverflow.com/q/1634268/1048572) và [nơi chúng nên đi] (http://stackoverflow.com/q/3384504/1048572) . – Bergi

+2

Xem thêm [Mục đích của việc bao gói toàn bộ các tệp Javascript trong các hàm ẩn danh là gì?] (Http://stackoverflow.com/q/2421911/1048572) – Bergi

+0

Tại sao nó có hai dấu ngoặc đơn cuối cùng, ngay trước dấu chấm phẩy? – johnny

Trả lời

321

Phạm vi toàn bộ biến số của nó. Các biến được khai báo trong hàm tự thi hành, theo mặc định, chỉ có sẵn cho mã trong hàm tự thực thi. Điều này cho phép mã được viết mà không cần quan tâm đến cách các biến được đặt tên trong các khối mã javascript khác.

+61

Ví dụ (đối với những người mà nó giúp tôi): '(function() {var foo = 3; alert (foo);}) (); alert (foo); 'đầu tiên sẽ cảnh báo" 3 "và sau đó ném một lỗi vào cảnh báo tiếp theo vì foo không được xác định. –

+7

Và cũng vì lợi ích của nhiều người trên mạng, bao gồm cả một nhóm kỹ sư Netflix: IT'S JUST A FUNCTION. Nó không nằm trong và chính nó là đại diện của việc đóng cửa. Đôi khi người dùng tự động được sử dụng kết hợp với các kịch bản có liên quan đến đóng cửa để thực hiện các công cụ gọn gàng, nhưng nếu bạn không nhìn thấy thứ gì đó chứa một tham chiếu có thể được thu gom rác và chuyển sang ngôn ngữ không đóng, nó không có gì để freaking làm với CLOSURES. –

+1

Vì vậy, điều này có nghĩa là, nó chủ yếu được sử dụng với đóng cửa? –

17

namespacing. Các phạm vi của JavaScript là mức chức năng.

+0

Được phân bổ. Không có tên ở đó. –

+5

downvotes vẫn đang đến vì tôi đã sử dụng * namespacing * instad of * scoping *; đây là một vấn đề định nghĩa - xem ví dụ [Wikipedia] (http://en.wikipedia.org/wiki/Namespace): * Một không gian tên trong khoa học máy tính (đôi khi còn được gọi là phạm vi tên), là một thùng chứa trừu tượng hoặc môi trường được tạo ra để giữ một nhóm hợp lý các mã định danh duy nhất hoặc các ký hiệu (ví dụ, tên). * và * Một mã định danh không gian tên có thể cung cấp ngữ cảnh (Phạm vi trong khoa học máy tính) cho một tên, và các thuật ngữ đôi khi được sử dụng thay thế cho nhau. * – Christoph

+5

Phạm vi chức năng của Javascript cung cấp không gian tên biến số sống trong, một không gian tên *; rằng nó là một ẩn danh không liên kết với một định danh không gian tên là không liên quan ... – Christoph

-2

IIRC nó cho phép bạn tạo các thuộc tính và phương thức riêng tư.

1

Vì chức năng trong Javascript là đối tượng hạng nhất, bằng cách xác định nó theo cách đó, nó định nghĩa một "lớp" giống như C++ hoặc C#.

Chức năng đó có thể xác định các biến cục bộ và có chức năng bên trong nó. Các hàm bên trong (các phương thức instance hiệu quả) sẽ có quyền truy cập vào các biến cục bộ (các biến mẫu có hiệu quả), nhưng chúng sẽ được tách biệt khỏi phần còn lại của tập lệnh.

6

Cách ly phạm vi, có thể. Vì vậy, các biến bên trong khai báo hàm không làm ô nhiễm không gian tên bên ngoài.

Tất nhiên, trên một nửa triển khai JS ở đó, chúng sẽ vẫn như thế.

+4

Chúng sẽ là gì? –

+1

Bất kỳ triển khai nào không được viết ở chế độ nghiêm ngặt và chứa khai báo var ngầm định và làm cho nó trở thành toàn cục. –

2

Một khác biệt là các biến mà bạn khai báo trong hàm là cục bộ, vì vậy chúng biến mất khi bạn thoát khỏi hàm và không xung đột với các biến khác trong mã khác.

6

Có thông số và "Bunch of code" trả về một hàm không?

var a = function(x) { return function() { document.write(x); } }(something); 

Đóng. Giá trị của something được sử dụng bởi hàm được gán cho a. something có thể có một số giá trị khác nhau (đối với vòng lặp) và mỗi khi có một hàm mới.

+0

+1; Tôi thích 'var x = something;' trong hàm bên ngoài trên 'x' làm tham số, mặc dù: imo dễ đọc hơn theo cách này ... – Christoph

+0

@Christoph: Nếu giá trị của" cái gì đó "thay đổi sau khi hàm được tạo , sau đó nó sẽ sử dụng giá trị mới và không phải là giá trị tại thời điểm tạo ra nó. – stesch

+0

@stesch: bạn đã lấy từ đâu? Theo như tôi biết, đó không phải là trường hợp; cách duy nhất để có được các tham chiếu thực sự trong JS là bằng cách sử dụng đối số-đối tượng, nhưng ngay cả điều đó không hoạt động trong tất cả các trình duyệt – Christoph

31

tự gọi (còn được gọi là tự động gọi) là khi một hàm thực hiện ngay khi định nghĩa của nó. Đây là mẫu lõi và đóng vai trò là nền tảng cho nhiều mẫu khác của JavaScript phát triển.

Tôi là một fan hâm mộ lớn :) của nó vì:

  • Nó giữ mã để tối thiểu
  • Nó thực thi tách hành vi từ trình bày
  • Nó cung cấp một kết thúc, giúp ngăn chặn các cuộc xung đột đặt tên

Rất nhiều - (Tại sao bạn nên nói là tốt?)

  • Đó là về cách xác định và thực hiện một chức năng cùng một lúc.
  • Bạn có thể có chức năng tự thực hiện trả về một giá trị và chuyển hàm này làm tham số cho một hàm khác.
  • Tốt cho việc đóng gói.
  • Nó cũng tốt cho phạm vi khối.
  • Vâng, bạn có thể đính kèm tất cả các tệp .js của bạn vào một chức năng tự thực thi và có thể ngăn ngừa ô nhiễm không gian tên chung. ;)

Thêm here.

+41

Điểm 1. Làm thế nào? Điểm 2. Đó là từ một thực hành tốt nhất hoàn toàn khác. Điểm 3. Chức năng nào không? 4,5,6,7. Mức độ liên quan? 8. Vâng, 1/8 không phải là xấu tôi đoán. –

+1

Bảy năm trễ nhưng, đối với điểm 1. nó không làm giảm mã ở tất cả, trên thực tế nó cho biết thêm tối thiểu hai dòng mã trong việc tạo ra các chức năng. – YungGun

11

Tôi không thể tin rằng không có câu trả lời nào đề cập đến các hình cầu được ngụ ý.

Các (function(){})() xây dựng không bảo vệ chống lại globals ngụ ý, mà với tôi là mối quan tâm lớn hơn, xem http://yuiblog.com/blog/2006/06/01/global-domination/

Về cơ bản các khối chức năng đảm bảo tất cả các "vars toàn cầu" phụ thuộc bạn đã xác định được giới hạn trong chương trình của bạn, nó không bảo vệ bạn chống lại việc xác định các hình cầu ngầm ẩn. JSHint hoặc tương tự có thể cung cấp các khuyến nghị về cách bảo vệ chống lại hành vi này.

Cú pháp var App = {} ngắn gọn hơn cung cấp mức độ bảo vệ tương tự và có thể được bao bọc trong khối chức năng khi ở trên các trang 'công khai'. (xem Ember.js hoặc SproutCore để biết các ví dụ thực tế về thư viện sử dụng cấu trúc này)

Cho đến khi bạn tạo một khung công cộng hoặc thư viện, nhưng nếu bạn cần triển khai chúng, Douglas Crockford có một số ý tưởng hay.

+7

Chế độ nghiêm ngặt bảo vệ chống lại các quả cầu ngụ ý. Điều đó kết hợp với một người tự động invoker sẽ có bạn bảo hiểm. Tôi chưa bao giờ hiểu được sự hooplah về tài sản cá nhân. Khai báo các vars bên trong của một hàm tạo func. Làm xong. Nếu ý nghĩ quên sử dụng từ khóa 'mới' giúp bạn thức dậy vào ban đêm, hãy viết chức năng của nhà máy. Xong lại. –

51

Đơn giản. Vì vậy, rất bình thường tìm kiếm, nó gần như an ủi:

var userName = "Sean"; 

console.log(name()); 

function name() { 
    return userName; 
} 

Tuy nhiên. Điều gì sẽ xảy ra nếu tôi đưa một thư viện javascript thực sự tiện dụng vào trang của mình để dịch các ký tự nâng cao vào các biểu diễn mức cơ sở của chúng?

Chờ ... cái gì?

Ý tôi là. Nếu một người nào đó trong một nhân vật với một số loại giọng trên đó (chẳng hạn như một nhân vật tiếng Pháp hoặc tiếng Tây Ban Nha) nhưng tôi chỉ muốn nhân vật 'tiếng Anh'? A-z trong chương trình của tôi? À ... Các ký tự tiếng Tây Ban Nha 'n ~' và tiếng Pháp 'e /' (tôi đã sử dụng hai ký tự cho mỗi ký tự, nhưng bạn có thể làm cho bước nhảy tinh thần vào ký tự đại diện cho dấu trọng âm), những ký tự đó có thể được dịch thành các ký tự cơ bản của 'n' và 'e'.

Vì vậy, một người nào đó tốt đẹp đã viết một bộ chuyển đổi ký tự toàn diện mà tôi có thể đưa vào trang web của tôi ... Tôi đưa nó vào.

Một vấn đề: nó có chức năng gọi là 'tên' giống như chức năng của tôi.

Đây được gọi là va chạm. Chúng tôi có hai hàm được khai báo trong cùng một phạm vi có cùng tên. Chúng tôi muốn tránh điều này.

Vì vậy, chúng tôi cần phải phân tích mã của chúng tôi bằng cách nào đó.

Cách duy nhất để mã phạm vi trong javascript là để bọc nó trong một hàm:

function main() { 
    // We are now in our own sound-proofed room and the 
    // character-converter libarary's name() function can exist at the 
    // same time as ours. 

    var userName = "Sean"; 

    console.log(name()); 

    function name() { 
    return userName; 
    } 
} 

Điều đó có thể giải quyết vấn đề của chúng tôi. Tất cả mọi thứ bây giờ được đính kèm và chỉ có thể được truy cập từ bên trong niềng răng mở và đóng của chúng tôi.

Chúng tôi có chức năng trong một chức năng ... thật kỳ lạ khi xem xét, nhưng hoàn toàn hợp pháp.

Chỉ có một vấn đề. Mã của chúng tôi không hoạt động. Biến userName của chúng tôi không bao giờ được lặp lại trong bảng điều khiển!

Chúng tôi có thể giải quyết vấn đề này bằng cách thêm một cuộc gọi đến chức năng của chúng tôi sau khi khối mã hiện tại của chúng tôi ...

function main() { 
    // We are now in our own sound-proofed room and the 
    // character-converter libarary's name() function can exist at the 
    // same time as ours. 

    var userName = "Sean"; 

    console.log(name()); 

    function name() { 
    return userName; 
    } 
} 

main(); 

Hoặc trước!

main(); 

function main() { 
    // We are now in our own sound-proofed room and the 
    // character-converter libarary's name() function can exist at the 
    // same time as ours. 

    var userName = "Sean"; 

    console.log(name()); 

    function name() { 
    return userName; 
    } 
} 

Mối quan tâm thứ hai: Cơ hội nào tên 'chính' chưa được sử dụng? ... rất, rất mỏng.

Chúng tôi cần thêm phạm vi. Và một số cách để tự động thực thi hàm main() của chúng ta.

Bây giờ chúng tôi đến các chức năng tự động thực hiện (hoặc tự thực hiện, tự chạy, bất kỳ điều gì).

(() {})();

Cú pháp là khó xử với tư cách là tội lỗi. Tuy nhiên, nó hoạt động.

Khi bạn quấn định nghĩa hàm trong dấu ngoặc đơn và bao gồm danh sách tham số (bộ khác hoặc dấu ngoặc đơn!), Nó hoạt động như một hàm gọi.

Vì vậy, cho phép xem xét mã của chúng tôi một lần nữa, với một số tự thực hiện cú pháp:

(function main() { 
    var userName = "Sean"; 

    console.log(name()); 

    function name() { 
     return userName; 
    } 
    } 
)(); 

Vì vậy, trong hầu hết các bài hướng dẫn bạn đọc, bạn bây giờ sẽ được bắn phá với thuật ngữ 'nặc danh tự thực hiện' hoặc một cái gì đó giống.

Sau nhiều năm phát triển chuyên nghiệp, tôi mạnh yêu cầu bạn đặt tên mọi chức năng bạn viết cho mục đích gỡ lỗi.

Khi xảy ra sự cố (và điều này sẽ xảy ra), bạn sẽ kiểm tra backtrace trong trình duyệt của mình. Nó là luôn luôn dễ dàng hơn để thu hẹp các vấn đề về mã của bạn khi các mục trong ngăn xếp dấu vết có tên!

Hugely long-winded và tôi hy vọng nó sẽ giúp!

+2

Cảm ơn bạn :) Tôi đã tìm kiếm trên Internet tất cả các cách xung quanh cố gắng để hiểu những lợi thế của IIFE liên quan đến chức năng bình thường về quyền riêng tư biến và câu trả lời của bạn chỉ đơn giản là tốt nhất. Mọi người đều nói rằng một trong những lợi thế tốt nhất là các biến và hàm bên trong IIFE là 'cuối cùng' riêng tư khi một hàm bình thường chỉ cung cấp cho bạn chính xác điều tương tự. Cuối cùng tôi nghĩ rằng tôi đã có nó thông qua quá trình giải thích của bạn. IIFE chỉ là một chức năng sau khi tất cả nhưng bây giờ tôi hiểu lý do tại sao để sử dụng nó. Cảm ơn bạn một lần nữa! – viery365

+0

Cảm ơn bạn đã dành thời gian giải thích điều này rất tốt. – MSC

+0

Câu trả lời hay. Tuy nhiên, khi bạn đề nghị rằng tất cả các chức năng được đặt tên, bạn có nói rằng có một cách để làm điều đó với các chức năng tự thực thi, hay gợi ý rằng mọi người đều đặt tên cho hàm đó rồi gọi nó? EDIT Ồ, tôi hiểu rồi. Cái này đã được đặt tên rồi. Duh. Có thể muốn chỉ ra rằng bạn đang biện minh cho việc bạn sử dụng một chức năng tự thực thi được đặt tên. – FireSBurnsmuP

1

tự gọi hàm trong javascript:

Một biểu thức tự gọi được gọi (bắt đầu) tự động, mà không bị gọi. Một biểu thức tự gọi được gọi ngay sau khi nó được tạo ra. Điều này về cơ bản được sử dụng để tránh đặt tên xung đột cũng như để đạt được đóng gói. Các biến hoặc các đối tượng được khai báo không thể truy cập được bên ngoài hàm này. Để tránh các vấn đề về giảm thiểu (filename.min) luôn sử dụng chức năng tự thực hiện.

4

Đây là một ví dụ vững chắc về cách tự gọi hàm ẩn danh có thể hữu ích.

for(var i = 0; i < 10; i++) { 
    setTimeout(function(){ 
    console.log(i) 
    }) 
} 

Output: 10, 10, 10, 10, 10...

for(var i = 0; i < 10; i++) { 
    (function(num){ 
    setTimeout(function(){ 
     console.log(num) 
    }) 
    })(i) 
} 

Output: 0, 1, 2, 3, 4...

+0

bạn có thể giải thích thêm một chút về những gì đang xảy ra cho bộ mã đầu tiên không –

0

Dường như câu hỏi này đã được trả lời tất cả đã sẵn sàng, nhưng tôi sẽ đăng nhập của tôi anyway.

Tôi biết khi nào tôi muốn sử dụng các chức năng tự thực hiện.

var myObject = { 
    childObject: new function(){ 
     // bunch of code 
    }, 
    objVar1: <value>, 
    objVar2: <value> 
} 

Hàm này cho phép tôi sử dụng thêm một số mã để xác định thuộc tính childObjects và thuộc tính cho mã sạch hơn, chẳng hạn như đặt biến thường dùng hoặc thực hiện phương trình toán học; Oh! hoặc kiểm tra lỗi. như trái ngược với bị giới hạn để lồng cú pháp đối tượng instantiation của ...

object: { 
    childObject: { 
     childObject: {<value>, <value>, <value>} 
    }, 
    objVar1: <value>, 
    objVar2: <value> 
} 

Mã hóa nói chung có rất nhiều cách mơ hồ làm rất nhiều những điều tương tự, làm cho bạn tự hỏi: "Tại sao bận tâm?" Nhưng tình huống mới tiếp tục xuất hiện ở nơi bạn không còn có thể dựa vào các nguyên tắc cơ bản/cốt lõi.

1

(function(){ var foo = { name: 'bob' }; console.log(foo.name); // bob })(); console.log(foo.name); // Reference error

Trên thực tế, các chức năng trên sẽ được coi là biểu hiện chức năng mà không có một cái tên.

Mục đích chính của việc bao bọc một hàm có dấu ngoặc đơn mở và mở là tránh gây ô nhiễm không gian toàn cầu.

Các biến và hàm bên trong biểu thức hàm trở thành riêng tư (nghĩa là chúng sẽ không có sẵn bên ngoài hàm.

1

Tôi đã đọc tất cả câu trả lời, điều gì đó rất quan trọng bị thiếu ở đây, tôi sẽ KISS. Có 2 nguyên nhân chính, tại sao tôi cần Chức năng tự thực hiện Anonymous, hay nói đúng hơn "Ngay-Được triệu gọi hàm Expression (IIFE)":

  1. quản lý không gian tên Better (Tránh ô nhiễm không gian tên -> JS Module)
  2. Đóng cửa (Mô phỏng các Thành viên Nhóm Riêng tư, được biết đến từ OOP)

Điều đầu tiên đã được giải thích rất rõ. Đối với một thứ hai, xin vui lòng nghiên cứu sau Ví dụ:

var MyClosureObject = (function(){ 
    var MyName = 'Michael Jackson RIP'; 
    return { 
    getMyName: function() { return MyName;}, 
    setMyName: function (name) { MyName = name} 
    } 
}()); 

Chú ý 1: Chúng tôi không gán một chức năng để MyClosureObject, thêm nhiều kết quả của cách gọi mà chức năng. Hãy lưu ý đến số () ở dòng cuối cùng.

Chú ý 2: gì bạn bổ sung phải biết về chức năng trong Javascript là các chức năng bên trong có được quyền truy cập vào các thông số và các biến trong những chức năng, chúng được định nghĩa bên trong.

Chúng ta hãy thử một số thí nghiệm:

tôi có thể nhận MyName sử dụng getMyName và nó hoạt động:

console.log(MyClosureObject.getMyName()); 
// Michael Jackson RIP 

Cách tiếp cận ngây thơ sau đây sẽ không hoạt động:

console.log(MyClosureObject.MyName); 
// undefined 

Nhưng tôi có thể thiết lập một tên khác và nhận kết quả mong đợi:

MyClosureObject.setMyName('George Michael RIP'); 
console.log(MyClosureObject.getMyName()); 
// George Michael RIP 

Chỉnh sửa: Trong ví dụ ở trên MyClosureObject được thiết kế để sử dụng mà không cần tiền tố new, do đó, quy ước không được viết hoa.

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