Cú pháp JavaScript / jQuery này hoạt động như thế nào: (hàm (cửa sổ, không xác định) {}) (cửa sổ)?


153

Bạn đã bao giờ xem qua mã nguồn của mã nguồn jQuery 1.4 và nhận thấy cách nó được gói gọn theo cách sau:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

Tôi đã đọc một bài viết về Không gian tên JavaScript và một bài viết khác có tên " Một cặp Parens quan trọng ", vì vậy tôi biết một số điều đang diễn ra ở đây.

Nhưng tôi chưa bao giờ thấy cú pháp đặc biệt này trước đây. Cái đó undefinedđang làm gì vậy? Và tại sao windowcần phải được thông qua và sau đó xuất hiện ở cuối một lần nữa?


3
Tôi muốn thêm rằng Paul Irish nói về điều này trong video này: paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie

@Bergi bạn đã đánh dấu câu hỏi của tôi là một bản sao của câu hỏi khác nhưng tôi đã hỏi câu hỏi gần một năm trước khi trùng lặp. Các diễn viên nên được cách khác xung quanh.
dkinzer

@dkinzer: Không phải là về việc được hỏi sớm hơn, mà là về câu trả lời chất lượng cao nhất. Phải thừa nhận rằng, trong trường hợp này là cổ và cổ, nhưng tôi thấy câu trả lời của CMS là câu trả lời hay nhất. Tuy nhiên, hãy đến chat.stackoverflow.com/rooms/17 để thảo luận về vấn đề này
Bergi

Câu trả lời:


161

Không xác định là một biến bình thường và có thể được thay đổi đơn giản với undefined = "new value";. Vì vậy, jQuery tạo ra một biến "không xác định" cục bộ thực sự không xác định.

Các biến cửa sổ được thực hiện cục bộ vì lý do hiệu suất. Bởi vì khi JavaScript tìm kiếm một biến, đầu tiên nó sẽ đi qua các biến cục bộ cho đến khi tìm thấy tên biến. Khi không tìm thấy, JavaScript sẽ chuyển qua phạm vi tiếp theo, v.v. cho đến khi nó lọc qua các biến toàn cục. Vì vậy, nếu biến cửa sổ được tạo cục bộ, JavaScript có thể tìm kiếm nó nhanh hơn. Thông tin khác: Tăng tốc JavaScript của bạn - Nicholas C. Zakas


1
Cảm ơn! đó là một video rất nhiều thông tin. Tôi nghĩ bạn cần chỉnh sửa phản hồi của mình để nói "biến cửa sổ được tạo cục bộ". Tôi nghĩ rằng đây là câu trả lời tốt nhất. Tôi đã đếm và thấy rằng đối tượng cửa sổ được gọi ít nhất 17 lần trong mã nguồn JQuery. Vì vậy, đã có một hiệu ứng đáng kể trong việc di chuyển cửa sổ vào Phạm vi địa phương.
dkinzer

@DKinzer Ồ vâng, bạn là rght, tất nhiên nó được làm tại địa phương. Vui mừng tôi có thể giúp bạn =)
Vincent

1
Theo thử nghiệm được cập nhật này, jsperf.com/short-scope/5 đi qua 'cửa sổ' thực sự chậm hơn, khi nói đến việc lấy một cửa sổ liên tục thông qua jQuery và đặc biệt xấu cho Safari. Vì vậy, trong khi 'không xác định' có trường hợp sử dụng, tôi không hoàn toàn tin rằng 'cửa sổ' đặc biệt hữu ích trong mọi trường hợp.
hexalys

1
Nó cũng có tác dụng tích cực để giảm thiểu mã. Vì vậy, mọi cách sử dụng "cửa sổ" và "không xác định" có thể được thay thế bằng một tên ngắn hơn bằng minifyer.
TLindig

2
@ lukas.pukenis Khi bạn tạo một thư viện được sử dụng trong hàng triệu trang web, sẽ không khôn ngoan khi đưa ra các giả định về những gì người điên sẽ làm.
JLRishe

