MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript中的隐式转换与运算符解析

2022-02-287.4k 阅读

JavaScript 中的隐式类型转换

什么是隐式类型转换

在 JavaScript 中,当操作符或函数遇到不同类型的操作数时,JavaScript 引擎会自动将操作数转换为合适的类型,以便完成操作,这个过程就是隐式类型转换。与显式类型转换不同,隐式类型转换是自动发生的,开发者不需要手动调用类型转换函数。例如:

console.log(5 + '3');

在上述代码中,数字 5 和字符串 '3' 进行加法运算。JavaScript 引擎会自动将数字 5 转换为字符串 '5',然后进行字符串拼接,最终输出 '53'

常见的隐式类型转换场景

算术运算符引发的隐式转换

  1. 加法运算符(+)
    • 当其中一个操作数是字符串时,另一个操作数会被转换为字符串,然后进行字符串拼接。
    console.log(1 + '2'); // '12'
    console.log(true + '3'); // 'true3'
    console.log(null + '4'); // 'null4'
    
    • 当两个操作数都是非字符串的基本类型时,会将它们转换为数字进行加法运算。
    console.log(2 + true); // 3,因为 true 被转换为 1
    console.log(5 + null); // 5,因为 null 被转换为 0
    console.log(3 + undefined); // NaN,因为 undefined 被转换为 NaN
    
  2. 减法、乘法、除法和取模运算符 这些运算符会将操作数转换为数字类型再进行运算。
console.log(5 - '2'); // 3,'2' 被转换为数字 2
console.log(3 * true); // 3,true 被转换为 1
console.log(10 / '2'); // 5,'2' 被转换为数字 2
console.log(7 % '3'); // 1,'3' 被转换为数字 3

比较运算符引发的隐式转换

  1. 相等运算符(==)
    • 如果两个操作数类型相同,直接比较值。
    console.log(5 == 5); // true
    console.log('hello' == 'hello'); // true
    
    • 如果两个操作数类型不同,会进行类型转换后再比较:
      • 字符串和数字比较:字符串会被转换为数字。
      console.log('5' == 5); // true,'5' 被转换为数字 5
      
      • 布尔值和其他类型比较:布尔值会先被转换为数字,true 转换为 1false 转换为 0
      console.log(true == 1); // true
      console.log(false == 0); // true
      
      • null 和 undefined 比较:它们彼此相等,且与其他任何值都不相等。
      console.log(null == undefined); // true
      console.log(null == 0); // false
      
  2. 不等运算符(!=)== 的相反逻辑,同样会进行隐式类型转换。
console.log('5' != 5); // false
console.log(true != 1); // false
  1. 严格相等运算符(===)和严格不等运算符(!==)
    • 严格相等运算符(===):只有在操作数类型和值都相等时才返回 true,不会进行类型转换。
    console.log(5 === 5); // true
    console.log('5' === 5); // false,类型不同
    
    • 严格不等运算符(!==):只要类型或值有一个不同就返回 true
    console.log('5'!== 5); // true
    

逻辑运算符引发的隐式转换

  1. 逻辑与(&&)和逻辑或(||)
    • 逻辑与(&&):如果第一个操作数是假值(false0nullundefinedNaN、空字符串 ''),则返回第一个操作数;否则返回第二个操作数。在这个过程中,操作数会被转换为布尔值进行判断。
    console.log(null && 'hello'); // null
    console.log(5 && 'world'); // 'world'
    
    • 逻辑或(||):如果第一个操作数是真值(除了假值以外的值),则返回第一个操作数;否则返回第二个操作数。同样,操作数会被转换为布尔值进行判断。
    console.log(5 || 'default'); // 5
    console.log(null || 'default'); // 'default'
    

其他场景引发的隐式转换

  1. 对象与基本类型的转换
    • 当对象与基本类型进行运算或比较时,对象会尝试转换为基本类型。对象转换为基本类型的过程涉及到 valueOf()toString() 方法。
    const obj = {
        valueOf: function() {
            return 5;
        }
    };
    console.log(obj + 3); // 8,obj 通过 valueOf 方法转换为数字 5
    
    • 如果对象没有自定义的 valueOf() 方法,会尝试调用 toString() 方法。
    const obj2 = {
        toString: function() {
            return '10';
        }
    };
    console.log(obj2 + 5); // '105',obj2 通过 toString 方法转换为字符串 '10'
    

