数据类型
有哪些数据类型
JavaScript 共有八种数据类型,分别是 Undefined、Nul、Boolean、Number、String、Object、Symbol、Biglnt
其中 Symbol 和 Biglnt 是 ES6 中新增的数据类型:
- Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
- Biglnt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
这些数据可以分为原始数据类型和引用数据类型:
- 栈:原始数据类型(Undefined、Null、Boolean、Number、.String)
- 堆:引用数据类型(对象、数组和函数)
区别
- 原始数据类型直接存储在栈(sack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储
- 引用数据类型存储在堆(hap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
基本数据类型和引用数据类型
- 基本数据类型: String Number Boolean undefined null Bigint Symbol,基本数据类型保存在内存中,保存的就是一个具体的值
- 引用数据类型(复杂数据类型):Object Function Array,引用数据类型保存在堆内存中,声明一个引用类型的变量,它保存的是引用类型数据的地址。假如声明两个引用类型同时指向了一个地址的时候,修改其中一个那么另外一个也会改变。
let obj = {
name: '张三',
}
let obj1 = obj
obj1.name = '李四'
console.log(obj.name) //李四
Symbol
Symbol 是 ES6 中引入的一种新的基本数据类型,用于表示一个独一无二的值。它是 JavaScript 中的第七种数据类型,与 undefined、null、Number(数值)、String(字符串)、Boolean(布尔值)、Object(对象)并列。
- 不能与其他的数据进行比较以及运算(唯一性)
let a1 = Symbol('11')
let a2 = Symbol('11')
console.log(a1 === a2) // flase

- 不能与其他数据进行运算
let result = a2 + 100 //报错
let result = a2 > 100 //报错
let result = a1 + a1 //报错
- 隐藏性,for···in 不能访问,使用 Object.getOwnPropertySymbols 方法可以进行访问
let a = Symbol('Nan')
let obj = {
[a]: 'Chen',
}
console.log(obj) //{Symbol(Nan): "Chen"}
for (const option in obj) {
console.log(obj[option]) //啥都没有
}
let array = Object.getOwnPropertySymbols(obj)
console.log(array) //[Symbol(Nan)]
console.log(obj[array[0]]) //Chen
null 与 undefind
- undefined
- 已声明,为赋值
- 对象某个属性不存在
- 函数调用缺少参数
- 函数默认的返回值
// 1、已声明,为赋值
let o
console.log(o) // undefined
// 2、对象某个属性不存在
let obj = {}
console.log(obj.a) // undefined
// 3、函数调用缺少参数
function fn(a, b) {
console.log(a, b)
}
fn(4) // 4,undefined
// 4、函数默认的返回值
function abc() {}
console.log(abc())
- null
- 手动释放内存
- 作为函数的参数(此参数不是对象)
- 原型链的顶端
数据类型检测
typeof
返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function 等 7 种数据类型。其中数组、对象、null 都会被判断为 object,其他判断都正确。
typeof Symbol() // symbol 有效
typeof '' // string 有效
typeof 1 // number 有效
typeof true //boolean 有效
typeof undefined //undefined 有效
typeof new Function() // function 有效
typeof null //object 无效
typeof [] //object 无效
typeof new Date() //object 无效
typeof new RegExp() //object 无效
typeof NaN
NaN指“不是一个数字”(not a number),NaN是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。
typeof NaN;//"number"
NaN是一个特殊值,它和自身不相等,是唯一个非自反(自反,reflexive,即x==X不成应)的值。而NaN!==NaN为true。
instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true
关于数组的类型判断,还可以用 ES6 新增 Array.isArray()
Array.isArray([]) // true
三个弊端
- 对于基本数据类型来说,字面量方式创建出来的结果和实例方式创建的是有一定的区别的
console.log(1 instanceof Number) //false
console.log(new Number(1) instanceof Number) //true
从严格意义上来讲,只有实例创建出来的结果才是标准的对象数据类型值,也是标准的 Number 这个类的一个实例;对于字面量方式创建出来的结果是基本的数据类型值,不是严谨的实例,但是由于 JS 的松散特点,导致了可以使用 Number.prototype 上提供的方法。
- 只要在当前实例的原型链上,我们用其检测出来的结果都是 true。在类的原型继承中,我们最后检测出来的结果未必准确。
var arr = [1, 2, 3]
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
function fn() {}
console.log(fn instanceof Function) // true
console.log(fn instanceof Object) // true
- 不能检测 null 和 undefined
对于特殊的数据类型 null 和 undefined,他们的所属类是 Null 和 Undefined,但是浏览器把这两个类保护起来了,不允许我们在外面访问使用。
实现原理
instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
function myInstanceof(left, right) {
//获取对象的原型
let proto = Object.getPrototypeOf(left)
//获取构造函数的prototype对象
let prototype = right.prototype
//判断构造函数的prototype对象是否在对象的原型链上
while (true) {
if (!proto) return false
if (proto == prototype) return true
//如果没有找到,就继续从其原型上找,Object.getPrototypeof方法用来获取指定对象的原型
proto = Object.getPrototypeOf(proto)
}
}
constructor
constructor 作用和 instanceof 非常相似。但 constructor 检测 Object 与 instanceof 不一样,还可以处理基本数据类型的检测。
var aa = [1, 2]
console.log(aa.constructor === Array) //true
console.log(aa.constructor === RegExp) //false
console.log((1).constructor === Number) //true
var reg = /^$/
console.log(reg.constructor === RegExp) //true
console.log(reg.constructor === Object) //false
弊端
null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
函数的 constructor 是不稳定的,这个主要体现在把类的原型进行重写,在重写的过程中很有可能出现把之前的 constructor 给覆盖了,这样检测出来的结果就是不准确的
function Fn() {}
Fn.prototype = new Array()
var f = new Fn()
console.log(f.constructor) //Array
Object.prototype.toString.call()
Object.prototype.toString.call() 最准确最常用的方式。首先获取 Object 原型上的 toString 方法,让方法执行,让 toString 方法中的 this 指向第一个参数的值。
Object.prototype.toString.call('') // [object String]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call(new Function()) // [object Function]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(new RegExp()) // [object RegExp]
Object.prototype.toString.call(new Error()) // [object Error]
Object.prototype.toString.call(document) // [object HTMLDocument]
Object.prototype.toString.call(window) //[object global] window是全局对象global的引用
数据类型转换
强制转换
其他类型数据转换为 String
- toString()
调用被转换数据类型的 toString()方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null 和 undefined 这两个值没有 toString,如果调用他们的方法,会报错。
var a = 123
a.toString() //"123"
var b = null
b.toString() //"报错"
var c = undefined
c.toString() //"报错"
采用 Number 类型的 toString() 方法的基模式,可以用不同的基输出数字,例如二进制的基是 2,八进制的基是 8,十六进制的基是 16
var iNum = 10
alert(iNum.toString(2)) //输出 "1010"
alert(iNum.toString(8)) //输出 "12"
alert(iNum.toString(16)) //输出 "A"
- String()
使用 String()函数做强制类型转换时,对于 Number 和 Boolean 实际上就是调用的 toString()方法,但是对于 null 和 undefined,就不会调用 toString()方法,它会将 null 直接转换为"null",将 undefined 直接转换为"undefined"
var a = null
String(a) //"null"
var b = undefined
String(b) //"undefined"
String 方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式
String({ a: 1 }) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
其他类型数据转换为 Number
- Number()
字符串转数字
- 如果是纯数字的字符串,则直接将其转换为数字
- 如果字符串中有非数字的内容,则转换为 NaN
- 如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0
Number('324') // 324
Number('324abc') // NaN
Number('') // 0
布尔值转数字:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0
undefined 转数字:转成 NaN
Number(undefined) // NaN
null 转数字:转成 0
Number(null) // 0
Number() 接受数值作为参数,此时它既能识别负的十六进制,也能识别 0 开头的八进制,返回值永远是十进制值
Number(3.15) //3.15
Number(023) //19
Number(0x12) //18
Number(-0x12) //-18
对象
Number({ a: 1 }) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
- parseInt() & parseFloat()
这种方式专门用来对付字符串,parseInt()一个字符串转换为一个整数,可以将一个字符串中的有效的整数内容取出来,然后转换为 Number。parseFloat()把一个字符串转换为一个浮点数。parseFloat()作用和 parseInt()类似,不同的是它可以获得有效的小数。
console.log(parseInt('.21')) //NaN
console.log(parseInt('10.3')) //10
console.log(parseFloat('.21')) //0.21
console.log(parseFloat('.d1')) //NaN
console.log(parseFloat('10.11.33')) //10.11
console.log(parseFloat('4.3years')) //4.3
console.log(parseFloat('He40.3')) //NaN
parseInt()在没有第二个参数时默认以十进制转换数值,有第二个参数时,以第二个参数为基数转换数值,如果基数有误返回 NaN
console.log(parseInt('13')) //13
console.log(parseInt('11', 2)) //3
console.log(parseInt('17', 8)) //15
console.log(parseInt('1f', 16)) //31
两者的区别:Number 函数将字符串转为数值,要比 parseInt 函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为 NaN。
parseInt('42 cats') // 42
Number('42 cats') // NaN
上面代码中,parseInt 逐个解析字符,而 Number 函数整体转换字符串的类型。 另外,对空字符串的处理也不一样
Number(' ') //0
parseInt(' ') //NaN
其他类型数据转换为 Boolean
它的转换规则相对简单:只有空字符串("")、null、undefined、+0、-0 和 NaN 转为布尔型是 false,其他的都是 true,空数组、空对象转换为布尔类型也是 true,甚至连 false 对应的布尔对象 new Boolean(false)也是 true
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
自动转换
自动转换为布尔值
JavaScript 遇到预期为布尔值的地方(比如 if 语句的条件部分),就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用 Boolean 函数。
逻辑运算符!如果对非布尔值进行运算,则会将其转换为布尔值,然后再取反。 所以我们可以利用该特点,来将一个其他的数据类型转换为布尔值, 可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和 Boolean()函数一样。
if ('abc') {
console.log('hello')
} // "hello"
自动转换为数值
算数运算符(+ - * /)跟非 Number 类型的值进行运算时,会将这些值转换为 Number,然后在运算,除了字符串的加法运算
true + 1 // 2
2 + null // 2
undefined + 1 // NaN
2 + NaN // NaN 任何值和NaN做运算都得NaN
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
一元运算符也会把运算子转成数值。
;+'abc' - // NaN
'abc' + // NaN
true - // 1
false // 0
自动转换为字符串
字符串的自动转换,主要发生在字符串的加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function () {} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
总结
- 强制转换

自动转换
只有空字符串("")、null、undefined、+0、-0 和 NaN 转为布尔型是 false,其他的都是 true
除了加法运算符(+)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值。一元运算符也会把运算子转成数值。
字符串的自动转换,主要发生在字符串的加法运算时。