Hiểu Rõ Toán Tử AND Logic (&&) Trong JavaScript

Giới thiệu về Toán tử AND Logic (&&)

Trong JavaScript, toán tử AND logic được biểu thị bằng dấu và kép (&&). Khác với các ngôn ngữ lập trình truyền thống nơi toán tử này chủ yếu làm việc với các giá trị boolean, && trong JavaScript có một hành vi linh hoạt hơn, có thể hoạt động trên mọi kiểu dữ liệu và không nhất thiết trả về một giá trị boolean.

Hành vi Cơ bản với Giá trị Boolean

Khi cả hai toán hạng của && là các giá trị boolean (true hoặc false), kết quả hoạt động giống như logic AND thông thường:

Toán hạng 1 Toán hạng 2 Kết quả
true true true
true false false
false true false
false false false

Cơ chế Đánh giá Rút gọn (Short-Circuiting) và Giá trị Trả về

Đặc điểm nổi bật và quan trọng nhất của toán tử && trong JavaScript là cơ chế đánh giá rút gọn (short-circuiting). Cơ chế này chi phối cách toán tử hoạt động và giá trị mà nó trả về:

  1. Đánh giá toán hạng đầu tiên: JavaScript sẽ luôn đánh giá toán hạng bên trái trước.
  2. Nếu toán hạng đầu tiên là falsy: Nếu giá trị của toán hạng đầu tiên được coi là false (ví dụ: false, 0, null, undefined, NaN, '' - chuỗi rỗng), toán tử && sẽ ngừng đánh giá ngay lập tức và trả về chính giá trị của toán hạng đầu tiên đó. Toán hạng thứ hai sẽ không bao giờ được đánh giá.
  3. Nếu toán hạng đầu tiên là truthy: Nếu giá trị của toán hạng đầu tiên được coi là true, JavaScript sẽ tiếp tục đánh giá toán hạng thứ hai và trả về giá trị của toán hạng thứ hai.

Hiểu cơ chế này là chìa khóa để nắm vững &&. Giá trị trả về không nhất thiết là boolean, mà có thể là bất kỳ kiểu dữ liệu nào từ một trong hai toán hạng.

Minh họa Cơ chế Short-Circuiting

Trường hợp 1: Toán hạng đầu tiên là falsy (không có lỗi khi toán hạng thứ hai chưa khai báo)

Trong ví dụ này, processEnabled là false (một giá trị falsy). Toán tử && sẽ dừng lại và trả về false mà không cần đánh giá undeclaredConfig. Do đó, sẽ không có lỗi xảy ra ngay cả khi undeclaredConfig là một biến chưa được khai báo.

let processEnabled = false;
// `undeclaredConfig` là một biến chưa được khai báo, nhưng sẽ không gây lỗi ở đây
let finalConfiguration = processEnabled && undeclaredConfig;
console.log("Kết quả khi toán hạng đầu tiên là falsy:", finalConfiguration); // Output: false

Trường hợp 2: Toán hạng đầu tiên là truthy (có thể gây lỗi nếu toán hạng thứ hai chưa khai báo)

Ở đây, isValidUser là true (một giá trị truthy). Toán tử && sẽ tiếp tục đánh giá userPermissions. Nếu userPermissions là một biến chưa được khai báo, việc truy cập nó sẽ gây ra một ReferenceError.

let isValidUser = true;
// `userPermissions` là một biến chưa được khai báo
try {
    let accessRights = isValidUser && userPermissions; // Sẽ gây lỗi ReferenceError
    console.log("Quyền truy cập:", accessRights);
} catch (error) {
    console.error("Lỗi xảy ra:", error.name + ": " + error.message);
    // Output: ReferenceError: userPermissions is not defined
}

Các Ví dụ về Giá trị Trả về của Toán tử &&

Dựa trên cơ chế short-circuiting, đây là một số ví dụ minh họa các giá trị trả về của && với các kiểu dữ liệu khác nhau:

  • true && "Hello" → "Hello" (true là truthy, trả về toán hạng thứ hai)
  • "Hello" && true → true ("Hello" là truthy, trả về toán hạng thứ hai)
  • false && "World" → false (false là falsy, trả về toán hạng đầu tiên)
  • 0 && "Message" → 0 (0 là falsy, trả về toán hạng đầu tiên)
  • {} && "Object is truthy" → "Object is truthy" ({} là truthy, trả về toán hạng thứ hai)
  • "Non-empty string" && null → null ("Non-empty string" là truthy, trả về toán hạng thứ hai)
  • null && "Not evaluated" → null (null là falsy, trả về toán hạng đầu tiên)
  • NaN && 123 → NaN (NaN là falsy, trả về toán hạng đầu tiên)
  • 123 && NaN → NaN (123 là truthy, trả về toán hạng thứ hai)
  • undefined && "Text" → undefined (undefined là falsy, trả về toán hạng đầu tiên)