54

Chưa xác định

Bằng cách khai báo undefinednhư một đối số nhưng không bao giờ chuyển một giá trị cho nó, đảm bảo rằng nó luôn luôn không được xác định, vì nó chỉ đơn giản là một biến trong phạm vi toàn cầu có thể được ghi đè. Điều này làm cho a === undefinedmột sự thay thế an toàn typeof a == 'undefined', giúp tiết kiệm một vài ký tự. Nó cũng làm cho mã thân thiện với trình khai thác hơn, ví dụ như undefinedcó thể rút ngắn để ulưu thêm một vài ký tự.

Cửa sổ

Truyền windowdưới dạng đối số sẽ giữ một bản sao trong phạm vi cục bộ, điều này ảnh hưởng đến hiệu suất: http://jsperf.com/short-scope . Tất cả các truy cập windowbây giờ sẽ phải đi một cấp ít hơn trong chuỗi phạm vi. Như với undefined, một bản sao cục bộ một lần nữa cho phép thu nhỏ mạnh mẽ hơn.


Sidenote:

Mặc dù điều này có thể không phải là ý định của các nhà phát triển jQuery, việc truyền vào windowcho phép thư viện được tích hợp dễ dàng hơn trong môi trường Javascript phía máy chủ, ví dụ: node.js - nơi không có windowđối tượng toàn cầu . Trong tình huống như vậy, chỉ cần thay đổi một dòng để thay thế windowđối tượng bằng một dòng khác. Trong trường hợp của jQuery, một windowđối tượng giả có thể được tạo và chuyển qua cho mục đích cạo HTML (một thư viện như jsdom có thể làm điều này).


2
+1 để đề cập đến Thu nhỏ, ước gì tôi có thể +2 cho jsperf - Phiên bản rút gọn là (function(a,b){})(window);- abngắn hơn nhiều so với windowundefined:)
gnarf

+1 Tôi đã nghe nói về chuỗi phạm vi trước đây, nhưng quên thuật ngữ này. Cảm ơn!
alex

Đây cũng là một cách thay thế cho việc sử dụng void (0) có nghĩa là cho cùng một lý do: void (0) là một cách an toàn để có được giá trị không xác định.
glmxndr

"Những gì bạn nói" có liên quan đến câu hỏi khác này: stackoverflow.com/questions/4945265/ Lời
gnarf

@gnarf, cảm ơn vì thông báo rằng các câu hỏi đã được hợp nhất. Tôi sẽ chỉnh sửa câu trả lời của mình để có ý nghĩa hơn một chút;)
David Tang

16

Những người khác đã giải thích undefined. undefinedgiống như một biến toàn cục có thể được xác định lại thành bất kỳ giá trị nào. Kỹ thuật này là để ngăn chặn tất cả các kiểm tra không xác định bị phá vỡ nếu ai đó viết nói, undefined = 10ở đâu đó. Một đối số không bao giờ được thông qua được đảm bảo là thực tế undefinedbất kể giá trị của biến undefined .

Lý do để vượt qua cửa sổ có thể được minh họa bằng ví dụ sau.

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

Bảng điều khiển đăng nhập cái gì? Giá trị của windowđối tượng phải không? Sai lầm! 10? Sai lầm! Nó đăng nhập undefined. Trình thông dịch Javascript (hoặc trình biên dịch JIT) viết lại theo cách này -

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

Tuy nhiên, nếu bạn lấy windowbiến làm đối số, không có var và do đó không có gì bất ngờ.

Tôi không biết jQuery có làm việc đó không, nhưng nếu bạn định nghĩa lại windowbiến cục bộ ở bất kỳ đâu trong hàm của bạn vì bất kỳ lý do gì, thì nên mượn nó từ phạm vi toàn cầu.


6

windowđược truyền vào như thế chỉ trong trường hợp ai đó quyết định xác định lại đối tượng cửa sổ trong IE, tôi giả sử như vậy undefined, trong trường hợp nó được gán lại theo một cách nào đó sau này.