隐式类型转换的规则总结

  1. 转换为数字
    • 字符串转换为数字:如果字符串是有效的数字格式(如 '123'),会转换为对应的数字;否则转换为 NaN(如 'abc')。parseInt()parseFloat() 函数在转换字符串为数字时,会有不同的规则,parseInt() 会忽略字符串开头的非数字字符,直到遇到数字字符,parseFloat() 可以处理浮点数。
    • 布尔值转换为数字true 转换为 1false 转换为 0
    • null 转换为数字:转换为 0
    • undefined 转换为数字:转换为 NaN
  2. 转换为字符串
    • 数字转换为字符串:直接将数字的字符形式作为字符串,例如 5 转换为 '5'
    • 布尔值转换为字符串true 转换为 'true'false 转换为 'false'
    • null 转换为字符串:转换为 'null'
    • undefined 转换为字符串:转换为 'undefined'
  3. 转换为布尔值
    • 假值false0nullundefinedNaN、空字符串 '' 转换为 false
    • 真值:除了上述假值以外的值,都转换为 true

JavaScript 运算符解析

算术运算符

  1. 加法运算符(+)
    • 如前文所述,加法运算符在 JavaScript 中有两种作用:数字相加和字符串拼接。这取决于操作数的类型。
    • 数字相加时,遵循数学上的加法规则。
    console.log(3 + 5); // 8
    
    • 字符串拼接时,会将所有操作数转换为字符串后连接起来。
    console.log('Hello' + ', World'); // 'Hello, World'
    
  2. 减法运算符(-)
    • 减法运算符用于计算两个数字的差。如果操作数不是数字,会先进行隐式类型转换为数字。
    console.log(10 - 3); // 7
    console.log(5 - '2'); // 3,'2' 转换为数字 2
    
  3. 乘法运算符(*)
    • 乘法运算符用于计算两个数字的乘积。同样,如果操作数不是数字,会先转换为数字。
    console.log(4 * 6); // 24
    console.log(3 * '5'); // 15,'5' 转换为数字 5
    
  4. 除法运算符(/)
    • 除法运算符用于计算两个数字的商。如果操作数不是数字,会进行类型转换。
    console.log(10 / 2); // 5
    console.log('15' / 3); // 5,'15' 转换为数字 15
    
  5. 取模运算符(%)
    • 取模运算符返回两个数字相除的余数。操作数同样会在必要时进行类型转换。
    console.log(11 % 3); // 2
    console.log(7 % '3'); // 1,'3' 转换为数字 3
    

比较运算符

  1. 大于(>)和小于(<)
    • 当操作数是数字时,直接比较大小。
    console.log(5 > 3); // true
    console.log(2 < 4); // true
    
    • 当操作数是字符串时,会按照字符的 Unicode 码点顺序进行比较。
    console.log('apple' < 'banana'); // true
    
    • 如果操作数类型不同,会进行隐式类型转换为数字后再比较。
    console.log('5' > 3); // true,'5' 转换为数字 5
    
  2. 大于等于(>=)和小于等于(<=)
    • 逻辑与大于(>)和小于(<)类似,只是包括相等的情况。
    console.log(5 >= 5); // true
    console.log(3 <= 4); // true
    

逻辑运算符

  1. 逻辑与(&&)
    • 逻辑与运算符是短路运算符。如果第一个操作数为假值,不会再计算第二个操作数,直接返回第一个操作数。如果第一个操作数为真值,则返回第二个操作数。
    console.log(false && 'Hello'); // false
    console.log(5 && 'World'); // 'World'
    
  2. 逻辑或(||)
    • 逻辑或运算符也是短路运算符。如果第一个操作数为真值,不会再计算第二个操作数,直接返回第一个操作数。如果第一个操作数为假值,则返回第二个操作数。
    console.log(true || 'default'); // true
    console.log(null || 'default'); // 'default'
    
  3. 逻辑非(!)
    • 逻辑非运算符用于对一个布尔值取反。如果操作数不是布尔值,会先转换为布尔值再取反。
    console.log(!true); // false
    console.log(!0); // true,0 转换为 false 后取反
    

赋值运算符

  1. 简单赋值运算符(=)
    • 用于将右侧的值赋给左侧的变量。
    let num = 5;
    console.log(num); // 5
    
  2. 复合赋值运算符
    • 加法赋值(+=):将变量自身与右侧的值相加,然后将结果赋给变量。
    let a = 3;
    a += 2;
    console.log(a); // 5
    
    • 减法赋值(-=):将变量自身与右侧的值相减,然后将结果赋给变量。
    let b = 7;
    b -= 4;
    console.log(b); // 3
    
    • 乘法赋值(*=):将变量自身与右侧的值相乘,然后将结果赋给变量。
    let c = 2;
    c *= 3;
    console.log(c); // 6
    
    • 除法赋值(/=):将变量自身与右侧的值相除,然后将结果赋给变量。
    let d = 10;
    d /= 2;
    console.log(d); // 5
    
    • 取模赋值(%=):将变量自身与右侧的值取模,然后将结果赋给变量。
    let e = 7;
    e %= 3;
    console.log(e); // 1
    

