javascript中的数据类型与类型转换

265 阅读5分钟
到目前为止,javascript有8种数据类型:
/*
1. String
2. Number
3. Boolean
4. Null
5. Undefined
6. Symbol
7. BigInt
8. Object
*/
其中前7种为基本类型,Object 为引用类型。Object 大概分为以下几种:
/*
1. Function 函数对象
2. RegExp	正则对象
3. Date	日期对象
4. Math	数学函数对象
5. Array	数组对象
*/
javascript的数据类型在初始化后会放在不同的内存中,大致分为2种:
  1. 基础类型存储在栈内存中,被引用和拷贝时,会创建完全相等的变量
  2. 引用类型会存储在堆内存中,存储的是地址,多个引用会指向同一个地址
let a = 1;
let b = a;
b = 2;
// 此时 a 的值还是 1,b 为 2。因为 a 的值为1是基本类型。把 a 的值赋值给 b 时,适用第一种规则。其它基本类型同理

let objA = {a: 1};
let objB = objA;
objB.a = 2;
// 此时 objA 与 objB 均为 {a: 2}。因为 objA 的值是引用类型。把 objA 的值赋值给 objB 时,适用第二种规则,当 objB 改变时,objA 也会改变
关于javascript的类型检测,大致有三种方式:
  • 通过 typeof
  • 通过 instanceof
  • 通过 Object.prototype.toString.call
typeof 的缺陷只能检测出基本类型(注意:typeof null == 'object' 是javascript历史原因,如需要判断是不是null 类型 直接用 a === null这样的方式 就行),而引用类型除了函数能正确判断其它的只会返回object。
instanceof 则与typeof相反,他无法正确判断基本类型,但可以判断出引用类型。
Object.prototype.toString.call 则能完美的检测出所有类型。
// 例子
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call('1'); // [object String]
Object.prototype.toString.call(new Date()); // [object Date]
javascript中类型转换分为强制类型转换与隐式类型转换,强制类型转换方式包括 Number()、parseInt()、parseFloat()、toString()、String()、Boolean()这几种。
Number()转换规则:
  • 如果是boolean,则 true 与 false 转换成 1 和 0

  • 如果是数字,返回本身

  • 如果是 null,返回0

  • 如果是 undefined,返回 NaN

  • 如果是字符串:

    • 字符串只包含数字(或者0X/0x开头的16进制数字字符串),转换成十进制。
    • 字符串为有效的浮点格式,转换成浮点数值。
    • 字符串为空串,返回0。
    • 其它格式字符串,返回NaN
  • 如果是 symbol,抛出错误

  • 如果是对象,并且部署了 [Symbol.toPrimitive] ,那么调用此方法,否则调用对象的 valueOf() 方法,然后依据前面的规则转换返回的值;如果转换的结果是 NaN ,则调用对象的 toString() 方法,再次依照前面的顺序转换返回对应的值

    Number(true) // 1
    Number(1) // 1
    Number(null) // 0
    Number(undefined) // NaN
    Number('') // 0
    Number('1') // 1
    Number('0x11') // 17
    Number('0.222') // 0.222
    Number('1222a') // NaN
    
    let obj = {
      [Symbol.toPrimitive](hint) {
        // [Symbol.toPrimitive] 有三种模式
        // number,该场合需要转换成数字
        // string,该场合需要转换成字符串
        // default,该场合即可转换成数字,也可转换成字符串
        switch (hint) {
          case 'number':
            return 123;
          case 'string':
            return 'str';
          case 'default':
            return 'default';
          default:
            throw new Error();
        }
    }
    Number(obj); // 123
    
    let objA = {
      valueOf: function() {
        return 123
      }
    }
    Number(objA); // 123
    
    let objB = {
      toString: function() {
        return 123
      }
    }
    Number(objB); // 123
    
Boolean()转换规则:
  • 除了 undefined、 null、 false、 ''、 0(包括 +0,-0)、 NaN 转换出来是 false,其他都是 true。即使在对象上布置了[Symbol.toPrimitive] 、valueOf、toString,也会转换成true
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(false) // false
Boolean(NaN) // false
Boolean({}) // true
Boolean([]) // true

let objB = {
  toString: function() {
    return false
  }
}
Boolean(objB) // true
凡是通过逻辑运算符 (&&、 ||、 !)、运算符 (+、-、*、/)、关系操作符 (>、 <、 <= 、>=)、相等运算符 (==) 或者 if/while 条件的操作,如果遇到两个数据类型不一样的情况,都会出现隐式类型转换。
'==' 的隐式类型转换规则
  • 如果类型相同,无须进行类型转换;
  • 如果其中一个操作值是 null 或者 undefined,那么另一个操作符必须为 null 或者 undefined,才会返回 true,否则都返回 false;
  • 如果其中一个是 Symbol 类型,那么返回 false;
  • 两个操作值如果为 string 和 number 类型,那么就会将字符串转换为 number;
  • 如果一个操作值是 boolean,那么转换成 number;
  • 如果一个操作值为 object 且另一方为 string、number 或者 symbol,就会把 object 转为原始类型再进行判断(调用 object 的 valueOf/toString 方法进行转换)。
&&、 ||、 ! 和 if/while 中需要真值时,则调用Boolean()进行转换
// 例子
if ([]) {
  console.log('true'); // 能打印出来,[] 转换成boolean值为 true
}

let a = !{}; // false,{} 转换成boolean值为 true 再取反为 false

var b = 1 && []; // 逻辑与运算,两边为真取第二个,否则取第一个假值,1 和 [] 都被转换成boolean值为 true
var b = 1 || []; // 逻辑或运算,两边为假取第二个,否则取第一个真值,1 和 [] 都被转换成boolean值为 true

// 经典题
[] == ![]; 
/* true。首先因为优先级关系先进行右边的逻辑非运算。右边[]转换成boolean值为 true 再取反为 false。
进行相等判断时,根据规则如果有一方为boolean值,转换成数字在进行判断 [] == ![] --> [] == fasle --> [] == 0。
左边的 [] 转换成数字前会依次调用 [Symbol.toPrimitive] 、valueOf、toString方法直到转换成基本类型。因此
通过toString方法转换成了空字符串"",在转换成数字为 0.则最后为 0 == 0,当然为 true
*/