“! -” ทำอะไรใน JavaScript?


376

ฉันมีรหัสชิ้นนี้ (นำมาจากคำถามนี้ ):

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

!--pendingฉันพยายามที่จะทำตามนั้นและผมคิดว่าผมเข้าใจทุกอย่างยกเว้นใกล้ถึงจุดสิ้นสุดที่มันว่า ในบริบทนี้คำสั่งนั้นทำอะไร

แก้ไข: ฉันขอขอบคุณความคิดเห็นเพิ่มเติมทั้งหมด แต่คำถามได้รับคำตอบหลายครั้ง ขอขอบคุณ!


222
มันเป็นวิธีที่ยอดเยี่ยมในการสร้างความสับสนให้กับบุคคลต่อไปเพื่อรักษารหัส
Eric J.

240
!~--[value] ^ trueฉันเรียกรหัสเช่นนี้ว่า "ความมั่นคงของงาน"
TbWill4321

63
สิ่งนี้ทำให้ฉันนึกถึงผู้ประกอบการชื่อ-->อะไร?
Soner Gönül

36
@ TbWill4321 ถ้าฉันทำรีวิวรหัสมันจะตรงกันข้ามกับความปลอดภัยของงาน
corsiKa

8
ผู้ประกอบการรวมกับชื่อตัวแปรทำให้ไม่เลวร้ายเกินไป โปรแกรมเมอร์จาวาสคริปต์ทุกคนที่มีประสบการณ์ไม่จำเป็นต้องใช้เวลามากกว่าสองวินาทีในการรู้ว่ามันทำงานอะไร
Christiaan Westerbeek

คำตอบ:


537

! กลับค่าและให้บูลีนตรงข้ามกับคุณ:

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

--[value] ลบหนึ่ง (1) จากตัวเลขจากนั้นส่งคืนหมายเลขนั้นเพื่อทำงานกับ:

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

ดังนั้น!--pendingลบหนึ่งออกจากรอดำเนินการแล้วส่งกลับค่าที่ตรงข้ามของความจริง / ค่าเท็จ (ไม่ว่าจะเป็นหรือไม่ก็ตาม0)

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

และใช่ติดตาม ProTip นี่อาจเป็นสำนวนที่พบบ่อยในภาษาการเขียนโปรแกรมอื่น ๆ แต่สำหรับการเขียนโปรแกรม JavaScript ที่มีการประกาศมากที่สุด


623
ProTip ™: อย่าทำสิ่งนี้ในรหัสของคุณมันไร้สาระ
Naftuli Kay

17
นี่ไม่ได้พูดถึงว่า--ทำหน้าที่เฉพาะกับตัวแปรเท่านั้น คุณไม่สามารถใช้กับค่าโดยทั่วไป
deltab

10
@deltab: validSimpleAssignmentTargets เป็นที่แน่นอน ซึ่งรวมถึงการอ้างอิงตัวระบุและการอ้างอิงคุณสมบัติ
Bergi

19
--0และ--1จะไม่ทำงาน ตัวอักษรตัวเลขไม่ถูกต้องแสดงออกทางด้านซ้ายมือ
PSWai

7
@Phap: เอ่อฉันมีปัญหาในการแยกวิเคราะห์i > -1สิ่งนั้นมากกว่าi--(และ btw คุณลืมi = init() - 1) นั่นคือสิ่งที่สำนวนสำหรับ ... และโปรแกรมเมอร์ทุกคนควรเรียนรู้พวกเขา
Bergi

149

นั่นไม่ใช่ตัวดำเนินการพิเศษมันเป็นตัวดำเนินการมาตรฐาน 2 ตัวต่อกัน:

  1. คำนำหน้าลดลง ( --)
  2. ตรรกะไม่ ( !)

นี่เป็นสาเหตุpendingที่จะลดลงแล้วทดสอบเพื่อดูว่ามันเป็นศูนย์


8
ฉันแน่นอนใหม่นี้ แต่ทำไมเป็นแบบนี้ดีกว่าที่จะใช้if(pending === 1)?
Kieran E

46
@ KieranE ไม่ได้มันเป็นชวเลขที่ละเมิดกฎของ "readability is king" อย่างสมบูรณ์
Ryan

23
@ KieranE มันไม่ได้เกี่ยวกับความเหนือกว่า แต่ก็แตกต่างกัน มันกลายพันธุ์ตัวแปร (ลดลง 1) จากนั้นใช้ค่าใหม่ในการทดสอบ
Amit

7
@KieranE if( pending === 1 ) { done... } else { pending = pending - 1; }ก็จะเทียบเท่ากับ
CompuChip

