第一章: JavaScript 核心语法:构建坚实基础

第一章: JavaScript 核心语法:构建坚实基础

摘要: 本章将是您 JavaScript 学习之旅的坚实起点。我们不会重复您已熟知的编程常识,而是直击 JavaScript 的核心特性与差异点,如果是完全从零基础,或是如果您对第一章还有看不懂的知识,请您去稍微的补充您的编程基础,但相信我这并不是很难的事情!您将了解到现代 JS 的语言构成与技术生态,深入探讨 letconst 为何能取代 var,并揭示 JS 独有的数据类型与“臭名昭著”的隐式类型转换机制。学完本章,您将对 JavaScript 的底层行为有更深刻的理解,为后续学习打下坚不可摧的基础。

注意: 阅读本笔记的重要前提是对于 JS 的最基础的语法要有一定的认知,包括运算符、函数、基本数据类型,只需要对于最基础的内容有所认知即可


在本章中,我们将聚焦于 JavaScript 的独特之处:

  1. 首先,我们将快速了解 JavaScript 的构成与标准,明确学习的重点。
  2. 接着,我们将深入 var 的历史遗留问题——变量提升,并理解为何 letconst 是现代 JS 的必然选择。
  3. 然后,我们将辨析 JS 特有的 数据类型,并剖析最关键的“陷阱”——隐式类型转换
  4. 最后,我们将跳过基础语法,仅探讨 JS 中 运算符与流程控制的特殊行为,例如 ===== 的本质区别,以及 for...infor...of 的正确用法。

1.1. 初识 JavaScript:语言构成与现代标准

技术背景: JavaScript(简称 JS)诞生于 1995 年,它与 Java 并无直接的血缘关系,是网景公司为了让浏览器处理动态交互而设计的脚本语言。发展至今,它已成为构建现代 Web 应用不可或缺的核心技术。

一份完整的 JavaScript 实现由三个不同部分组成:

  • 核心 (ECMAScript): 由 ECMA-262 规范定义,提供核心语言功能。我们通常所说的 ES5, ES6 (ECMAScript 2015), ES2025 等版本,就是指这个核心语法的标准。在 2025 年,ES6+ 已是行业开发标准
  • 文档对象模型 (DOM): 提供与网页内容(HTML 文档)交互的接口。
  • 浏览器对象模型 (BOM): 提供与浏览器窗口交互的接口。

解决方案: 在现代 Web 开发中,我们推荐将 JavaScript 代码分离到独立的 .js 文件中,并通过 <script> 标签引入,这是维护代码整洁和可复用性的最佳实践。

场景
任何规模项目,行业标准。

优势

  • 关注点分离:HTML、CSS、JS 互不干扰
  • 可维护:逻辑集中,易改易调
  • 性能好:浏览器可缓存 .js

scripts/main.js

1
console.log('Hello from an external file!');

index.html

1
<script src="scripts/main.js"></script>

场景
一次性、与页面高度耦合的临时脚本。

劣势

  • 代码混杂:破坏结构清晰度
  • 无法复用:其他页面无法共享
1
2
3
<script>
console.log('Hello from inside the HTML!');
</script>

注意:带 src<script> 标签内部若再写代码会被忽略。


1.2. 变量与常量:letconst 的时代与 var 的历史包袱

在上一节,我们了解了 JS 的基本构成。现在,我们来看编程的第一步:变量。您可能熟悉其他语言的变量声明,但在 JS 中,这是一个有“历史”的话题,也是理解其底层行为的关键。

痛点背景: 在 ES6 (2015) 之前,JavaScript 只有 var 一种声明变量的方式。var 存在一个与其他主流语言(如 Java, Python)截然不同的特性——变量提升 (Hoisting)。这会导致代码行为违反直觉,是许多早期 JS bug 的根源。

看下面的代码,在 Java 或 Python 中,这会直接抛出“变量未定义”的错误。但在 JavaScript 中…

1
2
3
4
5
6
7
function hoistingTest() {
console.log(myVar); // 在声明前使用变量
var myVar = "Hello, Prorise!";
console.log(myVar);
}

hoistingTest();

解决方案: 为了解决 变量提升 以及 var 带来的其他作用域问题,ES6 引入了 letconst。它们使用 块级作用域 (Block Scope),行为更符合预期,并且 let 声明的变量在声明前访问会直接报错,这被称为 暂时性死区 (Temporal Dead Zone, TDZ),从根本上杜绝了变量提升带来的问题。

1
2
3
4
5
6
7
8
9
10
11
function blockScopeTest() {
try {
console.log(myLet); // 在声明前访问,进入暂时性死区
} catch (e) {
console.error(e.message);
}
let myLet = "Hello, Modern JS!"; // 变量在这里才“出生”
console.log(myLet);
}

blockScopeTest();

2025 年最佳实践:

  • 停止使用 var
  • 默认使用 const 声明变量,这能确保变量不被重新赋值,增强代码的稳定性和可预测性。
  • 只在确定变量需要被重新赋值时,才使用 let

1.3. 数据类型:掌握程序的基石

JavaScript 的数据类型分为两大类:原始类型和引用类型。这个划分对其内存分配和变量赋值行为有着深远影响,本小节我们仅作为了解,在后续的章节我们会对于每一个数据类型进行深入剖析

类型:原始类型
特点:不可变文本序列

1
2
const str = 'Hello';
console.log(typeof str); // string

类型:原始类型
特点:所有数字(含整数、浮点、NaN、Infinity)

1
2
const n = 42;
console.log(typeof n); // number

类型:原始类型
特点truefalse

1
2
const b = true;
console.log(typeof b); // boolean

