Những gì làm!


376

Tôi có đoạn mã này (lấy từ câu hỏi này ):

var walk = function(dir, done) {
    var results = [];

    fs.readdir(dir, function(err, list) {
        if (err)
            return done(err);

        var pending = list.length;

        if (!pending) 
            return done(null, results);

        list.forEach(function(file) {
            file = path.resolve(dir, file);
            fs.stat(file, function(err, stat) {
                if (stat && stat.isDirectory()) {
                    walk(file, function(err, res) {
                        results = results.concat(res);

                        if (!--pending)
                            done(null, results);
                    });
                } else {
                    results.push(file);

                    if (!--pending) 
                        done(null, results);
                }
            });
        });
    });
};

Tôi đang cố gắng theo dõi nó, và tôi nghĩ rằng tôi hiểu tất cả mọi thứ ngoại trừ gần cuối nơi nó nói !--pending. Trong bối cảnh này, lệnh đó làm gì?

Chỉnh sửa: Tôi đánh giá cao tất cả các ý kiến ​​thêm, nhưng câu hỏi đã được trả lời nhiều lần. Dù sao cũng cảm ơn bạn!


222
Đó là một cách tuyệt vời để gây nhầm lẫn cho người tiếp theo để duy trì mã.
Eric J.

240
!~--[value] ^ trueTôi gọi mã như thế này là "bảo mật công việc"
TbWill4321

63

36
@ TbWill4321 Nếu tôi đang thực hiện đánh giá mã, nó sẽ hoàn toàn ngược lại với bảo mật công việc.
corsiKa

8
Các toán tử kết hợp với tên biến làm cho không quá tệ. Bất kỳ lập trình viên Javascript dày dạn nào cũng sẽ không cần nhiều hơn một vài giây để biết nó làm gì.
Christiaan Westerbeek

Câu trả lời:


537

! đảo ngược một giá trị và cung cấp cho bạn boolean ngược lại:

!true == false
!false == true
!1 == false
!0 == true

--[value] trừ một (1) từ một số và sau đó trả về số đó sẽ được làm việc với:

var a = 1, b = 2;
--a == 0
--b == 1

Vì vậy, !--pendingtrừ đi một trong số đó đang chờ xử lý, và sau đó trả về giá trị ngược lại với giá trị trung thực / giả của nó (cho dù nó có hay không 0).

pending = 2; !--pending == false 
pending = 1; !--pending == true
pending = 0; !--pending == false

Và vâng, làm theo ProTip. Đây có thể là một thành ngữ phổ biến trong các ngôn ngữ lập trình khác, nhưng đối với hầu hết lập trình JavaScript khai báo, điều này trông khá xa lạ.


623
ProTip ™: Không bao giờ làm điều này trong mã của bạn, thật nực cười.
Naftuli Kay

17
Điều này không đề cập rằng --chỉ hành động trên các biến; bạn không thể áp dụng nó cho các giá trị nói chung.
deltab

10
@deltab: validSimpleAssignmentTargets, chính xác. Điều đó bao gồm tài liệu tham khảo định danh và tài liệu tham khảo tài sản.
Bergi

19
--0--1sẽ không làm việc. Chữ số không phải là biểu thức bên trái hợp lệ
PSWai

7
@Pharap: Uh, tôi gặp nhiều rắc rối khi phân tích i > -1thứ đó hơn i--(và btw bạn đã quên i = init() - 1). Đó là những thành ngữ dành cho người chơi và mọi lập trình viên nên học chúng.
Bergi

149

Đó không phải là một toán tử đặc biệt, lần lượt là 2 toán tử tiêu chuẩn:

  1. Một tiền tố giảm ( --)
  2. Một logic không ( !)

Điều này gây ra pendingsự suy giảm và sau đó được kiểm tra xem nó có bằng không.


8
Tôi chắc chắn là mới đối với điều này, nhưng tại sao điều này là vượt trội so với việc sử dụng if(pending === 1)?
Kieran E

46
@KieranE, không phải vậy, nó là viết tắt hoàn toàn phá vỡ quy tắc "khả năng đọc là vua".
Ryan

23
@KieranE không phải là vượt trội, nó khác biệt. Nó biến đổi biến số (giảm đi 1) và sau đó sử dụng giá trị mới để kiểm tra
Amit

