JavaScript数据类型全梳理

94 阅读5分钟

大家好,我是蛇皮鸡,一名准前端工程师。我对外的输出文章并不会对某个技术进行非常详细的解释,简单的说就是大而泛,旨在了解技术的出现背景、原因和应该如何理解并且优雅的应对面试。只有建立起自己的知识系统,才能装下更多的知识,我希望能先把树干尽量的健壮,再去管叶子是否茂密

数据类型

类型分类

Primitive 原始数据类型

  • 空值(表示未定义的值,但是null会被typeof判断为对象属于系统的bug,地址000开头的表示对象,但是空值为 000000xx 全零

    • null(意料之内的空值)
    • undefined(表示未赋值)
  • 基础类型

    • string:非常多的接口,String上的各种操作字符串的函数,作为数组或者可迭代对象的各种函数,正则匹配的各种数组(KMP算法)
    • number:浮点类型、NaN、-+ / Infinity
    • boolean
  • Symbol

    • 使用场景:作为对象的键,不重复的属性名、消除开发自定义的不安全字符串常量、各种内置符号(Symbol.iterator、Symbol.toPrimitive、Symbol.hasInstance)
    • 用法:
      • Symbol() 传入字符串,非字符串被转换为字符串,生成独一无二的值
      • Symbol.for() 传入字符串,唯一的不同点就是,相当于单例模式:相同的字符串对应一个独一无二的值。在整个程序内,首次传入新字符串,创建一个Symbol值,再次传入该字符串,返回这个Symbol。Symbol.keyFor(Symbol.for('查询注册信息')) === '查询注册信息'
      • Symbol不允许隐式类型转换成 字符串/数字,Number强制转换会报错,String可以,Object的Symbol属性名无法被stringify序列化,也无法被for in遍历(即使通过getOwnPropertyDescriptor获取到为可枚举特性)
  • bigint:大整数,专门为超出JS浮点数范围二无法表示的数设计的

    • 使用场景:high-resolution timestamps、large interger IDs
    • 表示:整数+n、new Bigint
    • 使用:同为Bigint才能使用计算运算符操作,Bigint在相等判断下等同于同数值的Number类型

对象类型(引用)Object

  • 基本类型的包装对象:String、Number、Boolean
  • Array、Date、RegExp
  • 宿主环境,比如Web:Window、Location、
  • Map、Set、Weakxxx系列
  • Math工具函数:ceil(向上)、floor(向下)、round(四舍五入)、abs(绝对值)、pow/qurt、random(随机函数)
  • JSON
    • JSON.stringify、parse 转换JSON字符串、解析JSON字符串
    • toJSON 为对象提供 stringify 的序列化方法
    • JSON.stringify 第二个参数是数组的时候,如果输出的对象JSON会根据数组元素作为属性名进行筛选,转换成希望的“干净的”JSON(特别是在后端处理数据之后)
  • Promise
  • Error
    • EvalError、SyntaxError
    • RangeError:一些需要Number数值的内置函数传参的时候,如果数字不在范围内,throw该错误类型
    • ReferenceError:未声明的变量被使用(除非是 typeof)、ES6 const、let 块级作用域不能在声明前使用(临时死区)
    • TypeError:空值以对象访问属性、内置对象期望的类型和实参不同

数据类型转换

难理解的点其实在于"隐式转换",但核心的一点掌握:解释器会根据你的声明式代码猜测你想要的类型,为 当前的操作符 转换 出合适的操作数类型

  • 三大基本类型转换:
    • 遇到 + 号,优先考虑字符串加法、否则能转数字就转数字(非数字的两个操作数,在非+运算前,都是先转换数字再运算)、
    • null 转数值为 0,undefined 转数值为 NaN
    • null == undefined 为 true(特殊)、null === undefined 为 false
  • 对象转换为三大基本类型:
    • @@Symbol.toPrimitive
    • 如果上面未得到Primitive值,考虑当前操作符需要什么样的类型,优先调用[valueOf, toString]中的一个函数,如果还未得到,再调用另外一个函数
    • 都未得到最后返回NaN

判断类型

主要使用五个方面:

  • typeof(Function 特殊、null Bug,无法分辨引用类型,只对原始类型利好)
  • instanceof 可以被强制修改原型、hasInstance修改判断规则
  • Object.prototype.toString(对原始类型没啥用,对引用类型好用)
  • 能力检测(比如数组的一些特有方法)
  • 标准的检测方法(比如Array.isArray())

进制转换

默认是10进制转换,因此没有感知

  • parseInt(variate, radix) 解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数
    • 36 >= radix >= 2 ,不然返回NaN
    • 第一个非空格字符不能转换为数字,不然返回NaN
  • toString(variate, radix) 将variate看出10进制的数,转换为radix为基数的数

他们的功能刚好相反:
parseInt识别variate以radix为基,转换为10进制
toString识别variate以10为基,转换为radix进制

let variate = '34'
variate = parseInt(variate, 5)
variate = variate.toString(5)
//'34'

为什么0.1 + 0.2 === 0.3为false

计算机二进制的表示方法,小数的二进制也是通过0、1表示,只不过小数的权,每次需要除以2,1 => 1、0.1 => 0.5、0.01 => 0.25、0.001 => 0.125。只有0、1,因此0.1是无法完整表示的,只能逼近

那为什么 0.1 === 0.1,因为0.1的逼近规则是底层规定的,同样的数值有同样的逼近规则,比如JavaScript采用 64 位双精度浮点数表示