Gán biến trong câu lệnh if, thực hành tốt hay không? [đóng cửa]


113

Một năm trước, tôi đã chuyển từ các ngôn ngữ OO cổ điển như Java sang JavaScript. Mã sau chắc chắn không được khuyến nghị (hoặc thậm chí không đúng) trong 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;
}

Về cơ bản, tôi vừa phát hiện ra rằng tôi có thể gán một biến cho một giá trị trong câu lệnh điều kiện if và ngay lập tức kiểm tra giá trị được gán như thể nó là boolean.

Để đặt cược an toàn hơn, tôi thường tách nó thành hai dòng mã, gán trước sau đó kiểm tra biến, nhưng bây giờ tôi tìm thấy điều này, tôi chỉ tự hỏi liệu nó có phải là phương pháp tốt hay không dưới con mắt của các nhà phát triển JavaScript có kinh nghiệm?


"The following code is definitely not recommended (or event not correct) in Java..."Nó có chính xác trong JavaScript không? Bởi vì, theo như tôi thấy, bạn trả về một số nguyên ( return parseInt(...)) nếu dayNumber != -1là đúng, nhưng là boolean nếu sai.
Daniel Kvist

Câu trả lời:


117

Tôi sẽ không giới thiệu nó. Vấn đề là, có vẻ như một lỗi phổ biến khi bạn cố gắng so sánh các giá trị nhưng sử dụng một giá trị =thay vì ==hoặc ===. Ví dụ, khi bạn thấy điều này:

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

bạn không biết đó có phải là những gì họ muốn làm hay họ định viết điều này:

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

Nếu bạn thực sự muốn thực hiện nhiệm vụ tại chỗ, tôi cũng khuyên bạn nên thực hiện một phép so sánh rõ ràng:

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

1
@Matthew Crumley: điều này trả lời câu hỏi của tôi một cách rõ ràng. Tôi không kiểm tra bằng cách gán mà kiểm tra bất kỳ giá trị nào được đánh giá là sau khi gán. Cách hiểu này có đúng không?
Michael Mao

1
@Michael: vâng, chính xác. Thêm so sánh về cơ bản chỉ làm cho ý định của bạn rõ ràng hơn.
Matthew Crumley

4
Tuy nhiên, ví dụ cuối cùng không hoạt động nếu bạn đang kiểm tra sự thất bại / thành công của một hàm trả về boolean. Nói cách khác, while if (resultArr = myNeedle.exec(myHaystack)) {...}hoạt động, if ((resultArr = myNeedle.exec(myHaystack)) === true) {...}không phải vì việc gán cho resultArr luôn luôn đúng ngay cả khi kết quả của hàm không phải. Nếu ai sử dụng hàm này .. construct thì nhớ khai báo biến kết quả trước; 'var' không hợp pháp trong câu lệnh điều kiện if.
Ville

3
Bạn có thể sử dụng if (!!(value = someFunction())), nhưng như bạn đã nói, vấn đề là bạn không thể sử dụng varbên trong ifnên cuối cùng bạn sẽ tạo toàn cục hoặc không đạt được gì vì bạn phải khai báo valuetrong một dòng riêng. Thật xấu hổ, tôi thực sự thích cấu trúc này trong C ++.
riv

1
@riv, bạn nói đúng; trong trường hợp hàm trả về kiểu boolean - như tôi đã nói trong nhận xét ở trên - thì điều kiện hoạt động như mong đợi. Nhưng nếu hàm trả về một boolean (như trong ví dụ của tôi) thì toàn bộ cấu trúc là loại không hợp lý; rõ ràng dòng suy nghĩ của tôi - đánh giá từ ví dụ - rằng hàm sẽ trả về một mảng. Một kiểm tra nhanh chỉ ra rằng điều kiện chỉ đánh giá truekhi hàm trả về true, nhưng trong tất cả các trường hợp khác (bao gồm khi trả về một mảng, chuỗi, số hoặc null) thì nó sẽ đánh giá thành false.
Ville

27

Tôi không thấy bằng chứng nào cho thấy đó là một thực hành không tốt. Có, nó có thể trông giống như một sai lầm nhưng điều đó có thể dễ dàng khắc phục bằng cách nhận xét một cách thận trọng. Lấy ví dụ:

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