Phần đầu windowtrong tập lệnh đó chỉ là đặt tên cho đối số "cửa sổ", một đối số mang tính cục bộ hơn là windowtham chiếu toàn cầu và mã mà bên trong bao đóng này sẽ sử dụng. Các windowcuối cùng là thực sự xác định những gì để vượt qua cho các đối số đầu tiên, trong trường hợp này có nghĩa là hiện tại của window... hy vọng là bạn chưa screwed up windowtrước khi điều đó xảy ra.

Điều này có thể dễ dàng hơn để nghĩ về bằng cách hiển thị các trường hợp điển hình nhất được sử dụng trong jQuery, cắm .noConflict()xử lý, vì vậy đối với đa số mã bạn vẫn có thể sử dụng $, thậm chí nếu nó có nghĩa là một cái gì đó khác hơn so với jQuerybên ngoài phạm vi này:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);

Cảm ơn. Điều này làm cho rất nhiều ý nghĩa. Mặc dù vậy, tôi nghĩ rằng câu trả lời là sự kết hợp giữa điều này và những gì @ C.Zakas nói.
dkinzer

@DKinzer Tôi sợ tôi không phải C. Zakas;)
Vincent

@Vincent, xin lỗi về điều đó :-)
dkinzer

5

Đã thử nghiệm với 1000000 lần lặp. Kiểu nội địa hóa này không có tác dụng trong hiệu suất. Thậm chí không một mili giây trong 1000000 lần lặp. Điều này chỉ đơn giản là vô dụng.


2
Chưa kể, bao đóng mang tất cả các biến cùng với thể hiện của hàm, vì vậy nếu bạn có 100 byte trong bao đóng và 1000 thể hiện là 100kb bộ nhớ.
Akash Kava

@AkashKava và @Semra, tôi không nghĩ bài kiểm tra này thực sự giải thích lý do tại sao bạn sẽ vượt qua cửa sổ trong một tình huống thực tế. Việc tăng hiệu suất sẽ chỉ được nhìn thấy khi bạn có các bao đóng lồng nhau truy cập sâu vào biến đó dọc theo chuỗi phạm vi. rõ ràng nếu hàm của bạn chỉ cách một bước trong chuỗi phạm vi từ biến, bạn sẽ không thấy được nhiều lợi ích. nhưng nếu nó ở dưới đó và cần thiết để windowbạn có thể thấy một số lợi ích của một bản sao địa phương.
gabereal

@gabereal Các công cụ JavaScript mới giải quyết các lần đóng tại thời điểm biên dịch chính nó, không có hiệu suất tăng khi đóng khi chạy. Tôi nghi ngờ có bất kỳ mức tăng hiệu suất có thể đo lường được, nếu bạn rất tự tin, bạn có thể tạo ví dụ jsperf để chứng minh hiệu suất. Hiệu suất duy nhất chúng tôi có được là bằng cách cô lập các bao đóng dẫn đến việc thu nhỏ tốt hơn giúp giảm kích thước và hiệu suất đạt được bằng cách tải xuống và phân tích cú pháp nhanh hơn.
Akash Kava

@AkashKava điều gì khiến bạn nghĩ rằng tôi rất tự tin? Tôi chỉ nói "có thể thấy một số lợi ích" và "tôi không nghĩ". Tôi có thể tạo một bài kiểm tra, nhưng tôi không muốn vì tôi không bao giờ sử dụng mẫu này. bạn có thể giải thích ý của bạn bằng cách "giải quyết các lần đóng cửa tại thời điểm biên dịch chính nó" không? trái ngược với những gì thay thế? Làm thế nào các công cụ Javascript làm những điều khác nhau trước đây?
gabereal

Có một mã tìm cửa sổ ở cuối cửa sổ nhưng không được nhập vào các tham số của hàm, điều đó có nghĩa là gì? nó đảm bảo rằng các tham số tuân theo đối tượng phạm vi toàn cầu ban đầu ngay cả khi không có bất kỳ thông số cửa sổ nào tôi giả sử?
Nữ web
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.