Dấu chấm than làm gì trước hàm?


1242
!function () {}();

4
@Dykam Tính hữu dụng của nó được giải thích trong câu trả lời này: stackoverflow.com/questions/5422585/ Lời
hectorct



7
@befzz Tốt hơn nên đề cập đến điều này như là một biểu thức chức năng được gọi ngay lập tức, vì bài viết đó sau đó giải thích ("tự thực thi" ngụ ý đệ quy)
Zach Esposito

Câu trả lời:


2118

Cú pháp JavaScript 101. Đây là một khai báo hàm :

function foo() {}

Lưu ý rằng không có dấu chấm phẩy: đây chỉ là một khai báo hàm . Bạn sẽ cần một lời gọi foo(), để thực sự chạy chức năng.

Bây giờ, khi chúng ta thêm dấu chấm than dường như vô hại: !function foo() {}nó biến nó thành một biểu thức . Bây giờ nó là một biểu thức chức năng .

Tất nhiên, !một mình không gọi hàm, nhưng bây giờ chúng ta có thể đặt ()ở cuối: !function foo() {}()có độ ưu tiên cao hơn !và gọi hàm ngay lập tức.

Vì vậy, những gì tác giả đang làm là lưu một byte cho mỗi biểu thức hàm; một cách dễ đọc hơn sẽ là thế này:

(function(){})();

Cuối cùng, !làm cho biểu thức trở lại đúng. Điều này là do theo mặc định tất cả trở lại IIFE undefined, mà lá chúng tôi với !undefinedđó là true. Không đặc biệt hữu ích.


229
+1. Đây thực sự là câu trả lời tốt nhất ở đây, và thật đáng buồn, hầu như không được nêu lên. Rõ ràng, !trả về boolean, tất cả chúng ta đều biết điều đó, nhưng điểm tuyệt vời mà bạn đưa ra là nó cũng chuyển đổi câu lệnh khai báo hàm thành biểu thức hàm để hàm có thể được gọi ngay lập tức mà không cần gói nó trong ngoặc đơn. Không rõ ràng, và rõ ràng ý định của lập trình viên.
gilly3

64
+1 Đây là câu trả lời duy nhất thực sự giải quyết TẠI SAO bạn muốn làm điều này và tại sao người ta thấy nó được sử dụng nhiều hơn sự phủ định của kết quả trả về dường như sẽ được bảo đảm. Các nhà điều hành unary! (cũng ~, - và +) phân biệt từ một khai báo hàm và cho phép các parens ở cuối () gọi hàm tại chỗ. Điều này thường được thực hiện để tạo một phạm vi / không gian tên cục bộ cho các biến khi viết mã mô-đun.
Tom Auger

65
Một lợi ích khác là vậy! gây ra chèn nửa dấu hai chấm, do đó, phiên bản này không thể được nối sai với một tệp không kết thúc bằng; Nếu bạn có dạng (), nó sẽ coi đó là một hàm gọi của bất cứ thứ gì được định nghĩa trong tệp trước đó. Mẹo đội mũ cho đồng nghiệp của tôi.
Jure Triglav

5
@Carnix var foo =phá vỡ sự mơ hồ của câu lệnh / biểu thức và bạn có thể viết một cách đơn giản, var foo = function(bar){}("baz");v.v.
Neil

6
Điều này thường được thực hiện bằng các tập lệnh thu nhỏ / làm mờ, trong đó mỗi byte đều được tính.
Dima Slivin

367

Chức năng:

function () {}

trả về không có gì (hoặc không xác định).

Đôi khi chúng ta muốn gọi một chức năng ngay khi chúng ta tạo ra nó. Bạn có thể bị cám dỗ để thử điều này:

function () {}()

nhưng nó kết quả trong một SyntaxError.

Sử dụng !toán tử trước hàm làm cho nó được coi là một biểu thức, vì vậy chúng ta có thể gọi nó:

!function () {}()

Điều này cũng sẽ trở ngược lại boolean của giá trị trả về của hàm, trong trường hợp này true, vì !undefinedtrue. Nếu bạn muốn giá trị trả về thực tế là kết quả của cuộc gọi, thì hãy thử thực hiện theo cách này:

(function () {})()

28
Ai có thể cần cái này?
Andrey

13
đây là câu trả lời duy nhất giải thích trường hợp trong câu hỏi, bravo!
Andrey

