JavaScript 数据类型详解与转换规则

作者:清欢

时间:2026-10-25

浏览:445

本文系统梳理JavaScript的七种原始数据类型与对象类型,深入解析Symbol的唯一性机制及BigInt处理超大整数的方案。详细对比typeof、instanceof等检测方法的底层原理,并完整讲解Number、parseInt等显式转换与隐式转换规则,结合面试题剖析类型转换陷阱。

文章目录

数据类型的分类

JavaScript 中包含七种原始数据类型(Primitive Types):

  • 原始数据类型[值类型/基本数据类型]:
    • number(数字)
    • string(字符串)
    • boolean(布尔值)
    • null(空值,指向空对象指针)
    • undefined(未定义)
    • symbol(唯一标识符)
    • bigint(大整数)
  • 对象类型[引用数据类型]
    • object
      • 标准普通对象 object
      • 标准特殊对象,包括 Array、RegExp、Date、Math、Error 等
      • 非标准特殊对象,如 Number、String、Boolean 等包装对象
      • 可调用或执行对象,即函数 Function

非标准特殊对象

除 null 和 undefined 外,每个原始值类型都对应一个对象类型。

数据类型的分类

Symbol

Symbol 用于生成唯一值,无法通过 new 操作符创建。每次调用 Symbol() 都会生成一个全新的唯一标识,即使传入相同的描述字符串,生成的 Symbol 也不相等。例如,若将 A 赋值给 C,则 A 与 C 相等。

let A = Symbol('i东东')
let B = Symbol('i东东')
let C = A 
console.log(A === B); // false
console.log(A === C); // true

应用场景:

  • 为对象设置唯一属性名。ES6 之前属性名仅限字符串,ES6 引入 Symbol 作为属性名,可避免命名冲突。
  • Map 数据结构允许属性名为对象,结合 Symbol 可实现更复杂的键值映射。
  • Symbol.asyncIterator、Symbol.iterator、Symbol.hasInstance、Symbol.toStringTag 等是 JavaScript 底层机制的关键符号。
  • 在统一管理行为标识时,使用 Symbol 可确保标识的唯一性,避免覆盖。

使用字符串或数字类型均可获取属性值,例如 name 属性名本质为字符串类型。

数据类型的分类

下方代码console.log([Symbol('AA')])无法获取预期值,因为每次调用 Symbol() 都生成新唯一值,用新值访问旧属性必然失败。正确做法是复用同一个 Symbol 实例作为属性键。

let key = Symbol('88')
let obj = {
  num: 100,
  10:'数字',
  [Symbol('AA')]:'Symbol类型的属性名', // 在大括号中加Symbol语法要求必须加大括号
  [key]:400
}
console.log(obj[Symbol('AA')]); // undefined
console.log(obj[key]); // 400说

对象属性名不可重复,使用 Symbol 作为属性名可确保其唯一性,避免冲突。

let obj = {
  [Symbol('AA')]: 100,
  [Symbol('AA')]: 100,
  // 这两个是不同的两个值
}

BigInt 大数类型

JavaScript 最大安全整数为 Number.MAX_SAFE_INTEGER = 9007199254740991。

JavaScript 最小安全整数为 Number.MIN_SAFE_INTEGER = -9007199254740991。

超出安全范围进行运算或访问,结果可能不准确。数据库通常区分整型、长整型等,长整型支持范围大于 JS 安全数。若服务器返回超出最大安全值的长整型数字,客户端处理会出现精度丢失问题。

解决方案:

  • 服务器端将大数以字符串格式返回给客户端。
  • 客户端将其转换为 BigInt 类型。
  • 运算完成后,将 BigInt 转换回字符串再传回服务器。
console.log(BigInt('9007199254740991') + BigInt(12345)); // 9007199254753336n
console.log(9007199254753336n.toString()); // 9007199254753336
// 123n  在数字后面直接加`n`也表示大数BigInt

数据类型检测

  • typeof
  • instanceof
  • constructor
  • Object.prototype.toString.call
  • Array.isArray
  • isNaN

