กำหนดตัวแปรในคำสั่งเงื่อนไข if วิธีปฏิบัติที่ดีหรือไม่? [ปิด]


113

ฉันย้ายจากภาษา OO คลาสสิกเช่น Java ไปเป็น JavaScript เมื่อหนึ่งปีก่อน ไม่แนะนำให้ใช้โค้ดต่อไปนี้ (หรือไม่ถูกต้อง) ใน Java:

if(dayNumber = getClickedDayNumber(dayInfo))
{
    alert("day number found : " + dayNumber);
}
function getClickedDayNumber(dayInfo)
{
    dayNumber = dayInfo.indexOf("fc-day");
    if(dayNumber != -1) //substring found
    {
        //normally any calendar month consists of "40" days, so this will definitely pick up its day number.
        return parseInt(dayInfo.substring(dayNumber+6, dayNumber+8));
    }
    else return false;
}

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

เพื่อการเดิมพันที่ปลอดภัยยิ่งขึ้นฉันมักจะแยกโค้ดออกเป็นสองบรรทัดกำหนดก่อนแล้วจึงตรวจสอบตัวแปร แต่ตอนนี้ฉันพบสิ่งนี้ฉันแค่สงสัยว่ามันเป็นแนวทางปฏิบัติที่ดีหรือไม่ในสายตาของนักพัฒนา JavaScript ที่มีประสบการณ์?


"The following code is definitely not recommended (or event not correct) in Java..."มันถูกต้องใน JavaScript หรือไม่? เพราะเท่าที่ฉันเห็นคุณส่งคืนจำนวนเต็ม ( return parseInt(...)) ถ้าdayNumber != -1เป็นจริง แต่บูลีนถ้าเป็นเท็จ
Daniel Kvist

คำตอบ:


117

ฉันจะไม่แนะนำที่นี่ ปัญหาคือดูเหมือนว่าข้อผิดพลาดทั่วไปที่คุณพยายามที่จะเปรียบเทียบค่า แต่ใช้เพียงครั้งเดียว=แทนหรือ== ===ตัวอย่างเช่นเมื่อคุณเห็นสิ่งนี้:

if (value = someFunction()) {
    ...
}

คุณไม่รู้ว่านั่นคือสิ่งที่พวกเขาตั้งใจจะทำหรือตั้งใจจะเขียนสิ่งนี้:

if (value == someFunction()) {
    ...
}

หากคุณต้องการทำงานที่ได้รับมอบหมายจริงๆฉันขอแนะนำให้ทำการเปรียบเทียบอย่างชัดเจนเช่นกัน:

if ((value = someFunction()) === <whatever truthy value you are expecting>) {
    ...
}

1
@Matthew Crumley: สิ่งนี้ตอบคำถามของฉันได้อย่างชัดเจน ฉันไม่ได้ตรวจสอบโดยการกำหนด แต่ตรวจสอบว่าค่าใด ๆ ที่ประเมินว่าเป็นหลังจากงานมอบหมาย ความเข้าใจนี้ถูกหรือไม่?
Michael Mao

1
@ ไมเคิล: ใช่ถูกต้อง การเพิ่มการเปรียบเทียบโดยพื้นฐานแล้วจะทำให้ความตั้งใจของคุณชัดเจนยิ่งขึ้น
Matthew Crumley

4
อย่างไรก็ตามตัวอย่างสุดท้ายใช้ไม่ได้หากคุณกำลังทดสอบความล้มเหลว / ความสำเร็จของฟังก์ชันที่ส่งคืนบูลีน กล่าวอีกนัยหนึ่งในขณะที่ if (resultArr = myNeedle.exec(myHaystack)) {...}ทำงานif ((resultArr = myNeedle.exec(myHaystack)) === true) {...}ไม่ได้เนื่องจากการกำหนดให้กับ resultArr เป็นจริงเสมอแม้ว่าผลลัพธ์ของฟังก์ชันจะไม่เป็นเช่นนั้นก็ตาม หากใครใช้ .. โครงสร้างนี้อย่าลืมประกาศตัวแปรผลลัพธ์ก่อน; 'var' ไม่ถูกต้องตามกฎหมายในคำสั่งเงื่อนไข if
Ville

3
คุณสามารถใช้ได้if (!!(value = someFunction()))แต่อย่างที่คุณบอกปัญหาคือคุณไม่สามารถใช้varภายในได้ifดังนั้นคุณจึงต้องสร้าง global หรือไม่ได้อะไรเลยเพราะคุณต้องประกาศvalueแยกต่างหาก น่าเสียดายฉันชอบโครงสร้างนี้ใน C ++ มาก
riv