7
@CompuChip ดูเหมือนจริง - การส่งจะทำในกรณีใด ๆ ดังนั้นรหัสจึงเป็นดังนี้: if (! pending) {--pending; ฯลฯ } อื่น {- ส่ง; ฯลฯ } นี้ขึ้นอยู่กับdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ......
Paul Russell

109

คำตอบจำนวนหนึ่งอธิบายสิ่งที่คำสั่งนี้ทำ แต่ไม่ใช่สาเหตุที่ทำเช่นนั้นที่นี่

ฉันมาจากโลก C และฉันอ่าน!--pendingว่า "นับถอยหลังpendingและตรวจสอบว่ามันเป็นศูนย์" โดยไม่ได้คิดถึงมันจริงๆ มันเป็นสำนวนที่ฉันคิดว่าโปรแกรมเมอร์ในภาษาที่คล้ายกันควรรู้

ฟังก์ชั่นนี้ใช้readdirเพื่อรับรายการไฟล์และไดเรกทอรีย่อยซึ่งฉันจะเรียกว่า "รายการ"

ตัวแปรpendingติดตามจำนวนซากที่ต้องดำเนินการ มันเริ่มจากความยาวของรายการและนับลงไปที่ศูนย์ตามการประมวลผลแต่ละรายการ

รายการเหล่านี้อาจได้รับการดำเนินการตามลำดับซึ่งเป็นเหตุผลว่าทำไมจึงจำเป็นต้องนับถอยหลังมากกว่าเพียงแค่ใช้การวนรอบอย่างง่าย เมื่อทุกรายการได้รับการประมวลผลการเรียกกลับdoneถูกเรียกให้แจ้งโทรเดิมของความเป็นจริงนี้

ในการเรียกครั้งแรกไปยังdoneถูกเติมด้วยreturnไม่ใช่เพราะเราต้องการคืนค่า แต่เพียงเพื่อให้ฟังก์ชั่นหยุดการดำเนินการที่จุดนั้น มันจะมีรหัสทำความสะอาดได้ไปวางและวางทางเลือกในreturnelse


13
@Bergi เป็นสำนวนที่รู้จักกันดีในโลก C แต่ไม่ใช่จาวาสคริปต์ สำหรับนิพจน์ย่อยใด ๆ ระบบนิเวศที่แตกต่างกันจะมีสำนวนที่แตกต่างกันและเข้ากันไม่ได้ต่างกันอย่างไร 'มันเสร็จสิ้นแล้ว' อย่างไร การใช้สำนวนจากภาษาต่างประเทศเป็นกลิ่นรหัสที่ค่อนข้างแย่
Peteris

3
@Peteris: มันเป็นสำนวนในภาษา C-like ทั้งหมดที่มีตัวดำเนินการลดค่า แน่นอนว่าบางครั้งก็มีวิธีที่แตกต่างและดีกว่าในภาษา แต่ฉันไม่คิดว่า JS มีสำนวนพิเศษใด ๆ
Bergi

6
มีส่วนสำคัญของชุมชน Javascript รวมทั้งผู้นำที่มีอิทธิพลเช่นดักลาส Crockford คือว่าสนับสนุนการหลีกเลี่ยงเอกเพิ่มขึ้นและลดลงผู้ประกอบการทั้งหมด ฉันจะไม่เถียงที่นี่เกี่ยวกับว่าพวกเขาถูกต้องหรือไม่ ความตั้งใจของฉันเป็นเพียงการชี้ให้เห็นว่าเนื่องจากการทะเลาะวิวาทที่มีอยู่รหัสเช่น!--pendingจะล้มเหลวหลาย linters และแนวทางสไตล์ นั่นก็เพียงพอแล้วสำหรับฉันที่จะบอกว่ามันอาจจะไม่ใช่สำนวน (ไม่ว่าทางเลือกนั้นจะ "ดีกว่า")
GrandOpener

3
@GrandOpener "Idiomatic" หมายถึง "การแสดงออกที่เข้าใจกันโดยเจ้าของภาษา" Predecrement เป็นที่แน่นอนดีเข้าใจกับผู้ใช้งาน C-เช่นภาษาและโดยขยายเพื่อให้เป็น "! (- x)" หรือไม่ใช้สิ่งที่เป็นความคิดที่ดี®เป็นคำถามที่แยกจากกันโดยสิ้นเชิงว่ามันเป็นสำนวน (เช่นฉันสงสัยอย่างมากว่าคุณจะพบโปรแกรมเมอร์หลายภาษาที่ไม่เข้าใจว่า "goto" ทำอะไรแม้จะมีความคิดเห็นที่แสดงออกมาบ่อยๆว่าการรวมไว้ในโค้ดของคุณอยู่ระหว่าง "Lust" และ "Murder" ในรายการ ของบาปมหันต์แปดข้อ)
jmbpiano

