【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制



博客主页:[小ᶻZ࿆] 本文专栏: 前端


文章目录



💯前言

  • 在编写JavaScript程序 时,逻辑运算符&&(逻辑与)和||(逻辑或)是常用的构造工具,能够简洁地实现逻辑判断和条件分支。然而,在处理复杂逻辑时,这些运算符的优先级 和短路求值特性可能导致混淆和误解。对于开发者而言,深入理解这些运算符的工作机制不仅有助于编写高效、健壮的代码,还可以减少调试中遇到的意外情况。本文旨在深入探讨JavaScript 中逻辑运算符的优先级、短路求值,以及它们在编写高效、健壮代码中的应用。
    JavaScript

💯案例背景

  • 我有一次在JavaScript 中写了一个函数实现类似数组的push功能 ,在判断arguments的第 0 项是否为数组的这一行采用了逻辑运算符 ,发现自己对于逻辑运算符理解并不是很清晰确切,所以本文就是通过这段逻辑运算符代码来引入这一讨论。

    js 复制代码
    var arr = [1, 2, 3];
    function push() {
        var arr = Array.isArray(arguments[0]) && arguments[0] || [];
        console.log(arr);
        var argLen = arguments.length;
        for (var i = 1; i < argLen; i++) {
            var len = arr.length;
            arr[len] = arguments[i];
        }
        return arr;
    }
    
    console.log(push([1, 2, 3], 4, 5, 6, 7));

该代码结合使用了 &&|| 两个逻辑运算符,如下所示:

javascript 复制代码
var arr = Array.isArray(arguments[0]) && arguments[0] || [];

尽管这段代码看起来很简洁,但对于缺乏深入理解逻辑运算符优先级的程序员而言,容易产生混淆。特别是当 &&|| 结合在一起时,其执行顺序并不直观。为了全面理解这段代码的逻辑,我们需要首先明确 JavaScript 中运算符的优先级及逻辑运算符的短路求值机制。


💯逻辑运算符的优先级与短路求值


运算符优先级的概念

运算符优先级决定了当一个表达式包含多个运算符时,哪些运算符优先执行。在 JavaScript 中,不同运算符具有不同的优先级,这意味着在复杂表达式中,优先级高的运算符将首先被计算。例如,对于表达式 1 + 2 * 3,乘法运算符 * 的优先级高于加法运算符 +,因此乘法首先执行,最终结果为 1 + 6 = 7

在逻辑运算符中,&& 的优先级高于 ||,这意味着在包含这两个运算符的表达式中,&& 会先执行,然后才是 ||


短路求值的概念

短路求值是一种逻辑运算符的计算特性。在 &&|| 运算的过程中,JavaScript 会依据操作数的值来决定是否继续计算下一个操作数,或者直接返回最终结果。

  • 逻辑与 (&&) 的短路求值 :如果第一个操作数为 false,则整个表达式的结果必然为 false,此时不再计算第二个操作数。例如,false && anything 的结果一定是 false,因为无论 anything 的值是什么,只要第一个操作数为 false,结果就是 false

  • 逻辑或 (||) 的短路求值 :如果第一个操作数为 true,整个表达式的结果就是 true,无需再计算第二个操作数。例如,true || anything 的结果总是 true,因为只要有一个操作数为 true,整个表达式的值就是 true

理解这些特性后,我们便能够更深入地分析复杂的逻辑表达式。


💯分析案例代码的执行过程

回到我们的示例代码:

javascript 复制代码
var arr = Array.isArray(arguments[0]) && arguments[0] || [];

理解这段代码的执行过程需要按照逻辑运算符的优先级和短路求值规则进行逐步分析。


第一步:执行 && 运算

首先执行的是 Array.isArray(arguments[0]) && arguments[0],因为 && 的优先级高于 ||

  • Array.isArray(arguments[0]) :这部分代码用于检查 arguments[0] 是否是一个数组。
  • 如果 arguments[0] 是一个数组,则表达式的值为 arguments[0]
  • 如果 arguments[0] 不是数组或未定义,则表达式的值为 false

第二步:执行 || 运算