7
@KieranE, nó tương đương với if( pending === 1 ) { done... } else { pending = pending - 1; }.
CompuChip

7
@CompuChip Nó thực sự trông giống như - việc chi tiêu sẽ được thực hiện trong mọi trường hợp, vì vậy mã giống như: if (! Đang chờ xử lý) {--pending; v.v.} khác {--pending; v.v.} Điều này dựa trên developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Kẻ
Paul Russell

109

Một số câu trả lời mô tả những gì lệnh này làm, nhưng không phải tại sao nó được thực hiện theo cách đó ở đây.

Tôi đến từ thế giới C và tôi đọc !--pendinglà "đếm ngược pendingvà kiểm tra xem nó có bằng không" mà không thực sự nghĩ về nó. Đó là một thành ngữ mà tôi nghĩ rằng các lập trình viên trong các ngôn ngữ tương tự nên biết.

Hàm này sử dụng readdirđể lấy danh sách các tệp và thư mục con mà tôi sẽ gọi chung là "mục".

Biến pendingtheo dõi số lượng còn lại sẽ được xử lý. Nó bắt đầu bằng độ dài của danh sách và đếm xuống 0 khi mỗi mục được xử lý.

Các mục này có thể được xử lý theo thứ tự, đó là lý do tại sao cần phải đếm ngược thay vì chỉ sử dụng một vòng lặp đơn giản. Khi tất cả các mục đã được xử lý, cuộc gọi lại doneđược gọi để thông báo cho người gọi ban đầu về thực tế này.

Trong cuộc gọi đầu tiên doneđược chuẩn bị trước return, không phải vì chúng tôi muốn trả về một giá trị, mà chỉ đơn giản là làm cho hàm dừng thực thi tại thời điểm đó. Nó sẽ là mã sạch hơn để bỏ returnvà đặt thay thế trong một else.


13
@Bergi nó là một thành ngữ nổi tiếng trong thế giới C, nhưng nó không phải là thành ngữ Javascript. Đối với bất kỳ biểu hiện phụ nào, các hệ sinh thái khác nhau sẽ có nhiều thành ngữ khác nhau, không tương thích, làm thế nào "nó được thực hiện" ở đó. Sử dụng thành ngữ từ các ngôn ngữ 'nước ngoài' là một mùi mã khá xấu.
Peteris

3
@Peteris: Đây là một thành ngữ trong tất cả các ngôn ngữ giống như C có toán tử giảm dần. Tất nhiên, đôi khi có những cách khác nhau, tốt hơn có sẵn trong một ngôn ngữ, nhưng tôi không nghĩ rằng JS có bất kỳ thành ngữ đếm đặc biệt nào.
Bergi

6
Có một phần đáng kể của cộng đồng Javascript, bao gồm các nhà lãnh đạo có ảnh hưởng như Douglas Crockford, chủ trương tránh hoàn toàn các toán tử tăng và giảm đơn phương . Tôi sẽ không tranh luận ở đây về việc họ có đúng hay không; Mục đích của tôi chỉ là chỉ ra rằng vì tranh cãi tồn tại, mã như thế !--pendingsẽ thất bại nhiều nguyên tắc và phong cách. Điều đó đủ để tôi nói rằng nó có lẽ không phải là thành ngữ (bất kể sự thay thế là "tốt hơn").
GrandOpener

3
@GrandOpener "Thành ngữ" có nghĩa là "một thành ngữ thường được người bản ngữ hiểu". Dự đoán chắc chắn được hiểu rõ đối với người dùng ngôn ngữ giống như C và, bằng cách mở rộng, "! (- x)" cũng vậy. Có hay không sử dụng một cái gì đó là một ý tưởng tốt® là một câu hỏi riêng biệt hoàn toàn với việc nó là thành ngữ như thế nào. . trong số tám tội lỗi chết người.)
jmbpiano

3
@Pharap Đó là vì C # và Java không chuyển đổi intthành boolngầm định và hoàn toàn không liên quan gì đến việc sử dụng !--.
Agop

36

Đó là một tốc ký.

! không phải".

-- giảm giá trị

Vì vậy, !--kiểm tra nếu giá trị thu được từ việc phủ định kết quả giảm giá trị là sai.

Thử cái này:

var x = 2;
console.log(!--x);
console.log(!--x);

Đầu tiên là sai, vì giá trị của x là 1, thứ hai là đúng, vì giá trị của x là 0.

