JavaScript 数据类型
每一种数据都有自己的"类型":数字、文字、真假、空。JS 是动态类型语言——变量声明时不用写类型,赋什么值就是什么类型,而且同一个变量可以随时换类型。这一章我们认识 JS 的所有数据类型,以及怎么判断和转换它们。
1. 两大分类:原始类型 vs 引用类型
JS 把数据分成两大类:
- 原始类型(primitive):7 种。
number、string、boolean、null、undefined、symbol、bigint。按值传递,赋值时复制一份。 - 引用类型(reference):主要是
object(包括对象、数组、函数、日期、正则等)。按引用传递,赋值时只复制"地址指针",改一边影响另一边。
// 七种原始类型(primitive)
const num = 42; // number 数字
const float = 3.14; // number(整数小数都是 number)
const str = "hello"; // string 字符串
const bool = true; // boolean 布尔
const empty = null; // null 空值
const undef = undefined; // undefined 未定义
const sym = Symbol("id"); // symbol 唯一标识(ES6)
const big = 9007199254740993n; // bigint 大整数(ES2020)
// 用 typeof 查看类型
console.log(typeof num); // "number"
console.log(typeof str); // "string"
console.log(typeof bool); // "boolean"
console.log(typeof undef); // "undefined"
console.log(typeof null); // "object" ⚠️ 历史 bug
console.log(typeof sym); // "symbol"
console.log(typeof big); // "bigint"历史 bug:typeof null 返回 "object" 而不是 "null"。原因是 JS 早期实现里,值的类型用低位标签区分,对象的标签是 000,而 null 在内存里是空指针,所有位都是 0,所以被误判成对象。这个 bug 已经无法修复(会破坏海量老网页),只能记住。判断 null 要用 x === null。
2. number 类型
JS 不像 Java/C 区分 int、float、double——所有数字(整数和小数)都是 number 类型,底层是 IEEE 754 双精度浮点。这意味着 1 和 1.0 是同一个值。
// JS 不分整数和浮点,都是 number(IEEE 754 双精度)
console.log(typeof 10); // "number"
console.log(typeof 3.14); // "number"
// 特殊数字
console.log(1 / 0); // Infinity(正无穷)
console.log(-1 / 0); // -Infinity(负无穷)
console.log("abc" - 1); // NaN(Not a Number,无法计算时)
// 经典坑:0.1 + 0.2 不等于 0.3!
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
// 安全整数范围:-(2^53 - 1) ~ (2^53 - 1)
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// 超出后精度丢失,改用 bigint
// NaN 不等于自己,判断用 isNaN() 或 Number.isNaN()
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true0.1 + 0.2 ≠ 0.3 是 JS 面试最经典的题,本质是二进制浮点不能精确表示某些十进制小数。涉及金额、计息时,推荐先把元转成分(Math.round(0.1 * 100) + Math.round(0.2 * 100)),避免精度问题。
3. string 类型
JS 字符串用 Unicode 编码,支持任何语言文字。三种引号都可以,反引号最强大:
// 三种引号
const a = "双引号";
const b = '单引号';
const c = `反引号`; // 模板字符串,可换行、嵌入变量
// 字符串不可变,操作返回新串
let s = "hello";
s[0] = "H"; // 无效!
console.log(s); // 仍然是 "hello"
// 拼接
console.log("a" + "b"); // "ab"
console.log(`1+1=${1+1}`); // "1+1=2"
// 常用属性
console.log("hello".length); // 5字符串不可变——任何"修改"操作实际是返回一个新串,原串不变。后续章节会专门讲字符串方法。
4. null 和 undefined 的区别
这两个都表示"空",但语义不同,新手最容易混淆:
undefined:JS 自己产生的"没赋值"。变量声明未赋值、对象属性不存在、函数没 return,都是 undefined。null:程序员主动赋值的"空",表示"这里现在没值,但有意为之"。比如先声明let user = null,等拿到数据再赋值。
// null:有值,但值是"空"(程序员主动赋值)
let user = null; // 表示"目前没有用户"
// undefined:声明了但没赋值(JS 自己产生)
let age;
console.log(age); // undefined
// 函数没 return,返回 undefined
function f() {}
console.log(f()); // undefined
// 对象没有的属性
const obj = { name: "小明" };
console.log(obj.age); // undefined
// 区分两者
console.log(null == undefined); // true(== 把 null 和 undefined 视为相等)
console.log(null === undefined); // false(严格不等)
console.log(typeof null); // "object"(历史 bug)
console.log(typeof undefined); // "undefined"建议:变量暂时没值时,主动赋 null 而不是 undefined,让"我故意设的空"和"忘了赋值"区分开来。
5. boolean 类型与真假值
boolean 只有两个值:true 和 false。但 JS 在条件判断时,会把任何值转成布尔,分两类:
- 假值(falsy):
false、0、-0、0n、""、null、undefined、NaN。一共 8 个。 - 真值(truthy):其他所有,包括
"0"(字符串)、[](空数组)、{}(空对象)、"false"。
if ("hello") console.log("非空字符串是真值"); // 执行
if (0) console.log("0 是假值"); // 不执行
if ([]) console.log("空数组是真值"); // 执行
if (\"\") console.log("空字符串是假值"); // 不执行
// 短路求值:常用作默认值
const name = input || "匿名"; // input 为假值时取 "匿名"
const user = data ?? defaultValue; // ?? 只在 null/undefined 时取默认6. 类型转换
JS 是弱类型语言,运算时会自动转换类型,这经常让人困惑。除了隐式转换,也提供显式转换函数:
// 字符串 → 数字
console.log(Number("42")); // 42
console.log(parseInt("42px")); // 42(遇到非数字停下)
console.log(parseFloat("3.14abc")); // 3.14
console.log(+"42"); // 42(快捷写法)
// 数字 → 字符串
console.log(String(42)); // "42"
console.log((42).toString()); // "42"
console.log(`数字:${42}`); // "数字:42"
// 任意 → 布尔(Boolean())
// 假值:falsy - false、0、""、null、undefined、NaN
// 真值:truthy - 其他所有
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean("hello")); // true
console.log(Boolean(1)); // true
// 隐式转换的坑
console.log("3" + 2); // "32"(字符串拼接,2 被转字符串)
console.log("3" - 2); // 1(减号只用于数字,转数字)
console.log("3" * 2); // 6
console.log(0 == ""); // true(== 会转换类型)记住这条原则:显式优于隐式。需要转换就明确写 Number()、String()、Boolean(),不要依赖隐式行为,代码更可读、bug 更少。
7. 引用类型初识
// 原始类型:复制值
let a = 1;
let b = a;
b = 2;
console.log(a); // 1(不变)
// 引用类型:复制地址
const obj1 = { count: 1 };
const obj2 = obj1;
obj2.count = 99;
console.log(obj1.count); // 99(被影响了!两者指向同一对象)
// 想真复制对象,用展开运算符(浅拷贝)
const copy = { ...obj1 };
copy.count = 0;
console.log(obj1.count); // 99(不受影响)这是 JS 最容易踩的坑之一——对象赋值不是复制。要"复制"得用 ... 展开或 Object.assign()(浅拷贝),或 structuredClone()(深拷贝)。
小结
- JS 有 7 种原始类型 + 1 种引用类型(object)。
typeof判断原始类型(null例外);判断对象用instanceof或Array.isArray()。- 原始类型按值传递,引用类型按地址传递。
- 显式类型转换比隐式更安全。
下一篇我们看 JS 的运算符——算术、比较、逻辑,以及最容易踩坑的 == 和 ===。
← 上一篇 JavaScript 变量
下一篇 JavaScript 运算符 →