Câu hỏi Trong JavaScript, lợi thế của hàm () {} () trên (function () {}) () là gì? [bản sao]


Có thể trùng lặp:
Dấu chấm than làm gì trước hàm? 

Tôi đã sử dụng từ lâu để tự thực hiện, các chức năng ẩn danh trong JavaScript:

(function () { /* magic happens */ })()

Gần đây, tôi đã bắt đầu thấy nhiều trường hợp của mẫu sau (ví dụ: trong Bootstrap):

!function () { /* presumably the same magic happens */ }()

Bất cứ ai biết lợi thế của mô hình thứ hai là gì? Hoặc, nó chỉ là một sở thích phong cách?


76
2017-09-28 17:02


gốc


Xem bài này nó giải thích sự khác biệt giữa hai: stackoverflow.com/questions/3755606/… - Clive
Câu trả lời này đặc biệt stackoverflow.com/questions/3755606/… - Michael Jasper
Tôi muốn nói câu trả lời này: stackoverflow.com/questions/3755606/… - Ivar Bonsaksen
Chúng dành cho mục đích này (buộc đánh giá một hàm trong ngữ cảnh biểu thức, gọi nó ngay lập tức bỏ qua giá trị trả lại của nó) tương đương, nhưng IMHO I cảm thấy sử dụng Toán tử nhóm (dấu ngoặc đơn) là nhiều hơn "ngữ nghĩa chính xác" (và có thể phổ biến hơn và dễ đọc hơn), bởi vì đó là mục đích của nhà điều hành này, đánh giá các biểu thức ... Chỉ cần hai xu của tôi ... Chúc mừng! - CMS
Tôi nghĩ rằng thật đáng buồn rằng điều này đã bị đóng như một bản sao - câu hỏi này và tất cả các câu trả lời của nó là tốt hơn nhiều so với bất cứ điều gì trong bản sao. - Skilldrick


Các câu trả lời:


Hai kỹ thuật khác nhau này có chức năng Sự khác biệt cũng như sự khác biệt về ngoại hình. Những lợi thế tiềm năng của một kỹ thuật khác sẽ là do những khác biệt này.

Concision

Javascript là một ngôn ngữ nơi vết rạch có thể rất quan trọng vì Javascript là được tải xuống khi tải trang. Điều đó có nghĩa là Javascript ngắn gọn hơn, thời gian tải xuống càng nhanh. Vì lý do này, có Javascript minifiers và obfuscators để nén các tệp Javascript để tối ưu hóa thời gian tải xuống. Ví dụ: các khoảng trống trong alert ( "Hi" ) ; sẽ được tối ưu hóa thành alert("Hi");.

Lưu ý điều này, so sánh hai mẫu này

  • Bình thường đóng cửa: (function(){})()  16 ký tự
  • Phủ nhận đóng cửa: !function(){}()  15 ký tự

Đây là một tối ưu hóa vi mô ở mức tốt nhất, vì vậy tôi không thấy đây là một đối số đặc biệt hấp dẫn trừ khi bạn đang thực hiện mã golf Cuộc thi.

Bỏ qua giá trị trả lại

So sánh giá trị kết quả của a và b.

var a = (function(){})()
var b = !function(){}()

Kể từ khi a chức năng không trả lại bất cứ điều gì, a sẽ là undefined. Kể từ khi phủ nhận undefined Là true, b sẽ đánh giá true. Đây là một lợi thế cho những người muốn phủ nhận giá trị trả về của hàm hoặc có một fetish-tất cả mọi thứ-phải-trả-một-không-null-hoặc-undefined-value. Bạn có thể xem giải thích về cách hoạt động của tính năng này câu hỏi Stack Overflow khác.

Tôi hy vọng rằng điều này sẽ giúp bạn hiểu được lý do đằng sau việc khai báo hàm này thường được coi là một chống mẫu.


78
2017-09-28 17:11



Nếu ý định chỉ là để thực hiện hàm (đó là cách tôi sử dụng nó), sau đó quay trở lại undefined thực sự cảm thấy chính xác hơn với tôi. - Andrew Hedges
@AndrewHedges Tôi sẽ đồng ý. - Peter Olson
Tôi đã cho bạn một upvote, nhưng tôi có một số rắc rối với 'concision' ... - kennebec
Cố gắng làm cho mã nguồn Javascript ngắn gọn chỉ có vẻ giống như một công thức cho sự nhầm lẫn. Nó phải càng rõ ràng càng tốt - sự tồn tại của câu hỏi này làm cho nó khá rõ ràng rằng có một nhu cầu rất lớn về sự rõ ràng trong mã JS! Nó có thể được truyền qua một trong các bộ khai thác mà bạn tham chiếu như là một phần của quá trình xây dựng. - intuited
@intuited Right. Như tôi đã nói, đây có lẽ là một tối ưu hóa vi mô vô dụng. Điều đó nói rằng, minifiers sẽ không thực hiện điều này bởi vì nó thay đổi hành vi của hàm và có thể gây ra mã để phá vỡ. - Peter Olson


Tôi luôn rơi vào Phần Alte IIFE của Ben Alman cho các câu hỏi như thế này. Đó là dứt khoát như xa như tôi đang quan tâm.

Đây là thịt của bài viết:

// Either of the following two patterns can be used to immediately invoke
// a function expression, utilizing the function's execution context to
// create "privacy."