数据类型的分类

日常开发中最常用 typeof 检测数据类型。所有数据类型在计算机底层均以 64 位二进制存储,typeof 依据二进制高位值判断类型。

例如:

  • 二进制前三位为 0 时视为对象,若实现 call 方法则返回 'function',否则返回 'object'。
  • 第一位为 1 表示整数。
  • 010 表示浮点数。
  • 100 表示字符串。
  • 110 表示布尔值。
  • 000...(null 为 64 个 0)被 typeof 识别为 object,这是 typeof 的局限性。
  • ......

检测未声明变量时,typeof 返回 undefined。

console.log(a); // ReferenceError: a is not defined
console.log(typeof a); // undefined

场景1:检测当前值是否为对象。

const fn = options =>{
   let type = typeof options
   if(options !== null && (type === 'object' || type === 'function')){
    // 这样进来的才能确保是一个对象
   }
}
fn({
  num:10
})

场景2:支持更多模块导入方案,如兼容 CommonJS 和浏览器环境。

(function(){
  if(typeof window !== 'undefined') window.utils = utils
  if(typeof module === 'object' && typeof module.exports == 'object') module.exports = utils
})()

数据类型间的相互转换

类型转换分为隐式转换和显式转换。浏览器环境中常见隐式转换场景包括:

  • 数学运算
  • isNaN 检测
  • == 比较

数学运算时会触发隐式转换,例如 10-2 实际调用 Number('2') 转换后再计算。

数据类型的分类

isNaN 判断前值是否为有效数字,内部先调用 Number 转换再执行判断。

数据类型的分类

规则

Number([val])

  • 字符串转数字:空字符串变为 0,含非有效数字字符则返回 NaN。
  • 布尔值转数字:true 转为 1,false 转为 0。
  • null 转为 0,undefined 转为 NaN。
  • Symbol 无法转换为数字,会报错:Cannot convert a Symbol value to a number。
  • BigInt 去除 'n' 后缀(超出安全数值的按科学计数法处理)。
  • 对象转数字:先调用对象的Symbol.toPrimitive方法,若不存在则调用valueOf获取原始值,若非原始值再调用toString转为字符串,最后通过Number方法转换。
let time = new Date()
console.log(Number(time)); // 1662715039452
console.log(time[Symbol.toPrimitive]('number')); // 1662715039452
// 首先检测 Symbol.toPrimitive有没有, 结果:有 而且是一个函数 time[Symbol.toPrimitive]('number')
let arr = [10]
console.log(Number(arr));
/**
 * 首先arr[Symbol.toPrimitive] => undefined
 * 然后arr.valueOf() 数组是没有原始值的 => [10]  任何一个对象都有valueOf
 * 再然后 arr.toString => '10'
 * 最后在把字符串'10'转换为数字 => 10
*/
let num = new Number(10)
console.log(Number(num));
/**
 * 首先num[Symbol.toPrimitive] => undefined
 * 然后num.valueOf() 数组是没有原始值的 => 10
*/

parseInt([val],[radix]) parseFloat([val])

数据类型的分类

parseInt 可接收两个参数:

  • value 必须为字符串,非字符串先隐式转为字符串(通过 String([val])),对象则按前述三步转换。
  • radix 指定进制,不写或为 0 时默认为 10 进制;若字符串以 0x 开头默认为 16 进制,有效范围 2~36(超出则返回 NaN)。从字符串左侧开始查找符合 radix 进制的字符,遇到不符合则停止,将找到的内容按 radix 转为 10 进制。
console.log(parseInt('10103px13',2));  // 10
/**
 * 首先找到符合二进制的数 '1010'
 * 把这个二进制值转换为十进制 '按权展开求和'
 * 1*2^3+0*2^2+1*2^1+0*2^0 => 8+0+2+2 => 10
*/

这里放一道字节面试题

arr会输出什么,为什么?
let arr = [27.2,0,'0013','14px',123]
arr = arr.map(parseInt)