14
Mẫu mã thứ hai của bạn không hợp lệ JavaScript. Mục đích của việc !là biến khai báo hàm thành biểu thức hàm, chỉ vậy thôi.
Skilldrick

8
@Andrey twitter bootstrap sử dụng điều này trong tất cả các tệp plugin javascript (jQuery). Thêm bình luận này chỉ trong trường hợp những người khác cũng có thể có cùng một câu hỏi.
Anmol Saraf

2
d3.js cũng sử dụng !functioncú pháp
Kristian

65

Có một điểm tốt để sử dụng !cho việc gọi hàm được đánh dấu trên hướng dẫn JavaScript của airbnb

Nói chung ý tưởng cho việc sử dụng kỹ thuật này trên các tệp riêng biệt (còn gọi là mô-đun) mà sau này được nối lại. Thông báo trước ở đây là các tệp được cho là được nối bởi các công cụ đưa tệp mới vào dòng mới (đây là hành vi phổ biến đối với hầu hết các công cụ concat). Trong trường hợp đó, việc sử dụng !sẽ giúp tránh lỗi nếu mô-đun được nối trước đó bỏ qua dấu chấm phẩy, và điều đó sẽ giúp bạn linh hoạt đặt chúng theo bất kỳ thứ tự nào mà không phải lo lắng.

!function abc(){}();
!function bca(){}();

Sẽ hoạt động giống như

!function abc(){}();
(function bca(){})();

nhưng lưu một ký tự và tùy ý trông tốt hơn.

Và bằng cách này bất kỳ +, -, ~, voidkhai thác có hiệu quả tương tự, về cách gọi các chức năng, chắc chắn nếu bạn phải sử dụng một cái gì đó để trở về từ chức năng rằng họ sẽ đóng vai trò khác nhau.

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

nhưng nếu bạn sử dụng các mẫu IIFE cho một tệp một mã tách mô-đun và sử dụng công cụ concat để tối ưu hóa (làm cho một dòng một công việc tệp), thì hãy xây dựng

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

Sẽ thực hiện mã an toàn, giống như một mẫu mã đầu tiên.

Điều này sẽ gây ra lỗi vì JavaScript ASI sẽ không thể thực hiện công việc của nó.

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

Một lưu ý liên quan đến các toán tử đơn nguyên, chúng sẽ thực hiện công việc tương tự, nhưng chỉ trong trường hợp, chúng không được sử dụng trong mô-đun đầu tiên. Vì vậy, chúng không an toàn nếu bạn không có toàn quyền kiểm soát thứ tự nối.

Những công việc này:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

Không phải cái này:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()

3
Trên thực tế, những biểu tượng khác không có tác dụng tương tự. Có, họ cho phép bạn gọi một chức năng như được mô tả, nhưng chúng không giống nhau. Hãy xem xét: var foo =! Function (bar) {console.debug (bar); }("con dơi"); Bất kể biểu tượng nào bạn đặt ở phía trước, bạn sẽ nhận được "con dơi" trong bảng điều khiển. Bây giờ, thêm console.debug ("foo:", foo); - bạn nhận được kết quả rất khác nhau dựa trên biểu tượng bạn sử dụng. ! buộc một giá trị trả lại mà không phải luôn luôn mong muốn. Tôi thích cú pháp ({}) () cho sự rõ ràng và chính xác.
Carnix

29

Nó trả về việc tuyên bố có thể đánh giá thành sai. ví dụ:

!false      // true
!true       // false
!isValid()  // is not valid

Bạn có thể sử dụng nó hai lần để ép buộc một giá trị thành boolean:

!!1    // true
!!0    // false

Vì vậy, để trả lời trực tiếp hơn câu hỏi của bạn:

var myVar = !function(){ return false; }();  // myVar contains true

Chỉnh sửa: Nó có tác dụng phụ là thay đổi khai báo hàm thành biểu thức hàm. Ví dụ: đoạn mã sau không hợp lệ vì nó được hiểu là một khai báo hàm thiếu định danh bắt buộc (hoặc tên hàm ):

function () { return false; }();  // syntax error

6
Để rõ ràng cho những độc giả có thể muốn sử dụng một phép gán với hàm được gọi ngay lập tức, mã ví dụ của bạn var myVar = !function(){ return false; }()có thể bỏ qua lượt !thích var myVar = function(){ return false; }()và hàm sẽ thực thi chính xác và giá trị trả về sẽ không được chạm tới.
Đánh dấu Fox