位运算符

  1. 按位与(&)
    • 按位与运算符对两个操作数的每一位进行与操作(都为 1 时结果为 1,否则为 0)。
    console.log(5 & 3); // 1,5 的二进制是 101,3 的二进制是 011,按位与后为 001,即 1
    
  2. 按位或(|)
    • 按位或运算符对两个操作数的每一位进行或操作(只要有一个为 1 结果就为 1)。
    console.log(5 | 3); // 7,5 的二进制是 101,3 的二进制是 011,按位或后为 111,即 7
    
  3. 按位异或(^)
    • 按位异或运算符对两个操作数的每一位进行异或操作(不同为 1,相同为 0)。
    console.log(5 ^ 3); // 6,5 的二进制是 101,3 的二进制是 011,按位异或后为 110,即 6
    
  4. 按位非(~)
    • 按位非运算符对操作数的每一位进行取反操作(0 变 1,1 变 0)。
    console.log(~5); // -6,5 的二进制是 00000101,取反后为 11111010,在 JavaScript 中表示为 -6
    
  5. 左移(<<)
    • 左移运算符将操作数的二进制位向左移动指定的位数,右侧用 0 填充。
    console.log(5 << 2); // 20,5 的二进制是 101,左移 2 位后为 10100,即 20
    
  6. 右移(>>)
    • 右移运算符将操作数的二进制位向右移动指定的位数,左侧用符号位(最高位)填充。
    console.log(5 >> 1); // 2,5 的二进制是 101,右移 1 位后为 010,即 2
    
  7. 无符号右移(>>>)
    • 无符号右移运算符将操作数的二进制位向右移动指定的位数,左侧用 0 填充,不考虑符号位。
    console.log(-5 >>> 1); // 2147483645,-5 的二进制是 11111111111111111111111111111011,无符号右移 1 位后为 01111111111111111111111111111101,即 2147483645
    

条件运算符(三元运算符)

  1. 语法条件表达式? 表达式1 : 表达式2
    • 如果条件表达式为真,返回表达式 1 的值;否则返回表达式 2 的值。
    let age = 18;
    let message = age >= 18? '成年人' : '未成年人';
    console.log(message); // '成年人'
    

其他运算符

  1. 逗号运算符(,)
    • 逗号运算符用于将多个表达式连接起来,从左到右依次计算每个表达式,并返回最后一个表达式的值。
    let result = (1 + 2, 3 + 4);
    console.log(result); // 7
    
  2. typeof 运算符
    • typeof 运算符用于返回操作数的数据类型。
    console.log(typeof 5); // 'number'
    console.log(typeof 'hello'); //'string'
    console.log(typeof true); // 'boolean'
    console.log(typeof null); // 'object',这是 JavaScript 的一个历史遗留问题
    console.log(typeof undefined); // 'undefined'
    
  3. instanceof 运算符
    • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    function Person() {}
    let person = new Person();
    console.log(person instanceof Person); // true
    

运算符优先级

  1. 优先级规则
    • 运算符优先级决定了表达式中不同运算符的计算顺序。优先级高的运算符先计算,优先级相同的从左到右计算(赋值运算符是从右到左)。
    • 例如,在表达式 3 + 5 * 2 中,乘法运算符(*)优先级高于加法运算符(+),所以先计算 5 * 2,结果为 10,再计算 3 + 10,最终结果为 13
  2. 常见运算符优先级列表
    • 括号(()):最高优先级,用于改变运算顺序。
    • 一元运算符(如 ++、--、!、~、+、-):先于二元运算符计算。
    • 乘除取模(*、/、%):优先级高于加减。
    • 加减(+、-)
    • 比较运算符(>、<、>=、<=)
    • 相等运算符(==、!=、===、!==)
    • 逻辑与(&&)
    • 逻辑或(||)
    • 条件运算符(? :)
    • 赋值运算符(=、+=、-= 等):最低优先级。

通过深入理解 JavaScript 中的隐式类型转换和各种运算符,开发者能够编写出更准确、高效的代码,避免因类型转换和运算符优先级问题导致的错误。同时,对于一些复杂的表达式和逻辑判断,清晰的理解这些知识也有助于代码的调试和维护。在实际开发中,要注意隐式类型转换可能带来的潜在问题,尽量使用严格相等运算符(===)和显式类型转换来提高代码的可读性和健壮性。