Lưu ý bên: !x--sẽ kiểm tra nếu x là sai trước, và sau đó giảm nó.


RE ghi chú bên - bạn có chắc không? Có vẻ như sửa chữa sau có ưu tiên cao hơn !, trong khi sửa chữa trước có ưu tiên thấp hơn. Ưu tiên toán tử JavaScript
Dannnno

2
Đúng. (Thử var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);). Trong khi sửa lỗi sau --có thể thực thi đầu tiên, giá trị trả về của nó là giá trị của biến trước khi giảm dần (Bộ giảm tốc Opperator ).
Lucas

Tôi nghĩ bạn có ý muốn nói " !--trả về sự phủ định kết quả của việc giảm giá trị." Chỉ có mã bên ngoài, chẳng hạn như console.log(), kiểm tra xem nội dung của nó có đúng không.
mareoraft

31

!là toán tử JavaScript KHÔNG

--là một toán tử giảm trước. Vì thế,

x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
          // which makes the condition to be true

8
WTH là một "tuyên bố sai lệch"?
Bergi

5
@Bergi Tôi giả sử bạn thực sự biết ý nghĩa của nó, nhưng trong trường hợp bạn không (hoặc bất kỳ ai khác không) thì đây là một lời giải thích cho JavaScript cũng dịch tốt phần nào sang các ngôn ngữ khác có khái niệm này (chẳng hạn như Python).
Dannnno

1
@Pharap nó không phải là câu trả lời của tôi, vì vậy tôi sẽ không chạm vào nó.
Dannnno

2
@Dannnno: Vâng, tôi biết những gì falsy giá trị đang có và những gì tuyên bố là, và vì vậy tôi biết rằng không có điều như vậy giống như một "tuyên bố falsy" trong JS. Tôi cho rằng điều này không đề cập đến thuật ngữ logic .
Bergi

1
Tôi không hiểu làm thế nào các --nhà điều hành có thể làm việc với một hằng số ... có thể bạn có ý --xthay vì --0?
SJuan76

24
if(!--pending)

có nghĩa

if(0 == --pending)

có nghĩa

pending = pending - 1;
if(0 == pending)

4
đó là cách rõ ràng để viết mã. Tất nhiên, tôi thích if(0 == pending)vì tiềm năng cho lỗi chính tả. if(0 = pending)là một lỗi cú pháp. if(pending = 0)là một nhiệm vụ sẽ gây ra hành vi khó hiểu trong mã hoàn thành.
Theo Brinkman

3
@TheoBrinkman: thực sự if(0 = pending)không phải là lỗi cú pháp - nó phân tích cú pháp tốt - nhưng là Lỗi tham chiếu vì 0không thể gán được (ref ECMA-262 (lần thứ 6) giây 12,14.1).
hmakholm còn lại trên Monica

13

Đó không phải là toán tử theo sau bởi bộ khử trước tại chỗ.

Vì vậy, nếu pendinglà một số nguyên có giá trị 1:

val = 1;
--val; // val is 0 here
!val // evaluates to true

11

Nó chỉ giảm đi pendingmột và có được bổ sung logic (phủ định). Bổ sung logic của bất kỳ số nào khác 0 là false, với 0 là true.


Xin lưu ý rằng "phủ định" có ý nghĩa khác về số so với trên
booleans

1
Được rồi, tôi sẽ sử dụng một thuật ngữ khác. Không chắc chắn nếu điều này là tốt hơn mặc dù.
MinusFour

11

Giải trình

Đây là 2 toán tử, a !và a--

!--x 

Vì vậy, các số --giảm x 1, sau đó !trả về true nếu x bây giờ là 0 (hoặc NaN ...), sai nếu không. Bạn có thể đọc thành ngữ này một cái gì đó như "chúng tôi giảm x và nếu điều đó làm cho nó bằng không ..."

Nếu bạn muốn làm cho nó dễ đọc hơn, bạn có thể:

var x = 1
x = x - 1   
if(!x){ //=> true
    console.log("I understand `!--` now!") 
}
x //=> 0

Dùng thử:

