Javascript 零基础教程

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): 使用 0b0B 前缀。例如:0b101 (即十进制的 5)。
  • 八进制 (Octal, 基数 8): 使用 0o0O 前缀。例如:0o12 (即十进制的 10)。注意:八进制直接量在严格模式下已被废弃。
  • 十六进制 (Hexadecimal, 基数 16): 使用 0x0X 前缀。例如: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);  // 输出: 42

1.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); // 输出: 123000

1.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.14

1.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);   // 输出: false

3.2 真值 (Truthy) 和 假值 (Falsy)

在 JavaScript 中,并非只有布尔类型的 truefalse 才能在布尔上下文中使用。JavaScript 有“真值 (truthy)”和“假值 (falsy)”的概念。

  • 真值 (Truthy): 在布尔上下文中遇到时被视为 true 的值。
  • 假值 (Falsy): 在布尔上下文中遇到时被视为 false 的值。

以下值始终是假值 (Falsy):

  • false
  • 0 (零)
  • -0 (负零)
  • 0n (BigInt 零)
  • "" (空字符串)
  • null
  • undefined
  • NaN

所有其他值都是真值 (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(如意外修改数据)的来源。