数据类型
基本数据类型:
- Undefined、Null、Boolean、Number、String、Symbol(ES6)、BigInt(ES10);
- 栈内存,占空间小,大小固定,频繁使用,被引用和拷贝,会创建一个新变量
引用数据类型:
- Object (包含:Object、Array、RegExp、Date、Math、Function)
- 堆内存,存储的是地址,多个引用指向同一个地址。占空间大,大小不固定。引用数据类型栈中存储指针,指针指向堆中存储的实体的启始地址。解释器通过指针,检索地址,寻找实体
数据检测
typeof
typeof 计算机底层基于数据类型的值(二进制)进行检测
- typeof 检测基本数据类型,
- Null 和 引用类型 不能检测出来
- typeof null 为 object ****原因是对象存在在计算机中,都是以000开始的二进制存储,所以检测出来的结果是对象
- Function 检测出来是 function
- typeof NaN === 'number'
instanceof
instanceof 内部机制通过判断对象的原型链是不是能找到类型 prototype
- instanceof 检测引用类型
constructor
TODO // 深入了解下原理
- 可以支持基本类型,但是除了Null、Undefined、NaN
- 如果更改原对象的prototype,检测就不准了
Object.prototype.toString.call([val])
toSring()通过Object的原型进行检测,所有数据类型统一返回“[object xxxx]”的字符串,其中xxx字符串就是数据类型。
- 满足所有的数据类型检测
数据类型检测方法
function getType(o) {
let type = typeof o;
if (type !== 'object') return type;
return Object.prototype.toString.call(o).replace(/^[object (\S+)]$/, '$q');
}
类型转换
常见数据转换有三种形式
- 转换布尔值
- 转换字符串
- 转换数字
- 转布尔值(Boolean)
-
- undefined、null 、false 、NaN、''、0、-0转换值为false
- 其他所有的转换值都是true。
- 转字符串
-
- boolean、function、Symbol转换为 'true'
- 数组直接转换,不管是否是多维数组,都可以直接转换为扁平化的字符串
- 对象转换字符串直接转换'[object Object]';具体参考对象转化原始值
- 转数字
-
- 字符串除了数字类型字符串,其他都转换为NaN
- 空数组转化为0;只有一个元素的数组且第一个元素为数组,转换为数字;其他情况都是NaN
- 除了数组其他引用类型都转换为NaN
- Symbol转换直接抛错
- 空数组、空字符串('')、null、false转化为0
- undefined 转化为 NaN
对象转换原始值(基本数据)
- 如果已经是原始类型,不需要转换
- 会调用内置的【ToPrimitive】函数 高优先级
- 调用x.valueOf() 次优先级
- 调用x.toString() 低优先级
- 如果没有返回原始数据类型,就会抛错
隐式转换
比较运算符
大于(>)小于(<)等于(===)
- 对象 比较 通过对象转化原始值方式
- 字符串 比较 通过unicode字符串索引
双等号 '==' 比较
- null和undefined 比较,另一方为 null或undefined 则是true,其他值都是false
- 一方为symbol类型,则对比为false
- 两个操作值为string和number类型,string类型转化为number之后在进行比较
- 一方为boolean,转化为number类型后比较
- 一方为object,通过对象转化原始值方式后在进行比较
null == undefined // true 规则1
null == 0 // false 规则1
'' == null // false 规则1
'' == 0 // true 规则3 字符串转隐式转换成Number之后再对比
'123' == 123 // true 规则3 字符串转隐式转换成Number之后再对比
0 == false // true 规则4 布尔型隐式转换成Number之后再对比
1 == true // true 规则4 布尔型隐式转换成Number之后再对比
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3); //true f规则 Object隐式转换
// 注:但是执行过3遍之后,再重新执行a==3或之前的数字就是false,因为value已经加上去了,这里需要注意一下
四则运算
- 一方为字符串,就会把另一方转化为字符串
- 一方既不是字符串或数字,那会把它转化为字符串或数字
- 除了加法运算,其他运算符只要其中一方是数字,另一方就会转化为数字
- ⚠️特殊注意 '+' (加号运算)
-
- 可以用作数字相加,还可以用作字符串拼接
- 可以看出加号运算符 字符串优先级高
true + true // ---- > 2
1 + [1,2,3] // ---- > 11,2,3
'a' ++ 'b' // ----> aNaN
2 * [] // ----> 0
4 + [1, 2] // ----> NaN
面试相关问题
问题1: JavaScript中的数据如何在内存中存储?
- 执行过程中,主要分为三种存储空间:代码空间、栈空间、堆空间 。
-
- 代码空间: 存储可执行代码
- 栈空间:基础数据类型和引用类型的地址(指针) 【执行上下问】
- 堆空间:引用数据类型
- 编译过程中,如果判断是闭包,也会在堆空间换一个
closure(fn)的对象(内部对象,js外部无法访问到),用来存储闭包中的变量。所以闭包中的变量也存储在堆内存 - 总结
-
- 栈空间维护执行上下文的状态。如果栈空间大,会影响上下文的切换效率。通常都不会设置太大
- 堆空间会比较大,存放很多大的数据。但是分配内存和回收内存比较占用时间
- 因此需要栈和堆两种空间
问题2: null 和 undefined 的区别?
- null表示未定义,通常表示为空对象(其实不是真对象),主要用于针对对象初始化
-
- null其实不是对象,虽然 typeof 输出是 object;这是js的一个历史悠久bug。在JS最初版本中使用32位系统,为了性能考虑使用了低位存储变量的信息类型,000开通代表对象,然而null表示全零,所以错误的判断为object。虽然内部类型判断代码已经改了,但是bug永久留下来
- undefined表示变量已经声明但是没有定义
-
- undefined作为保留字,意味可以使用undefined作为变量名,但是不安全,影响undefined判断逻辑;
- 可以使用 void 0 这个方法获得 安全的undefined
- 双等号判断是 true,三等号判断是 false