基础一:八种数据类型和判断方法

277 阅读6分钟

一、数据类型

  • 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 也是二进制形式。

  • 指数部分:决定了数值的大小。
  • 小数部分:决定了数值的精度。

image.png

其实 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

参考文章

0.1 + 0.2 !== 0.3