Mã Kiểm tra Thực tế

Đoạn mã JavaScript sau đây minh họa các trường hợp khác nhau của toán tử && trong môi trường trình duyệt, sử dụng document.write để hiển thị kết quả.


    document.write("<h2>Kiểm tra hoạt động của toán tử AND (&&) JavaScript</h2>");
    document.write("<hr/>");

    // Trường hợp 1: Cả hai toán hạng là boolean
    let flagA = true;
    let flagB = false;
    document.write("<p><b>1. Toán hạng boolean:</b></p>");
    document.write(`<p><code>${flagA} && ${flagB}</code> &rarr; <code>${flagA && flagB}</code></p>`);
    document.write("<hr/>");

    // Trường hợp 2: Toán hạng là đối tượng và boolean
    let configObject = { mode: "dark", userId: 101 };
    let isActiveState = true;
    let isInactiveState = false;

    document.write("<p><b>2. Đối tượng và Boolean:</b></p>");
    document.write(`<p><code>${isActiveState} && configObject</code> &rarr; <code>${(isActiveState && configObject) === configObject ? JSON.stringify(configObject) + " (là đối tượng)" : isActiveState && configObject}</code></p>`);
    document.write(`<p><code>configObject && ${isInactiveState}</code> &rarr; <code>${configObject && isInactiveState}</code></p>`);
    document.write("<hr/>");

    // Trường hợp 3: Cả hai toán hạng là đối tượng
    let itemData1 = { id: 1, name: "Product X" };
    let itemData2 = { id: 2, name: "Product Y" };
    document.write("<p><b>3. Hai đối tượng:</b></p>");
    let resultObjects = itemData1 && itemData2;
    document.write(`<p><code>itemData1 && itemData2</code> &rarr; <code>${(resultObjects === itemData2 ? JSON.stringify(itemData2) + " (là đối tượng itemData2)" : JSON.stringify(resultObjects))}</code></p>`);
    document.write("<hr/>");

    // Trường hợp 4: Toán hạng là null, NaN, undefined
    let valueNull = null;
    let valueNaN = NaN;
    let valueUndefined; // Đã khai báo nhưng giá trị undefined
    let someText = "Valid String";

    document.write("<p><b>4. null, NaN, undefined:</b></p>");
    document.write(`<p><code>${valueNull} && ${someText}</code> &rarr; <code>${valueNull && someText}</code></p>`);
    document.write(`<p><code>${valueNaN} && ${someText}</code> &rarr; <code>${valueNaN && someText}</code></p>`);
    document.write(`<p><code>${valueUndefined} && ${someText}</code> &rarr; <code>${valueUndefined && someText}</code></p>`);
    document.write("<hr/>");

    // Trường hợp 5: Minh họa Short-circuiting với biến chưa khai báo
    document.write("<p><b>5. Short-circuiting với biến chưa khai báo:</b></p>");
    let isConfigLoaded = false;
    // `settingsData` là biến chưa khai báo (lưu ý: không dùng `let settingsData;` ở đây để mô phỏng)
    document.write("<p>Khi toán hạng đầu tiên là <code>false</code>, toán hạng thứ hai (chưa khai báo) sẽ KHÔNG được đánh giá, không gây lỗi:</p>");
    try {
        let resultSafe = isConfigLoaded && settingsData; // `settingsData` is not defined at this point.
        document.write(`<p><code>${isConfigLoaded} && settingsData</code> &rarr; <code>${resultSafe}</code></p>`);
    } catch (err) {
        document.write(`<p>Lỗi không mong muốn trong thử nghiệm an toàn: <code>${err.name}: ${err.message}</code></p>`);
    }

    document.write("<p>Khi toán hạng đầu tiên là <code>true</code>, toán hạng thứ hai sẽ được đánh giá. Nếu là biến chưa khai báo, SẼ gây lỗi <code>ReferenceError</code>:</p>");
    let needsValidation = true;
    // `validationEngine` là biến chưa khai báo
    try {
        let resultError = needsValidation && validationEngine; // `validationEngine` is not defined at this point.
        document.write(`<p><code>${needsValidation} && validationEngine</code> &rarr; <code>${resultError}</code></p>`);
    } catch (err) {
        document.write(`<p>Đã xảy ra lỗi: <code>${err.name}: ${err.message}</code></p>`);
    }

Thẻ: JavaScript LogicalOperators ANDOperator ShortCircuiting TruthyFalsy

Đăng vào ngày 31 tháng 10 lúc 17:36