1
Để rõ ràng, bạn có thể sử dụng nó một lần để ép buộc Boolean, bởi vì đó là một toán tử không logic . ! 0 = đúng và! 1 = sai. Đối với mục đích thu nhỏ JavaScript, bạn muốn thay thế truebằng !0falsebằng !1. Nó tiết kiệm 2 hoặc 3 ký tự.
Triynko

9

Nó chỉ để lưu một byte dữ liệu khi chúng tôi thực hiện thu nhỏ javascript.

xem xét chức năng ẩn danh dưới đây

function (){}

Để thực hiện chức năng tự gọi ở trên, chúng ta thường sẽ thay đổi mã ở trên là

(function (){}())

Bây giờ chúng tôi đã thêm hai ký tự (,)ngoài việc thêm ()vào cuối hàm cần gọi hàm. Trong quá trình thu nhỏ, chúng tôi thường tập trung để giảm kích thước tệp. Vì vậy, chúng ta cũng có thể viết các chức năng trên như

!function (){}()

Cả hai đều là các hàm tự gọi và chúng tôi cũng lưu một byte. Thay vì 2 ký tự, (,)chúng tôi chỉ sử dụng một ký tự!


1
Điều này rất hữu ích vì thường bạn sẽ thấy điều này trong js rút gọn
Đối với Tên

5

! là một toán tử KHÔNG logic , nó là một toán tử boolean sẽ đảo ngược một cái gì đó ngược lại.

Mặc dù bạn có thể bỏ qua dấu ngoặc đơn của hàm được gọi bằng cách sử dụng BANG (!) Trước hàm, nó vẫn sẽ đảo ngược trả về, có thể không phải là điều bạn muốn. Như trong trường hợp của IEFE, nó sẽ trả về không xác định , khi đảo ngược trở thành boolean true.

Thay vào đó, hãy sử dụng dấu ngoặc đơn đóng và BANG ( ! ) Nếu cần.

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

Các nhà khai thác khác làm việc ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

Toán tử kết hợp ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1

5

Dấu chấm than làm cho bất kỳ chức năng nào luôn trả về một boolean.
Giá trị cuối cùng là phủ định giá trị được trả về bởi hàm.

!function bool() { return false; }() // true
!function bool() { return true; }() // false

Bỏ qua !trong các ví dụ trên sẽ là một SyntaxError .

function bool() { return true; }() // SyntaxError

Tuy nhiên, một cách tốt hơn để đạt được điều này sẽ là:

(function bool() { return true; })() // true

Điều này là không chính xác. !thay đổi cách thời gian chạy phân tích hàm. Nó làm cho bộ thực thi coi hàm như một biểu thức hàm (và không phải là khai báo). Nó thực hiện điều này để cho phép nhà phát triển gọi hàm ngay lập tức bằng ()cú pháp. !cũng sẽ áp dụng chính nó (tức là phủ định) vào kết quả của việc gọi biểu thức hàm.
Ben Aston

3

Một cách khác để viết IIFE (biểu thức hàm được gọi ngay lập tức).

Cách viết khác của nó -

(function( args ) {})()

giống như

!function ( args ) {}();

Chà, nó không hoàn toàn giống nhau; dạng thứ 2 phủ nhận kết quả của lệnh gọi hàm (và sau đó ném nó đi, vì không có giá trị gán). Tôi hoàn toàn thích (function (args) {...})()cú pháp rõ ràng hơn và để !functionbiểu mẫu đó cho các công cụ thu nhỏ và mã hóa.
Tobias

-1

! sẽ phủ nhận (ngược lại) bất cứ điều gì bạn mong đợi như kết quả, tức là nếu bạn có

var boy = true;
undefined
boy
true
!boy
false

Khi bạn gọi boy, kết quả của bạn sẽ là true, nhưng thời điểm bạn thêm !khi gọi boy, tức là !boykết quả của bạn sẽ có false. Nói cách khác, ý bạn là NotBoy , nhưng lần này về cơ bản nó là kết quả boolean, truehoặc false.

Đó là điều tương tự xảy ra với !function () {}();biểu thức, chỉ chạy function () {}();sẽ đánh dấu lỗi của bạn, nhưng thêm !ngay trước function () {}();biểu thức của bạn , làm cho nó ngược lại với biểu thức function () {}();sẽ trả về bạn true. Ví dụ có thể được nhìn thấy dưới đây:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
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.