JavaScript 数据类型

每一种数据都有自己的"类型":数字、文字、真假、空。JS 是动态类型语言——变量声明时不用写类型,赋什么值就是什么类型,而且同一个变量可以随时换类型。这一章我们认识 JS 的所有数据类型,以及怎么判断和转换它们。

1. 两大分类:原始类型 vs 引用类型

JS 把数据分成两大类:

// 七种原始类型(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 双精度浮点。这意味着 11.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)); // true

0.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 的区别

这两个都表示"空",但语义不同,新手最容易混淆:

// 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 只有两个值:truefalse。但 JS 在条件判断时,会把任何值转成布尔,分两类:

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 的运算符——算术、比较、逻辑,以及最容易踩坑的 =====

← 上一篇 JavaScript 变量

下一篇 JavaScript 运算符

✈️💬