1. 基本数据类型
根据最新的ECMAScript标准,基本数据类型共有七种:number
、string
、null
、undefind
、symbol
、bigint
、boolean
。 剩下的就是引用数据类型 object
。
1.1 基本数据类型的不可变性
关于基本数据类型有一个特点就是它的不可变性。这是一个让我当时觉得很难以理解的概念。后来发现是我混淆了变量和值的概念。如果一个值是基本数据类型,它的本身是不可变的。就比如3=2
我们想把值3变成2。这是不可能也是不合乎逻辑也是没有任何意义的。所以按照这个思路不难理解其他的基本数据类型为什么是不可变的原因。
1.2 Symbol&BigInt
新晋的原始数据类型Symbol和BigInt这里虽然单独提出来总结,但是并不会总结具体的方法和属性。只介绍一下它们的用途和场景,留个印象。
- Symbol
根据MDN上的介绍:每个从 Symbol() 返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的
。我们可以知道,它是为我们解决烦恼的。也就是说什么情况下我们会为了对象属性不唯一而烦恼。
(1) 避免污染:当项目多个人员开发,或者引用了很多外部库的时候。有时候我们需要给一个对象增加一个属性来完成工作的时候。为了不去覆盖对象原有的属性、不去污染作用域。通过使用symbol我们可以避免这种忧虑,因为它返回的是唯一值,也就是不论你目前对象中有多少个属性,它的存在都是唯一的。
当然,解决问题的方式有多种,给一个对象的属性命名特殊点,比如加上公司的名称缩写、特殊的字符。或者优先判断下对象里有没有你要命名的属性都是能行得通的。
(2) 隐藏对象属性:通过使用Symbol来作为对象的属性名,可以让该属性名一定程度上被隐藏掉,也就是不可以被for in
、Object.getOwnPropertyNames()
、Object.keys()
这些方式获取到。但是可以用in
、Object.hasOwnProperty()
判断属性是否存在。
- BigInt
BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。
1.3 判断值的类型
1.3.1 typeof
通过它可以用来判断基本数据类型还包括 function
,object
,但是 null
除外。而且我们无法准确分析出值的类型是否是 array
、object
。因为它们通过 typeof
得出来的类型都是object。
1.3.2 Object.prototype.toString.call()
该方法可以返回调用者的对象类型,不会有typeof所产生情况。所以平时的时候可以用来进行类型的准确获取和判断。比如如果是字符串调用则返回 '[object String]' 。
1.3.3 instanceof
instanceof虽然放在了判断值类型的标题下, 但是其实它最主要的功能并不是判断值的类型。根据MDN上的介绍:instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
,我们可以很清楚的得知,当要判断实例是不是某个构造函数通过 new
关键字创建出来的时候,用它再合适不过。
1.3.4 constructor
constructor可以返回数据包装对象的构造函数。然后通过比较构造函数来实现判断出变量的类型。
instanceof和constructor两个方法都无法对null和undefined来进行操作。在使用时会报错,按照undefined和null的定义不难理解这个后果。
1.4 包装对象
大概是为了“万物皆对象”的口号吧, JavaScript中基础数据类型都存在一个与之对应的包装对象(null和undefined除外)。这也能解释为什么我们声明一个字符串字面量变量的时候,我们可以调用变量的toString方法等属于对象才能实现的功能。
number
、string
、boolean
、bigint
、symbol
都有对应的包装对象,且可以显示的进行声明调用。
// 字面量声明字符串
var str = 'hello';
// String(val) 不使用new的时候,是进行数据类型的转换
var str1 = String('hello');
// 生成字符串的包装对象
var str2 = new String('hello');
console.log(str instanceof String); // false
console.log(str1 instanceof String); // false
console.log(str2 instanceof String); // true
2. 引用类型
引用类型差不多可以说是前端程序员接触最多的数据类型。典型的代表就是 Array
、Object
。引用类型的一些概念和用法不是几百字可以总结概括出来的,所以这里只从数据类型方面来进行简单的整理归纳。
2.1. 引用类型和基本数据类型的不同
- 存放位置不同
- 存储内容不同
- 赋值方式不同
- 存储内容大小不同
基本类型 | 引用类型 | |
存放位置 | 栈 | 堆 |
存储内容 | 值本身 | 值内存地址 |
赋值方式 | 复制值 | 复制值内存地址 |
存储内容大小 | 小 | 大 |
2.2. 堆、栈
2.1.中提到了堆、栈的概念。它们都是数据结构。方便我们的使用以及计算机对值的读取。如下图所示。
var a = 1;
var b = a;
var c = {};
var d = c;
从上面的图例和代码中可以明确的看到基础数据类型和引用数据类型的不同点。可能不明显的就是两种数据类型存储值的大小这一点。如果是基本数据类型,会在栈中根据值来进行开辟空间,所以空间大小是根据值的大小固定的。但是引用类型的值是不固定的, 我们随时可以在引用类型的变量中进行添加,所以存储的空间是不固定的,某种程度来说也是比基本类型大的。
3. 隐式转换
隐式转换是与数据类型息息相关的特性。我对它最直观的理解就是“偷摸的进行了类型的变换,来完成我给它的任务”。
3.1. 存在在数学计算中的转换
3.2. 当我们的表达式是一个数学计算,使用了运算符+、-、*、/的时候, 如果两个变量的数据类型不同,就会发生隐式转换。
- 在使用-、*、/的时候,会把非数值类型的变量转换成数值类型。
- 在使用 + 的时候有着不同,如果使用 + 的表达式中有一个变量存在字符串,则会将另外一个变量转换成字符串类型。
3.3. 数值比较 ==
使用 ==
进行数值比较的时候,会对数值进行隐式转换。遵守两条规则 对象 -> 字符串 -> 数值
和 布尔值 -> 数值
。
3.4. if语句
在if语句中,条件都要转化为布尔类型。转化的过程也是隐式转换。
3.5. object的特殊性
object类型有两个方法 valueOf()
,toString()
。这里需要注意的是在强制转换为数值类型时,会优先调用valueOf()
;强制转换为字符串类型,会优先调用 toString()
。
在强制转换为数值类型调用了valueOf方法, 如果该方法没有被重写,会被忽略掉,从而调用toString方法获取字符串,再将获得到的字符串转化为数值类型。