Tại sao hàm đó nên được phép chạy lần thứ 2 với:

alert(processorIntensiveFunction());

Vì phiên bản đầu tiên LOOKS xấu? Tôi không thể đồng ý với logic đó.


32
Không phải để đào một bình luận cũ, nhưng tôi không đồng ý với lập luận của bạn. Mã có thể đọc được phải tự giải thích mà không cần nhận xét - thêm nhận xét vào mã gây nhầm lẫn không phải là cách khắc phục. Đối với phần thứ hai, nói rằng giải pháp thay thế là gọi lại hàm, tôi không nghĩ có ai có ý định làm điều đó. Thay vào đó bạn sẽ làm gìx = processorItensiveFunction(); if(x) { alert(x); }
Maksim

8
@maksim: Tôi thích mã có thể đọc được, nhưng điều đó không nhất thiết có nghĩa là mã phải quá dài hoặc quá dài. Việc trải rộng mọi thứ trên nhiều dòng và tung giá trị giữa các biến thực sự có thể dẫn đến mã tồi tệ hơn. Mã được chèn có thể có các tác dụng phụ không lường trước được trong một ngôn ngữ được gõ yếu / linh hoạt như JS. Phép gán trong một câu lệnh điều kiện là hợp lệ trong javascript, vì bạn chỉ hỏi "nếu phép gán có hợp lệ, hãy làm điều gì đó có thể bao gồm kết quả của việc gán". Nhưng quả thực, việc gán trước điều kiện cũng hợp lệ, không quá dài dòng và được sử dụng phổ biến hơn.
okdewit

1
@maksim tại sao bạn nghĩ if ( ! x = anyFunction() )là không đọc được? Nó không cần bất kỳ bình luận nào.
JDrake

Nếu bạn sửa chữa OPC, làm việc với các nhà phát triển khác ở các cấp độ kỹ năng khác nhau (nói cách khác, bạn là một người chuyên nghiệp), bạn sẽ ghét rằng điều này thậm chí có thể xảy ra.
davidjmcclelland

2
@maksim - cũng là giải pháp của bạn sẽ rất bất tiện trong một if-elsetình huống. Hãy xem xét- if (condition) {...} else if (x = processorIntensiveFunction()) {alert(x)} Trước đó của bạn x = processorIntensiveFunction();sẽ là một nỗ lực lãng phí nếu ban đầu conditionlà đúng.
Adrian Bartholomew

16

Tôi đã làm điều đó nhiều lần. Để bỏ qua cảnh báo JavaScript, tôi thêm hai parens:

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

Bạn nên tránh nó, nếu bạn thực sự muốn sử dụng nó, hãy viết bình luận bên trên nó cho biết bạn đang làm gì.


1
@SHiNKiROU: làm cách nào để xem cảnh báo javascript? Có trình biên dịch Javascript không? hay trình thông dịch sẽ tạo ra một số loại cảnh báo? Tôi đang sử dụng bảng điều khiển Firefox như trong gỡ lỗi javascript mọi lúc nhưng không bao giờ thấy bất kỳ kết quả nào tương tự. Xin lỗi về kinh nghiệm hạn chế của tôi.
Michael Mao

5
@Michael: JSLint ( jslint.com ) là một chương trình / thư viện phổ biến giúp kiểm tra các chương trình JavaScript để tìm các lỗi có thể xảy ra hoặc mã xấu.
Matthew Crumley

Sử dụng Mozilla Firefox với phần mở rộng Firebug và / hoặc Web Developer để kiểm tra các cảnh báo.
Ming-Tang

Tôi vừa thử nó với if ((a = [1, 2]).length > 0) { console.log(a); }nơi achưa được khởi tạo ở đâu và nó thực sự đã hoạt động (thật tuyệt! Làm cho việc sử dụng regex dễ dàng hơn nhiều). Điều này có chính xác mà tôi không cần bất kỳ điều var|const|letgì ở đây? Bạn có biết nơi tôi có thể đọc thêm về thủ thuật này không?
t3chb0t

4

Bạn cũng có thể làm điều này trong Java. Và không, đó không phải là một thực hành tốt. :)

(Và sử dụng ===trong Javascript để nhập bình đẳng. Đọc cuốn sách Những phần hay của Crockford trên JS.)