接下来执行 && 运算后的结果与 || [] 进行逻辑或运算。

  • 如果 && 部分的结果是一个数组(即 arguments[0] 是数组),则 || 运算符左边的值为 true,直接返回该数组。
  • 如果 && 部分的结果是 false,则 || 会返回右侧的值 [],即空数组。

💯运算示例

以下示例演示了不同输入下该段代码的执行过程。


示例 1:arguments[0] 为数组

javascript 复制代码
function test() {
    var arr = Array.isArray(arguments[0]) && arguments[0] || [];
    console.log(arr);
}

test([1, 2, 3]);  // 输出:[1, 2, 3]
  • 在此示例中,arguments[0][1, 2, 3],即数组。
  • Array.isArray(arguments[0]) 返回 true,因此 && 的右侧 arguments[0] 被返回,即 [1, 2, 3]
  • 因为 && 部分结果为数组,|| 操作符不会再执行右侧的 [],最终结果为 [1, 2, 3]

示例 2:arguments[0] 不是数组

javascript 复制代码
test("not an array");  // 输出:[]
  • 在此示例中,arguments[0]"not an array",不是数组。
  • Array.isArray(arguments[0]) 返回 false,因此 && 部分的结果为 false
  • || 操作符执行右侧的 [],最终结果为空数组 []

示例 3:未传入参数

javascript 复制代码
test();  // 输出:[]
  • 在这种情况下,arguments[0]undefined
  • Array.isArray(arguments[0]) 返回 false,因此 && 部分的结果为 false
  • || 操作符执行右侧的 [],最终结果为空数组 []

💯按顺序计算运算符的重要性


  • 在逻辑运算中,不能直接将 || [] 看作一个整体,而必须依据运算符优先级和计算顺序来进行判断。&&|| 都是从左到右逐步执行的。
  1. 首先计算 && 部分 :由于 && 的优先级更高,必须首先计算 Array.isArray(arguments[0]) && arguments[0]
  2. 然后计算 || 部分 :根据 && 的计算结果,执行 || []。若前者结果为 false,则返回空数组 []

💯逻辑运算符的最佳实践

了解了这些基础知识之后,我们可以总结出一些逻辑运算符的最佳实践,以便在实际开发中编写出更易于维护和理解的代码。


1. 使用逻辑运算符进行安全的参数检查

在 JavaScript 中,逻辑运算符常用于进行参数检查,以确保参数的类型和有效性。例如:

javascript 复制代码
function safePush(arr, value) {
    var array = Array.isArray(arr) && arr || [];
    array.push(value);
    return array;
}

safePush 函数利用 &&|| 运算符来确保输入的 arr 是一个数组。如果 arr 不是数组,则使用空数组作为默认值,从而避免传入无效参数导致程序崩溃的风险。


2. 简化代码逻辑

逻辑运算符可以用于简化代码逻辑,使其更加简洁明了。例如:

javascript 复制代码
var isValid = condition1 && condition2 || defaultValue;

这种写法可以减少 if...else 语句的使用,使代码更为精练。但在使用这种简化写法时,必须理解运算符的优先级和短路求值规则,以避免逻辑错误。


3. 确保逻辑表达式易于理解

尽管逻辑运算符可以简化代码,但过度使用复合逻辑运算符可能导致代码的可读性降低。因此,推荐对复杂逻辑进行注释说明,或者将逻辑拆分为多个步骤以提高可读性。

例如,将原代码拆分为易于理解的步骤:

javascript 复制代码
var isArray = Array.isArray(arguments[0]);
var arr = isArray ? arguments[0] : [];

这种写法虽然稍显冗长,但逻辑清晰,易于维护。


4. 结合空值合并运算符 ??

JavaScript 中的新特性空值合并运算符 ?? 可以与逻辑运算符结合使用,进一步提高代码的可读性。例如:

javascript 复制代码
var arr = Array.isArray(arguments[0]) ? arguments[0] : []; 
var finalArr = arr ?? [];

空值合并运算符使得当 arrnullundefined 时,可以自动提供一个默认值,从而使代码更加健壮。


💯JavaScript 运算符优先级表