1
@riv คุณพูดถูก; ในกรณีที่ฟังก์ชั่นส่งคืนบูลีน - เหมือนที่ฉันพูดในความคิดเห็นข้างต้นของฉัน - จากนั้นเงื่อนไขจะทำงานตามที่คาดไว้ แต่ถ้าฟังก์ชันส่งคืนบูลีน (ดังตัวอย่างของฉัน) โครงสร้างทั้งหมดจะเป็นแบบที่ไม่สมเหตุสมผล เห็นได้ชัดว่าแนวความคิดของฉันคือ - ตัดสินจากตัวอย่าง - ว่าฟังก์ชันจะส่งคืนอาร์เรย์ การทดสอบอย่างรวดเร็วบ่งชี้ว่าสภาพประเมินtrueเฉพาะเมื่อผลตอบแทนการทำงานtrueแต่ในกรณีอื่น ๆ ทั้งหมด (รวมถึงเมื่ออาร์เรย์สตริงตัวเลขหรือ null ถูกส่งกลับ) falseมันประเมิน
วิลล์

28

ฉันไม่เห็นข้อพิสูจน์ว่าเป็นการปฏิบัติที่ไม่ดี ใช่อาจดูเหมือนเป็นความผิดพลาด แต่สามารถแก้ไขได้อย่างง่ายดายโดยการแสดงความคิดเห็นอย่างมีวิจารณญาณ ยกตัวอย่างเช่น:

if (x = processorIntensiveFunction()) { // declaration inside if intended
    alert(x);
}

เหตุใดจึงควรอนุญาตให้ฟังก์ชันนั้นทำงานเป็นครั้งที่ 2 ด้วย:

alert(processorIntensiveFunction());

เพราะรุ่นแรก LOOKS แย่? ฉันไม่เห็นด้วยกับตรรกะนั้น


32
ไม่ใช่ขุดความคิดเห็นเก่า ๆ แต่ฉันไม่เห็นด้วยกับข้อโต้แย้งของคุณ โค้ดที่อ่านได้ควรอธิบายตัวเองโดยไม่ต้องมีความคิดเห็น - การเพิ่มความคิดเห็นในโค้ดที่ทำให้สับสนไม่ใช่วิธีการแก้ไข สำหรับส่วนที่สองซึ่งกล่าวว่าทางเลือกคือการเรียกใช้ฟังก์ชันอีกครั้งฉันไม่คิดว่าจะมีใครตั้งใจจะทำเช่นนั้น แต่คุณจะทำx = processorItensiveFunction(); if(x) { alert(x); }
maksim

9
@maksim: ฉันชอบโค้ดที่อ่านได้ แต่นั่นไม่ได้หมายความว่าโค้ดควรจะโง่ลงหรือละเอียดเกินไป การกระจายหลาย ๆ บรรทัดและการเล่นกลค่าระหว่างตัวแปรอาจทำให้โค้ดแย่ลงได้ โค้ดที่แทรกอาจมีผลข้างเคียงที่คาดไม่ถึงในภาษาที่พิมพ์ผิด / ยืดหยุ่นเช่น JS การมอบหมายในคำสั่งเงื่อนไขสามารถใช้ได้ในจาวาสคริปต์เนื่องจากคุณเพียงถามว่า "การมอบหมายถูกต้องหรือไม่ให้ทำบางสิ่งที่อาจรวมถึงผลลัพธ์ของการมอบหมาย" แต่แท้จริงแล้วการกำหนดก่อนเงื่อนไขก็ใช้ได้เช่นกันไม่ละเอียดเกินไปและใช้กันทั่วไป
okdewit

1
@maksim ทำไมถึงคิดว่าif ( ! x = anyFunction() )อ่านไม่ออก? ไม่จำเป็นต้องมีความคิดเห็นใด ๆ
JDrake

หากคุณแก้ไข OPC ทำงานร่วมกับนักพัฒนาอื่น ๆ ที่มีระดับความสามารถแตกต่างกัน (หรืออีกนัยหนึ่งคือคุณเป็นมืออาชีพ) คุณจะเกลียดที่สิ่งนี้เป็นไปได้
davidjmcclelland

2
@maksim - วิธีแก้ปัญหาของคุณจะไม่สะดวกในif-elseสถานการณ์ พิจารณา - if (condition) {...} else if (x = processorIntensiveFunction()) {alert(x)} ก่อนหน้านี้ของคุณx = processorIntensiveFunction();จะเป็นความพยายามที่สูญเปล่าหากการเริ่มต้นconditionเป็นจริง
Adrian Bartholomew

