Javascript操作符运算合集

javascript的语法中,不同类型通过操作符运算后求最终值这样的运算,在面试过程中会经常碰到,因此,今天我们就来一起探讨一下对应的规则。

四则运算符

+ 加法

加法有两种运算法则,分别为数字加法字符串连接。运算时,它首先将两个操作数强制转换基本类型后再计算。我们来一一举例:

  • 若有一方是字符串,则另一方会被转换成字符串,最后再连接起来形成新的字符串

    1
    2
    3
    4
    const str = 'chuyuxuan'
    const num = 1
    console.log(str + num) // chuyuxuan1
    typeof (str + num) // 'string'
  • 若双方都是 BigInt,则执行 BigInt 加法。如果一方是 BigInt 而**另一方[1]**不是,会抛出 TypeError。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 双方都是 BigInt
    const bnum1 = 2024n
    const bnum2 = 2025n
    console.log(bnum1 + bnum2) // 4049n
    typeof (bnum1 + bnum2) // 'bigint'

    // 一方不是 BigInt
    const bigNum = 2024n
    const myBoolean = true
    console.log(bigNum + myBoolean) // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

    [1]注意,如果有一方是**字符串**,则会执行上述字符串的规则

  • 除上述外,若双方都是其他基本类型,则都会被转换成 数字类型[2],再进行加法运算。如果有任一操作数是 NaN,则返回 NaN

    1
    2
    3
    4
    5
    6
    7
    const myNull = null
    const myBoolean = false
    const myUndefined= undefined
    console.log(myNull + myBoolean) // 0
    console.log(myNull + myUndefined) // NaN
    // myNull 会转换成数字 0, myBoolean 会转换成数字 0 ,myUndefined 会转换成 NaN

    [2]调用 Number() 将其参数转换为数字进行混合计算时。部分参数转换结果如下,其他请参考MDN

    • Number 将按原样返回,包括正负号
    • Infinity 返回自身,包括正负号
    • undefined 转换为NaN
    • null转换为0
    • true转换成1false转换成0
    • 空字符串 转换为 0
    • 字符串 转换为 NaN (加法法则中则会都转成字符串)
    • BigInt抛出 TypeError
    • Symbol抛出 TypeError

注意,在BigInt中,使用Number(12n) 会得到 12 ,在ECMAScript是被允许的,但是这样会导致BigInt类型的数据丢失精度。但是,如果利用BigInt 与 Number()实例进行混合运算则会报错,因为两者的基本类型不一致。详情可以见MDN对BigInt的描述。

- 减法

减法首先会把两边转换为数值,并根据两个操作数的类型执行数字减法,如果类型不匹配,则抛出 TypeError。与加法不一样,字符串也会优先转换成数值了,而不是先调用toString()进行转换。让我们来具体看下例子:

  • 若双方都是数值,则执行数值减法。

    1
    2
    3
    const num1 = 100
    const num2 = 50
    console.log(num1 - num2) // 50
  • 若双方都是 BigInt,则执行 BigInt减法。如果一方是 BigInt 而**另一方[3]**不是,会抛出 TypeError。

    1
    2
    3
    4
    5
    const b1 = 100n
    const b2 = 50n
    const str = 'chuyuxuan'
    console.log(b1 - b2) // 50n
    console.log(b1 - str) // TypeError: Cannot mix BigInt and other types, use explicit conversions

[3] 除了BigInt的其他基本类型

  • 若双方都是非数值,则会先用Number()转换成数值,再计算。Number转换规则见注解2。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 一方是 string,一方是 null
    const str = 'chuyuxuan'
    const myNull = null
    console.log(myNum - myNull) // NaN
    // string 会被转换成NaN ,NaN 除了 BitInt 基本类型外,相减(无论是减数还是被减数)都为NaN

    // 一方是 Infinity,一方是 boolean
    const myBoolean = false
    const num = Infinity
    console.log(myBoolean - num) // -Infinity
    // myBoolean 会被转换为 0, 0 减去一个数值,取反。

    // 一方是 Symbol ,一方是 undefined
    const ms = Symbol('chuyuxuan')
    const myUndefined = undefined
    console.log(myUndefined - ms) // TypeError: Cannot convert a Symbol value to a number
    // Symbol 不能和任何 Number 实例混合运算

* 乘法

乘法运算也一样,会先把两边转换为数值基本类型再进行乘积运算。不过需要注意一下Infinity 与 NaN:

  • Infinity 相乘

    1
    2
    3
    4
    const myIn = Infinity
    const mystr = 'chuyuxuan'
    console.log( myIn * mystr ) // NaN
    console.log( myIn* myIn) // Infinity 与数值相乘都为无穷

    非数值基本类型相乘

    1
    2
    3
    4
    5
    const myNull = null
    const mystr = ''
    const myUn = undefined
    console.log(myNull * mystr) // 0
    console.log(myNull * myUn) // NaN

/ 除法

会先把两边转换为数值基本类型再进行除法运算,除法中,如果除数(符号右边)是 数值0,则会为 Infinity。有NaN 则为NaN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(12 / 2);
// Expected output: 6

console.log(3 / Infinity );
// Expected output: 0 , 因为对于无穷来说,任何都趋近于0

console.log(Infinity / Infinity );
// Expected output: NaN 没有意义,不是一个数

console.log(12 / 'ewe');
// Expected output: NaN

console.log(Infinity / 0);
// Expected output: Infinity

逻辑运算符

==相等(松散相等)

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

