JavaScript 是现代开发中最重要的语言之一,但零散的知识常让人难以形成体系。本文从核心语法到运行机制,从基础概念到常见难点,系统梳理 JavaScript 的关键知识点,帮助你建立清晰、完整的认知框架。无论入门还是进阶,看完这一篇,理解 JavaScript 就够了。
一、快速开始
众所周知,html、CSS、JS是前端三大知识点。html是骨架,CSS是脸面,JS则是动作。他们三者都是直接在浏览器上运行的语言。因此,我们在调试JS时通常在浏览器上调试。
React、Vue、Angular、Svelte、Bootstrap都是JS的前端框架,是它的库。高度集成,开箱则用。
Node.js虽然也是JS的库,但也是一个让 JavaScript 能在浏览器之外运行的运行环境。Node.js 让 JavaScript 不只写网页,还能写服务器和各种工具。
TS (另一门语言,即TypeScript)是 JS 的“加强版”,代码最终都会变成 JS,所有合法的 JS 都是合法的 TS,但需要先编译成 JS,才能在浏览器或 Node.js 中执行。
Javascript 脚本位于html的 <script>与</script>标签之间,我们在这里写代码,来操作浏览器元素(htmlCSS)。右键浏览器,点击检查,一般都可以看到前端代码,也能看到JS脚本。调试时,还可以在上面写程序,来达到知己想要的效果。
1、安装node.js来运行JS脚本
下载安装配置好环境即可,不算复杂(请参阅官方文档):Node.js — Run JavaScript Everywhere
环境配置好后,可在cmd输入node直接进入运行环境
2、使用node.js的工具npm随便安装一些库
打开cmd,如输入以下安装指令,来安装electron库:
npm install electronnpm list electron
electron.js不能使用node指令来运行,因为有些模块(如app模块)只能在 Electron 主进程 中使用。
3、使用node运行内置模块
const path = require('path');path.join('folder1', 'folder2', 'file.txt')
特别的,有些内置方法不能再模块里用,如console.log('当前目录:', __dirname);,这是因为node.js是基于 REPL的,__dirname和 __filename是 Node.js 为模块文件注入的局部变量,而 REPL 是全局执行环境,没有模块包装器,因此不存在这些变量。
因此,我们使用node最佳的运行方式是运行一个JS工程或文件,而不是进入内部运行,特别的,使用npm指令也可运行脚本,只不过要配置JSON。
使用node运行脚本时,可以这样(如果其他模块提供命令行运行脚本的方式,则将node替换即可):
node xxx.js
再比如,使用的是electron.js项目,Json中脚本这一块可这样写(如果是node项目,将electron替换成node即可):
"scripts": { "start": "set NODE_OPTIONS=--no-warnings --trace-warnings && chcp 65001 && electron .", }
附完整JSON:
{ "name": "ElectricFlounder", "version": "1.0.0", "description": "", "author": "", "main": "src/main/main.js", "scripts": { "start": "set NODE_OPTIONS=--no-warnings --trace-warnings && chcp 65001 && electron .", "build": "electron-builder" }, "devDependencies": { "electron": "^29.4.6", "electron-builder": "^24.9.1" }, "build": { "icon": "build/icons/logo.ico", "extraFiles": [ { "from": "build/icons/logo.ico", "to": "resources/build/icons/logo.ico" } ] }, "dependencies": { "@codemirror/basic-setup": "^0.20.0", "@codemirror/commands": "^6.10.1", "@codemirror/language": "^6.11.3", "@codemirror/search": "^6.5.11", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.39.4", "chart.js": "^3.9.1", "monaco-editor": "^0.55.1" }}
4、网页调试JS
推荐这种方式!新建一个文本,不写入数据,直接改名为test.html :
使用浏览器打开该文件,该文件为空,右侧有打开文件的按钮,打开后显示内容为空
使用文本编辑内容后,前端展示了内容
我们添加JS与CSS,只要点击快速开始就能变化颜色
<!DOCTYPE html><html><head> <style> .red { color: red; background: #ffe6e6; padding: 20px; cursor: pointer; } .green { color: green; background: #e6ffe6; padding: 20px; cursor: pointer; } </style></head><body> <div id="quickStart" onclick="toggleColor()">快速开始</div> <script> let isRed = true; function toggleColor() { const element = document.getElementById('quickStart'); if (isRed) { element.className = 'green'; } else { element.className = 'red'; } isRed = !isRed; } </script></body></html>
5、代码解析
以“快速开始变化颜色”的代码为例,我们来解析一下代码:
(1)HTML解析与DOM构建(渲染前准备)
①DOCTYPE声明:<!DOCTYPE html> 触发浏览器使用标准模式(而不是怪异模式)解析HTML ② 字节流解析:浏览器接收HTML字节流,字符解码器转换为UTF-8字符 ③令牌化(Tokenization):HTML解析器扫描字符,生成开始标签、结束标签、属性、文本等令牌
<html>开始 → <head>开始 → <style>开始 → CSS文本 → </style>结束 → <body>开始 → <div>开始 → 文本"快速开始" → </div>结束 → <script>开始 → JS文本 → </script>结束
④DOM树构建:令牌送入DOM构建器,创建节点并建立父子关系 • 创建HTMLDocument对象
• 创建html元素节点 → 作为文档根
• 创建head元素节点 → 作为html子节点
• 创建style元素节点 → 放入head
• 创建文本节点(CSS内容) → 放入style
• 创建body元素节点 → 作为html子节点
• 创建div元素节点 → 放入body
• 创建文本节点"快速开始" → 放入div
• 创建script元素节点 → 放入body
• 创建JS文本节点 → 放入script
(2)CSS解析与样式计算(样式准备)
①CSSOM构建:解析<style>标签内容
CSS解析器工作流程:
.red { color: red; background: #ffe6e6; padding: 20px; cursor: pointer; } ↓ 词法分析:['.red', '{', 'color', ':', 'red', ';', ...] ↓ 语法分析:生成CSSOM规则 ↓ 样式规则对象:{ selector: '.red', style: { 'color': { value: 'red', priority: '' }, 'background': { value: '#ffe6e6', priority: '' }, 'padding': { value: '20px', priority: '' }, 'cursor': { value: 'pointer', priority: '' } } }
② 渲染树构建:DOM + CSSOM = 渲染树 • 从DOM根开始遍历
• 对div节点:匹配.red选择器 → 计算样式
• 创建渲染对象(RenderObject/LayoutObject),包含几何信息
(3)JavaScript执行环境初始化(交互能力准备)
①全局执行上下文创建:
GlobalExecutionContext = { VariableEnvironment: { // 预定义的全局对象 window: global, document: document, // 你的代码将在下面添加 }, LexicalEnvironment: {...}, ThisBinding: window }
②变量提升阶段: // 扫描代码,提升声明 let isRed; // 提升但不初始化(TDZ开始) function toggleColor() { ... } // 整个函数提升
(4)页面渲染流水线(像素生成)
DOM + CSSOM ↓ 渲染树 (Render Tree) ← 包含可见节点 ↓ 布局 (Layout/Reflow) ← 计算位置尺寸 ↓ 分层 (Layer) ← 创建合成层 ↓ 绘制 (Paint) ← 生成绘制列表 ↓ 栅格化 (Raster) ← 像素填充 ↓ 合成 (Composite) ← 层合并
div的布局计算:
content box: 文本"快速开始"的尺寸 + padding: 20px + border: 0 (默认) + margin: 0 (默认) = 最终渲染矩形
(5)事件系统初始化(交互准备)
①onclick属性解析: • HTML解析器遇到οnclick="toggleColor()"
• 创建属性节点,值为"toggleColor()"
• 在div的扩展属性中存储
②事件监听器注册: // 浏览器内部执行: element.addEventListener('click', function(event) { // 包裹你的代码 toggleColor(); // 调用全局函数 }, false);
③事件流模型建立:
捕获阶段: window → document → html → body → div 目标阶段: div (执行toggleColor) 冒泡阶段: div → body → html → document → window
(6)内存与变量状态(运行时状态)
// 全局变量环境GlobalEnv = { EnvironmentRecord: { // 编译阶段 isRed: <uninitialized>, // TDZ状态 // 执行阶段 toggleColor: <function object> { [[Scope]]: GlobalEnv, [[Code]]: 函数体字节码, [[FormalParameters]]: [], [[ThisMode]]: "global" } }, outer: null}
(7)用户点击事件的完整执行链(核心交互)
物理点击 → 浏览器内核处理 ↓操作系统中断 → 鼠标驱动程序 → 窗口消息循环 ↓浏览器接收WM_LBUTTONDOWN消息 ↓坐标转换:屏幕坐标 → 视图坐标 ↓命中测试 (Hit Testing): 1. 遍历渲染树 2. 检查点(100,100)在哪个渲染对象内 3. 找到div对应的RenderObject ↓创建事件对象: MouseEvent = { type: 'click', target: div元素引用, clientX: 100, clientY: 100, timeStamp: 1234567890 } ↓事件派发 (Dispatch): // 捕获阶段 window.dispatchEvent(event, CAPTURING_PHASE) document.dispatchEvent(event, CAPTURING_PHASE) ... // 目标阶段 div.dispatchEvent(event, AT_TARGET) ↓执行监听器: // 查找div的onclick处理器 const handler = div.getAttribute('onclick'); // "toggleColor()" ↓调用toggleColor():
(8)toggleColor()函数的微观执行
// 1. 创建函数执行上下文toggleColorEC = { VariableEnvironment: { element: <uninitialized>, arguments: { length: 0 } }, LexicalEnvironment: { // 闭包相关 }, ThisBinding: window}// 2. 变量初始化element = document.getElementById('quickStart');// document.getElementById的实现:// - 调用Document.getElementById// - 在DOM树中搜索id="quickStart"// - 返回第一个匹配的元素引用// - 内部使用哈希映射加速:document.idMap['quickStart']// 3. 条件判断if (isRed) { // 读取全局变量 // isRed = true时的执行路径}// 4. className赋值 - 这是最关键的操作!element.className = 'green';
(9)className赋值的底层连锁反应
// 赋值触发一系列操作:element.className = 'green'; ↓// 1. 属性设置器HTMLDivElement.prototype.className.setter('green') ↓// 2. 更新class属性element.setAttribute('class', 'green') ↓// 3. 修改DOM属性element._attributes['class'] = 'green' ↓// 4. 触发样式重新计算element.style = null // 清除旧计算样式 ↓// 5. 重新匹配CSS规则// 遍历所有样式规则,找到匹配的// .red 不匹配(因为)// .green 匹配! ↓// 6. 计算新样式StyleResolver.computeStyle(element) { // 收集所有匹配的规则 // 级联、继承、默认值 return { color: 'rgb(0, 128, 0)', // green background: 'rgb(230, 255, 230)', // #ffe6e6 padding: '20px', cursor: 'pointer' }} ↓// 7. 标记需要重绘element.renderObject.markForRepaint(); ↓// 8. 触发重排/重绘流程
(10)渲染更新流程(视觉变化)
标记脏区域 (Dirty Rectangle) ↓ 样式重新计算完成 ↓ 布局更新 (Reflow) ↓ 更新图层属性 ↓ GPU命令生成 ↓ VSync信号等待 ↓ 帧提交到GPU ↓ 屏幕扫描输出
关键优化:现代浏览器使用增量布局和合成器线程
主线程: JS执行 → 样式计算 → 布局 ↓ 合成器线程: 分层 → 绘制 → 分块 → 栅格化 ↓ GPU进程: 纹理上传 → 图块合成 → 显示
(11)内存回收与清理
// 函数执行结束 toggleColorEC = null; // 执行上下文可回收
// 变量更新 isRed = false; // 修改全局变量值
// 事件对象清理 MouseEvent = null; // 事件对象可回收
(12)整个流程的CPU指令级视图
用户点击时的大致CPU执行流程1. 中断处理: INT 33h 或 现代中断控制器2. 消息循环: GetMessage → TranslateMessage → DispatchMessage3. 浏览器内核: HandleMouseClick(x, y)4. 事件派发: dispatchEvent5. JS引擎调用: - 查找toggleColor函数地址 - 准备参数栈 - CALL toggleColor6. toggleColor函数体: - PUSH 寄存器保存 - 调用getElementById - 比较isRed值 - JNE/JE 条件跳转 - 设置className属性 - POP 寄存器恢复 - RET
(13)浏览器多进程架构视角
Browser Process ↓ IPC Renderer Process (标签页进程) ├── Main Thread: DOM、样式、JS ├── Compositor Thread: 合成 └── Raster Threads: 栅格化 ↓ IPC GPU Process ↓ 显示器
(14)关键性能优化点
1. 样式计算优化:浏览器维护样式映射表,避免重复计算2. 布局缓存:renderObject缓存几何信息3. 合成层提升:某些CSS属性触发GPU加速4. 事件委托:本示例是直接绑定,实际项目多用事件委托5. 变量访问:isRed是全局变量,访问比局部变量慢
6、关键字
以下是JS的关键字,其中列出了java的一些关键字,对于这些关键字,你不能直接将其作为变量名。之所以列出java的一些关键字,主要原因是JS语法仿照的是java,给大家澄清一下。
| 类别 | 关键字 | 状态 | ES版本 | 说明 |
|---|---|---|---|---|
| 实际关键字 | break |
✅ 可用 | ES1 | 跳出循环或switch |
case |
✅ 可用 | ES3 | switch的分支 | |
catch |
✅ 可用 | ES3 | 异常捕获 | |
class |
✅ 可用 | ES6 | 类声明 | |
const |
✅ 可用 | ES6 | 常量声明 | |
continue |
✅ 可用 | ES1 | 继续下一次循环 | |
debugger |
✅ 可用 | ES3 | 调试器断点 | |
default |
✅ 可用 | ES3 | switch默认分支 | |
delete |
✅ 可用 | ES1 | 删除对象属性 | |
do |
✅ 可用 | ES1 | do-while循环 | |
else |
✅ 可用 | ES1 | if的否则分支 | |
export |
✅ 可用 | ES6 | 模块导出 | |
extends |
✅ 可用 | ES6 | 类继承 | |
false |
⚠️ 字面量 | ES1 | 布尔假值 | |
finally |
✅ 可用 | ES3 | 最终执行块 | |
for |
✅ 可用 | ES1 | for循环 | |
function |
✅ 可用 | ES1 | 函数声明 | |
if |
✅ 可用 | ES1 | 条件判断 | |
import |
✅ 可用 | ES6 | 模块导入 | |
in |
✅ 可用 | ES3 | 属性存在检查 | |
instanceof |
✅ 可用 | ES3 | 实例检查 | |
let |
✅ 可用 | ES6 | 块级变量 | |
new |
✅ 可用 | ES1 | 创建实例 | |
null |
⚠️ 字面量 | ES1 | 空值 | |
return |
✅ 可用 | ES1 | 函数返回值 | |
static |
✅ 可用 | ES6 | 类静态成员 | |
super |
✅ 可用 | ES6 | 父类引用 | |
switch |
✅ 可用 | ES3 | 多路分支 | |
this |
✅ 可用 | ES1 | 当前对象 | |
throw |
✅ 可用 | ES1 | 抛出异常 | |
true |
⚠️ 字面量 | ES1 | 布尔真值 | |
try |
✅ 可用 | ES3 | 异常尝试 | |
typeof |
✅ 可用 | ES1 | 类型检查 | |
var |
✅ 可用 | ES1 | 变量声明(旧) | |
void |
✅ 可用 | ES1 | 返回undefined | |
while |
✅ 可用 | ES1 | while循环 | |
with |
✅ 可用 | ES1 | 作用域扩展(废弃) | |
| 未来保留字 | enum |
526