js的隐式类型转换规则

247 阅读1分钟

隐式类型转换会让代码晦涩难懂,也会减少冗余,让代码更简洁

  1. 字符串和数字之间的隐式类型转换
    const a = "32";
    const b = 54;
    // a + a = "3232";
    // a + b = "3254";
    // b + d = 57;
    const arr1 = [1, 2];
    const arr2 = [3, 4];
    // arr1 + arr2 = "1,23,4";
  • 根据ES5规范,如果某个操作数是字符串或者能转换为字符串的话,+操作符进行拼接操作,数组的valueOf()操作无法得到一个基本类型值,于是它转而调用toString(),因此上面的两个数组变成了"1, 2"和"3, 4",+拼接为"1, 23, 4";
  • 运算符会将字符串强制转化为数字,因为数字才有这个操作符,数组会先调用toString()转化为字符串,然后再转化为数字。
const a = "3.14";
const b = a - 0;
console.log(b); // 3.14
const c = [4];
const d = [2];
console.log(c - d); // 2
  1. 布尔值到数字的隐式类型转换
// 用于判断参数是否只有一个为真值
function onlyOne() {
    let sum = 0;
    for (let i = 0; i < arguments.length; i++) {
        if (arguments[i]) {
            sum += arguments[i];
            console.log(sum);
        }
    }
    return sum == 1;
}
  1. 隐式类型转换为布尔值
  • 条件判断
    • falsely变量,判断条件为false,truly变量,条件判断为true
    • 以下是falsely变量,除此之外都是truly变量
      • 0(+0,-0) NaN "" null undefined false
    • 条件判断语句
      • if()语句中的条件判断表达式
      • for语句中条件判断表达式
      • while循环中的条件判断表达式
      • 三目运算符中的条件判断表达式
      • 逻辑运算法|| ,&& 作为条件判断表达式的部分
  1. null和undefined之间的相等比较
  • null == undefined (true),也就是说,在==中,null和undefined是一回事,可以互相进行隐式类型转换:
const a = null;
const b;
a == b; // true
b == null // true
a == false; //false
b == false; // false
a == ""; // false
b == ""; //false
a == 0; // false
b == 0; // false
  1. 对象与非对象之间的相等比较
  • 关于对象(对象、函数、数组)和基本类型(字符串、数字、布尔值)之间的比较

    (1) 如果Type(x)是字符串或者数组,Type(y)是对象,则返回x==ToPrimitive(y)的结果 (1) 如果Type(x)是对象,Type(y)是字符串或者数组,则返回ToPrimitive(x)==y的结果

    const a = 42;
    const b = [42];
    a == b; // true
    // [42]会首先调用ToPrimitive抽象操作,返回"42",变成"42" == 42,然后又变成42 == 42
  • "拆封"封装对象如(new String("abc")), 返回其中的基本类型"abc"。 == 中的ToPrimitive强制类型转换也会发生这样的情况
const a = null;
const b = Object(a);
a == b; // false
a === b; // false
  • 有一些值不是这样,原因是==算法中其他优先级更高的规则
    const a = null;
    const b = Object(a);
    a == b; //false

    const c = undefined;
    const d = Object(c);
    c == d; // false

    const e = NaN;
    const f = Object(e);
    e == f; // false
    // 因为没有对象的封装对象,所以null和undefined不能被封装,Object(null)和Object()均返回一个常规对象
    // NaN能够封装为数字类型对象,但拆封后NaN!=NaN
  1. 比较少见的情况
  • 返回其他数字
        Number.prototype.valueOf = function() {
            return 3;
        }
        new Number(2) == 3; // true
        // 2 == 3不会有这种问题,而Number(2)涉及ToPrimitive转换
  • 如果让 a == 2 && a == 3同时成立
        let i = 2;
        Number.prototype.valueOf = function() {
            return i++;
        }
        let a = new Number(454);
        if (a == 2 && a == 3) {
            console.log("good!"); // good
        }
  • 假值的相等比较(容易造成混乱的情况)
        console.log("0" == false); // true
        console.log(0 == false); // true
        console.log([] == false); // true
        console.log("" == false); // true

        console.log(0 == ""); // true
        console.log(0 == []); // true
        console.log("" == []); // true
        // 极端情况
        [] == ![] // true
        // 首先根据ToBoolean规则,进行值的显示类型转换,所以[] == ![]变成了[] == false

数组进行ToPrimitive是会转化为字符,[null]会直接转化为""

    2 == [2]; //true
    "" == [null]; //true
    0 == "\n"; //true
  • 安全运用隐式类型转换

    (1) 如果两边的值其中有true或者false,千万不要使用== (2) 如果两边值中有[],"",或者0,尽量不要使用==

  • 抽象关系比较

    const a = ["123"];
    const b = ["21"];
    console.log(a < b); // true
    const c = ["123"];
    const d = 21;
    console.log(c < d); // false
    // 比较双方首先调用ToPrimitive,结果如果出现非字符串,就根据ToNumber规则将双方转化为数字进行比较,如果都是字符串,就从头比较字符编码。
    const a = {b:42}
    const b = {b:43}
    // 因为a转化为字符串是[object Object] b也是[object Object]
    console.log(a<b); //false
    console.log(a==b); //false
    console.log(a>b); //false
    // 下面这两个情况就很特殊了
    console.log(a>=b); //true
    console.log(a<=b); // true
    // 为true的原因是在JavaScript中<=应该是不大于的意思,>=是不小于的意思
    // a<=b处理为 !(a>b) 同理a>=b处理为 !(a<b>)