深入解析JavaScript中的操作符:从基础到高级

90 阅读11分钟

深入解析JavaScript中的操作符:从基础到高级

引言

在 JavaScript 的编程世界中,操作符是我们日常编码不可或缺的一部分。它们不仅用于执行基本的数学运算,还用于比较值、逻辑判断、位操作等。理解这些操作符的工作原理和最佳实践,对于编写高效、健壮的代码至关重要。在本文中,我们将一起探索 JavaScript 中的各类操作符,包括它们的用法、优先级以及常见陷阱,帮助你更加熟练地运用它们来构建复杂而强大的应用程序。

运算元

运算元(Operands)指的是操作符操作的对象,也就是参与操作的数据。

  • 一元操作符:需要一个运算元。例如,一元加和减(+/-
// 一元加由一个加号(+)表示,放在变量前面,对数值没有任何影响
var num = 25
num = +num
console.log(num)
​
// 一元减由一个减号(-)表示,放在变量前面,主要用于把数值变成负值
var num = 25
num = -num
console.log(num)
  • 二元操作符:需要两个运算元。大多数操作符都属于这一类,如加法(+)、减法(-)、乘法(*)等。
var diff = 15 - 10
console.log(diff) // 5

三元操作符:需要三个运算元。条件操作符是 JavaScript 唯一使用三个操作数的操作符。

// 语法格式:condition ? exprIfTrue : exprIfFalse
var a = 10, b = 20var result = a > b ? a : b
console.log(result)

一元操作符

递增/递减操作符

将变量的值增加或减少 1

前置和后置的区别

递增和递减操作符可以放在变量的前面或后面,并且它们的行为有所不同

前置递增/递减(++x / --x)

先将变量的值增加/减少1,然后返回增加/减少后的值

var x = 5
console.log(++x) // 6 x 先自增,然后返回自增后的值

后置递增/递减(x++/x–)

先返回变量的当前值,然后将变量的值增加/减少1

var x = 5
console.log(x++) // 5 先返回 x 的当前值,然后 x 自增
console.log(x) // 6 显示 x 自增后的值

非整数递增/递减

  • 对于字符串,如果是有效的数值形式,则转换为数值再应用改变
  • 对于字符串,如果不是有效的数值形式,直接返回 NaN
  • 对于布尔值,true 转换为 1, false 转换为 0 再应用改变
  • 对于浮点值,加 1 或减 1
var s1 = "10"
var s2 = "12px"
var b1 = true
var b2 = false
var f = 1.1// 10 + 1
console.log(++s1) // 11// NaN + 1
console.log(++s2) // NaN// 1 - 1
console.log(--b1) // 0// 0 + 1
console.log(++b2) // 1// 1.1 - 1
console.log(--f) // 0.10000000000000009

一元加操作符

不会改变数字的值,但如果用在非数字值前面,它会尝试将该值转换为数值

console.log(+12) // 12
console.log(+true) // 1
console.log(+false) // 0
console.log(+"12") // 12
console.log(+"1.2") // 1.2
console.log(+"") // 0
console.log(+undefined) // NaN
console.log(+null) // 0

一元减操作符

会改变数字的正负性,如果用在正数前面,它会变成负数;如果用在负数前面,它会变成正数

console.log(-true) // -1
console.log(-false) // -0
console.log(-"12") // -12
console.log(-"1.2") // -1.2
console.log(-"12px") // NaN
console.log(-undefined) // NaN
console.log(-null) // -0

逻辑操作符

逻辑非

首先将操作数转换为布尔值,然后再对其取反

console.log(!"Hello JavaScript") // false
console.log(!"") // true
console.log(!1) // false
console.log(!0) // true
console.log(!null) // true
console.log(!undefined) // true
console.log(!NaN) // true
console.log(!true) // false
console.log(!false) // true
console.log(!{}) // false

双重否定,将其他类型转为布尔类型

console.log(!!"") // false
console.log(!!"Hello") // true
console.log(!!1) // true
console.log(!!0) // false
console.log(!!undefined) // false
console.log(!!null) // false
console.log(!!NaN) // false

逻辑与

左右两个操作数都为真时,才返回真值。如果任一操作数为假,那么结果就是假

expr1 && expr2

如果 expr1 能被转换为 false,那么返回 expr1,否则返回 expr2。在逻辑与操作中,expr1 为假时不会执行 expr2(短路行为)

常被用于条件执行

var obj = {
  condition: true,
  message: "Hello"
}
​
obj.condition && console.log(obj.message)

逻辑或

左右两个操作数至少有一个为真时,返回真值。只有当两个操作数都为假时,结果才是假

expr1 || expr2

如果 expr1 能被转换为 true,那么返回 expr1,否则返回 expr2。在逻辑或操作中,expr1 为真时不会执行 expr2(短路行为)

常用于为变量提供默认值

// 为变量提供默认值
var uname = ""
var findName = uname || "默认用户名"
console.log(findName) // 默认用户名

算数操作符

算术操作符以二个数值(字面量或变量)作为操作数,并返回单个数值

加法操作符

连接字符串或计算数字之和

数值加法

console.log(2 + 3) // 5
console.log(-2 + 3) // 1
console.log(-2 + -2) // -4

特殊值参与运算

  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 InfinityInfinity,则返回 Infinity
  • 如果是 -Infinity-Infinity,则返回 -Infinity
  • 如果是 Infinity-Infinity,则返回 NaN
console.log(NaN + 1) // NaN
console.log(Infinity + Infinity) // Infinity
console.log(-Infinity + -Infinity) // -Infinity
console.log(Infinity + -Infinity) // NaN

非数值加法

如果操作数是非字符串的基本类型,先在后台使用 Number() 将其转 换为数值,然后再执行数学运算

// 1 + 0 = 1
console.log(true + false) // 1// 1 + 0 = 1
console.log(true + null) // 1// 0 + NaN = NaN
console.log(false + undefined) // NaN

字符串连接

只要任一操作数为字符串类型,则自动将另一个操作数转为字符串类型,执行字符串拼接操作

console.log("Hello" + " " + "World!") // "Hello World!"
​
// "2" + "2"
console.log(2 + "2") // "22"
​
// "false" + "true"
console.log(false + "true") // falsetrue
​
// "" + "undefined"
console.log("" + undefined) // "undefined"
​
// "" + null
console.log("" + null) // "null"
​
// 5 + "4"
console.log(2 + 3 + "4") // "54"

减法操作符

计算两个数字之差

数值减法

console.log(5 - 3) // 2
console.log(3 - 5) // -2

特殊值参与运算

  • 如果有任一操作数是 NaN,则返回 NaN

  • 如果两个操作数都是无限值

    • 符号相同,返回 NaN
    • 符号不同,返回第一个操作数
console.log(5 - NaN) // NaN
console.log(Infinity - Infinity) // NaN
console.log(-Infinity - -Infinity) // NaN
console.log(Infinity - -Infinity) // Infinity
console.log(-Infinity - Infinity) // -Infinity

非数值减法

如果有任一操作数是非数值的基本数据类型,先在后台使用 Number() 将其转 换为数值,然后再执行数学运算

// 5 - 1
console.log(5 - true) // 4// 1 - 0
console.log(true - false) // 1// 5 - 0
console.log(5 - "") // 5// 5 - 2
console.log(5 - "2") // 3// 5 - 0
console.log(5 - null) // 5// 5 - NaN
console.log(5 - undefined) // NaN

乘法操作符

计算两个数字之积

数值乘法

console.log(2 * 3) // 6
console.log(2 * -3) // -6
console.log(-2 * -3) // 6

特殊值参与运算

  • 如果有任一操作数是 NaN,则返回 NaN

  • 如果是 Infinity 乘以 0,则返回 NaN

  • 如果是 Infinity 乘以非 0 的有限数值,则根据第二个操作数的符号返回 Infinity-Infinity

  • 如果两个操作数都是无限值

    • 符号相同,返回 Infinity
    • 符号不同,返回 -Infinity
console.log(NaN * 1) // NaN
console.log(Infinity * 0) // NaN
console.log(Infinity * 2) // Infinity
console.log(Infinity * -2) // -Infinity
console.log(Infinity * Infinity) // Infinity
console.log(-Infinity * -Infinity) // Infinity
console.log(Infinity * -Infinity) // -Infinity
console.log(-Infinity * Infinity) // -Infinity

非数值乘法

先在后台使用 Number() 转型函数,将其转换为数值

// 1 * 2
console.log(true * 2) // 2// 0 * 5
console.log(false * 5) // 0// 10 * 3
console.log("10" * "3") // 30// 0 * 5
console.log("" * 5) // 0// NaN * 2
console.log(undefined * 2) // NaN// 0 * 3
console.log(null * 3) // 0

除法操作符

计算两个数字的商

数值除法

console.log(66 / 11) // 6
console.log(-66 / -11) // 6
console.log(-66 / 11) // -6

特殊值参与运算

  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 0 除以 0,则返回 NaN
  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity-Infinity
  • 如果是 Infinity 除以任何有限值,根据第二个操作数的符号返回 Infinity-Infinity
  • 如果两个操作数都是无限值,返回 NaN
console.log(2 / NaN) // NaN
console.log(Infinity / Infinity) // NaN
console.log(0 / 0) // NaN
console.log(5 / 0) // Infinity
console.log(-5 / 0) // -Infinity
console.log(Infinity / 2) // Infinity
console.log(Infinity / -2) // -Infinity
console.log(Infinity / -0) // -Infinity

非数值除法

先在后台使用 Number() 转型函数将其转换为数值

// 1 / 2
console.log(true / 2) // 0.5// 0 / 5
console.log(false / 5) // 0// 10 / 3
console.log("10" / "3") // 3.3333333333333335// 0 / 5
console.log("" / 5) // 0// NaN / 2
console.log(undefined / 2) // NaN// 0 / 3
console.log(null / 3) // 0

取模操作符

计算两个数字的余数

数值取模

console.log(9 % 3) // 0
console.log(-9 % 2) // -1
console.log(1 % 3) // 1

特殊值参与运算

  • 如果被除数是无限值,除数是有限值,则返回 NaN
  • 如果第二个操作数为 0,返回 NaN
  • 如果第一个操作数小于第二个操作数,返回第一个操作数
  • 如果两个操作数都为无限值,返回 NaN
console.log(Infinity % 5) // NaN
console.log(-Infinity % 5) // NaN
console.log(5 % 0) // NaN
console.log(Infinity % Infinity) // NaN
console.log(-Infinity % -Infinity) // NaN
console.log(Infinity % -Infinity) // NaN
console.log(-Infinity % Infinity) // NaN
console.log(5 % Infinity) // 5
console.log(-5 % Infinity) // -5
console.log(0 % 3) // 0

非数值取模

先在后台使用 Number() 转型函数将其转换为数值

// 1 % 0
console.log(true % false) // NaN// 0 % 1
console.log(false % true) // 0// NaN % 0
console.log(undefined % null) // NaN// 0 % 4
console.log(null % 4) // 0// 9 % 2
console.log(9 % "2") // 1

指数操作符

返回第一个操作数取第二个操作数的幂的结果

基本求幂

console.log(2 ** 3) // 8
console.log(3 ** 2) // 9
console.log(16 ** 0.5) // 4// 如果操作数不是数值,先在后台使用 Number() 转型函数将其转换为数值
console.log("3" ** "2")

结合性

// 2 ** (3 ** 2)
console.log(2 ** 3 ** 2) // 512
​
console.log((2 ** 3) ** 2) // 64

关系操作符

比较两个值之间的关系,根据比较的结果返回一个布尔值

小于操作符

检查左侧的操作数是否小于右侧的操作数

console.log(2 < 10) // true
console.log(10 < 2) // false

当比较不同类型的值时,会尝试进行类型转换

  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码
  • 如果有任一操作数是数值,则将另一个操作数转换为数值
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较
// "2":字符编码为 50
// "1":字符编码为 49
// 50 < 49
console.log("2" < "10") // false// 2 < 10
console.log(2 < "10") // true// 0 < 1
console.log(false < true) // true

大于操作符

检查左侧的操作数是否大于右侧的操作数

console.log(10 > 2) // true
console.log(2 > 10) // false

当比较不同类型的值时,与其他关系操作符一致

// 97 > 98
console.log("a" > "b") // false// 4 > 3
console.log("4" > 3) // true// NaN > 3
console.log("abc" > 3) // false// 0 > 1
console.log(null > true) // false

小于等于操作符

检查左侧的操作数是否小于或等于右侧的操作数

console.log(3 <= 3) // true
console.log(3 <= 4) // true
console.log("a" <= "b") // true
console.log(undefined <= null) // false
console.log(true <= false) // false

大于等于操作符

检查左侧的操作数是否大于或等于右侧的操作数

console.log(3 >= 3) // true
console.log(3 >= 4) // false
console.log("a" >= "b") // false
console.log(undefined >= null) // false
console.log(true >= false) // true

相等操作符

相等操作符

检查其两个操作数是否相等,返回一个布尔值结果。会比较不同类型的操作数,并尝试强制类型转换

console.log(5 == 5) // true
console.log(5 == "5") // true

当尝试类型转换时会遵循如下规则:

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等
  • 如果一个操作数是字符串,另一个操作数是数值,尝试将字符串转换为数值
  • nullundefined 相等
  • nullundefined 不能转换为其他类型的值再进行比较
  • 如果有任一操作数是 NaN,直接返回 false
// 1 == 2
console.log(true == 2) // false// 10 == 10
console.log(10 == "10") // true// null 和 undefined 是相等的
console.log(null == undefined) // true// 不能转为其他类型
console.log(null == 0) // false// 不能转为其他类型
console.log(undefined == "undefined") // false

不等操作符

检查其两个操作数是否不相等,并返回布尔结果。它会转换并比较不同类型的操作数

console.log(1 != 2) // true
console.log("Hello" != "hello") // trueconsole.log(1 != 1) // false
console.log("Hello" != "Hello") // falseconsole.log(1 != `1`) // false
console.log(null != 0) // true
console.log(true != 1) // false

全等操作符

检查两个操作数是否相等且数据类型是否相同,并返回布尔结果

console.log(10 === "10") // false
console.log(null === undefined) // false
console.log(6 === 6) // true

非全等操作符

与全等操作符相反

console.log("Hello" !== "Hello") // false
console.log("Hello" !== "hello") // true
console.log(1 !== true) // true
console.log(3 !== "3") // true

条件操作符

格式为 条件 ? 表达式1 : 表达式2。如果条件为真,执行表达式1,否则执行表达式2

var age = 26
var beverage = age >= 21 ? "Beer" : "Juice"
console.log(beverage) // "Beer"

赋值操作符

赋值操作符将右边的操作数的值分配给左边的操作数

简单赋值

简单赋值操作符(=)用于给变量赋值

// 简单赋值
var a = 10
var b = 20// 链式
var x, y, z
x = y = z = 20
console.log(x, y, z) // 20 20 20

复合赋值

复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。这些赋值操作符是类似如下常见赋值操作的简写形式

var num = 10
num = num + 10
// 以上代码的第二行可以通过复合赋值来完成
var num = 10
num += 10

每个算数操作符以及其他一些操作符都有对应的复合赋值操作符:

  • 乘后赋值(*=)
  • 除后赋值(/=)
  • 取模后赋值(%=)
  • 加后赋值(+=)
  • 减后赋值(-=)
  • 求幂后赋值(**=)

注意:这些操作符仅仅是简写语法,使用它们不会提升性能

操作符的优先级

priority.png

总结

JavaScript 中的操作符丰富多样,涵盖了从基础算术到高级位操作的各个方面。熟练掌握这些操作符的用法、优先级和常见陷阱,对于提升编程效率和代码质量具有重要意义。通过本文的深入解析,希望你能更加自信地运用 JavaScript 中的操作符来构建出更加出色的应用程序。