@quixoto: Tôi có thể thực hiện thủ thuật này trong Java không? Tôi tự hỏi ... Tôi không có jdk bằng atm nên tôi không thể lấy mã mẫu trong Java. Từ bộ nhớ kém của tôi, Java sẽ chỉ khiến bạn gặp lỗi Runtime nếu giá trị trả về đánh giá thứ gì đó không phải là boolean như trong câu lệnh điều kiện if, phải không?
Michael Mao

1
À, vâng, trong Java, nó được kiểm tra kiểu là kiểu boolean. Nhưng bạn có thể làm đượcif (foo = getSomeBoolValue()) { }
Ben Zotto

Vâng đúng rồi. một biến boolean để kiểm tra xem điều gì đó đã thành công hay chưa và một biến khác để lưu trữ giá trị được trả về. Đó là cách Java làm công việc của mình, tôi quá quen thuộc với điều đó vì vậy tôi cảm thấy lạ để xem Javascript có thể làm hai việc trong một đơn thuần dòng :)
Michael Mao

@BenZotto nó không phải là thực hành tốt tại sao? "Để tránh việc vô tình sử dụng sai một biến, thông thường nên đưa biến vào phạm vi nhỏ nhất có thể. Đặc biệt, tốt nhất là nên trì hoãn việc định nghĩa một biến cho đến khi người ta có thể cung cấp cho nó một giá trị ban đầu ... Một trong những ứng dụng tuyệt vời nhất của hai nguyên tắc này là khai báo một biến trong một điều kiện. " - Stroustrup, "Ngôn ngữ lập trình C ++."
JDrake

1
Xin chào, tôi đến đây với tư cách là người dùng javascript node.js. Tại sao lại không hay trong một trường hợp tôi gặp phải vấn đề này: if (myvar = 'just a test') Tạo myvar biến node.js TOÀN CẦU ( nodejs.org/docs/latest-v12.x/api/globals.html #globals_global ). Vì vậy, nếu bạn giống như tôi và đã sử dụng biến đó trong xử lý yêu cầu máy chủ (quay lại nó sau vài giây khi các yêu cầu khác có thể đã bỏ qua và nhiều thứ khác), bạn có thể ngạc nhiên về kết quả mình nhận được. Vì vậy, khuyến nghị là: Lưu ý rằng mẫu này tạo ra một biến toàn cục trong node.js.
pein-consults.de

4

Có một trường hợp khi bạn làm điều đó, với while-loops.
Khi đọc tệp, bạn thường làm như thế này:

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

Nhìn vào while-loop ở dòng 9. Tại đó, một dòng mới được đọc và lưu trữ trong một biến, sau đó nội dung của vòng lặp được chạy. Tôi biết đây không phải là mộtif -statement, nhưng tôi đoán một vòng lặp while cũng có thể được đưa vào câu hỏi của bạn.

Lý do là khi sử dụng a FileInputStream, mỗi khi bạn gọi FileInputStream.readLine(), nó sẽ đọc dòng tiếp theo trong tệp, vì vậy nếu bạn đã gọi nó từ vòng lặp fileIn.readLine() != nullmà không cần gán biến, thay vì gọi(currentLine = fileIn.readLine()) != null , và sau đó gọi nó từ bên trong vòng lặp, bạn sẽ chỉ nhận được mỗi dòng thứ hai.

Hy vọng bạn hiểu, và chúc may mắn!


3

Bạn cũng có thể thực hiện các phép gán trong câu lệnh if trong Java. Một ví dụ điển hình là đọc một cái gì đó và viết nó ra:

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

Mật mã:

// 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: cảm ơn vì ví dụ này. Tôi thực sự không chuyên nghiệp trong cả hai Java hoặc javascript ... Nó là tốt để biết phương pháp này cũng có tính khả thi trong Java :)
Michael Mao

Tôi không thấy vấn đề này. Bạn có thể làm điều đó bằng Java, PHP và nhiều ngôn ngữ khác. Câu hỏi là về Javascript.
pmrotule

Không, không nhất thiết bạn phải đọc lại câu hỏi một cách cẩn thận.
Nitrodist

3

Nếu bạn đã tham khảo cuốn sách Refactoring của Martin Fowlers cải thiện thiết kế của mã hiện có ! Sau đó, có một số trường hợp mà nó sẽ là thực hành tốt, ví dụ. các điều kiện phức tạp dài để sử dụng một hàm hoặc phương thức gọi để xác nhận trường hợp của bạn:

"Động lực

Một trong những khu vực phức tạp phổ biến nhất trong chương trình nằm ở logic điều kiện phức tạp. Khi bạn viết mã để kiểm tra các điều kiện và thực hiện nhiều thứ khác nhau tùy thuộc vào các điều kiện khác nhau, bạn nhanh chóng kết thúc với một phương pháp khá dài. Độ dài của một phương pháp tự nó là một yếu tố làm cho nó khó đọc hơn, nhưng các điều kiện làm tăng độ khó. Vấn đề thường nằm ở chỗ mã, cả trong kiểm tra điều kiện và trong các hành động, cho bạn biết điều gì xảy ra nhưng có thể dễ dàng che khuất lý do tại sao nó xảy ra.

Như với bất kỳ khối mã lớn nào, bạn có thể làm cho ý định của mình rõ ràng hơn bằng cách phân tách nó và thay thế các đoạn mã bằng một lệnh gọi phương thức được đặt tên theo ý định của khối mã đó. > Với các điều kiện, bạn có thể nhận được thêm lợi ích bằng cách làm điều này cho phần có điều kiện và từng lựa chọn thay thế. Bằng cách này, bạn làm nổi bật điều kiện và làm cho nó rõ ràng những gì bạn> đang phân nhánh. Bạn cũng nêu rõ lý do phân nhánh. "

Và có câu trả lời của anh ấy cũng hợp lệ cho các triển khai Java. Nó không gán hàm điều kiện cho một biến mặc dù trong các ví dụ.


1

Đó không phải là thực hành tốt. Bạn sẽ sớm nhầm lẫn về nó. Nó trông giống với một lỗi phổ biến: sử dụng sai các toán tử "=" và "==".

Bạn nên chia nó thành 2 dòng mã. Nó không chỉ giúp làm cho mã rõ ràng hơn mà còn dễ dàng cấu trúc lại trong tương lai. Hãy tưởng tượng rằng bạn thay đổi điều kiện IF? Bạn có thể vô tình xóa dòng và biến của bạn không còn nhận được giá trị được gán cho nó nữa.


@thethanghn: đó chính xác là điều tôi sợ. khi tôi già đi và lười biếng Tôi chỉ không muốn gõ nhiều hơn vào mã nếu tổ hợp phím ít sẽ chỉ đủ :)
Michael Mao

