一、数据类型
- 7 种基本类型:Undefined、Null、Boolean、Number、String,ES6 新增 Symbol 和
BigInt(ES2020,也属于ES6)。 - 1 种引用类型:广义对象,又可细分为 Function、Array、Object。
1. ES6 的新增类型
- Symbol:表示独一无二的值,可以避免与对象的其他属性名冲突!
let s = Symbol() // s的独一无二的值
- BigInt 大整数:可以表示 大于2^53 - 1的整数(没有等于)。
BigInt(num)
2. 基本类型和引用类型
- 基本类型的值直接存储在栈中。基本类型的赋值是拷贝;
- 引用类型在栈中存储了指针,这个指针指向堆内存的地址。引用类型的赋值是地址引用。
堆内存和栈内存都是内存中用来存储的区域。有三点区别:
- 管理方式:是否自动分配和释放
- 申请的大小限制
- 内存区域是否连续
栈是一块由系统自动分配的连续的内存区域,有大小限制、如果申请的内存空间超出栈的剩余空间将提示栈溢出,使用完后由系统自动释放。存放变量值、函数的参数值等。
堆一般是由开发人员去申请分配的不连续的内存区域,有较大的自由区域,使用完后需要手动释放(垃圾回收)。对于引用类型的变量,它实际是一个存放在栈内存的指针,指针指向堆内存。
3. null 和 undefined 的 4 点区别
它们在 if 判断中都会自动转为false,区别在于:
- 含义和使用场景(其实几乎同义)
null表示 “没有对象”, 典型应用场景有- 原型链的终点
- 如果变量在将来用于保存对象,那么最好初始化为null。
undefined表示 “缺少值”,典型应用场景有- 变量已经声明但未赋值,就等于
undefined - 对象没有赋值的属性,该属性的值为
- 调用函数时,应该提供的参数没有提供,该参数就等于...
- 函数没有返回值时,默认返回...
- 变量已经声明但未赋值,就等于
Number转换时:Number(null)为0、Number(undefined)为 NaN。(如果使用parseInt则都转为 NaN)- 严格相等运算:
null == undefined (true)和null === undefined (false)。 typeof类型判断。
补充:相等运算符会将两边的值先进行类型转换再比较,而严格相等运算符不会进行类型转换,要求类型相同 && 值相等,一个只比较值,一个还要比较类型。 两种比较方式在比较对象时,都会看是否指向同一对象。
4. if 中的 "空" 判断
if() 的判定情况如下:
- true:空对象{}、空数组[]。
- false:空字符串""、非数字NaN、undefined、null、0。
二、类型判断:注意返回形式
1. Object.prototype.toString.call:字符串
返回的是该对象的类型字符串。
因为 Array、String、Function对象等都自定义了 toString 方法,覆盖了 Object.prototype.toString,所以我们在判断数据类型时,还是要用原始的Object.prototype.toString.call(),其中call绑定任意值,可换成apply。
// 前一个都是 object,后一个是 构造函数!
Object.prototype.toString.call(new Map()) // "[object Map]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hello') // "[object String]"
对于arguments对象、Date对象、RegExp对象、Error对象,都会返回的更具体,
例如 "[object Error]",然后其他的对象都会返回 "[object Object]"。
2. typeof:小写字符串
- 对于null,array,object,一律返回 object。
- 对于function,symbol等剩下的类型,都返回它们的准确类型。
返回值是小写的字符串!
typeof null // object
typeof typeof null // string
typeof NaN // number
3. instanceof:布尔值
适用对象:只适用于函数、数组、对象(不包括Null),返回布尔值。它是通过检查右侧构造函数的prototype属性 是否在 左侧实例对象的原型链上。。
const arr = new Array();
arr instanceof Array; // true
arr instanceof Object; // true
4. constructor:构造函数形式的字符串
实例对象会继承它原型对象的所有属性和方法,所以 原型中的 constructor 属性也存在于实例对象中,指向其构造函数。
null 和 undefined 是无效的对象,不能通过 constructor 判断!
因为可以自定义构造函数,所以函数的 constructor 是不稳定的。建议尽量不用 constructor。
(1).constructor === Number // true
('hello').constructor === String // true
([1, 2, 3]).constructor === Array // true
三、 浮点数
1. js 中的数字
JS 中所有数字都是以 64位浮点数 形式存储,分别为 1位符号位s + 11位指数位e + 52位小数位f,其中小数部分 f 也是二进制形式。
- 指数部分:决定了数值的大小。
- 小数部分:决定了数值的精度。
其实 js 的 最大安全整数(浮点数和整数是一对一的) 是 Math.pow(2, 53) - 1,这是因为 二进制表示的数字总是 1.xxx 的形式,第一位默认为 1,说小数部分52位就是省略了这1位,所以 js提供的有效数字最长是53位(64位浮点的后52位 + 被省略的1位)。
整数 2^53 在 js 中表示为 1.00..00(52个0,因为最多只能放52位尾数,所以会截掉第53位尾数,即第54位数字),而整数2^53 + 1 在 js 中也是同样的表示,所以会出现 一个浮点数 对应 两个整数的情况,不是安全整数。
2. 说明0.1 + 0.2 !== 0.3
在数字运算时,
- 先将 0.1 和 0.2 转为相应的二进制数:0.1 和 0.2转换成二进制后会无限循环,超出位数限制的需要截掉。
- 对阶运算:就是让它们的指数相同,即 e值相等。这步也会有精度损失。
- 计算并转为十进制。
精度损失可能出现在进制转化和对阶运算过程中!
3. 怎么解决精度问题
可以借助数学库Math.js等解决。
四、隐式转换
1.自动转换的3种情况
- 不同类型的数据运算
123 + 'abc'
// '123abc'
- 对非布尔类型的数据求布尔值
if ('abc') {
console.log('hello')
}
// "hello"
- 对非数值类型的数据使用一元运算符
+、-
+ {foo: 'bar'} // NaN
- [1, 2, 3] // NaN
自动转换的规则是:(1)预期什么类型的值,就调用该类型的转换函数;(2)如果该位置既可以是字符串、又可以是数值,那么默认转为数值。
2. 自动转换为布尔值
除了undefined、null、0、NaN、‘’这 5 个值,其他都会自动转为true,比如空对象{}、空数组[]
3. 自动转换为字符串
js 遇到预期为字符串的地方,就会将非字符串的值自动转为字符串,具体规则是:先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串。
'5' + true // "5true"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
4. 自动转换为数值
除了+有可能把运算子转为字符串,其他运算符都会将运算子自动转成数值。
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
Number(null) === 0,Number(undefined) === NaN。
一元运算符也会把运算子转成数值。
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
参考文章