1.如果操作数具有相同的基本类型
  • Object:仅当两个操作数引用同一个对象时返回 true

    1
    2
    3
    4
    5
    6
    const obj1 = {name:'chuyuxuan'}
    const obj2 = obj1
    console.log(obj1 == obj2) // true

    const obj3 = {name:'chuyuxuan'}
    console.log(obj1 == obj3) // false
  • String:只有当两个操作数具有相同的字符且顺序相同时才返回 true

    1
    2
    3
    4
    const str1 = "chuyuxuan"
    const str2 = "chuyuxuan0v0"
    console.log(str1 == str2) // false
    console.log(str1 == str1) // true
  • Number:如果两个操作数的值相同,则返回 true+00-0 被视为相同的值。如果任何一个操作数是 NaN,返回 false;所以,NaN 永远不等于 NaN

    1
    2
    3
    4
    5
    console.log(18 == 18) // true
    console.log( -0 == +0) // true
    console.log(Infinity == Infinity) // true
    console.log(NaN == NaN) // false
    console.log( Number('chuyuxuan') == 18) //false
  • Boolean:仅当操作数都为 true 或都为 false 时返回 true,否则返回false

    1
    2
    3
    console.log(true == true) // true
    console.log( false == false) // true
    console.log(false == true) // false
  • BigInt:仅当两个操作数值相同时返回 true

    1
    2
    console.log(18n == 18n) // true
    console.log(18n == 19n) // false
  • Symbol:仅当两个操作数引用相同的符号时返回 true

    1
    2
    3
    4
    5
    6
    const sy = Symbol('chuyuxuan')
    const mb = sy
    console.log(sy == mb) // true

    const ol = Symbol('chuyuxuan0v0')
    console.log(sy == ol) // false
  • Null :两个操作数互为 null 时返回true

    1
    console.log(null == null) // true
  • Undefined:两个操作数互为 undefined 时返回true

    1
    console.log(undefined == undefined) // true
2.如果操作数基本类型不同

​ 2.1 如果其中一个操作数为 nullundefined,另一个操作数为 nullundefined 以返回 true。否则返回 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
null == null // true
null == undefined // true
null == 1 // false
null == Symbol(1) // false
null == '' // false
null == true // false
null == 18n // false
null == [] // false

undefined == null // true
undefined == undefined // true
undefined == 1 // false
undefined == Symbol(1) // false
undefined == '' // false
undefined == true // false
undefined == 18n // false
undefined == [] // false

​ 2.2 如果其中一个操作数是 Symbol 而另一个不是,返回 false

1
2
3
4
5
6
7
Symbol('chuyuxuan') == null // false
Symbol('chuyuxuan') == 18n // false
Symbol('chuyuxuan') == 1 // false
Symbol('chuyuxuan') == 'chuyuxuan' // false
Symbol('chuyuxuan') == true // false
Symbol('chuyuxuan') == [] // false
Symbol('chuyuxuan') == undefined // false

​ 2.3 如果其中一个操作数是布尔型而另一个是其他类型,将布尔型转换为数值true 转换为 1,false 转换为 0。然后再次松散地比较两个操作数(即一个操作数是数值,另一个不是的规则进行比较)。

1
console.log(true == 1 ) // true

​ 2.4 如果一个操作数是字符串而另一个是数值则会把字符串转换为数值,如果转换失败,则为 NaNNaN 则会导致 false。

1
2
console.log("chuyuxuan" == 18 ) // false
console.log("5" == 5) // true

​ 2.5 如果一个操作数是BigInt而另一个是数值则会把BigInt通过Number转换为数值,如果数值为 ±∞NaN,则为false。

1
2
console.log( 18n == 18 ) // true
console.log( 18n == Infinity ) // false
3.如果操作数有引用类型

​ 如果一个操作数是对象,另一个是基本类型则会把对象的转成基本类型

  • 对象会首先调用内置的 Symbol.toPrimitive方法转(详情见MDN),若没有该方法,则往下。

  • 对象再通过 valueOf() 方法尝试转换,如果返回依然不是基本类型,则往下。

  • 对象会继续调用toString(),最后再执行基本类型比较。(从2.1开始,操作数具有相同的类型,接着往下,直至比较出结果跳出循环)。

1
2
3
4
5
6
const myNumberObj = new Number(18)
console.log(myNumberObj == 18) // true

const myStr = '[object Object]'
const myStringObj = {name:'chuyuxuan',age:18}
console.log(myStringObj == myStr) // true

MDN 中对 valueOf()解释如下:

Object 实例的 valueOf() 方法将 this转换成对象。该方法旨在被派生对象重写,以实现自定义类型转换逻辑。

大白话就是 new对象给的初始值就是 valueOf的返回值,例如 let strObj = new String('chuyuxuan'),则在 strObj 上this指向值为 ‘chuyuxuan’

在上述比较过程中,valueOf()的返回值不是属于基本类型里面的,则会继续调用toString()方法,转换成String类型,进行比较。

!逻辑非

如果其操作数可以转化为 true,则返回 false,否则返回 true。虽然挺废话的,但也是如此,😂。

其实我们可以这样理解,! 就是对 操作数转成Boolean类型之后,进行取反。如何把一个类型转成Boolean类型?我们可以借用 Boolean() 构造函数。**Boolean()** 构造函数可以创建 Boolean 对象或返回布尔类型的原始值。

在Boolean构造函数中,当起参数为一下基本类型:

  • 没有值(或者省略)

  • 0

  • -0

  • +0

  • 0n

  • null

  • false

  • NaN

  • undefined

  • “” (空字符串)

Boolean() 构造函数 返回 false

若参数为

  • 任何对象 (当然,这里也包括 [] 空数组)
  • 非空字符串

Boolean() 构造函数 返回 true

所以,我们对以上进行逻辑非,那么就能够得到其正确的值。

逻辑非的优先级高于四则运算。