类型:原始类型
特点:表示“空对象引用”,历史原因导致 typeof null === 'object'

1
2
const val = null;
console.log(val); // null

类型:原始类型
特点:声明但未赋值的默认结果

1
2
let u;
console.log(u); // undefined

类型:原始类型 (ES6)
特点:唯一、不可变标识符

1
2
const sym = Symbol('id');
console.log(typeof sym); // symbol

类型:原始类型 (ES2020)
特点:任意精度整数

1
2
const big = 9007199254740993n;
console.log(typeof big); // bigint

类型:引用类型
特点:键值对集合,属性可变

1
2
const obj = { name: 'Prorise' };
console.log(typeof obj); // object

类型:引用类型(特殊 Object)
特点:有序数据集合

1
2
const arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true

类型:引用类型(特殊 Object,可调用)
特点:可执行代码块,一等公民

1
2
const fn = () => console.log('hi');
console.log(typeof fn); // function

类型:引用类型
特点:Date、RegExp(正则表达式)、Map、Set 等

1
2
const date = new Date();
console.log(date instanceof Date); // true

1.4. 深度解析:类型转换与隐式强制的“陷阱”

这是 JavaScript 与 Java、Python 等语言最大的区别之一,也是面试中的高频考点。JavaScript 是一门 弱类型 语言,在运算时会自动进行类型转换,这个过程被称为 隐式强制类型转换 。虽然这提供了灵活性,但也埋下了很多“陷阱”。

痛点背景: 观察以下代码,其结果在强类型语言中是不可想象的,但在 JS 中却真实发生。

1
2
3
4
5
console.log(`'5' - 1 = ${'5' - 1}`);
console.log(`'5' + 1 = ${'5' + 1}`);
console.log(`true + 1 = ${true + 1}`);
console.log(`' ' == 0 is ${' ' == 0}`);
console.log(`null == undefined is ${null == undefined}`);

一、== vs ===:核心区别

  • === 严格相等:不做任何类型转换,只要类型或值不同就返回 false
  • == 宽松相等:先按隐式规则把两边转成同一类型,再比较值。规则复杂、结果难预测,99 % 的场景请用 ===
1
2
'1' === 1   // false  (类型不同)
'1' == 1 // true (字符串 '1' 被转成数字 1)

二、算术 / 拼接运算符的隐式转换

  • 加法 +:只要有一个操作数是字符串,就执行 字符串拼接
  • 其余 -*/%:两边都尽量 转成数字 再运算。
1
2
3
'10' + 5   // "105"
'10' - 5 // 5
'6' * '2' // 12

三、实战建议

  1. 比较时始终 优先使用 ===
  2. 运算或比较前 主动显式转换,避免依赖隐式规则。
  3. 判断“空”时,用 value == null 可一次性覆盖 nullundefined,这是少数 ===== 更简洁且不易出错的场景。

1.5. 运算符与流程控制中的 JS 特性

您已经熟悉了大多数运算符和流程控制结构。这里我们仅聚焦于 JavaScript 中行为独特且常用的部分。在 Java 或 Python 中,逻辑运算符 &&|| 通常返回布尔值。但在 JavaScript 中,它们返回的是决定了整个表达式结果的那个 操作数的值,这种特性被称为 短路求值

  • expr1 && expr2: 如果 expr1 能被转换为 false 值,则返回 expr1 的值,否则返回 expr2 的值。
  • expr1 || expr2: 如果 expr1 能被转换为 true 值,则返回 expr1 的值,否则返回 expr2 的值。

实战场景: 这种行为常被用于设置函数参数的默认值或执行条件代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 场景 1: 设置默认值
function greet(name) {
const finalName = name || "Guest";
console.log(`Hello, ${finalName}!`);
}

greet("Prorise");
greet(); // name is undefined (false), 所以会打印 Guest

// 场景 2: 条件函数执行
let userIsAdmin = true;
// 如果 userIsAdmin 为 true,则执行后面的函数
userIsAdmin && console.log("Admin panel functions loaded.");

JavaScript 提供了两种独特的 for 循环变体来遍历数据结构,它们的用途完全不同。

  • for...in: 遍历对象的可枚举属性名 (key)。它主要用于遍历普通对象。不推荐用它来遍历数组,因为它会遍历到数组原型链上的属性,且顺序无法保证。
  • for...of (ES6 新增): 遍历可迭代 (Iterable) 对象的值 (value)。这是遍历数组、字符串、Map、Set 等数据结构的 推荐方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const user = {
name: "Prorise",
role: "Admin"
};

const permissions = ["read", "write", "execute"];

// 使用 for...in 遍历对象属性
console.log("--- for...in (Object) ---");
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}

// 使用 for...of 遍历数组元素
console.log("\n--- for...of (Array) ---");
for (const permission of permissions) {
console.log(permission);
}

1.6. 本章核心速查与高频面试题

核心速查总结

分类关键项核心描述
变量声明let, const(推荐) 块级作用域,无变量提升,存在暂时性死区。
var(废弃) 函数作用域,存在变量提升,易导致意外行为。
核心区别原始类型 vs 引用类型赋值时,前者复制值,后者复制引用(内存地址)。
相等比较=== (严格相等)(推荐) 不进行类型转换,同时比较类型和值。
== (宽松相等)(慎用) 会进行隐式类型转换,规则复杂,易出错。
类型转换Falsy 值false, 0, "", null, undefined, NaN。这 6 个值转为布尔为 false
循环遍历for...of(推荐) 用于遍历数组、字符串等可迭代对象的值。
for...in用于遍历对象的键(属性名)。