数据类型概念
JavaScript分为基本数据类型和引用数据类型。
基本数据类型:
- Undefined
- Null
- String
- Number
- Boolean
- bigInt
- Symbol
引用数据类型:
- Object
- Function
- Date
- Math
- RegExp
- Array
不同的数据类型会存放在不同的内存中。
- 基础类型存储在栈内存(Stack)中,被引用或者拷贝时,会创建一个完全相等的变量。
- 引用数据类型存储在堆内存(Heap)中,然后在栈内存中存储变量的堆内存地址,当多个引用指向同一个地址时,实际上指向的是同一个堆内存。
注:由于 Math 本身就是一个object,并没有Math这种类型,下面类型判断中不会检测Math。
类型检测
因为 JavaScript类型系统是松散的,所以需要一种手段来确认任意变量的数据类型。
typeof
console.log(typeof undefined); // undefined
console.log(typeof null); // object
console.log(typeof '213'); // string
console.log(typeof 213); // number
console.log(typeof false); // boolean
console.log(typeof BigInt(213)) // bigint
console.log(typeof Symbol(12)); // symbol
console.log(typeof {}); // object
console.log(typeof function() {}); // function
console.log(typeof new RegExp(/\s/)); // object
console.log(typeof []); // object
可以发现使用 typeof 会存在很多问题:
- 基础数据类型中检测 null 有误,没有正确返回 null,而是返回 object。
- 引用数据类型中只能检测 object 和 function 数据类型,其他的都只能返回 object。
instanceof
instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例的原型对象上。
console.log(undefined instanceof undefined); // 报错
console.log(null instanceof null); // 报错
console.log(new Number(123) instanceof Number); // true
console.log(new String('123') instanceof String); // true
console.log(new Boolean(true) instanceof Boolean); // true
console.log(Symbol(12) instanceof Symbol); // false
console.log(BigInt(1234) instanceof BigInt) // false
console.log({} instanceof Object); // true
console.log(function() {} instanceof Function); // true
console.log(new RegExp(/\s/) instanceof RegExp); // true
console.log([] instanceof Array); // true
运行之后可以发现instanceof还是会存在不少问题:
- 由于undefined、null、Symbol、BigInt、不是构造函数,无法被 new,所以也就无法在其 prototype 上查找是凑是现在该实例的原型对象
constructor
constructor 与 instanceof 非常相似,但是 constructor 检测与 instanceof 不一样,还可以处理基本数据类型。
console.log(("1").constructor === String); // true
console.log((1).constructor === Number); // true
console.log((false).constructor === Boolean); // true
console.log((Symbol(123)).constructor === Symbol); // true
console.log((BigInt(23)).constructor === BigInt); // true
// console.log((null).constructor === null);
// console.log((undefined).constructor === undefined);
console.log(({}).constructor === Object); // true
console.log((function(){}).constructor === Function); // true
console.log((new Date()).constructor === Date); // true
console.log((new RegExp(/\s/)).constructor === RegExp); // true
console.log([].constructor === Array); // true
缺点:
- 因为null和undefined是无效的对象,因此是不会有constructor存在的。
- 函数的 constructor 是不稳定的,这个主要体现在把类的原型进行重写,重写之后类型判断就会出现问题。
Object.prototypf.toString.call()
这也是我们最推荐的方法,使用此方法无论什么数据类型都能正确返回。
toString 是 Object 原型上的方法,调用该方法统一返回“[object Xxxx]”的字符串,其中Xxx就是调用者的数据类型(首字母大写)。
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
数据类型转换
将值从一种数据类型转换为另一种数据类型叫做类型转换。在日常的业务开发中,经常遇到JavaScript数据类型转换的问题,有的时候需要我们主动强制转换,而有的时候JavaScript会进行隐式转换。
强类型转换
- toString
| 原始值 | 转换结果 |
|---|---|
| undefined | 'undefined' |
| Boolean | 'true' / 'false' |
| Number | 对应的字符串类型 |
| Symbol | Uncaught TypeError: can't convert symbol to string |
| Object | 先调用 toPrimitive。再调用 toNumber |
- toNumber
| 原始值 | 转换结果 |
|---|---|
| undefined | NaN |
| null | 0 |
| true | 1 |
| false | 0 |
| string | 根据语法和转换规则来转换 |
| Symbol | Uncaught TypeError: can't convert symbol to number |
| Object | 先调用 toPrimitive。再调用 toNumber |
注:toPrimitive 会先检查该值是否有 valueOf 方法,如果有就返回基本数据类型值,如果没有就是用 toString 的返回值来进行强制类型转换。
String 转 Number 类型规则:
- 如果字符串中只包含数字,那么就转换成对应的数字
- 如果字符串只包含十六进制格式,那么就转换为对应的十进制数字。
- 如果字符串为空,那么转换为0。
- 如果字符串包含上述之外的字符串,那么转为NaN。
- toBoolean
| 原始值 | 转换结果 |
|---|---|
| undefined | false |
| number | 0和NaN为false,其他都为true |
| Symbol | true |
| Object | true |
隐式类型转换
隐式类型转换一般是在涉及到运算符的时候才会出现的情况。
- “==” 的隐式类型转换规则
- 如果类型相同,无需进行转换。
- 如果其中一个操作值是null或者undefined,那么另一个操作符必须是null或者undefined,才会返回true,否则都返回false。
- 如果其中一个是Symbol类型,那么返回false
- 两个操作符如果是string和nunber类型,那么就会将字符串转换为number
- 如果一个操作符是boolean,那么转换为number
- 如果一个操作值为object且另一方为string、number或者symbol,就会把object类型转为原始数据类型在判断
- “+” 的隐式转换类型
- 如果其中一个是字符串,另外一个是 undefined、null或者boolean,则调用toString方法进行字符串拼接
- 如果一个是数字,另一个是undefined、null、布尔值或者数字,则会将其转换为数字进行加法运算
- 如果其中一个是字符串、一个是数字,则按照字符串规则进行拼接。