JavaScript数据类型概述
引言
JavaScript有6种简单数据类型(也称为原始类型),分别是Undefined、Null、Boolean、Number、String、Symbol。还有一种复杂数据类型叫Object(对象)。Object是一种无序名值对的集合。因为在JavaScript中不能定义自己的数据类型,所有值都可以用上述7种数据类型之一来表示。
1 变量的数据类型
JavaScript是一种弱类型语言,不用提前声明变量的数据类型就可以使用。在js程序运行过程中,变量的数据类型会被自动确定。
- 强类型语言:如:Java、C、C++、Python
- 弱类型语言:如:JavaScript、PHP
JavaScript中的数据类型分为两大类,基本数据类型和复杂数据类型(也称为引用数据类型)
- 基本数据类型:Number(数字型)、String(字符串型)、Boolean(布尔型)、Null(空型)、Undefined(未定义型)、Symbol(符号)、BigInt(ES6新增)
- 复杂数据类型:Object(普通对象、数组对象、正则对象、日期对象、Math对象、函数对象)
2 基本数据类型
2.1 Number(数字型)
Number类型使用IEEE 754
格式表示整数和浮点值(在某些语言中也叫双精度值),不同的数值类型相应地也有不同的数值字面量格式。
1.整数
整数可以用八进制(以8为基数)、十进制(以10为基数)或十六进制(以16为基数)字面量表示。
对于八进制字面量,第一个数字必须是0,然后是相应的八进制数字(数值0~7)。如果字面量中包含的数字超出了应有的范围,就会忽略前缀的0,后面的数字会被当成十进制数,如下所示:
let number1 = 065; // 八进制的53
let number2 = 098; // 无效的八进制数,会被当成98来处理
十进制字面量就是我们常用的表示方法,如下所示:
let number1 = 76; // 整数76
对于十六进制字面量,数值前缀是0x,然后是十六进制数字(0~9以及A~F)。十六进制数字中的字母大小写均可,如下所示:
let number1 = 0xa78; // 十六进制2680
let number2 = 0x1f; // 十六进制31
常见的进制:
- 八进制数:在数字开头加上0,八进制数由0~7组成,逢8进位
- 十进制数:是组成以10为基础的数字系统,有0~9组成,逢10进位
- 十六进制数:在数字开头加上0x,十六进制数由0 ~ 9,a ~ f组成
2.浮点数
要定义浮点值,数值中必须包含小数点,并且小数点后面要有数字,例如:
let number1 = 3.4; // 小数
let number2 = .2; // 有效写法,代表0.2
let number3 = 5.; // 小数点后面没用数字,当成整数5处理
let number4 = 2.0; // 小数点后面是0,当成整数2处理
对于非常大或者非常小的数字,可以使用科学计数法来表示,默认情况下,js会将小数点后至少包含6个0的浮点值转换为科学计数法,例如:
let number1 = 2.345e7; // 等于23450000
let number2 = 0.00000000000000000005; // 等于5e-20
let number3 = 3e-5; // 等于0.00003
浮点值的精确度最高可达17位小数,但在计算中不如整数精确,例如0.1加0.2得到的不是0.3,而是0.30000000000000004。
3.范围
ECMAScript可以表示的最大值与最小值分别是:
- 最大值:
Number.MAX_VALUE
,输出结果:1.7976931348623157e+308 - 最小值:
Number.MIN_VALUE
,输出结果:5e-324
如果计算得到的数值超过了JavaScript表示的范围,那么表示为Infinity
(正无穷大)和-Infinity
(负无穷大)。
判断这个值是不是无穷大,可以使用isFinite()
函数,如下所示:
let result = Number.MAX_VALUE + Number.MAX_VALUE;
isFinite(result); // false
4.NaN
NaN
意思是不是数值,它用来表示数学运算没有成功的值,而且任何涉及NaN的操作会始终返回NaN。例如:
5 / 0 // NaN
NaN - 1 // NaN
并且,NaN
是一个自反的值,即:
NaN === NaN // false
NaN == NaN // false
NaN !== NaN // true
ECMAScript提供了isNaN()
函数,该函数可以判断这个参数是否“不是数值”。把一个值传给 isNaN()
后,该函数会把它转换为数值,可以转化成数值的为true
,不能转化为数值的为false
,例如:
isNaN(NaN) // true
isNaN(40000) // false
isNaN("jumaochibupang") // true,不可以转化为数值
isNaN("300") // false,可以转化为数值300
isNaN(false) // false,可以转化为数值0
2.2 String(字符串型)
字符串是指计算机中用于表示文本的一系列字符,在JavaScript中使用单引号''
或双引号""
或反引号``
来包裹字符串。
var str1 = '单引号字符串';
var str2 = "双引号字符串";
var str3 = `反引号字符串`;
在单引号字符串中可以直接书写双引号,在双引号字符串中也可以直接书写单引号
var str1 = 'I am a "programmer"';
var str2 = "I'm a 'programmer'";
将以上语句输出,结果如下:
以某种引号作为字符串开头,必须仍然以该种引号作为字符串结尾,错误用法:
var str1 = 'I'm a programmer'; // 单引号错误用法
var str2 = "I'm a "programmer""; // 双引号错误用法
var str3 = 'I am a programmer"; // 单双引号混用
更多用法可以查看:JavaScript字符串概述
2.3 Boolean(布尔型)
布尔型通常用于逻辑判断,它有两个值:true
和false
,表示事物的“真”和“假”,其中true
的值为1,false
的值为0。
console.log(true); // 输出结果:true
console.log(false); // 输出结果:false
console.log(true + 1); // 输出结果:2
console.log(false + 1); // 输出结果:1
2.4 undefined(未定义型)
如果一个变量声明后没有赋值,则变量的值就是undefined
。JS会自动将声明但没有进行初始化的变量设为undifined
。
var a;
console.log(a); // 输出结果:undefined
console.log(a + '_'); // 输出结果:undefined_(字符串型)
console.log(a + 1); // 输出结果:NaN
console.log(typeof a); // 输出结果:undefined
2.5 Null(空型)
null
不能通过JS来自动赋值,必须要我们自己手动来给某个变量赋值为null
。
给一个变量赋一个null
值,下面通过代码演示null
值的使用。
var b = null;
console.log(b); // 输出结果:null
console.log(b + '_'); // 输出结果:null_(转化为字符串型)
console.log(b + 1); // 输出结果:1(null转换为0)
console.log(b + true); // 输出结果:1(null转换为0,true转换为1)
console.log(typeof b); // 输出结果:object
null
有属于自己的类型Null
,而不属于Object
类型,typeof
之所以会判定为Object
类型,是因为JS数据类型在底层都是以二进制的形式表示的,二进制的前三位为0会被typeof
判断为对象类型,而null
的二进制位恰好都是0,因此,null
被误判断为Object
类型。
2.6 Symbol(符号)
Symbol是ES6中新出现的一种基本数据类型,它是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用,表示独一无二的值。凡是属性名属于Symbol
类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
Symbol值通过Symbol()函数初始化,例如:
let name = Symbol();
typeof name; // symbol
调用Symbol()
函数时,可以传入一个字符串参数作为对符号的描述(description
),但是这个字符串参数与符号标识无关
let name = Symbol("橘猫吃不胖");
s1; // Symbol(橘猫吃不胖)
s1.description; // "橘猫吃不胖"
Symbol
不能与其他类型的值进行运算,但是可以显式转为字符串,也可以转为布尔值,但是不能转为数值。
let s1 = Symbol("橘猫吃不胖");
s1.toString() // "Symbol(橘猫吃不胖)"
Boolean(s1); // true
s1 + 1; // TypeError: Cannot convert a Symbol value to a number
"aaa" + s1; // TypeError: Cannot convert a Symbol value to a number
Number(s1); // TypeError: Cannot convert a Symbol value to a number
2.6.1 Symbol的一些方法
1.Symbol.for()和Symbol.keyFor()
如果想在不同的代码中共享同一个符号值的话,可以使用Symbol.for()
方法注册符号。Symbol.for()
方法接收一个字符串作为参数,任何值传给Symbol.for()
都会被转化成字符串,作为目标符号的标识符与描述信息。
let uid = Symbol.for("uid");
let obj1 = {};
obj1[uid] = "1111111111";
let cid = Symbol.for("uid");
let obj2 = {};
obj2[cid] = "2222222222";
uid === cid // true
Symbol.for()
对每个字符串键都执行幂等操作,它会首先搜索全局符号注册表,看看是否存在键为uid
的符号值,如果存在,则会返回这个已经存在的符号值,如果不存在,则会创建一个新的符号值并返回,并将其记录到全局符号注册表中
如果要查询全局符号注册表,可以使用Symbol.keyFor()
函数,这个方法接收符号作为参数,最后返回该全局符号对应的字
符串键。如果查询的不是全局符号,则返回undefined
。
let uid = Symbol.for("uid");
let s1 = Symbol();
Symbol.keyFor(uid); // uid
Symbol.keyFor(s1); // undefined
2.7 BigInt
Number的局限性:JavaScript 中的 Number 是双精度浮点型,这意味着精度有限。Number.MAX_SAFE_INTEGER是安全范围内的最大值,为9007199254740991。最小安全值为 Number.MIN_SAFE_INTEGER,值为-9007199254740991,超出安全值的计算都会丧失精度。
BigInt 是一个新的原始类型,可以实现任意精度计算。创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可。也可以使用全局方法 BigInt(value) 转化,入参 value 为数字或数字字符串。
let a = BigInt(1234567890);
let b = 1234567890n;
console.log(a === b); // true
BigInt 与 Number 类型的值不严格相等:
console.log(123 === 123n); // false
console.log(123 == 123n); // true
3 复杂数据类型Object
Object
对象其实就是一组数据和功能的集合,对象可以通过new
操作符后跟对象类型的名称来创建。Object
类型也是派生其他对象的基类,其所有的属性和方法在派生的对象上同样存在。
每个Object
实例都有如下属性和方法:
constructor
:用于创建当前对象的函数hasOwnProperty(propertyName)
:用于判断当前对象实例上是否存在给定的属性,要检查的属性名必须是字符串或符号isPrototypeOf(object)
:用于判断当前对象是否为另一个对象的原型propertyIsEnumerable(propertyName)
:用于判断给定的属性是否可以使用for-in
语句枚举,属性名必须是字符串toLocaleString()
:返回对象的字符串表示,该字符串反映对象所在的本地化执行环境toString()
:返回对象的字符串表示valueOf()
:返回对象对应的字符串、数值或布尔值表示
4 数据类型转换
4.1 显式类型转换
4.1.1 转换为Number类型
1.Number()
Number()
可用于转化任何数据类型,会基于如下规则执行转换:
// (1)参数为布尔值,true为1,false为0
Number(true); // 1
Number(false); // 0
// (2)参数为数值,直接返回当前数值
Number(00000999); // 999,忽略0
Number(99.99); // 99.99
Number(0x68); // 104
// (3)参数为null,返回0
Number(null); // 0
// (4)参数为undefined,返回NaN
Number(undefined); // NaN
// (5)参数为字符串:①数值字符串,返回该数值;②参数为空字符串,返回0;③参数为其他字符串,返回NaN
Number("00023.78"); // 23.78
Number(""); // 0
Number("ju mao chi bu pang"); // NaN
2.parseInt()
parseInt()
函数主要用在字符串转化为整数,更专注于字符串是否包含数值模式,它接受2个参数,第一个参数为字符串,第二个参数为2-36之间的整数,表示转换进制,如果不传第二个参数,默认按照十进制进行转换,其转换规则如下:
// (1)如果字符串前面有空格,空格会被忽略,从第一个非空格字符开始转换
parseInt(" 82 "); // 82
// (2)如果第一个非空格字符不是数字、+或者-,立即返回NaN,空字符串也会返回NaN
parseInt(" jumao666"); // NaN
parseInt(""); // NaN
// (3)如果第一个字符是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符
parseInt("-8989"); // -8989
parseInt("-8989ssssss111"); // -8989,ssssss111会被忽略
parseInt("-8989.8989"); // -8989,小数点会被忽略
// (4)如果只传1个参数,该函数可以识别不同的整数格式转化为10进制,例如8进制转10进制、16进制转10进制等,如果传入第2个参数,则会解析为指定进制数
parseInt("0xAF"); // 175,10进制175
parseInt("AF", 16); // 175,指定进制数可以省略前缀
parseInt("100", 2); // 4,100按照2进制解析为4
parseInt("100", 8); // 64,100按照8进制解析为64
parseInt("100", 10); // 4,100按照10进制解析为100
parseInt("100", 16); // 4,100按照16进制解析为256
3.parseFloat()
parseFloat()
函数跟parseInt()
函数类似,都是从位置0开始检测每个字符,解析到字符串末尾或者解析到一个无效的浮点数值字符为止。
它的不同之处在于,第一次出现的小数点是有效的,但第二次出现的小数点就无效了,并且始终忽略字符串开头的0,其转换规则如下:
parseFloat("1234jumaochibupang"); // 1234,按整数解析
parseFloat("0xA"); // 0,十六进制始终返回0,只能解析十进制数字
parseFloat("22.5"); // 22.5
parseFloat("22.34.5"); // 22.34,第二次出现的小数点无效
parseFloat("0908.5"); // 908.5,忽略字符串开头的0
parseFloat("3.125e7"); // 31250000
4.1.2 转换为String类型
1.toString()
除了null
和undefined
值,几乎所有值都有toString()
方法,这个方法会返回当前值的字符串等价物。
let age = 11;
let ageAsString = age.toString(); // 字符串"11"
let found = true;
let foundAsString = found.toString(); // 字符串"true"
当Number
类型调用这个方法时,toString()
可以接收1个底数作为参数,作为数字转换的进制数,例如:
let num = 10;
num.toString(); // "10",默认情况下返回10进制字符串
num.toString(2); // "1010",输出10的2进制字符串
num.toString(8); // "12",输出10的8进制字符串
num.toString(16); // "a",输出10的16进制字符串
2.String()
String()
可以转化任何数据类型,它遵循以下转化规则:
- 如果值有
toString()
方法,则调用该方法; - 如果值为
null
和undefined
,则返回"null"
和"undefined"
示例代码如下:
String(10); // "10"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
4.1.3 转换为Boolean类型
要将一个其他类型的值转换为布尔值,可以调用特定的Boolean()
转型函数:
Boolean("jumaochibupang");
Boolean()
转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值,不同类型与布尔值之间的转换规则如下:
Boolean("aaaaa"); // true,非空字符串转化为true
Boolean(""); // false,空字符串转化为false
Boolean(1223); // true,非0数值转化为true
Boolean(0); // 0转化为false
Boolean([]); // true,任意对象转化为true
Boolean(null); // false,null转化为false
Boolean(undefined); // false,undefined转化为false
4.2 隐式类型转换
4.2.1 转换为Number类型
4.2.2 转换为String类型
4.2.3 转换为Boolean类型
if
等流控制语句中,会自动执行其他类型值到布尔值的转换,例如:
let message = "Hello world!";
if (message) {
console.log("橘猫吃不胖");
}
在这个例子中,log
会输出字符串"橘猫吃不胖"
,因为字符串message
会被自动转换为等价的布尔值 true
。
3 两种数据类型存储方式(区别)
基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基本类型值和执行代码的空间。
引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
4 数据类型检测
1、typeof:除了数组、对象、null会被判定为object,其他的判断都是正确的。
typeof 被判断的值 // 方式1
typeof(被判断的值) // 方式2
示例代码:
console.log(typeof 3); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // object
typeof判断null为object类型的原因:JavaScript数据类型在底层都是以二进制表示的,二进制前3为是0会被判断为object类型,null的二进制都是0,因此会被判断为object类型。
2、instanceof:可以判断引用数据类型,原理是判断在被判断者的原型链中能不能找到该类型的原型。
a instanceof b
示例代码:
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
但是如果将数字型、字符串形和布尔型经过实例化后,就可以使用instanceof判断了:
new Number(1) instanceof Number // true
new String("str") instanceof String // true
new Boolean(true) instanceof Boolean // true
3、constructor
a.constructor === 类型
示例代码:
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
注意:如果创建一个实例对象改变它的原型,该方法判断数据类型就会失效。
function fun() { };
fun.prototype = new Array();
var f = new fun(); // 创建实例对象
console.log(f.constructor === Function); // false
console.log(f.constructor === Array); // true
4、Object.prototype.toString.call():使用Object对象的原型方法toString()来判断数据类型,这种方式所有的数据类型都可以被判断。
Object.prototype.toString.call(被判断的数据)
示例代码:
Object.prototype.toString.call(10) // [object Number]
Object.prototype.toString.call('90') // [object String]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(function () { }) // [object Function]
Object.prototype.toString.call(this) // [object Window]
5 判断数组的方式
1、Array.isArray(被判断对象)
:返回true为数组,false不是数组
Array.isArray([1, 2, 3])
2、利用原型判断,true为数组,false不是数组
arr.__proto__ == Array.prototype
3、通过instanceof
判断,true为数组,false不是数组
obj instanceof Array
4、通过Object.prototype.toString.call()
判断
Object.prototype.toString.call(arr) // [object Array]
6 null和undefined的区别
undefined表示未定义的值,声明了但是不赋值,类型就是undefined。
null表示空值,必须手动为变量赋值为null,才可以成为null类型