解题步骤:

  • arr.map 方法迭代数组每一项,修改后返回新数组,原始数组不变。迭代时 item 为当前值,index 为索引。
  • map 接收函数,此处将parseInt作为函数传入,每次迭代执行parseInt并传递 item 和 index。
/**
 * parseInt(27.2,0) => 找符合二进制的'27',当做十进制转成十进制=> 27
 * parseInt(0,1) => 超出有效进制范围 => NaN
 * parseInt('0013',2) => 001 当做二进制转化为十进制 => 0+0+1 => 1
 * parseInt('14px',3) => 1 当做三进制转成十进制 => 1
 * parseInt(123,4) => parseInt('123',4) => 123当做四进制转成十进制 1*4^2+2*4^1+3*4^0 => 16+8+4 => 27
*/

最终结果:arr = [27, NaN, 1, 1, 27]。

注:

parseInt('0013',2) => 001 当做二进制转化为十进制 => 0+0+1 => 1
parseInt(0013,2) => 0013 当js中遇到以0开头的数字会默认当做8进制转为10进制然后在进行其他运算 => 当做八进制转为十进制 0*8^3+0*8^2+1*8^1+3*8^0 => 11 => 二进制转十进制 11   2+1=3

将其他类型转换为 String

转换规则:

  • 当 '+' 左右两边存在字符串或某些对象时,按字符串拼接规则处理。
  • 当 '+' 出现在值左侧时,转换为数字。
console.log(10 + '10'); // 1010
console.log(10 + new Number(10));
// new Number(10)[Symbol.toPrimitive] -> undefined
// new Number(10).valueOf() => 10 
//  10 + 10 = 20
console.log(10 + new Date());
// new Date()[Symbol.toPrimitive]('default') -> 不知道传递什么会传defaulu => 'Tue Sep 13 2022 17:00:40 GMT+0800 (中国标准时间)'
// 最终等于:10'Tue Sep 13 2022 17:00:40 GMT+0800 (中国标准时间)'
console.log(10 + [10]);
// [10][Symbol.toPrimitive] -> undefined
// [10].valueOf => '[10]' 
// [10].toString() => '10'
// 最终等于: '1010'
let num = '10'
console.log(+num) // 10 转换为数字

将其他类型的值转换成 Boolean

Boolean(0) = false,除 0、NaN、undefined、空字符串、null 外均为 true。

== 两个等号比较规则

  • == 相等时,若两边类型不同,先转换为相同类型再比较。
  • 对象 == 字符串:对象先转字符串(通过[Symbol.toPrimitive]->valueOf()->toString())。
  • null == undefined 返回 true,null/undefined 与其他任何值都不相等。
  • null === undefined 返回 false。
  • 对象 == 对象:比较堆内存地址,地址相同则相等。
  • NaN !== NaN。
  • 除上述情况外,类型不一致时均转为数字再比较。'===' 绝对相等,类型不同直接返回 false,不转换类型。
NaN == NaN // false NaN永远不可能等于NaN
console.log(Object.is(NaN,NaN)); // true

贴一道面试题 下面输出为什么?

console.log([]==false); // 首先都会转换为数字, 0 == 0 => true
console.log(![]==false); // 先处理![]=>false == false  =>true

Js中的装箱和拆箱

装箱和拆箱是浏览器进行数据类型隐式转换的过程。

let num = 10
console.log(num.toFixed(2)); // 10.00
// num是原始值,不是对象,按常理来讲是不能做'成员访问'的
// 默认会做装箱操作:  new Number(num) 变为非标准铁树对象,这样就可以调用toFixed了
let num = new Number(10)
console.log(num + 10); // 20 
// 在操作的过程中浏览器会将num这个非标准特殊对象变为原始值  [Symbol.toPrimitive] -> valueOf -> toString 这个操作叫做拆箱
// 装箱和拆箱都是浏览器进行数据类型隐式转换的过程

到此这篇关于JavaScript数据类型及相互间的转换规则的文章就介绍到这了,更多相关JavaScript数据类型内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章