3
@Pharap นั่นเป็นเพราะ C # และ Java ไม่แปลงintไปโดยปริยายและมีอะไรอย่างจะทำอย่างไรกับการใช้งานของbool !--
Agop

36

มันเป็นชวเลข

! ไม่ใช่".

-- ลดค่า

ดังนั้น!--ตรวจสอบว่าค่าที่ได้จากการลบล้างผลลัพธ์ของการลดค่าเป็นเท็จหรือไม่

ลองสิ่งนี้:

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

ที่แรกเป็นเท็จเนื่องจากค่าของ x คือ 1 ที่สองเป็นจริงเนื่องจากค่าของ x คือ 0

หมายเหตุด้านข้าง: !x--จะตรวจสอบว่า x เป็นเท็จก่อนแล้วค่อยลดลง


โปรดทราบอีกครั้ง - คุณแน่ใจเหรอ ดูเหมือนว่าการแก้ไขหลังมีลำดับความสำคัญสูงกว่า!ในขณะที่การแก้ไขล่วงหน้ามีความสำคัญต่ำกว่า ตัวดำเนินการของ JavaScript มีความสำคัญ
Dannnno

2
ใช่. (ลองvar x = 2; console.log(!x--); console.log(!x--); console.log(!x--);) ในขณะที่โพสต์แก้ไข--อาจดำเนินการก่อนค่าส่งคืนคือค่าของตัวแปรก่อนการลดค่า ( Decrement Opperator )
ลูคัส

ฉันคิดว่าคุณตั้งใจจะพูดว่า " !--คืนค่าลบจากผลของการลดค่า" รหัสภายนอกเท่านั้นเช่นconsole.log()ตรวจสอบว่าเนื้อหามีความจริงหรือไม่
mareoraft

31

!เป็น JavaScript ไม่ดำเนินการ

--เป็นตัวดำเนินการลดค่าล่วงหน้า ดังนั้น,

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

8
WTH คือ "ข้อความเท็จ"?
Bergi

5
@Bergi ฉันคิดว่าคุณรู้ว่ามันหมายถึงอะไรจริง ๆ แต่ในกรณีที่คุณไม่ได้ (หรือคนอื่นไม่) นี่เป็นคำอธิบายสำหรับ JavaScriptที่แปลค่อนข้างดีกับภาษาอื่นด้วยแนวคิดนี้ (เช่น Python)
Dannnno

1
@Phap มันไม่ใช่คำตอบของฉันดังนั้นฉันจะไม่แตะต้องมัน
Dannnno

2
@ Dannnno: ฉันรู้ว่าค่าเท็จและสิ่งที่เป็นงบและเพื่อให้ฉันรู้ว่าไม่มีสิ่งเช่น "คำสั่งเท็จ" ใน JS ผมถือว่านี้ไม่ได้หมายถึงระยะตรรกะ
Bergi

1
ฉันไม่เข้าใจว่า--โอเปอเรเตอร์สามารถทำงานกับค่าคงที่ ... บางทีคุณอาจหมายถึง--xแทนที่จะเป็น--0อย่างไร
SJuan76

24
if(!--pending)

วิธี

if(0 == --pending)

วิธี

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

4
และนั่นเป็นวิธีที่ชัดเจนในการเขียนรหัส แน่นอนฉันชอบif(0 == pending)เพราะมีความเป็นไปได้สำหรับการพิมพ์ผิด if(0 = pending)เป็นข้อผิดพลาดทางไวยากรณ์ if(pending = 0)คือการมอบหมายที่จะทำให้เกิดพฤติกรรมที่สับสนในรหัสเสร็จ
Theo Brinkman

3
@TheoBrinkman: จริง ๆ แล้วif(0 = pending)ไม่ใช่ข้อผิดพลาดทางไวยากรณ์ - แยกวิเคราะห์ได้ดี - แต่เป็นข้อผิดพลาดอ้างอิงเนื่องจาก0ไม่สามารถกำหนดได้ (อ้างอิง ECMA-262 (6th ed) วินาที 12.14.1)
hmakholm ออกเดินทางจากโมนิกา

13

มันไม่ใช่ตัวดำเนินการตามด้วยการลดลงล่วงหน้าแบบแทนที่

ดังนั้นถ้าpendingเป็นจำนวนเต็มที่มีค่า 1:

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

11

มันแค่ลดลงpendingทีละตัวและรับส่วนเติมเต็มเชิงตรรกะ (negation) ที่สมบูรณ์ตรรกะของที่แตกต่างกันจำนวนใดมากกว่า 0 false, 0 trueมันเป็น


โปรดสังเกตว่า "ลบล้าง" มีความหมายแตกต่างกันในเรื่องของตัวเลขมากกว่าบูลีน
Bergi