16

ฉันทำมันหลายครั้ง ในการข้ามคำเตือน JavaScript ฉันเพิ่มสอง parens:

if ((result = get_something())) { }

คุณควรหลีกเลี่ยงถ้าคุณต้องการใช้มันจริงๆให้เขียนความคิดเห็นไว้ข้างบนว่าคุณกำลังทำอะไรอยู่


1
@SHiNKiROU: ฉันจะดูคำเตือนจาวาสคริปต์ได้อย่างไร มีคอมไพเลอร์ Javascript หรือไม่? หรือล่ามจะสร้างคำเตือนบางอย่าง? ฉันใช้คอนโซล Firefox ในการดีบักจาวาสคริปต์ตลอดเวลา แต่ไม่เห็นผลลัพธ์ที่คล้ายกัน ขออภัยเกี่ยวกับประสบการณ์ที่ จำกัด ของฉัน
Michael Mao

5
@ Michael: JSLint ( jslint.com ) เป็นโปรแกรม / ไลบรารียอดนิยมที่ตรวจสอบโปรแกรม JavaScript เพื่อหาข้อผิดพลาดหรือโค้ดที่ไม่ถูกต้อง
Matthew Crumley

ใช้ Mozilla Firefox พร้อมส่วนขยาย Firebug และ / หรือ Web Developer เพื่อตรวจสอบคำเตือน
Ming-Tang

ฉันเพิ่งลองใช้โดยif ((a = [1, 2]).length > 0) { console.log(a); }ที่aยังไม่ได้เริ่มต้นที่ไหนเลยและมันใช้งานได้จริง (ดีมาก! ทำให้การใช้ regex ง่ายขึ้นมาก) ถูกต้องvar|const|letไหมที่ฉันไม่ต้องการที่นี่ คุณบังเอิญรู้หรือไม่ว่าฉันสามารถอ่านเพิ่มเติมเกี่ยวกับเคล็ดลับนี้ได้ที่ไหน
t3chb0t

4

คุณสามารถทำได้ใน Java ด้วย และไม่มันไม่ใช่แนวทางปฏิบัติที่ดี :)

(และใช้===ใน Javascript เพื่อความเท่าเทียมกันของการพิมพ์อ่านหนังสือ The Good Parts ของ Crockford ใน JS)


@quixoto: ฉันสามารถทำเคล็ดลับนี้ใน Java ได้หรือไม่? ฉันสงสัยว่า ... ฉันไม่มี jdk ด้วยมือ atm ดังนั้นฉันจึงไม่สามารถรับโค้ดตัวอย่างใน Java ได้ จากหน่วยความจำที่ไม่ดีของฉัน Java จะทำให้คุณได้รับข้อผิดพลาดรันไทม์หากค่าที่ส่งคืนประเมินบางสิ่งที่ไม่ใช่บูลีนเหมือนกับคำสั่งเงื่อนไขใช่ไหม?
Michael Mao

1
อ่าใช่ใน Java มีการตรวจสอบประเภทว่าเป็นประเภทบูลีน แต่คุณสามารถทำได้if (foo = getSomeBoolValue()) { }
Ben Zotto

ใช่ถูกต้อง. ตัวแปรบูลีนเพื่อทดสอบว่าบางสิ่งประสบความสำเร็จหรือไม่และอีกตัวแปรหนึ่งในการจัดเก็บค่าที่ส่งคืน นั่นเป็นวิธีที่ Java ทำงานได้ดีฉันคุ้นเคยกับมันมากเกินไปดังนั้นฉันจึงรู้สึกแปลก ๆ ที่เห็นว่า Javascript สามารถทำสองสิ่งในบรรทัดเดียว :)
Michael Mao

@BenZotto ฝึกไม่ดีทำไม? "เพื่อหลีกเลี่ยงการใช้ตัวแปรในทางที่ผิดโดยไม่ได้ตั้งใจโดยปกติควรแนะนำตัวแปรให้อยู่ในขอบเขตที่เล็กที่สุดเท่าที่จะเป็นไปได้โดยเฉพาะอย่างยิ่งควรชะลอการกำหนดตัวแปรจนกว่าจะสามารถให้ค่าเริ่มต้นได้ ... หนึ่งในแอปพลิเคชันที่สง่างามที่สุดของหลักการทั้งสองนี้คือการประกาศตัวแปรในเงื่อนไข " - Stroustrup "ภาษาการเขียนโปรแกรม C ++"
JDrake

