【JS之路】“简单”的数据类型
- 作者: 最帅的小丑
- 来源: 51数据库
- 2021-08-16
JavaScript数据类型
ECMAScript规定了8种数据类型,又分为两种:简单数据类型和复杂数据类型。
简单数据类型
Number:整数或者浮点数(里面还包括一些特殊值,例如:Infinity 代表无穷大,-Infinity 代表无穷小,NaN(not a number))Boolean:只包含两个值 true和false字符串:用单引号或者双引号包裹起来的都叫字符串underfined:未定义的null:空
在ES6+中加入了
Symbol、BigInt(本文暂不作讨论)
复杂数据类型:
object:对象类型,平时用的包括Object、Function、Array、Math、Date等都是特殊的对象。
为什么要区分简单数据类型和复杂数据类型
说简单点,相对于java、c++等高级语言一样,就是变量和对象的区别,这种区别是可显式的。谈及变量自然离不开内存,今天我们主要以JavaScript为始,以内存的存储方式来展开。
不可变性
在ECMAScript标准中,简单数据类型被定义为primitive values,即原始数值,代表他的值是不可变的。 看到这里,肯定有读者发出疑问:JavaScript里面都是变量啊!不能改变,那不是就成常量了?
别急,我们从内存来逐步理解:
在JavaScript中,变量是松散类型的,变量可以保存任何类型的数据。简单来说,每个变量仅仅是一个占位符而已,它里面可以任意存值。
JavaScript中的内存总体分为两种:栈内存和堆内存
栈内存
特点:
- 存储空间的
大小固定(即创建时的大小是确定的) - 总空间较小
- 可以直接操作变量,运行效率高(因为
JavaScript代码是在栈区执行的) - 由编译器分配空间
而JavaScript中的简单数据类型的值,其实就是存储在栈区。
先看一下栈区的存储方式

下面我们通过一道简单的运算题来理解栈内存的存储模式。
var str="abc";
str=str+"d"; // "abcd"
上面代码中,我们对str变量进行了:str+“d”,实际上是在栈中又开辟了一块内存空间用来存储新的值“abcd”,然后将str变量指向这块新的内存空间。但是,这并不违背不可变性这一特点

引用对象
JavaScript中的对象其实就是一堆数据和功能的集合,算是一种数据结构。我们在这里说一下复杂数据类型在内存的存储方式,也就是堆内存。
堆内存
特点:
- 存储空间大小不固定,可以动态调整
- 空间很大
- 在JavaScript代码中,无法操作其内部存储,要使用地址读取 (运行效率低)
- 通过代码分配空间

复杂数据类型就不存在不可变性,我们可以随意改变他们的值:
obj1.name="李四";
obj2.age=19;

Number类型
简称数字类型,可以表示整数和浮点数。在计算机中,数据都是以二进制存储的。当进行计算的时候,数据都是以二进制的形式进行计算,然后将结果转换为十进制打印。 在这个过程中会发生一个很经典的问题:精度丢失,这个问题发生在各种语言中。这是因为存储长度的原因。
JS中对于二进制的存储方式
对于整数来说,除非超过最大范围,否则是不会存在精度问题。主要问题在于小数的存储。因为大多数小数的二进制是无限循环的。
ECMAScript中的Number类型遵循IEEE 754标准。使用64位固定长度来表示。
这其中包括:符合位 、 指数位、尾数位。
对于JavaScript来说,它使用的是64位双精度浮点数编码,所以它的符号位占1位,指数位占11位,尾数位占52位。
(图片来自网络,侵权删)
- 符号位:就是表示这个数的正负关系,1表示负,0表示正
- 指数位:存储科学计数法的
指数; - 尾数位:存储科学计数法的
有效数字;
由于尾数位只能存储52位,那么对于53位的数字来说:第53位及以后的数字是不能存储的,它遵循,如果是1就向前一位进1,如果是0就舍弃的原则。这也就是为什么小数计算会产生误差,也就是经典的"0.1+0.2!=0.3"。
null 和undefined
在ECMAScript规定中,有两个特殊的数据类型,它们有且仅有一个值。
null:仅有一个值null,从逻辑上看,null表示一个空对象指针,因为当我们使用typeof操作符时,检测值为null的变量会返回object。我们可以利用这个值来销毁一些对象的行为。undefined:未定义的。当我们声明一个变量但是未对其赋值,这个变量的值就是undefined。但是有特殊情况,当你直接调用一个未声明的变量时,这个变量时undefined。
因为JavaScript是一门动态语言,除了有表示空的null之外,还可能根本就不存在,但是只有在代码运行时才能知道。JavaScript中对于未初始化的变量会自动被赋予undefined。
类型检测
typeof
通过typeof操作符,我们很容易能区分出各种简单数据类型。
typeof 123 // number
typeof true // boolean
typeof '123' // string
typeof undefined // undefined
但对于复杂数据类型来说,因为存在特殊的对象,例如Date 、function、Array等等。
typeof [] // object
typeof {} // object
typeof new Array() // object
typeof function(){} //function
有一个特例,也就是函数(function)。 其他都会被简单判定为object。
instanceof
对于上面检测引用类型不准确的情况,instanceof操作符能很好地帮助我们解决问题。
[] instanceof Array // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true
instanceof操作符的原理,简单来说就是,左侧是否继承于右侧。在JavaScript中来说,左侧是否在右侧的原型链上。
toString
对于引用类型来说,调用toString方法,可以实现引用类型到字符串的转换,并且返回"[object type]"。这里的type,就是这个对象的类型。
{}.toString() //[object Object]
但是经过我测试后发现,大部分特殊的对象不能实现正确输出。原来,比如Array、Date、RegExp 等都重写了toString()方法,所以我们得不到正确答案。
那么我们怎么才能获得正确结果呢? 其实我们只要调用原始toString()方法就可以了,这时候我们就要借助原始类型上的方法:
Object.prototype.toString().call(arg) arg的值为特殊的对象。
最后
今天的分享就到这里了。虽然总结了不少细节知识,但肯定有我遗漏的知识,希望大家在阅读完毕之后提出宝贵意见!