/* This is an example of the above, you can read this, but it is not needed for !-- */function interactive(a){$("span.code").keydown(function(e){if(13==(e.keyCode||e.which)){var t=$(this);t.clone().html("code").insertAfter(t.next().next()).show().focus().after(template.clone().removeClass("result-template").show()).next().after("<br>"),interactive(),e.preventDefault()}}).keyup(function(e){13!=(e.keyCode||e.which)&&run()})}var template=$(".result-template").hide(),code=$("span.code");code.attr("contenteditable","true").each(function(e,t){template.clone().removeClass("result-template").insertAfter(t)}),interactive(),$.fn.reduce=[].reduce;function run(){var b=!1,context={};$("span.code").each(function(){var a=$(this),res=a.next().show().removeClass("error");try{with(context)res.html(b?"":"  //=> "+eval(a.text()))}catch(e){b=e,res.html("  Error: "+b.message).addClass("error")}})};run();
/* This is an example of the above, you can read this, but it is not needed for !-- */span.result.error{display:block;color:red}.code{min-width:10px}body{font-family:Helvetica,sans-serif}
<!-- This is an example of the above, you can read this, but it is not needed for `!--` --><span class="result result-template"> //=> unknown </span> <h2>Edit This Code:</h2><code><span class="code">x = 1</span><br><span class="code">!--x</span><br><span class="code"> x </span><br></code> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Fiddle (Mã thử)


8

Vấn đề thực sự ở đây là thiếu khoảng trống giữa hai toán tử !--.

Tôi không biết tại sao mọi người hiểu rằng bạn không bao giờ sử dụng khoảng trắng sau !toán tử. Tôi nghĩ rằng nó xuất phát từ việc áp dụng cứng nhắc các quy tắc khoảng trắng cơ học thay vì lẽ thường. Gần như mọi tiêu chuẩn mã hóa tôi từng thấy đều cấm không gian sau tất cả các toán tử đơn nguyên, nhưng tại sao?

Nếu đã từng có trường hợp bạn rõ ràng cần không gian đó, thì đây là một trường hợp.

Hãy xem xét đoạn mã này:

if (!--pending)
    done(null, results);

Không chỉ !và bị --nghiền nát với nhau, bạn cũng đã (đâm sầm vào chúng. Không có gì ngạc nhiên khi thật khó để nói những gì được kết nối với những gì.

Một khoảng trắng nhiều hơn làm cho mã rõ ràng hơn nhiều:

if( ! --pending )
    done( null, results );

Chắc chắn, nếu bạn đã quen với các quy tắc cơ học như "không có không gian bên trong parens" và "không có không gian sau một toán tử đơn nguyên", điều này có vẻ hơi xa lạ.

Nhưng hãy nhìn vào cách các nhóm khoảng trắng bổ sung và phân tách các phần khác nhau của ifcâu lệnh và biểu thức: Bạn đã có --pending, vì vậy --rõ ràng là toán tử riêng của nó và được liên kết chặt chẽ pending. (Nó giảm dần pendingvà trả về kết quả giảm dần.) Sau đó, bạn đã !tách ra khỏi đó để rõ ràng đó là một toán tử riêng biệt, phủ nhận kết quả. Cuối cùng, bạn đã có if()bao quanh toàn bộ biểu thức để biến nó thành một iftuyên bố.

Và vâng, tôi đã loại bỏ khoảng trống giữa if(, bởi vì ( thuộc về if. Đây (không phải là một phần của một số loại (!--cú pháp vì nó dường như là trong bản gốc, (nếu là một phần của cú pháp của ifchính câu lệnh.

Khoảng trắng ở đây phục vụ để truyền đạt ý nghĩa , thay vì tuân theo một số tiêu chuẩn mã hóa cơ học.


1
Phiên bản màu trắng dễ đọc hơn đối với tôi. Khi tôi lần đầu tiên nhìn thấy câu hỏi tôi giả sử !--là một số toán tử javascript tôi không biết. Tại sao không sử dụng dấu ngoặc đơn để làm cho nó rõ ràng hơn? if(!(--pending))
emory

1
Không hướng dẫn phong cách Tôi nhận thức cấm sử dụng ++hoặc --, nhưng nhiều người làm cấm sử dụng không gian không cần thiết chẳng hạn như sau khi !khai thác tiền tố, và dấu ngoặc không cần thiết.

1
Khoảng trắng sau các toán tử đơn nguyên không được khuyến khích vì nó tách nó khỏi biểu thức mà nó hoạt động. Bạn muốn nó được đọc cùng nhau ... ít nhất là imho
aldrin
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.