1
สวัสดีฉันมาที่นี่ในฐานะผู้ใช้จาวาสคริปต์ node.js เหตุใดจึงมีวิธีปฏิบัติที่ไม่ดีในกรณีหนึ่งที่ฉันเจ็บปวดด้วย: if (myvar = 'just a test') สร้างตัวแปร GLOBAL node.js myvar ( nodejs.org/docs/latest-v12.x/api/globals.html #globals_global ) ดังนั้นหากคุณชอบฉันและใช้ตัวแปรนั้นในการจัดการคำขอของเซิร์ฟเวอร์ (กลับมาที่ตัวแปรหลังจากนั้นไม่กี่วินาทีเมื่อคำขออื่น ๆ อาจตกหล่นและมีสิ่งของมากมาย) คุณจะประหลาดใจกับผลลัพธ์ที่ได้รับ คำแนะนำคือ: โปรดทราบว่ารูปแบบนี้สร้างตัวแปรส่วนกลางใน node.js
pein-consulting.de

4

มีอยู่กรณีหนึ่งเมื่อคุณทำเช่นนี้กับwhile-loops
เมื่ออ่านไฟล์คุณมักจะทำเช่นนี้:

void readFile(String pathToFile) {
    // Create a FileInputStream object
    FileInputStream fileIn = null;
    try {
        // Create the FileInputStream
        fileIn = new FileInputStream(pathToFile);
        // Create a variable to store the current line's text in
        String currentLine;
        // While the file has lines left, read the next line,
        // store it in the variable and do whatever is in the loop
        while((currentLine = in.readLine()) != null) {
            // Print out the current line in the console
            // (you can do whatever you want with the line. this is just an example)
            System.out.println(currentLine);
        }
    } catch(IOException e) {
        // Handle exception
    } finally {
        try {
            // Close the FileInputStream
            fileIn.close();
        } catch(IOException e) {
            // Handle exception
        }
    }
}

ดูwhile-loop ที่บรรทัด 9 ที่นั่นบรรทัดใหม่จะถูกอ่านและเก็บไว้ในตัวแปรจากนั้นเนื้อหาของลูปจะถูกรัน ฉันรู้ว่านี่ไม่ใช่if-statement แต่ฉันเดาว่า while loop อาจรวมอยู่ในคำถามของคุณด้วย

เหตุผลก็คือเมื่อใช้ a FileInputStreamทุกครั้งที่คุณเรียกFileInputStream.readLine()มันจะอ่านบรรทัดถัดไปในไฟล์ดังนั้นหากคุณจะเรียกมันจากลูปfileIn.readLine() != nullโดยไม่ต้องกำหนดตัวแปรแทนที่จะเรียก(currentLine = fileIn.readLine()) != nullแล้วเรียกมันจาก ภายในลูปด้วยคุณจะได้ทุกบรรทัดที่สองเท่านั้น

หวังว่าคุณจะเข้าใจและขอให้โชคดี!


3

คุณสามารถกำหนดภายในคำสั่ง if ใน Java ได้เช่นกัน ตัวอย่างที่ดีคือการอ่านบางอย่างและเขียนออกมา:

http://www.exampledepot.com/egs/java.io/CopyFile.html?l=new

รหัส:

// Copies src file to dst file.
// If the dst file does not exist, it is created
void copy(File src, File dst) throws IOException 
{
    InputStream in = new FileInputStream(src);
    OutputStream out = new FileOutputStream(dst);

    // Transfer bytes from in to out
    byte[] buf = new byte[1024];
    int len;
    while ((len = in.read(buf)) > 0) {
        out.write(buf, 0, len);
    }
    in.close();
    out.close();
}

@ Nitrodist: ขอบคุณสำหรับตัวอย่างนี้ ฉันไม่ได้เป็นมืออาชีพทั้งใน Java หรือ Javascript ... เป็นเรื่องดีที่รู้ว่าวิธีนี้เป็นไปได้ใน Java :)
Michael Mao

ฉันไม่เห็นประเด็นนี้ คุณสามารถทำได้ใน Java, PHP และภาษาอื่น ๆ อีกมากมาย คำถามเกี่ยวกับ Javascript
pmrotule

ไม่จำเป็นคุณต้องอ่านคำถามใหม่อย่างละเอียด
Nitrodist

3

หากคุณจะอ้างถึงหนังสือ Martin Fowlers Refactoring ปรับปรุงการออกแบบโค้ดที่มีอยู่ ! จากนั้นมีหลายกรณีที่ควรปฏิบัติเช่น เงื่อนไขที่ซับซ้อนยาวเพื่อใช้การเรียกใช้ฟังก์ชันหรือเมธอดเพื่อยืนยันกรณีของคุณ:

"แรงจูงใจ

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

