JavaScript 数据类型
与许多编程语言一样,JavaScript 依赖数据类型来分类和处理不同种类的值。
在本章节中,我们将探索 JavaScript 中的 8 种数据类型,包括 7 种原始数据类型(Numbers, Strings, Booleans, Undefined, Null, BigInt, Symbol)以及 1 种引用数据类型(Object)。
我们将深入研究它们的特性、如何使用它们,以及为什么它们对于构建复杂的程序至关重要。
1. JavaScript 中的数值 (Numbers)
JavaScript 使用 Number 类型来表示整数和浮点数值。
与某些其他语言不同,JavaScript 没有像 int(整数)和 float(浮点数)这样的独立类型。所有的数字都存储为双精度 64 位浮点数(遵循 IEEE 754 标准)。
这意味着你可以表示很大范围的数值,但也需要注意潜在的精度问题,特别是在处理非常大或非常小的数字,或者进行复杂计算时。
1.1 整数直接量 (Integer Literals)
整数直接量可以用不同的进制系统来表示:
- 十进制 (Decimal, 基数 10): 这是我们表示数字最常用的方式。例如:
10,100,-5。 - 二进制 (Binary, 基数 2): 使用
0b或0B前缀。例如:0b101(即十进制的 5)。 - 八进制 (Octal, 基数 8): 使用
0o或0O前缀。例如:0o12(即十进制的 10)。注意:八进制直接量在严格模式下已被废弃。 - 十六进制 (Hexadecimal, 基数 16): 使用
0x或0X前缀。例如:0x0A(即十进制的 10)。
let decimalNumber = 42;
let binaryNumber = 0b101010; // 42 的二进制表示
let octalNumber = 0o52; // 42 的八进制表示 (严格模式下已废弃)
let hexadecimalNumber = 0x2A; // 42 的十六进制表示
console.log(decimalNumber); // 输出: 42
console.log(binaryNumber); // 输出: 42
console.log(octalNumber); // 输出: 42
console.log(hexadecimalNumber); // 输出: 421.2 浮点数直接量 (Floating-Point Literals)
浮点数表示带有小数部分的数字。它们可以用小数点或者科学计数法(指数表示法)来书写。
let pi = 3.14159;
let scientificNotation = 1.23e5; // 等同于 1.23 * 10^5 = 123000
console.log(pi); // 输出: 3.14159
console.log(scientificNotation); // 输出: 1230001.3 特殊数值
JavaScript 有一些特殊的数值:
- NaN (Not a Number, 非数值): 代表一个无效数学运算的结果。
- Infinity (无穷大): 代表超过 JavaScript 数值表示上限的数字。
- -Infinity (负无穷大): 代表超过 JavaScript 数值表示下限的数字。
let notANumber = 0 / 0;
let infinity = 1 / 0;
let negativeInfinity = -1 / 0;
console.log(notANumber); // 输出: NaN
console.log(infinity); // 输出: Infinity
console.log(negativeInfinity); // 输出: -Infinity
console.log(NaN === NaN); // 输出: false (NaN 永远不等于它自己)
console.log(Number.isNaN(notANumber)); // 输出: true (使用 Number.isNaN() 来检查是否为 NaN)重要提示:NaN是一个棘手的值,因为它不等于它自己。你应该始终使用Number.isNaN()函数来检查一个值是否为NaN。
1.4 Number 方法
Number 对象提供了几个处理数字的有用方法:
- toFixed(digits): 使用定点表示法格式化数字(指定小数位数)。
- toPrecision(precision): 将数字格式化为指定的精度(有效数字位数)。
- parseInt(string, radix): 解析一个字符串并返回指定基数(进制)的整数。
- parseFloat(string): 解析一个字符串并返回浮点数。
let num = 3.14159;
console.log(num.toFixed(2)); // 输出: "3.14" (返回字符串)
console.log(num.toPrecision(4)); // 输出: "3.142" (返回字符串)
console.log(parseInt("10", 10)); // 输出: 10 (解析十进制数)
console.log(parseInt("0b101", 2)); // 输出: 5 (解析二进制数)
console.log(parseFloat("3.14")); // 输出: 3.141.5 Number 属性
Number 对象也有一些有用的属性:
- Number.MAX_VALUE: JavaScript 中能表示的最大数值。
- Number.MIN_VALUE: JavaScript 中能表示的最小正数值(最接近 0)。
- Number.POSITIVE_INFINITY: 代表正无穷大。
- Number.NEGATIVE_INFINITY: 代表负无穷大。
- Number.NaN: 代表 "非数值" (Not-a-Number)。
2. JavaScript 中的字符串 (Strings)
字符串用于表示文本数据。在 JavaScript 中,字符串是不可变的 (immutable),这意味着它们的值在创建后就不能被更改。任何看起来修改了字符串的操作实际上都是创建了一个新的字符串。
字符串可以用单引号 ('...')、双引号 ("...") 或反引号 (`...`) 括起来。反引号用于模板字面量,我们将在稍后的课程中介绍。
字符串直接量
let singleQuotedString = 'Hello, world!';
let doubleQuotedString = "这是一个字符串。";
let backtickString = `这也是一个字符串。`;
console.log(singleQuotedString); // 输出: Hello, world!
console.log(doubleQuotedString); // 输出: 这是一个字符串。
console.log(backtickString); // 输出: 这也是一个字符串。2.1 转义序列 (Escape Sequences)
转义序列是特殊的字符组合,允许你在字符串中包含难以或无法直接输入的字符。
| 转义序列 | 代表的字符 |
|---|---|
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 (Tab) |
\b | 退格符 |
\f | 换页符 |
let escapedString = "这个字符串包含一个\"双引号\"和一个反斜杠 \\。";
let multiLineString = "这个字符串跨越了\n多行。";
console.log(escapedString); // 输出: 这个字符串包含一个"双引号"和一个反斜杠 \。
console.log(multiLineString); // 输出: 这个字符串跨越了
// 多行。2.2 字符串属性和方法
JavaScript 提供了一套丰富的属性和方法来处理字符串。
length: 返回字符串的长度。charAt(index): 返回指定索引位置的字符。substring(startIndex, endIndex): 返回指定索引之间的子字符串。slice(startIndex, endIndex): 类似于 substring,但也接受负数索引。toUpperCase(): 将字符串转换为大写。toLowerCase(): 将字符串转换为小写。indexOf(searchValue, fromIndex): 返回指定值第一次出现的索引。lastIndexOf(searchValue, fromIndex): 返回指定值最后一次出现的索引。replace(searchValue, replaceValue): 将第一次出现的值替换为另一个值。replaceAll(searchValue, replaceValue): 将所有出现的值替换为另一个值。trim(): 移除字符串两端的空白字符。startsWith(searchString, position): 检查字符串是否以指定字符串开头。endsWith(searchString, position): 检查字符串是否以指定字符串结尾。includes(searchString, position): 检查字符串是否包含指定字符串。split(separator, limit): 将字符串分割成子字符串数组。
let myString = " Hello, World! ";
console.log(myString.length); // 输出: 17
console.log(myString.charAt(0)); // 输出: " "
console.log(myString.substring(2, 7)); // 输出: "Hello"
console.log(myString.slice(2, 7)); // 输出: "Hello"
console.log(myString.toUpperCase()); // 输出: " HELLO, WORLD! "
console.log(myString.toLowerCase()); // 输出: " hello, world! "
console.log(myString.indexOf("World")); // 输出: 9
console.log(myString.lastIndexOf("o")); // 输出: 10
console.log(myString.replace("World", "Universe")); // 输出: " Hello, Universe! "
console.log(myString.trim()); // 输出: "Hello, World!"
console.log(myString.startsWith(" ")); // 输出: true
console.log(myString.endsWith(" ")); // 输出: true
console.log(myString.includes("Hello")); // 输出: true
console.log(myString.split(",")); // 输出: [ ' Hello', ' World! ' ]3. JavaScript 中的布尔值 (Booleans)
布尔类型表示一个逻辑值:true (真) 或 false (假)。布尔值是代码决策的基础,它们被用于条件语句和逻辑运算中。
3.1 布尔直接量
let isTrue = true;
let isFalse = false;
console.log(isTrue); // 输出: true
console.log(isFalse); // 输出: false3.2 真值 (Truthy) 和 假值 (Falsy)
在 JavaScript 中,并非只有布尔类型的 true 或 false 才能在布尔上下文中使用。JavaScript 有“真值 (truthy)”和“假值 (falsy)”的概念。
- 真值 (Truthy): 在布尔上下文中遇到时被视为
true的值。 - 假值 (Falsy): 在布尔上下文中遇到时被视为
false的值。
以下值始终是假值 (Falsy):
false0(零)-0(负零)0n(BigInt 零)""(空字符串)nullundefinedNaN
所有其他值都是真值 (Truthy)。
console.log(Boolean(true)); // 输出: true
console.log(Boolean(false)); // 输出: false
console.log(Boolean("hello")); // 输出: true (真值)
console.log(Boolean("")); // 输出: false (假值)
console.log(Boolean(1)); // 输出: true (真值)
console.log(Boolean(0)); // 输出: false (假值)
console.log(Boolean(null)); // 输出: false (假值)
console.log(Boolean(undefined)); // 输出: false (假值)
console.log(Boolean(NaN)); // 输出: false (假值)
console.log(Boolean({})); // 输出: true (真值) - 即使是空对象也是真值
console.log(Boolean([])); // 输出: true (真值) - 即使是空数组也是真值3.3 布尔运算符
布尔运算符用于组合或修改布尔表达式。我们将在“控制流”课程中详细介绍这些内容,这里先做一个简要概述:
&&(逻辑与 AND): 如果两个操作数都为true,则返回true。||(逻辑或 OR): 如果至少有一个操作数为true,则返回true。!(逻辑非 NOT): 返回操作数布尔值的相反值。
let x = 5;
let y = 10;
console.log(x < 10 && y > 5); // 输出: true (两个条件都为真)
console.log(x > 10 || y > 5); // 输出: true (至少有一个条件为真)
console.log(!(x > 10)); // 输出: true (false 的相反值是 true)4. 未定义 (Undefined)
undefined 类型只有一个值,那就是 undefined。它代表一个变量已被声明,但尚未被赋予任何值。
当你声明一个变量但不初始化它时,它的默认值就是 undefined。它通常由 JavaScript 引擎自动分配。
let unassignedVariable;
console.log(unassignedVariable); // 输出: undefined
console.log(typeof unassignedVariable); // 输出: "undefined"5. 空值 (Null)
与 undefined 不同,null 是一个被显式赋值的特殊关键字。它代表“无”、“空”或“未知的值”。通常,开发者会主动将变量赋值为 null,以清空其内容或表示该变量目前不指向任何对象。
let emptyValue = null;
console.log(emptyValue); // 输出: null历史遗留问题: 如果你执行 typeof null,JavaScript 会返回 "object"。这是一个从 JavaScript 诞生之初就存在的知名 Bug,为了保证旧代码的兼容性,它一直未被修复。但 null 实际上是一个原始类型,并非对象。
6. 大整数 (BigInt)
BigInt 是一种较新的数据类型,用于表示任意长度的整数。
标准的 Number 类型只能安全地表示介于 -(2^53 - 1) 和 2^53 - 1 之间的整数。如果需要处理超出此范围的巨大整数(例如高精度时间戳或加密运算),就需要使用 BigInt。
创建 BigInt 的方法是在整数末尾加上字母 n,或者调用 BigInt() 函数。
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 输出: 9007199254740991
const bigIntNum = 900719925474099123456789n;
const anotherBigInt = BigInt("900719925474099123456789");
console.log(typeof bigIntNum); // 输出: "bigint"7. 符号 (Symbol)
Symbol 是在 ES6 (ECMAScript 2015) 中引入的。调用 Symbol() 函数会创建一个唯一的、不可变的值。
它最常被用来作为对象属性的唯一标识符(键),以防止属性名冲突,特别是在使用第三方库或处理复杂对象结构时。
let sym1 = Symbol("id");
let sym2 = Symbol("id");
// 即使描述相同,每一个 Symbol 都是独一无二的
console.log(sym1 === sym2); // 输出: false
console.log(typeof sym1); // 输出: "symbol"8. 对象 (Object) - 引用类型
前面介绍的 7 种类型都是“原始类型”,它们只能存储单一的值。而 Object(对象)是唯一的引用类型,它可以存储键值对的集合或更复杂的数据结构。
在 JavaScript 中,引用数据类型(通常统称为“对象类型”)的核心特征是:变量本身并不直接存储数据值,而是存储一个指向内存堆(Heap)中实际数据的“地址”(引用)。
对象几乎是 JavaScript 的核心。在 JavaScript 中,数组 (Arrays) 和 函数 (Functions) 本质上也是对象的特殊形式。
8.1 对象字面量
使用大括号 {} 可以创建一个对象,内部包含零个或多个键值对(属性和方法)。
let user = {
name: "Alice", // 字符串类型的属性
age: 28, // 数值类型的属性
isActive: true, // 布尔类型的属性
greet: function() { // 方法 (作为对象属性的函数)
console.log("Hello!");
}
};
console.log(user.name); // 输出: Alice
user.greet(); // 输出: Hello!8.2 数组 (Arrays)
数组是有序的数据集合,用方括号 [] 表示。虽然用 typeof 检查数组会返回 "object",但数组拥有自己独特的方法(如 push, map 等)。
let colors = ["red", "green", "blue"];
console.log(colors[0]); // 输出: "red"
console.log(typeof colors); // 输出: "object" (因为数组是特殊的对象)8.3 函数(Function)
在 JavaScript 中,函数是“一等公民”。这意味着函数本质上是一个特殊的对象。
- 特点:
- 它可以像变量一样被赋值、传递给其他函数或作为返回值。
- 因为它也是对象,所以你可以给函数添加属性(虽然不常用)。
- 示例:
function sayHi() { console.log("Hello!"); }
sayHi.description = "一个问候函数"; // 给函数对象添加属性8.4 Map & Set(ES6 新增集合)
为了弥补基础对象(Object)在某些场景下的不足,ES6 引入了这两个更强大的数据结构:
Map(映射):
- 区别:Object 的键只能是字符串或 Symbol,而 Map 的键可以是任何类型(包括对象、函数或数字)。
- 优点:非常适合需要频繁增删键值对的场景,且拥有内置的
size属性和易用的遍历方法。
let map = new Map();
map.set({id: 1}, "User Data"); // 使用对象作为键Set(集合):
- 特点:只能存储唯一值,不允许重复。
- 用途:常用于去重,比如过滤掉数组中的重复元素。
let uniqueNumbers = new Set([1, 2, 2, 3]); // 结果是 {1, 2, 3}8.5 总结:为什么区分“引用”?
理解引用类型最关键的地方在于赋值操作:
let objA = { count: 1 };
let objB = objA; // 这里复制的是“地址”,而不是对象本身
objB.count = 2;
console.log(objA.count); // 输出 2,因为 objA 和 objB 指向同一个内存地址对比原始类型: 如果你写 let a = 1; let b = a;,b 得到的是数字 1 的副本。修改 b 不会影响 a。而引用类型一旦发生赋值,两者就“绑定”在一起了,这是很多编程 Bug(如意外修改数据)的来源。