JavaScript之数据类型

266 阅读6分钟

JavaScript中,有StringNumberNullUndefinedBooleanObjectSymbolBigInt八种数据类型,在Object中,又包含普通对象ArrayRegExpDateFunction等几种很常见的引用数据类型。

JavaSript中的数据类型检测

方法说明
typeof [value]用来检测数据类型的运算符
instanceof用来检测当前实例是否属于某个类
Object.prototype.toString检测数据类型的最好办法

typeof

基于typeof检测出来的结果

  • 首先这个结果是一个字符串
  • 字符串中包含数据类型

typeof的局限性

  • typeof null 结果是 object ,但是null并不是对象,这是JavaScript存在的很久远的一个bug
  • 对于引用数据类型的值,除了function,其他对象类型使用typeof检测的结果都是object,无法知道具体是哪个引用数据类型(Object/Array/RegExp/Date)的值
typeof null 'object'
typeof function(){} 'function'
typeof {} 'object'
typeof []  'object'
typeof /\d/ 'object'

基于typeof检测的结果都是字符串,所以只要两个及以上同时检测,最后结果必然是'string'

typeof typeof typeof [] // 'string'

instanceof

  • instanceof是基于原型链查询
  • 可以准确判断引用类型的数据类型
  • 不能正确判断基础数据类型

核心原理实现

function myInstanceof(left, right) {
    // 如果判断的是基础数据类型,就直接返回false
    if(typeof left !== 'object' || left === null) return false
    let proto = Object.getPrototypeOf(left)
    while(true) {
        // 如果已经找到了尽头还没有找到,就返回false
        if(proto === null) return false
        // 如果找到原型对象就返回true
        if(proto === right.prototype) return true
        proto = Object.getPrototypeOf(proto)
    }
}

Object.prototype.toString

Object.prototype.toString方法返回[object Type]这个字符串,可以更准确展示当前数据类型

const string = 'string' // "[object String]"
const number = 88       // "[object Number]"
const nul = null        // "[object Null]"
const und = undefined   // "[object Undefined]"
const bol = true        // "[object Boolean]"
const symbol = Symbol('symbol') // "[object Symbol]"
const bigInt = BigInt(1) // "[object BigInt]"
const obj = {}          // "[object Object]"
const arr = []          // "[object Array]"
const reg = /\d+/       // "[object RegExp]"
const date = new Date() // "[object Date]"
const fn = function(){} // "[object Function]"

JavaSript中的数据类型转换

JS中类型转换分为强制类型转换隐式类型转换

  • 通过 Number()parseInt()parseFloat()toString()String()Boolean()进行强制类型转换。

  • 逻辑运算符(&&、||、!)、运算符(+、-、*、/)、关系操作符(>、 <、 <= 、>=)、相等运算符(==)或者 if/while 的条件,可能会进行隐式类型转换。

原始值转数字

我们使用Number函数将原始值转为数字,如果不传参数,返回+0,有参数时会调用底层的ToNumber方法

ToNumber方法具体传值类型和结果如下表:

参数类型结果
UndefinedNaN
Null0
Boolean如果参数是 true,返回 1。参数为 false,返回 0
Number返回自身
String如下
Number('007')
Number('12.3')    // 12.3
Number('12.00')   // 12
Number('123e-1')  // 12.3
Number('')        // 0
Number('0x11')    // 17
Number('0b11')    // 3
Number('0o11')    // 9
Number('foo')     // NaN
Number('100a')    // NaN
Number(Infinity)  // Infinity
Number('-Infinity') //-Infinity
  • 字符串中只包含数字,则将数字转换为十进制数字

  • 字符串中包含有效浮点格式,将其转换为浮点数值

  • 空字符串转换为0

  • 不是以上格式都返回NaN

数字包含常规数字NaN

NaN

  • not a number,不是一个数,但是它属于数字类型

NaN任何值(包括自己)都不相等

  • isNaN(val)

检测一个值是否为非有效数字,不是有效数字返回true,是有效数字返回false

isNaN('AA') // true
isNaN(10) // false
isNaN('22') // false

