Apa yang dilakukan “! -” di JavaScript?

376

Saya memiliki kode ini (diambil dari pertanyaan ini ):

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);
                }
            });
        });
    });
};

Saya mencoba untuk mengikutinya, dan saya pikir saya mengerti segalanya kecuali mendekati akhir di mana dikatakan !--pending. Dalam konteks ini, apa yang dilakukan perintah itu?

Sunting: Saya menghargai semua komentar lebih lanjut, tetapi pertanyaannya telah dijawab berkali-kali. Bagaimanapun, terima kasih!

Kieran E
sumber
222
Ini adalah cara yang bagus untuk membingungkan orang berikutnya untuk menjaga kode tersebut.
Eric J.
240
!~--[value] ^ trueSaya menyebut kode seperti ini "keamanan pekerjaan"
TbWill4321
63
Ini mengingatkan saya. Apa nama -->operatornya?
Soner Gönül
36
@ TbWill4321 Jika saya melakukan review kode, itu akan menjadi kebalikan dari keamanan kerja.
corsiKa
8
Operator yang digabung dengan nama variabel membuatnya tidak terlalu buruk. Setiap programmer Javascript berpengalaman tidak perlu lebih dari beberapa detik untuk mengetahui fungsinya.
Christiaan Westerbeek

Jawaban:

537

! membalikkan suatu nilai, dan memberi Anda boolean yang berlawanan:

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

--[value] kurangi satu (1) dari angka, dan lalu kembalikan angka itu untuk dikerjakan:

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

Jadi, !--pendingkurangi satu dari yang tertunda, dan kemudian kembalikan kebalikan dari nilai kebenaran / kepalsuannya (baik itu benar atau tidak 0).

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

Dan ya, ikuti ProTip. Ini mungkin idiom umum dalam bahasa pemrograman lain, tetapi untuk sebagian besar pemrograman JavaScript deklaratif ini terlihat sangat asing.

TbWill4321
sumber
623
ProTip ™: Jangan pernah melakukan ini dalam kode Anda, itu konyol.
Naftuli Kay
17
Ini tidak menyebutkan bahwa --hanya bertindak pada variabel; Anda tidak dapat menerapkannya pada nilai secara umum.
deltab
10
@deltab: validSimpleAssignmentTargets, tepatnya. Itu termasuk referensi pengidentifikasi dan referensi properti.
Bergi
19
--0dan --1tidak akan bekerja. Literal numerik tidak berlaku untuk ekspresi sisi kiri
PSWai
7
@Pharap: Uh, saya memiliki lebih banyak kesulitan untuk menguraikan i > -1hal itu daripada i--(dan btw Anda lupa i = init() - 1). Itulah idiom untuk ... dan setiap programmer harus mempelajarinya.
Bergi
149

Itu bukan operator khusus, ini 2 operator standar satu demi satu:

  1. Pengurangan awalan ( --)
  2. A logical not ( !)

Ini menyebabkan pendingakan dikurangi dan kemudian diuji untuk melihat apakah itu nol.

Amit
sumber
8
Saya benar-benar baru dalam hal ini, tetapi mengapa ini lebih baik daripada menggunakan if(pending === 1)?
Kieran E
46
@ Kerier, bukan, itu singkatan yang benar-benar melanggar aturan "keterbacaan adalah raja".
Ryan
23
@KieranE ini bukan tentang menjadi superior, ini berbeda. Ini memutasikan variabel (menguranginya dengan 1) dan kemudian menggunakan nilai baru untuk menguji
Amit
7
@ Kanier, ini setara dengan if( pending === 1 ) { done... } else { pending = pending - 1; }.
CompuChip
7
@ CompuChip Ini sebenarnya terlihat --pending akan dilakukan dalam hal apa pun, jadi kodenya lebih seperti: if (! Pending) {--pending; etc.} else {--pending; etc.} Ini berdasarkan pada developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Paul Russell
109

Sejumlah jawaban menjelaskan apa yang dilakukan perintah ini, tetapi tidak mengapa dilakukan seperti itu di sini.

Saya berasal dari dunia C, dan saya membaca !--pending"hitung mundur pendingdan periksa apakah itu nol" tanpa benar-benar memikirkannya. Ini adalah ungkapan yang menurut saya harus diketahui oleh programmer dalam bahasa yang sama.

Fungsi ini digunakan readdiruntuk mendapatkan daftar file dan subdirektori, yang secara kolektif saya sebut "entri".

Variabel pendingmelacak berapa banyak dari ini yang masih diproses. Dimulai sebagai panjang daftar, dan dihitung ke bawah menuju nol karena setiap entri diproses.

Entri-entri ini dapat diproses tidak sesuai pesanan, oleh karena itu perlu untuk menghitung mundur daripada hanya menggunakan loop sederhana. Ketika semua entri telah diproses, panggilan balik donedipanggil untuk memberi tahu penelepon asli tentang fakta ini.