1
Không, tôi không bối rối và tôi làm điều đó mọi lúc. Có những lợi ích cho nó.
JDrake

Nó thực sự phụ thuộc, mặc dù, phải không? Nếu bạn đến từ nền tảng 'C' (và các ngôn ngữ khác dựa trên C), thì cấu trúc này rất quen thuộc và các lựa chọn thay thế rất khó xử. IMO, nó là thứ học một lần rồi biết. Nó không phải là thứ bạn sẽ đi hơn một lần.
Max Waterman

0

Tôi sẽ coi đây là một phong cách C kiểu cũ; nó không thực sự tốt trong JavaScript, vì vậy bạn nên tránh nó.


8
Tôi cũng không coi đó là một thực hành tốt trong C.
Matthew Crumley

1
Tôi coi đó là một thực hành tốt trong nhiều ngôn ngữ.
JDrake

Chỉ nói 'thực hành không tốt' là không đủ, imo. Nó thực sự chỉ là về giáo dục - nó được học một lần, và thế là xong.
Max Waterman

0

bạn có thể làm điều gì đó như vậy:

if (value = /* sic */ some_function()){
  use_value(value)
}

0

Tôi đến đây từ golang, nơi thường thấy những thứ như

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

Trong đó errchỉ phạm vi cho ifkhối đó . Như vậy, đây là những gì tôi đang làm trong es6, có vẻ khá xấu, nhưng không làm thay đổi các quy tắc eslint khá nghiêm ngặt của tôi và đạt được điều tương tự.

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

Dấu ngoặc nhọn bổ sung xác định một "phạm vi từ vựng" mới, uh,? Có nghĩa là tôi có thể sử dụng consterrkhông có sẵn cho khối bên ngoài.

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.