JS之数据类型
一、变量数据类型
为什么需要数据类型
在计算机中,不同的数据所需占用的存储空间不同,为了充分利用存储空间,于是定义了不同的数据类型。而且,不同的数据类型,寓意也不同。
我们都知道,无论这个变量是字符串类型,还是数字类型,我们都可以直接用 `var` 去定义它。比如:
```javascript
var a = 'hello word';
var b = 123;
```
为什么可以这样做呢?这是因为:JavaScript 是一种「弱类型语言」,或者说是一种「动态语言」,这意味着不需要提前声明变量的类型,在程序运行过程中,类型会自动被确定。
**JS 的变量数据类型,是在程序运行的过程中,根据等号右边的值来确定的**。而且,变量的数据类型是可以变化的。比如说:
```javascript
var name = 'qianguyihao';
name = 123; // 强制将变量 name 修改为 数字类型
```
JS中一共有六种数据类型
- **基本数据类型(值类型)**:String 字符串、Number 数值、Boolean 布尔值、Null 空值、Undefined 未定义。
- **引用数据类型(引用类型)**:Object 对象。引用数据类型只有一种
基本和引用数据类型之间的区别**:
- 基本数据类型:参数赋值的时候,传数值。
- 引用数据类型:参数赋值的时候,传地址(修改的同一片内存空间)。
注意:内置对象 Function、Array、Date、RegExp、Error等都是属于 Object 类型。也就是说,除了那五种基本数据类型之外,其他的,都称之为 Object类型。
注意以下两个例子
**基本数据类型举例**:
```javascript
var a = 23;
var b = a;
a++;
console.log(a); // 打印结果:24
console.log(b); // 打印结果:23
```
上面的代码中:a 和 b 都是基本数据类型,让 b 等于 a,然后**改变 a 的值之后,发现 b 的值并没有被改变**。
**引用数据类型举例**:
```javascript
var obj1 = new Object();
obj1.name = 'smyh';
// 让 obj2 等于 obj1
var obj2 = obj1;
// 修改 obj1 的 name 属性
obj1.name = 'vae';
console.log(obj1.name); // 打印结果:vae
console.log(obj2.name); // 打印结果:vae
```
从上面的例子中,可以反映出,基本数据类型和引用数据类型是有区别的。
栈内存和堆内存
JS中,所有的**变量**都是保存在**栈内存**中的。
**基本数据类型**:
基本数据类型的值,直接保存在栈内存中。值与值之间是独立存在,修改一个变量不会影响其他的变量。
**引用数据类型**:
对象是保存到**堆内存**中的。每创建一个新的对象,就会在堆内存中开辟出一个新的空间;而**变量保存了对象的内存地址**(对象的引用),保存在栈内存当中。如果两个变量保存了同一个对象的引用,当一个通过一个变量修改属性时,另一个也会受到影响。
二、基础数据类型
1、String 字符串
字符串型可以是引号中的任意文本,其语法为:双引号 `""` 或者单引号 `''`。
```javascript
var a = "abcde";
var b = "千古壹号";
var c = "123123";
var d = '哈哈哈哈哈';
var e = ""; //空字符串
var f = haha; // 没使用引号,到这里会直接报错。因为会被认为是js代码,但是之前并没有定义 haha。
console.log(typeof a);
console.log(typeof b);
console.log(typeof c);
console.log(typeof d);
console.log(typeof e);
```
控制台输出如下:
```
string
string
string
string
string
```
注意:
1、单引号和双引号不能混用。比如下面这样写是不可以的:
```javascript
var str = 'hello"; // 报错:Uncaught SyntaxError: Invalid or unexpected token
```
2、同类引号不能嵌套:双引号里不能再放双引号,单引号里不能再放单引号。
3、单引号里可以嵌套双引号;双引号里可以嵌套单引号。
转义字符
在字符串中我们可以使用`\`作为转义字符,当表示一些特殊符号时可以使用`\`进行转义。
- `\"` 表示 `"`
- `\'` 表示 `'`
- `\\` 表示`\`
- `\r` 表示回车
- `\n` 表示换行。n 的意思是 newline。
- `\t` 表示缩进。t 的意思是 tab。
- `\b` 表示空格。b 的意思是 blank。
获取字符串的长度
字符串是由若干个字符组成的,这些字符的数量就是字符串的长度。我们可以通过字符串的 length 属性可以获取整个字符串的长度。
```javascript
var str1 = '张三';
var str2 = '张三和李四';
var str3 = 'qianguyihao';
var str4 = 'qianguyihao, keep moving!';
console.log(str1.length); // 4
console.log(str2.length); // 5
console.log(str3.length); // 11
console.log(str4.length); // 25
```
由此可见,字符串的 length 属性,在判断字符串的长度时,会认为:
- 一个中文算一个字符,一个英文算一个字符
- 一个标点符号(包括中文标点、英文标点)算一个字符
- 一个空格算一个字符
字符串拼接
多个字符串之间可以使用加号 `+` 进行拼接。
**拼接语法**:
```
字符串 + 任意数据类型 = 拼接之后的新字符串;
```
**拼接规则**:拼接前,会把与字符串相加的这个数据类型转成字符串,然后再拼接成一个新的字符串。
**代码举例**:(字符串与六大数据类型相加)
```javascript
var str1 = '张三' + '永不止步';
var str2 = '张三' + 666;
var str3 = '张三' + true;
var str4 = '张三' + null;
var str5 = '张三' + undefined;
var obj = { name: '张三', age: 28 };
var str6 = '张三' + obj;
console.log(str1);
console.log(str2);
console.log(str3);
console.log(str4);
console.log(str5);
console.log(str6);
```
打印结果:
```
张三永不止步
张三666
张三true
张三null
张三undefined
张三[object Object]
```
2、数值型:Number
在JS中所有的数值都是 Number 类型,包括整数和浮点数(小数)。
```javascript
var a = 100; // 定义一个变量 a,并且赋值整数100
console.log(typeof a); // 输出变量 a 的类型
var b = 12.3; // 定义一个变量 b,并且赋值浮点数 12.3
console.log(typeof a);
```
上方代码的输出结果为:
```
number
number
```
再次补充:在 JS 中,只要是数,就是 Number 数值型的。无论整浮、浮点数(即小数)、无论大小、无论正负,都是 Number 类型的。
数值范围
由于内存的限制,ECMAScript 并不能保存世界上所有的数值。
- 最大值:`Number.MAX_VALUE`,这个值为: 1.7976931348623157e+308
- 最小值:`Number.MIN_VALUE`,这个值为: 5e-324
如果使用 Number 表示的变量超过了最大值,则会返回Infinity。
- 无穷大(正无穷):Infinity
- 无穷小(负无穷):-Infinity
注意:`typeof Infinity`的返回结果是number。
NaN
**NaN**:是一个特殊的数字,表示Not a Number,非数值。比如:
```javascript
console.log("abc" / 18); //结果是NaN
console.log("abc" * "abcd"); //按理说,字符串相乘是没有结果的,但如果你非要让JS去算,它就一定会给你一个结果。结果是NaN
```
注意:`typeof NaN`的返回结果是 number。
Undefined和任何数值计算的结果为 NaN。NaN 与任何值都不相等,包括 NaN 本身。
浮点数的运算
在JS中,整数的运算**基本**可以保证精确;但是**小数的运算,可能会得到一个不精确的结果**。所以,千万不要使用JS进行对精确度要求比较高的运算。
如下:
```javascript
var a = 0.1 + 0.2;
console.log(a); //打印结果:0.30000000000000004
```
上方代码中,打印结果并不是0.3,而是0.30000000000000004。
我们知道,所有的运算都要转换成二进制去计算,然而,二进制是无法精确表示1/10的。因此存在小数的计算不精确的问题。
连字符和加号的区别
键盘上的`+`可能是连字符,也可能是数字的加号。如下:
```
console.log("我" + "爱" + "你"); //连字符,把三个独立的汉字,连接在一起了
console.log("我+爱+你"); //原样输出
console.log(1+2+3); //输出6
```
输出:
```
我爱你
我+爱+你
6
```
**总结**:如果加号两边**都是**数值,此时是加。否则,就是连字符(用来连接字符串)。
隐式转换
我们知道,`"2"+1`得到的结果其实是字符串,但是`"2"-1`得到的结果却是数值1,这是因为计算机自动帮我们进行了“**隐式转换**”。
也就是说,`-`、`*`、`/`、`%`这几个符号会自动进行隐式转换。例如:
```javascript
var a = "4" + 3 - 6;
console.log(a);
```
输出结果:
```javascript
37
```
虽然程序可以对`-`、`*`、`/`、`%``这几个符号自动进行“隐式转换”;但作为程序员,我们最好自己完成转换,方便程序的可读性。
3、布尔值:Boolean
布尔型有两个值:true 和 false。主要用来做逻辑判断: true 表示真,false 表示假。
布尔值直接使用就可以了,千万不要加上引号。
代码:
```javascript
var a = true;
console.log(typeof a);
```
控制台输出结果:
```
boolean
```
布尔型和数字型相加时, true 按 1 来算 ,false 按 0 来算。
Null 和 Undefined
null:空值
专门用来表示一个为空的**对象**(例如:`var a = null`)。注意,专门用来表示**空对象**。
- Null类型的值只有一个,就是null。比如 `var a = null`。
- 使用 typeof 检查一个null值时,会返回object。
如果你想定义一个变量,以后打算存储为对象,但是还没想好放什么内容,这个时候,可以给 null。
比如:
```js
var myObj = null;
cosole.log(typeof myObj); // 打印结果:object
```
`undefined`:未定义
**声明**了一个变量,但是没有**赋值**(例如:`var a;`),此时它的值就是 `undefined`。
- Undefined类型的值只有一个,就是undefind。比如
- 使用 type of 检查一个undefined时,会返回undefined。
null和undefined有很大的相似性。看看null == undefined的结果(true)也就更加能说明这点。
但是null === undefined的结果(false)。它们虽然相似,但还是有区别的,其中一个区别是:和数字运算时,10 + null结果为:10;10 + undefined结果为:NaN。
- 任何数据类型和undefined运算都是NaN;
- 任何值和null运算,null可看做0运算。
## 变量值的传递(赋值)
语句:
```
a = b;
```
把b的值赋给a,b不变。
将等号右边的值,赋给左边的变量;等号右边的变量,值不变。
来做几个题目。
举例1:
```js
//a b c
var a = 1; //1
var b = 2; //1 2
var c = 3; //1 2 3
a = b + c; //5 2 3
b = c - a; //5 -2 3
c = a * b; //5 -2 -10
console.log(a);
console.log(b);
console.log(c);
```
输出:
```
5
-2
-10
```
举例2:
```js
//a b c
var a = 1;
var b = 2;
var c = 3; //1 2 3
a = a + b; //3 2 3
b = b + a; //3 5 3
c = c + b; //3 5 8
console.log(a); //3
console.log(b); //5
console.log(c); //8
```
输出:
```
3
5
8
```
举例3:
```js
//a b
var a = "1";
var b = 2; //"1" 2
a = a + b; //"12" 2
b = b + a; //"12" "212"
console.log(a); //输出12
console.log(b); //输出212
```
输出:
```
12
212
```
举例4:
```js
//a b
var a = "1";
var b = 2;
a = b + a; //"21" 2
b = b + a; //"21" "221"
console.log(a); //21
console.log(b) //221
```
效果:
```
21
221
```
举例5:(这个例子比较特殊,字符串减去数字)
```js
var a = "3";
var b = 2;
console.log(a-b);
```
效果:(注意,字符串 - 数值 = 数值)
```
1
```
三、数据类型的转换
通常有三种形式的类型转换:
- 转换为字符串类型
- 转换为数字型
- 转换为布尔型
1、typeof 运算符
typeof()`表示“**获取变量的数据类型**”,返回的是小写,语法为:(两种写法都可以)
```javascript
// 写法1
typeof 变量;
// 写法2
typeof(变量);
```
typeof 这个运算符的返回结果就是变量的类型。那返回结果的类型是什么呢?是字符串。
备注 1:为啥 `typeof null`的返回值也是 object 呢?因为 null 代表的是**空对象**。
备注 2:`typeof NaN`的返回值是 number,上一篇文章中讲过,NaN 是一个特殊的数字。
console.log(type []); // 空数组的打印结果:object
console.log(type {}); // 空对象的打印结果:object
代码解释:这里的空数组`[]`、空对象`{}` ,为啥他们在使用 typeof 时,返回值也是 `object`呢?因为这里的返回结果`object`指的是**引用数据类型**。空数组、空对象都是**引用数据类型 Object**。
2、变量的类型转换的分类
显示类型转换
- toString()
- String()
- Number()
- parseInt(string)
- parseFloat(string)
- Boolean()
隐式类型转换
- isNaN ()
- 自增/自减运算符:`++`、`—-`
- 正号/负号:`+a`、`-a`
- 加号:`+`
- 运算符:`-`、`*`、`/`
隐式类型转换(特殊)
- 逻辑运算符:`&&`、`||`、`!` 。非布尔值进行**与或**运算时,会先将其转换为布尔值,然后再运算,但运算结果是**原值**。
- 关系运算符:`<`、`>` `<=` `>=`等。关系运算符,得到的运算结果都是布尔值:要么是true,要么是false。
三、常用类型转换举例
1、字符串
方法一(隐式类型转换):字符串拼接
格式:变量+"" 或者 变量+"abc"
举例:
var a = 123; // Number 类型
console.log(a + ''); // 转换成 String 类型
console.log(a + 'haha'); // 转换成 String 类型
上面的例子中,打印的结果,都是字符串类型的数据。实际上内部是调用的 String() 函数。也就是说,`c = c + ""` 等价于 `c = String(c)`。
方法二:调用 toString()方法
变量.toString()
【重要】该方法**不会影响到原变量**,它会将转换的结果返回。当然我们还可以直接写成`a = a.toString()`,这样的话,就是直接修改原变量。
注意:null 和 undefined 这两个值没有 toString()方法,所以它们不能用方法二。如果调用,会报错。
另外,Number 类型的变量,在调用 toString()时,可以在方法中传递一个整数作为参数。此时它将会把数字转换为指定的进制,如果不指定则默认转换为 10 进制。例如:
```javascript
var a = 255;
//对于Number调用toString()时可以在方法中传递一个整数作为参数
//此时它将会把数字转换为指定的进制,如果不指定则默认转换为10进制
a = a.toString(2); // 转换为二进制
console.log(a); // 11111111
console.log(typeof a); // string
方法三(强制转换):使用 String()函数
语法:
```javascript
String(变量)
```
使用 String()函数做强制类型转换时:
- 对于 Number 和 Boolean 而言,本质上就是调用 toString()方法。
- 但是对于 null 和 undefined,则不会调用 toString()方法。它会将 null 直接转换为 "null"。将 undefined 直接转换为 "undefined"。
prompt():用户的输入
prompt()就是专门用来弹出能够让用户输入的对话框。重要的是:用户不管输入什么,都当字符串处理。
2、number
使用 Number() 函数
**情况一:字符串 --> 数字**
- 1.如果字符串中是纯数字,则直接将其转换为数字。
- 2.只要字符串中包含了非数字的内容(`小数点`按数字来算),则转换为 NaN。
- 3.如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0。
**情况二:布尔 --> 数字**
- true 转成 1
- false 转成 0
**情况三:null --> 数字**
- 结果为:0
**情况四:undefined --> 数字**
- 结果为:NaN
补充:怎么理解这里的 **NaN** 呢?可以这样理解,使用 Number() 函数之后,**如果无法转换为数字,就会转换为 NaN**。
使用 parseInt()函数:字符串 -> 整数
**parseInt()的作用是将字符串中的有效的整数内容转为数字**。parse 表示“转换”,Int 表示“整数”(注意`Int`的拼写)。例如:
parseInt("5");
得到的结果是数字 5。
parseInt()的转换情况如下。
**情况一:字符串 --> 数字**
- 1.**只保留字符串最开头的数字**,后面的中文自动消失。
- 2.如果字符串不是以数字开头,则转换为 NaN。
- 3.如果字符串是一个空串或者是一个全是空格的字符串,转换时会报错。
**情况二:Boolean --> 数字**
- 结果为:NaN
**情况三:Null --> 数字**
- 结果为:NaN
**情况四:Undefined --> 数字**
- 结果为:NaN
Number() 函数和 parseInt() 函数的区别:
就拿`Number(true)` 和 `parseInt(true)/parseFloat(true)`来举例,二者在使用时,是有区别的:
- Number(true) :千方百计地想转换为数字。
- parseInt(true)/parseFloat(true) :先转为字符串,再提取出最前面的数字部分;没提取出来,那就返回 NaN。
**parseInt()具有以下特性**:
(1)**只保留字符串最开头的数字**,后面的中文自动消失。例如:
```javascript
console.log(parseInt("2017在公众号上写了6篇文章")); //打印结果:2017
console.log(parseInt("2017.01在公众号上写了6篇文章")); //打印结果仍是:2017 (说明只会取整数)
console.log(parseInt("aaa2017.01在公众号上写了6篇文章")); //打印结果:NaN (因为不是以数字开头)
```
(2)如果对**非 String**使用 parseInt()或 parseFloat(),它会**先将其转换为 String** 然后再操作。【重要】
比如:
```javascript
var a = 168.23;
console.log(parseInt(a)); //打印结果:168 (因为是先将c转为字符串"168.23",然后然后再操作)
var b = true;
console.log(parseInt(b)); //打印结果:NaN (因为是先将a转为字符串"true",然后然后再操作)
var c = null;
console.log(parseInt(c)); //打印结果:NaN (因为是先将b转为字符串"null",然后然后再操作)
var d = undefined;
console.log(parseInt(d)); //打印结果:NaN (因为是先将b转为字符串"undefined",然后然后再操作)
```
(3)自动带有截断小数的功能:**取整,不四舍五入**。
例 1:
```javascript
var a = parseInt(5.8) + parseInt(4.7);
console.log(a);
```
打印结果:
```
9
```
例 2:
```javascript
var a = parseInt(5.8 + 4.7);
console.log(a);
```
打印结果:
```javascript
10;
```
(4)带两个参数时,表示在转换时,包含了进制转换。
代码举例:
```javascript
var a = '110';
var num = parseInt(a, 16); // 【重要】将 a 当成 十六进制 来看待,转换成 十进制 的 num
console.log(num);
```
打印结果:
```
272
```
如果你对打印结果感到震惊,请仔细看上面的代码注释。就是说,无论 parseInt() 里面的进制参数是多少,最终的转换结果是十进制。
我们继续来看下面的代码,打印结果是多少。
```javascript
var a = '5';
var num = parseInt(a, 2); // 将 a 当成 二进制 来看待,转换成 十进制 的 num
console.log(num); // 打印结果:NaN。因为 二进制中没有 5 这个数,转换失败。
```
parseFloat()函数:字符串 --> 浮点数(小数)
parseFloat()的作用是:将字符串转换为**浮点数**。
parseFloat()和 parseInt()的作用类似,不同的是,parseFloat()可以获得有效的小数部分。
代码举例:
```javascript
var a = '123.456.789px';
console.log(parseFloat(a)); // 打印结果:123.456
```
parseFloat() 的几个特性,可以参照 parseInt()。
3、转换为 Boolean
将其他的数据类型转换为 Boolean,可以使用 Boolean()函数。情况如下:
- 情况一:数字 --> 布尔。除了 0 和 NaN,其余的都是 true。也就是说,`Boolean(NaN)`的结果是 false。
- 情况二:字符串 ---> 布尔。除了空串,其余的都是 true。全是空格的字符串,转换结果也是 true。字符串`'0'`的转换结果也是 true。
- 情况三:null 和 undefined 都会转换为 false。
- 情况四:引用数据类型会转换为 true。注意,空数组`[]`和空对象`{}`,转换结果也是 true。
PS:转换为 Boolean 的这几种情况,**很重要**,开发中会经常用到。
4、其他进制的数字
- 16 进制的数字,以`0x`开头
- 8 进制的数字,以`0`开头
- 2 进制的数字,`0b`开头(不是所有的浏览器都支持:chrome 和火狐支持,IE 不支持)
比如`070`这个字符串,如果我调用 parseInt()转成数字时,有些浏览器会当成 8 进制解析,有些会当成 10 进制解析。
所以,比较建议的做法是:可以在 parseInt()中传递第二个参数,来指定当前数字的进制。例如:
```javascript
var a = "070";
a = parseInt(a, 8); //将 070 当成八进制来看待,转换结果为十进制。
console.log(a); // 打印结果:56。这个地方要好好理解。
```
四、隐式类型转换
隐式类型转换,内部调用的都是显式类型的方法
1、isNaN() 函数
isNaN(参数);
解释:判断指定的参数是否为 NaN(非数字类型),返回结果为 Boolean 类型。也就是说:**任何不能被转换为数值的参数,都会让这个函数返回 true**。
**执行过程**:
(1)先调用`Number(参数)`函数;
(2)然后将`Number(参数)`的返回结果和`NaN`进行比较。
代码举例:
```javascript
console.log(isNaN('123')); // 返回结果:false。
console.log(isNaN('abc')); // 返回结果:true。因为 Number('abc') 的返回结果是 NaN
console.log(isNaN(null)); // 返回结果:false
console.log(isNaN(undefined)); // 返回结果:true
console.log(isNaN(NaN)); // 返回结果:true
```
2、自增/自减运算符:`++`、`—-`
**举例 1**:
```javascript
var a = "666";
a++;
console.log(typeof a); // 打印结果: number
console.log(a); // 打印结果:667
```
执行过程:
(1)先调用`Number(参数)`函数;
(2)然后将`Number(参数)`的返回结果进行 加 1 操作。
**举例 2**:
```javascript
var a = 'abc';
a++;
console.log(typeof a); // 打印结果:number
console.log(a); // 打印结果:NaN。因为 Number('abc')的结果为 NaN,再自增后,结果依然是 NaN
```
3、正号/负号:`+a`、`-a`
> 注意,这里说的是正号/负号,不是加号/减号。
任何值做`+a`、`-a`、`/a`运算时,运算结果都会自动转换为 Number 类型。 内部调用的是 Number() 函数。
**举例**:
```javascript
var a = '666';
var b = +a;
console.log(typeof a); // 打印结果:string。说明 a 的数据类型保持不变。
console.log(a); // 打印结果:666
console.log(typeof b); // 打印结果:number。说明 b 的数据类型发生了变化。
console.log(b); // 打印结果:666
```
4、加号:`+`
**情况一**:字符串 + 数字
- 当加号的两边,只要有一个是字符串的时候,就会调用 String() 函数将数字转为字符串,然后再计算。导致最终的运算结果是字符串。
**情况二**:Boolean + 数字
- Boolean 型和数字型相加时, true 按 1 来算 ,false 按 0 来算。这里其实是先调 Number() 函数,将 Boolean 类型转换为 Number类型,然后再和 数字相加。
**情况三**: null + 数字
- 等价于:0 + 数字
**情况四**: undefined + 数字
- 计算结果:NaN
5、运算符:`-`、`*`、`/`
1、任何非 Number 类型的值做`-`、`*`、`/`运算时,会将这些值转换为Number然后再运算(内部调用的是 Number() 函数),运算结果是 Number 类型。(注:`任何值 + 字符串`是特例,运算结果是字符串)
比如:
```javascript
result1 = true + 1; // 2 = 1+ 1
result2 = true + false; // 1 = 1+ 0
result3 = 1 + null; // 1 = 1+ 0
result4 = 100 - '1' // 99
```
2、任何的值和字符串做加法运算,都会先转换为字符串,然后再做拼串操作。
比如:
```javascript
result1 = 1 + 2 + '3' // 33
result2 = '1' + 2 + 3; // 123
```
3、任何值和NaN做运算的结果都是NaN。