Dalam panggilan pertama ke donediawali dengan return, bukan karena kami ingin mengembalikan nilai, tetapi hanya untuk membuat fungsi berhenti dieksekusi pada saat itu. Itu akan menjadi kode yang lebih bersih untuk menjatuhkan returndan meletakkan alternatif dalam sebuah else.

Stig Hemmer
sumber
13
@Bergi itu adalah idiom terkenal di dunia C, tetapi itu bukan Javascript idiomatik. Untuk setiap subekspresi yang diberikan, ekosistem yang berbeda akan memiliki berbagai idiom yang berbeda dan tidak sesuai, bagaimana 'itu dilakukan' di sana. Menggunakan idiom dari bahasa 'asing' adalah bau kode yang agak buruk.
Peteris
3
@Peteris: Ini adalah idiom dalam semua bahasa mirip-C yang memiliki operator penurunan. Tentu saja, kadang-kadang ada cara yang berbeda dan lebih baik tersedia dalam bahasa, tetapi saya tidak berpikir JS memiliki idiom penghitungan khusus.
Bergi
6
Ada sebagian besar komunitas Javascript, termasuk para pemimpin berpengaruh seperti Douglas Crockford, yang menganjurkan untuk menghindari kenaikan unrement dan pengurangan operator secara keseluruhan . Saya tidak akan berdebat di sini tentang apakah mereka benar atau tidak; maksud saya hanya untuk menunjukkan bahwa karena kontroversi ada, kode seperti !--pendingakan gagal banyak linters dan pedoman gaya. Itu cukup bagi saya untuk mengatakan itu mungkin tidak idiomatis (terlepas dari apakah alternatifnya "lebih baik").
GrandOpener
3
@GrandOpener "Idiomatik" berarti "ekspresi yang biasanya dipahami oleh penutur asli". Predecrement adalah pasti baik-dipahami pengguna dari C-seperti bahasa dan, dengan perluasan, begitu juga "! (- x)". Apakah atau tidak menggunakan sesuatu adalah Good Idea® adalah pertanyaan yang terpisah sepenuhnya dari seberapa idiomatiknya. (Misalnya saya sangat ragu Anda akan bertemu dengan banyak programmer dari bahasa apa pun yang tidak mengerti apa yang "goto" lakukan, meskipun pendapat yang sering diungkapkan bahwa memasukkannya ke dalam kode Anda berada di antara "Nafsu" dan "Pembunuhan" dalam daftar. dari delapan dosa yang mematikan.)
jmbpiano
3
@Pharap Itu karena C # dan Java tidak mengkonversi intke boolimplisit, dan sama sekali tidak ada hubungannya dengan penggunaan !--.
Agop
36

Itu sebuah steno.

! tidak".

-- mengurangi nilai.

Jadi, !--periksa apakah nilai yang diperoleh dari meniadakan hasil penurunan nilai salah.

Coba ini:

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

Yang pertama adalah false, karena nilai x adalah 1, yang kedua adalah benar, karena nilai x adalah 0.

Catatan samping: !x--akan memeriksa apakah x salah pertama, dan kemudian menurunkannya.

Lucas
sumber
RE catatan samping - Anda yakin? Sepertinya post-fix memiliki prioritas lebih tinggi daripada !, sedangkan pre-fix memiliki prioritas lebih rendah. Diutamakan Operator JavaScript
Dannnno
2
Iya. (Coba var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);). Sementara Post-fix --mungkin dijalankan pertama kali, nilai kembalinya adalah nilai variabel sebelum decrementing ( Decrement Opperator ).
Lucas
Saya pikir Anda bermaksud mengatakan " !--mengembalikan negasi hasil penurunan nilai." Hanya kode luar, seperti console.log(), yang memeriksa apakah isinya benar.
mareoraft
31

!adalah operator JavaScript NOT

--adalah operator pra-pengurangan. Begitu,

x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
          // which makes the condition to be true
Sterling Archer
sumber
8
WTH adalah "pernyataan palsu"?
Bergi
5
@Bergi Saya berasumsi Anda benar-benar tahu apa artinya, tetapi jika Anda tidak (atau orang lain tidak) di sini adalah penjelasan untuk JavaScript yang juga menerjemahkan agak baik ke bahasa lain dengan konsep ini (seperti Python).
Dannnno
1
@Pharap itu bukan jawaban saya, jadi saya tidak akan menyentuhnya.
Dannnno
2
@ Donnno: Yah saya tahu apa nilai-nilai palsu dan apa pernyataan itu, dan jadi saya tahu bahwa tidak ada yang namanya "pernyataan palsu" di JS. Saya menganggap ini tidak merujuk pada istilah logika .
Bergi
1
Saya tidak mengerti bagaimana --operator bisa bekerja dengan konstan ... mungkin maksud Anda, --xbukan --0?
SJuan76
24
if(!--pending)

