数据类型
目前为至,js中共有8种数据类型(data type),分别是
- 基本数据类型(primitive data type)
- Undefined
- Null(引用类型)
- Boolean
- Number
- BigInt
- String
- Symbol
- 复杂数据类型
- Object
- Function(值类型)
- Array
- Date
- RegExp
- …...
- Object
typeof操作符
因为js是一种弱类型语言,我们需要一种手段来检测给定变量中值或者值的数据类型
js中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值
typeof就是负责提供这方面信息的操作符,然后返回一个字符串
很多人会错以为typeof是一个函数
typeof是一个操作符 就像 let 一样
所以我们在使用的时候是
typeof 变量
console.log(typeof undefined); //undefined
console.log(typeof null); //object
console.log(typeof true); // boolean
console.log(typeof 123); //number
console.log(typeof '123'); // string
console.log(typeof Symbol('test')); //symbol
let author = {
name: 'wcdaren',
sayName: () => {
console.log(this.name);
}
}
console.log(typeof author); //object
具体的为什么答案是那个值,我们会在下面讲解类型时说明
八种数据类型
Undefined
Undefined类型表示未定义(即声明,但没有初始化),只有一个值,即特殊的undefined.
undefined会被赋给一个没有初始化的变量
let a
console.log(a); //undefined
console.log(typeof a); //undefined
正常情况下,是不会显式给一个值赋 undefined 的。undefined这个值的主要目的是用于比较,区别空对象指针(后面将要讲到的null)和未初始化的变量。
not defined ≠ undefined
let a
console.log(a); //undefined
console.log(b); // ReferenceError: b is not defined
b is not defined 很容易让人理解成 b is undefined。但是我们说过了 undefined:是已声明了没赋值
而这里的b是没有声明,所以我们应该把 b is not defined 的意思是 变量b没有声明,即 b is not defined 应该换成 b is undeclared 。
typeof undeclared
但是,如果我们执行下面这段代码
console.log(typeof a); //undefined
返回的竟然是undefined,是不是上面讲的不对呢?不是的,没有报错而且返回一个undefined是因为typeof有一个特殊的安全防范机制(不会发生引用错误)。
对于未声明过的变量,只能执行一项操作,即使用typeof操作符检测其数据类型
当然,返回undeclared是比较更合理的。可惜不是这样。
undefined不是关键字
值得一提的是,undefined在js中竟然不是关键字,即我们可以使用undefined作为变量名。那么如果我们显式地赋值,且不小心创建了一个undefined变量时,就会出现下面这种情况
let undefined = '123'
let a = undefined
console.log(a); //123
所以为了避免不小心篡改了undefined的值,我们可以使用 void 0来显示的赋值
let undefined = '123'
let a = void 0
console.log(a); //undefined
但是还是如前面所说的,我们在编程中是不会显式赋值undefined的
Null
Null类型表示定义了但是为空,只有一个值,即null
与undefined不显式赋值刚好相反,如果我们需要一个变量,但是我们的值还没获得。
我们就会给这个变量定义null值
let a = null
console.log(a); //null
console.log(typeof null); //object
null需要注意的可能就是typeof null 返回的值
按正常的思考 我们觉得 应该返回的是 null
确实,返回null的想法是对的,这也是js公认的设计错误了
那为什么返回的是一个object呢?
因为null是一个空的对象指针,所以null也算是一个对象即object
基于这上面这种情况,判断一个值是否为空类型的最佳方法是直接和null比较
let a = null
console.log(a === null); //true
这里又要注意一点 我们在js中基本使用 === 来比较 而不使用 == 来比较,因为 == 会默认自带类型转换 ,会带来很多意想不到的结果
Boolean
Boolean类型有两个值,true和false,表示逻辑上的真假
let a = true
let b = false
console.log(a); //true
console.log(b); //false
console.log(typeof a); //boolean
console.log(typeof b); //boolean
Number
let a = 123
let b = 1.2
console.log(a); //123
console.log(b); //1.2
console.log(typeof a);//number
console.log(typeof b);//number
根据语言规范,JavaScript 采用“遵循 IEEE 754 标准的双精度 64 位格式”("double-precision 64-bit format IEEE 754 values")表示数字。
即,在js中不区分整数值和浮点值,所有数字均用浮点数值表示。
console.log(0.1 + 0.2);//0.30000000000000004
但是,在具体实现时,比如在浏览器中,整数值是以int类型存储(32位),但当它被用于执行某些32位整型不支持的操作时就会自动转换为上述的双精度64位存储。
console.log(1+2); //3
浮点数值
在上面中我们知道了
console.log(0.1 + 0.2);//0.30000000000000004
所以在涉及小数运算时
let a = 0.1
let b = 0.2
console.log((a + b) === 0.3); //false
永远不要去测试某个特定的浮点数值
那如果我们要进行浮点数的数值判断应该怎么办呢?这会在我们后续再讲到。
进制
整数可以用十进制(基数为10)、十六进制(基数为16)、八进制(基数为8)以及二进制(基数为2)表示。
- 十进制整数字面量由一串数字序列组成,且没有前缀0。
- 八进制的整数以 0(或0O、0o)开头,只能包括数字0-7。
- 十六进制整数以0x(或0X)开头,可以包含数字(0-9)和字母 a~f 或 A~F。
- 二进制整数以0b(或0B)开头,只能包含数字0和1。
严格模式下,八进制整数字面量必须以0o或0O开头,而不能以0开头。
// 十进制
let a = 10
console.log(a); //10
// 八进制
let b1 = 070
let b2 = 0O70
let b3 = 0o070
console.log(b1); //56
console.log(b2); //56
console.log(b3); //56
// 十六进制
let c1 = 0xA
let c2 = 0Xa
console.log(c1); //10
console.log(c2); //10
// 二进制
let d1 = 0b0011
let d2 = 0B0011
console.log(d1); //3
console.log(d2);//3
数值范围
5e-324~1.7976931348623157e+308
我们讲在后面再介绍我们是如何得知这个范围的。
console.log(Number.MIN_VALUE); //5e-324
console.log(Number.MAX_VALUE); //1.7976931348623157e+308
那如果我们真的要描述一个超过这个范围的值该怎么办呢?这就有后面我们要讲到的bigint类型
Infinity和-Infinity
值得一提的就是在其他语言中,如java,除以零是会抛错的,而在js中除0会得到一个无穷大的数
无穷大又分正无穷和负无穷:Infinity和-Infinity
let a = 1/0
let b = -1/0
console.log(a); //Infinity
console.log(b); //-Infinity
NaN
NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如, 0除以 0
console.log(0 / 0); //NaN
console.log('wcdaren' - 100); //NaN
NaN值有两个特点
- 任何涉及NaN的操作都返回NaN
- NaN与任何值都不相等,包括NaN本身
console.log(NaN / 10); //NaN
console.log(NaN === NaN); //false
BigInt
let a = 9007199254740992n
console.log(typeof a);//bigint
bigint,就是我们在谈Number时,超过Number范围的值的解决方法。
在涉及到类型转换或者运算方面,我们可以简单地理解为bigint和int是等效的。
但是实际上,他们两者是不等同的。
尤其有一点需要注意的是bigint类型是无法和number类型一起运算的
let a = 1n
let b = 1
console.log(a + b); //报错
String
字符串可以由双引号(")或单引号(')和反引号(`)表示。
//单引号
let name = 'wcdaren'
//双引号
let email = "wcdaren@gmail.com"
//单双混合
let address = "China ,'Guangzhou'"
let job = '"PM"'
//反引号
let girlfriend = `gugu`
console.log(name); //wcdaren
console.log(email); // wcdaren@gmail.com
console.log(address); //China ,'Guangzhou'
console.log(job); //"PM"
console.log(girlfriend);//gugu
console.log(typeof name); //string
console.log(typeof email);//string
console.log(typeof address);//string
console.log(typeof job);//string
console.log(typeof girlfriend);//string
反引号`是ES6新添加的语法,我们到时会再单独介绍。
UTF-16编码
js 中的字符串是一串Unicode 字符序列。再具体而言,即在js中字符串是一串UTF-16代码单元(code unit)的序列,每一个代码单元(code unit)由一个 16 位二进制数表示。每一个Unicode字符由一个或两个代码单元(code unit)来表示。
每个代码单元可以表示 [U+0000,U+FFFF]范围中的码点(code point)。
码点(code point):一个编码表中的某个字符对应代码值。
我们可以使用'\u1234'语法,即使用 [U+0000,U+FFFF]范围中的码点,获得字符
console.log('\u0061'); //a
在js中还有以下的码点来表示一个字符,我们在下面特殊字符再介绍
我们在前面说到,在js中每个Unicode字符由一个或两个代码单元来表示。
因为一旦码点超过了U+FFFF,比如,马这个表情 ——> 🐎
console.log('\ud83d\udc0e'); //🐎
可是如果使用这种语法,我们就很难确定这到底是一个字符还是两个字符
所以我们还可以使用另一种书写方式来表示两个代码单元即表示超过U+FFFF范围的码点
console.log('\u{1f40e}');//🐎
当然,这个只是一个语法糖,实际上🐎还是由两个代码单元组成的。
特殊字符
在String类型中包含一些特殊的字符,用于表示非打印字符来,或者据有其他用途的字符。
| 字符 | 含义 |
|---|---|
| \0 | Null字节 |
| \b | 退格符 |
| \f | 换页符 |
| \n | 换行符 |
| \r | 回车符 |
| \t | Tab (制表符) |
| \v | 垂直制表符 |
| \' | 单引号(‘),在用单引号表示的字符串中使用 |
| \" | 双引号 , 在用双引号表示的字符串中使用 |
| \\ | 反斜杠字符(\) |
| \XXX | 由从0到377最多三位八进制数XXX表示的 Latin-1 字符。例如,\251是版权符号的八进制序列。 |
| \xXX | 由从00和FF的两位十六进制数字XX表示的Latin-1字符。例如,\ xA9是版权符号的十六进制序列。 |
| \uXXXX | 由四位十六进制数字XXXX表示的Unicode字符。例如,\ u00A9是版权符号的Unicode序列。见Unicode escape sequences (Unicode 转义字符). |
| \u*{XXXXX}* | Unicode码点 (code point) 转义字符。例如,\u{2F804} 相当于Unicode转义字符 \uD87E\uDC04的简写。 |
严格模式下,不能使用八进制转义字符。
console.log('句子从这里开始结束\0,\0表示null,那么我们在输出字符时是从第一个遍历直到遍历到null,遍历即结束,所以如果我们在字符串某个位置填上null值,字符串输出便结束'); //句子从这里开始结束
console.log('I\' a good man!'); //I' a good man!
console.log("you are a \"HaoRen\""); //you are a "HaoRen"
console.log('This is \\'); //This is \
// 范围:0~377 \XXX (不推荐使用,严格模式不可使用)
console.log('\251'); //©
// 范围:00~FF \xXX
console.log('\xA9'); //©
// 范围:0000~FFFF
console.log('\u00a9'); //©
// 范围:00000~FFFFF
console.log('\u{1f40e}');//🐎
字符串的特点
字符串是不可变的。所以在涉及修改字符串的时候,其实我们只不过是把销毁原来的字符串后创建了新的字符串
let name = 'java'
name = name + 'script'
console.log(name); //javascript
上面的代码在真实的操作是
- 创建一个能容纳10个字符(即javascript的长度)的字符串
- 在这个字符串中填入'java'和'script'
- 销毁原来的字符串'java'和字符串'script'
Symbol
let a = Symbol('123')
console.log(a); //Symbol(123)
console.log(typeof a); //symbol
它表示一个独一无二的值(非字符串)。关于symbol,我们会在单独在后面再讲,这里我们只要知道它是一个独一无二的值就可以了。
Obejct
对象的创建,我们这里先提两种,也是最常用的两种
- 对象字面量
let a = {
name: 'wcdaren',
}
console.log(a); //{ name: 'wcdaren' }
console.log(typeof a); //object
- 构造函数
let a = new Object(
{ name: 'wcdaren' }
)
console.log(a); //{ name: 'wcdaren' }
console.log(typeof a); //object
至此,js中类型的分类已经全部分完了。那函数和数组之类的呢?他们都隶属于object,我们将在深入理解对象中介绍。