为了更好地理解逻辑运算符的优先级,以下列出了 JavaScript 中部分常用运算符的优先级(从高到低):

优先级等级 描述 示例符号
1 成员访问、数组下标、函数调用 ., [], ()
2 一元运算符 !, typeof, 等
3 乘除、加减 *, /, +, -
4 比较运算符 <, <=, >, >=
5 相等运算符 ==, ===, !=, !==
6 逻辑与 &&
7 逻辑或 `
8 空值合并 ??

通过掌握这些运算符的优先级,可以更好地理解复杂表达式的计算顺序,从而编写出更为健壮的代码。


💯 拓展:短路求值的应用场景


1. 条件赋值

短路求值常用于条件赋值。例如:

javascript 复制代码
var name = user && user.name || 'Guest';

此段代码用于从 user 对象中获取 name 属性,如果 useruser.namenullundefined,则返回默认值 'Guest'。这种写法简洁明了。


2. 条件执行函数

短路求值还可以用于根据条件执行函数,例如:

javascript 复制代码
isLoggedIn && showDashboard();

当且仅当 isLoggedIntrue 时,showDashboard() 函数才会被调用。如果 isLoggedInfalse,则不会执行任何操作。这种方式可以有效减少冗余的 if 判断。


3. 防止空引用

在操作嵌套对象的属性时,短路求值可以防止出现空引用错误。例如:

javascript 复制代码
var street = user && user.address && user.address.street;

这种写法确保了只有在 useruser.address 都存在的情况下,才会访问 user.address.street,否则返回 undefined。这种方式可以有效避免嵌套对象引用时的潜在错误。


4. 与默认参数结合

短路求值还可以用于结合函数的默认参数使用。例如:

javascript 复制代码
function getConfig(config) {
    var finalConfig = config || { theme: 'dark', layout: 'grid' };
    return finalConfig;
}

如果未提供 config 参数,或者参数值为 nullundefined,则使用默认配置对象。这种方式能够确保函数的行为一致性,并且提高代码的可维护性。


💯小结

  • 在 JavaScript 中,逻辑运算符 &&|| 的组合使用具有极大的灵活性和表达力。理解它们的优先级与短路求值机制是编写高质量代码的关键。在本文中,我们通过示例代码详细分析了逻辑运算符的执行过程,并讨论了它们的最佳实践。

  • 逻辑与 (&&) 的优先级高于逻辑或 (||) ,因此在复杂表达式中需要特别注意运算符的执行顺序。

  • 短路求值特性 可以帮助简化代码逻辑,但在使用时必须小心,避免逻辑上的误解。

  • 在实际编写代码时,利用逻辑运算符简化参数检查和条件赋值操作时,应始终确保代码的可读性和可维护性。

  • 结合空值合并运算符 以及其他新特性可以进一步提升代码的健壮性和灵活性。



相关推荐
用户02670019949412 分钟前
[深入了解Google Trends API的使用与优化指南]
前端
hunter20620622 分钟前
linux通过web向mac远程传输字符串,mac收到后在终端中直接打印。
linux·前端·macos
无敌最俊朗@33 分钟前
unity——Prejct3——背景音乐
java·开发语言·unity·游戏引擎
糖炒栗子要加糖34 分钟前
imread和jpeg_read在MATLAB中处理图像时的不同
开发语言·matlab
火烧屁屁啦41 分钟前
【JavaEE进阶】SpringMVC 响应
java·开发语言·java-ee
我是菜鸟0713号1 小时前
上位机工作感想-2024年工作总结和来年计划
开发语言·qt
SomeB1oody1 小时前
【Rust自学】13.5. 迭代器 Pt.1:迭代器的定义、iterator trait和next方法
开发语言·后端·rust
Hello.Reader1 小时前
用 Rust 写下第一个 “Hello, World!”
开发语言·后端·rust
叫我:松哥1 小时前
基于python对抖音热门视频的数据分析与实现
开发语言·python·数据挖掘·数据分析·数据可视化·情感分析·lda主题分析
我曾经是个程序员1 小时前
htmlcssJavaScript网页开发:年会手机号抽奖案例
javascript