เช่นเดียวกับบล็อกโค้ดขนาดใหญ่คุณสามารถทำให้ความตั้งใจของคุณชัดเจนขึ้นได้โดยการแยกย่อยและแทนที่โค้ดส่วนหนึ่งด้วยการเรียกเมธอดที่ตั้งชื่อตามจุดประสงค์ของโค้ดบล็อกนั้น > ด้วยเงื่อนไขคุณจะได้รับประโยชน์เพิ่มเติมโดยการทำเช่นนี้สำหรับส่วนที่มีเงื่อนไขและแต่ละทางเลือก ด้วยวิธีนี้คุณจะเน้นเงื่อนไขและทำให้ชัดเจนว่าคุณกำลังแตกกิ่งก้านสาขาอะไร คุณยังเน้นเหตุผลของการแตกแขนง "

และใช่คำตอบของเขาก็ใช้ได้กับการใช้งาน Java มันไม่ได้กำหนดฟังก์ชันเงื่อนไขให้กับตัวแปรแม้ว่าในตัวอย่าง


1

มันไม่ใช่การปฏิบัติที่ดี คุณจะสับสนในไม่ช้า ดูเหมือนว่าจะคล้ายกับข้อผิดพลาดทั่วไป: ใช้ตัวดำเนินการ "=" และ "==" ในทางที่ผิด

คุณควรแบ่งรหัสออกเป็น 2 บรรทัด ไม่เพียง แต่ช่วยให้โค้ดชัดเจนขึ้น แต่ยังง่ายต่อการ refactor ในอนาคตอีกด้วย ลองนึกภาพว่าคุณเปลี่ยนเงื่อนไข IF? คุณอาจลบบรรทัดโดยไม่ได้ตั้งใจและตัวแปรของคุณไม่ได้รับค่าที่กำหนดให้อีกต่อไป


@thethanghn: นั่นคือสิ่งที่ฉันกลัว เมื่อฉันอายุมากขึ้นและขี้เกียจฉันก็ไม่อยากพิมพ์รหัสมากขึ้นถ้าการกดแป้นพิมพ์น้อยลงก็จะเพียงพอ :)
Michael Mao

1
ไม่ฉันไม่สับสนและฉันทำมันตลอดเวลา มีประโยชน์กับมัน
JDrake

แต่มันขึ้นอยู่กับว่าจริงๆใช่ไหม? หากคุณมาจากพื้นหลัง 'C' (และภาษาอื่น ๆ ที่ใช้ C) โครงสร้างจะคุ้นเคยมากและทางเลือกอื่นก็น่าอึดอัดมาก IMO มันคือสิ่งที่เรียนรู้ครั้งเดียวแล้วคุณจะรู้ ไม่ใช่สิ่งที่คุณจะเดินทางมากกว่าหนึ่งครั้ง
Max Waterman

0

ฉันจะพิจารณารูปแบบ C แบบโรงเรียนเก่ามากกว่านี้ มันไม่ใช่แนวปฏิบัติที่ดีจริงๆใน JavaScript ดังนั้นคุณควรหลีกเลี่ยง


8
ฉันไม่ถือว่าเป็นแนวปฏิบัติที่ดีใน C เช่นกัน
Matthew Crumley

1
ฉันคิดว่ามันเป็นการฝึกฝนที่ดีในหลาย ๆ ภาษา
JDrake

แค่พูดว่า 'ปฏิบัติไม่ดี' ไม่เพียงพอ imo มันเป็นแค่เรื่องการศึกษา - เรียนรู้ครั้งเดียวเท่านั้นเอง
Max Waterman


0

ฉันมาที่นี่จากโกลังซึ่งเป็นเรื่องธรรมดาที่จะได้เห็นอะไรแบบนี้

if (err := doSomething(); err != nil) {
    return nil, err
}

ซึ่งerrกำหนดขอบเขตไว้ที่ifบล็อกนั้นเท่านั้น ด้วยเหตุนี้นี่คือสิ่งที่ฉันทำใน es6 ซึ่งดูน่าเกลียด แต่ก็ไม่ได้ทำให้กฎ eslint ที่ค่อนข้างเข้มงวดของฉันกลายเป็นเสียงหอนและประสบความสำเร็จเช่นเดียวกัน

{
  const err = doSomething()
  if (err != null) {
    return (null, err)
  }
}

เครื่องหมายวงเล็บเสริมกำหนดใหม่เอ่อ "ขอบเขตศัพท์"? ซึ่งหมายความว่าฉันสามารถใช้ได้constและerrไม่สามารถใช้ได้กับบล็อกด้านนอก

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