在使用isNaN检测值的时候,首先会验证检测的值是否为number类型,如果不是,先基于Number方法,将值转换为number类型,然后再进行检测

原始值转字符串

我们使用String函数将其他原始类型转为字符串,如果不传参数,空字符串,有参数时会调用底层的ToString方法

ToString方法具体传值类型和结果如下表:

参数类型结果
Undefined'undefined'
Null'null'
Boolean如果参数是 true,返回 'true'。参数为 false,返回 'false'
Number如下
String返回自身
String(0)   // "0"
String(88)  // "88"
String(-22) // "-22"
String(NaN) // "NaN"
String(Infinity) // "Infinity"
String(-Infinity) // "-Infinity"

原始值转布尔

只有0NaNundefinednull''false六个值转换为false,其他都转换为true

Boolean(0)   // false
Boolean(NaN) // false
Boolean(undefined) // false
Boolean(null) // false
Boolean('')   // false
Boolean(false) // false

原始值转对象

除了nullundefined,调用各自包装函数转换为各自的包装对象

对象转数字和字符串

对象转数字和字符串主要参照对象的toString方法和valueOf方法。

  • valueOf方法一般返回对象本身,但是对于Date例外,它会返回一个表示1970年1月1日以来的毫秒数
({}).valueOf() // {}
([]).valueOf() // []
(/\d+/).valueOf() // /\d+/
(function foo(){}).valueOf() //function foo(){}
(new Date('2020-02-02')).valueOf() // 1580601600000
  • 对于toString方法来说,不同类型的对象调用toString方法的返回值不同

    • 普通对象调用toString,返回"[object Object]"

    • 数组调用toString,返回一个数组元素以逗号拼接的字符串

    • 函数调用toString,返回源代码字符串

    • 日期调用toString,返回一个可读的日期和时间字符串

    • RegExp调用toString,返回一个正则表达式字符串

({}).toString() // "[object Object]"
([]).toString() // ""
([1, 2, 3]).toString() // "1,2,3"
(function(){}).toString() // "function(){}"
(/\d+/g).toString() // "/\d+/g"
(new Date(2020, 02, 02)).toString() // "Mon Mar 02 2020 00:00:00 GMT+0800 (中国标准时间)"

对象转为字符串/数字的时候,默认会调用底层ToPrimitive方法

ToPrimitive(input[, PreferredType]) 
  • 第一个参数input,表示要处理的值

  • 第二个参数PreferredType,如果input日期类型,相当于传入String,否则,都相当于传入Number

  • 如果传入的input是原始类型,就返回自身

如果是ToPrimitive(obj, Number)

  1. 如果obj是原始类型的值,就直接返回自身
  2. 否则,调用valueOf方法,如果返回的是原始类型的值,就将其返回
  3. 否则,调用toString方法,如果返回的是原始类型的值,就将其返回
  4. 否则,跑出一个类型异常错误

如果是ToPrimitive(obj, String)

  1. 如果obj是原始类型的值,就直接返回自身
  2. 否则,调用toString方法,如果返回的是原始类型的值,就将其返回
  3. 否则,调用valueOf方法,如果返回的是原始类型的值,就将其返回
  4. 否则,抛出一个类型异常错误

对象转数字

  • 调用对象的valueOf方法,如果返回的是原始类型的值,就按照上面的原始值转数字方法将这个值转为数字并返回

  • 否则,调用对象的toString方法,如果返回的是原始类型的值,就按照上面的原始值转数字方法将这个值转为数字并返回

  • 否则,抛出一个类型异常错误

对象转字符串

  • 调用对象的toString方法,如果返回的是原始类型的值,就按照上面的原始值转字符串方法将这个值转为字符串并返回

  • 否则,调用对象的valueOf方法,如果返回的是原始类型的值,就按照上面的原始值转字符串方法将这个值转为字符串并返回

  • 否则,抛出一个类型异常错误

Number({}) // NaN
String({}) // 'object Object'

Number({})

  1. 调用{}valueOf方法,返回{},不是原始类型,继续调用{}的toString方法

  2. 调用{}toString方法,返回[object Object]字符串,符合原始类型,按照字符串转数字规则,返回NaN

对象转布尔

所有对象都转换布尔都为true