(function(){ /* code */ }()); // Crockford recommends this one
(function(){ /* code */ })(); // But this one works just as well

// Because the point of the parens or coercing operators is to disambiguate
// between function expressions and function declarations, they can be
// omitted when the parser already expects an expression (but please see the
// "important note" below).

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

// If you don't care about the return value, or the possibility of making
// your code slightly harder to read, you can save a byte by just prefixing
// the function with a unary operator.

!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

// Here's another variation, from @kuvos - I'm not sure of the performance
// implications, if any, of using the `new` keyword, but it works.
// http://twitter.com/kuvos/status/18209252090847232

new function(){ /* code */ }
new function(){ /* code */ }() // Only need parens if passing arguments

51
2017-09-28 17:19



Ooh tuyệt đó. Tôi đã không thấy điều đó. Cảm ơn! - Andrew Hedges
Ugh, javascript là một mớ hỗn độn. - BlueRaja - Danny Pflughoeft
JS không phải là một mớ hỗn độn, nó thực sự khá mạnh mẽ - nó chỉ có một số cú pháp ít hơn khá đẹp tại các điểm. ;) - Brian Arnold Sinclair
Với tất cả các cú pháp bổ sung cần nhớ (có, đúng, on..do ... false ..no, false, off), tôi sẽ cho rằng CoffeeScript phức tạp hơn Javascript. Mặc dù CoffeeScript có ít ký tự hơn nhưng nó không đơn giản hơn. gist.github.com/1248911 - William
Nó tiết kiệm được 1 byte nhưng tôi nghĩ nó cũng có thể là một thứ thẩm mỹ. Tôi không nghĩ ! là khá nhưng tôi nghĩ rằng nó ít xấu xí hơn so với lồng nhau ((){}()) kết cấu... - Chris Burt-Brown


Dường như điều quan trọng là bạn về cơ bản giữ cho trình phân tích cú pháp giải thích hàm như là một khai báo hàm, và thay vào đó nó được hiểu là một biểu thức hàm ẩn danh.

Sử dụng các parens để nhóm các biểu thức hoặc sử dụng! để phủ nhận lợi tức là cả hai chỉ là kỹ thuật thay đổi phân tích cú pháp. Sau đó nó ngay lập tức được gọi bởi các parens sau. Bất kỳ và tất cả các biểu mẫu này đều có cùng hiệu ứng ròng trong vấn đề đó, giả sử không có giá trị trả về rõ ràng:

(function(){ /* ... */ })(); // Arguably most common form, => undefined
(function(){ /* ... */ }()); // Crockford-approved version, => undefined
!function(){ /* ... */ }();  // Negates the return, so => true
+function(){ /* ... */ }();  // Attempts numeric conversion of undefined, => NaN
~function(){ /* ... */ }();  // Bitwise NOT, => -1

Nếu bạn không nắm bắt được giá trị trả về, không có sự khác biệt đáng kể nào. Người ta có thể lập luận rằng ~ có thể là một op nhanh hơn vì nó chỉ lật bit, hoặc có thể! là một op nhanh hơn vì nó là một kiểm tra đúng / sai và trả về phủ định.

Tuy nhiên, vào cuối ngày, cách mà hầu hết mọi người đang sử dụng mẫu này là họ đang cố gắng phá vỡ một phạm vi mới để giữ cho mọi thứ sạch sẽ. Bất kỳ và tất cả công việc. Các hình thức sau được phổ biến bởi vì trong khi họ giới thiệu một hoạt động bổ sung (thường không cần thiết), tiết kiệm mỗi byte phụ giúp.

Ben Alman có một bài viết tuyệt vời về chủ đề này: http://benalman.com/news/2010/11/immediately-invoked-function-expression/


14
2017-09-28 17:21





"Mẫu" đầu tiên gọi hàm ẩn danh (và có kết quả của giá trị trả về) trong khi hàm thứ hai gọi hàm ẩn danh và phủ nhận kết quả của nó.

Đó là những gì bạn đang yêu cầu? Họ làm không phải Làm điều tương tự.


4
2017-09-28 17:07





Nó là hầu hết chỉ có sở thích về phong cách, ngoại trừ thực tế là ! cung cấp một hàm trả về (tức là trả về true, xuất phát từ !undefined).

Ngoài ra, đó là một nhân vật ít hơn.


4
2017-09-28 17:10





Trong trường hợp đầu tiên bạn đang sử dụng ( ) để bọc đối tượng bạn muốn thực thi với tập hợp tiếp theo (), và trong trường hợp tiếp theo, bạn đang sử dụng toán tử nhận một đối số (toán tử phủ định!) và bạn đang làm cho nó ngầm bao bọc đối số của nó (funcion) với ( ) vì vậy bạn thực sự nhận được !(function () { })(), thực thi hàm và phủ nhận kết quả nó trả về. Điều này cũng có thể làm việc với -, +, ~ trong cùng một nguyên tắc vì tất cả những người vận hành đó đều có một đối số.

!function () { /* presumably the same magic happens */ }()
-function () { /* presumably the same magic happens */ }()
+function () { /* presumably the same magic happens */ }()
~function () { /* presumably the same magic happens */ }()

Tại sao bạn muốn làm điều này? Tôi đoán nó là một sở thích cá nhân hoặc nếu bạn có lớn .JS và muốn tiết kiệm 1 char cho mỗi cuộc gọi chức năng nặc danh ...: D


1
2017-09-28 17:20