cara

if(0 == --pending)

cara

pending = pending - 1;
if(0 == pending)
james turner
sumber
4
Dan itu adalah cara yang jelas untuk menulis kode. Tentu saja, saya lebih suka if(0 == pending)karena potensi kesalahan ketik. if(0 = pending)adalah kesalahan sintaksis. if(pending = 0)adalah tugas yang akan menyebabkan perilaku membingungkan dalam kode selesai.
Theo Brinkman
3
@TheoBrinkman: sebenarnya if(0 = pending)bukan kesalahan sintaksis - ia mem-parsing dengan baik - tetapi Kesalahan Referensi karena 0tidak dapat ditentukan (ref ECMA-262 (edisi ke-6) detik 12.14.1).
hmakholm tersisa Monica
13

Ini bukan operator yang diikuti oleh pre-decrementer di tempat.

Jadi jika pendinginteger dengan nilai 1:

val = 1;
--val; // val is 0 here
!val // evaluates to true
Brendan Abel
sumber
11

Ia hanya berkurang pendingsatu dan memperoleh komplemen logisnya (negasi). Komplemen logis dari angka yang berbeda dari 0 adalah false, untuk 0 adalah true.

MinusFour
sumber
Harap perhatikan bahwa "negate" memiliki arti yang berbeda pada angka daripada pada boolean
Bergi
1
Baiklah, saya akan menggunakan istilah yang berbeda. Tidak yakin apakah ini lebih baik.
MinusFour
11

Penjelasan

Ini adalah 2 operator, a !dan a--

!--x 

Jadi, --pengurangan x dengan 1, maka !mengembalikan true jika x sekarang 0 (atau NaN ...), salah jika tidak. Anda mungkin membaca idiom ini, seperti "kita mengurangi x dan jika itu membuatnya nol ..."

Jika Anda ingin membuatnya lebih mudah dibaca, Anda dapat:

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

Cobalah:

/* 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 (Kode Coba)

Ben Aubin
sumber
8

Masalah sebenarnya di sini adalah kurangnya ruang antara dua operator !dan --.

Saya tidak tahu mengapa orang-orang berpikir bahwa Anda tidak dapat menggunakan ruang setelah !operator. Saya pikir itu berasal dari penerapan aturan spasi spasial mekanis bukannya akal sehat. Hampir setiap standar pengkodean yang saya lihat melarang spasi setelah semua operator unary, tapi mengapa?

Jika ada kasus di mana Anda jelas membutuhkan ruang itu, ini adalah satu.

Pertimbangkan sedikit kode ini:

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

Tidak hanya dihaluskan !dan --dihancurkan bersama-sama, Anda juga dapat (membantingnya. Tidak heran sulit untuk mengatakan apa yang terhubung dengan apa.

Spasi yang sedikit lebih membuat kode lebih jelas:

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

Tentu, jika Anda terbiasa dengan aturan mekanis seperti "tidak ada ruang di dalam parens" dan "tidak ada ruang setelah operator unary", ini mungkin tampak agak asing.

Tapi lihat bagaimana ruang spasi ekstra mengelompokkan dan memisahkan berbagai bagian dari ifpernyataan dan ekspresi: Anda punya --pending, jadi --jelas operatornya sendiri dan terkait erat pending. (Ini mengurangi pendingdan mengembalikan hasil yang dikurangi.) Kemudian Anda !memisahkannya sehingga jelas merupakan operator yang berbeda, meniadakan hasilnya. Akhirnya, Anda memiliki if(dan )mengelilingi seluruh ekspresi untuk membuatnya menjadi ifpernyataan.

Dan ya, saya dihapus ruang antara ifdan (, karena ( milik ke if. Ini (bukan bagian dari beberapa jenis (!--sintaksis seperti yang tampak pada aslinya, bagian (if dari sintaks dari ifpernyataan itu sendiri.

Ruang kosong di sini berfungsi untuk mengkomunikasikan artinya , alih-alih mengikuti beberapa standar pengkodean mekanis.

Michael Geary
sumber
1
Versi spasi putih lebih mudah dibaca oleh saya. Ketika saya pertama kali melihat pertanyaan yang saya asumsikan !--adalah beberapa operator javascript saya tidak tahu. Mengapa tidak menggunakan tanda kurung untuk membuatnya lebih eksplisit? if(!(--pending))
emory
1
Tidak ada panduan gaya yang saya tahu melarang penggunaan ++atau --, tetapi banyak yang melarang menggunakan ruang yang tidak penting seperti setelah !operator awalan, dan tanda kurung yang tidak penting.
1
Spasi setelah operator unary tidak disarankan karena memisahkannya dari ekspresi yang dioperasikannya. Anda ingin itu dibaca bersama ... setidaknya imho
aldrin