1
เอาล่ะฉันจะใช้คำอื่น ไม่แน่ใจว่ามันดีกว่าไหม
MinusFour

11

คำอธิบาย

นี่คือโอเปอเรเตอร์ 2 รายการคือ a !และ a--

!--x 

ดังนั้นการ--ลดลง x 1 โดยดังนั้น!ผลตอบแทนที่แท้จริงถ้า x ตอนนี้ 0 (หรือ NaN ... ), เท็จถ้ามันไม่ คุณอาจอ่านสำนวนนี้แบบ "เราลด x และถ้านั่นทำให้มันเป็นศูนย์ ... "

หากคุณต้องการทำให้อ่านง่ายขึ้นคุณสามารถ:

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

ลองดูสิ:

/* 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>

ซอ (ลองโค้ด)


8

ปัญหาที่เกิดขึ้นจริงที่นี่คือการขาดพื้นที่ระหว่างสองผู้ประกอบการและ!--

ฉันไม่รู้ว่าทำไมคนถึงได้รับในหัวของพวกเขาที่คุณไม่สามารถใช้พื้นที่หลังจาก!ผู้ประกอบการ ฉันคิดว่ามันมาจากการใช้กฎของช่องว่างเชิงกลที่เข้มงวดแทนที่จะใช้สามัญสำนึก ทุกมาตรฐานการเข้ารหัสที่ฉันเห็นไม่อนุญาตให้เว้นวรรคหลังจากผู้ประกอบการที่ไม่พร้อมทำ แต่ทำไม?

หากมีกรณีที่คุณต้องการอย่างชัดเจนพื้นที่ว่างนี่เป็นกรณี

พิจารณารหัสนี้หน่อย:

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

ไม่เพียง แต่จะถูกบดขยี้!และผสม--เข้าด้วยกันเท่านั้นคุณยังได้สิ่งนั้น(กระทบต่อพวกเขาเช่นกัน ไม่น่าแปลกใจที่จะบอกว่าเชื่อมต่อกับอะไร

ช่องว่างอีกเล็กน้อยทำให้รหัสชัดเจนมากขึ้น:

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

แน่นอนว่าหากคุณคุ้นเคยกับกฎเชิงกลเช่น "ไม่มีที่ว่างภายใน parens" และ "ไม่มีที่ว่างหลังจากผู้ประกอบการเอก" สิ่งนี้อาจดูแปลกไปหน่อย

แต่ดูที่วิธีการกลุ่มเสริมช่องว่างและแยกชิ้นส่วนต่าง ๆ ของifคำสั่งและการแสดงออก: คุณได้--pendingดังนั้นเห็นได้ชัดว่าผู้ประกอบการของตัวเองและจะเชื่อมโยงอย่างใกล้ชิดกับ-- pending(มันจะลดpendingและส่งกลับผลลัพธ์ที่ลดลง) จากนั้นคุณก็!แยกออกมาจากนั้นจึงเห็นได้ชัดว่ามันเป็นโอเปอเรเตอร์ที่แตกต่างกันโดยมองข้ามผลลัพธ์ ในที่สุดคุณก็มีif(และ)ล้อมรอบการแสดงออกทั้งหมดเพื่อให้เป็นifคำสั่ง

และใช่ฉันลบช่องว่างระหว่างifและ(เพราะ( เป็นifไป นี่(ไม่ใช่ส่วนหนึ่งของ(!--ไวยากรณ์บางชนิดตามที่ปรากฏในต้นฉบับ(ถ้าส่วนหนึ่งของไวยากรณ์ของifคำสั่งนั้น

ช่องว่างที่นี่ทำหน้าที่สื่อสารความหมายแทนที่จะทำตามมาตรฐานการเข้ารหัสเชิงกล


1
เวอร์ชั่นที่อ่านได้ง่ายสามารถอ่านได้มากขึ้น ครั้งแรกที่ฉันเห็นคำถามที่ฉันคิดว่า!--เป็นตัวดำเนินการจาวาสคริปต์ฉันไม่ทราบ ทำไมไม่ใช้วงเล็บเพื่อทำให้ชัดเจนยิ่งขึ้น? if(!(--pending))
emory

1
ไม่มีคำแนะนำสไตล์ฉันรู้ห้ามใช้++หรือ--แต่หลายคนไม่ห้ามการใช้พื้นที่ที่ไม่จำเป็นเช่นหลังจากที่!ผู้ประกอบการคำนำหน้าและวงเล็บที่ไม่จำเป็น

1
ช่องว่างหลังจากตัวดำเนินการ unary ถูกกีดกันเนื่องจากแยกออกจากนิพจน์ที่ทำงาน คุณต้องการให้มันอ่านด้วยกัน ... อย่างน้อยอิมโฮ
อัลดริน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.