一、JavaScript 历史演进
JavaScript最初由Brendan Eich在1995年为Netscape浏览器开发,是一种脚本编程语言,它可以在网页上实现复杂的功能,网页展现给你的不再是简单的静态信息,而是实时的内容更新——交互式的地图、2D/3D 动画、滚动播放的视频等等——JavaScript 就在其中。它是标准 Web 技术蛋糕的第三层(HTML,CSS)
graph LR
A[1995 Netscape] --> B(ECMAScript 标准化)
B --> C{ES6 革命}
C --> D[模块化]
C --> E[箭头函数]
C --> F[Class]
1.1 诞生目的(1995)
- 为 Netscape 浏览器实现轻量级脚本功能
- 解决表单验证等简单交互需求
- 减少服务器端压力(最初仅处理点击验证等基础操作)
1.2 核心特性设计
// 演示早期表单验证场景
function validateForm() {
var email = document.forms["myForm"]["email"].value;
if (email.indexOf("@") == -1) { // 基础校验逻辑
alert("Invalid email!");
return false;
}
}
1.3 2005年 AJAX 革命
Ajax 的出现标志着 Web 从「文档浏览」向「应用平台」的质变,为现代 Web 开发奠定了异步通信的基石。
// 传统表单提交方式
form.onsubmit = function() {
// 同步页面刷新
window.location.reload();
};
首先,Ajax的出现解决了传统Web应用中同步加载的问题。在Ajax之前,每次用户交互都需要重新加载整个页面,导致体验差。而Ajax允许异步通信,可以在后台与服务器交换数据,更新部分页面内容,提升用户体验。
核心技术
XMLHttpRequest 对象
// 创建XHR实例
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', true); // 异步标志
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
document.getElementById("content").innerHTML = xhr.responseText;
}
};
xhr.send();
异步通信
异步通信的关键步骤,包括事件触发、请求发送、响应处理和回调执行
sequenceDiagram
participant 用户操作
participant 主线程
participant WebAPI
participant 回调队列
participant 渲染引擎
用户操作->>主线程: 触发事件(点击/输入)
主线程->>WebAPI: 发送异步请求(XHR/fetch)
WebAPI-->>回调队列: 完成请求后放入回调
主线程->>渲染引擎: 继续执行同步代码
Note right of 渲染引擎: 页面保持可交互状态
回调队列->>主线程: 事件循环检查队列
主线程->>主线程: 执行回调函数
技术突破
| 传统模式 | Ajax 模式 |
|---|---|
| 全页面刷新 | 局部更新 |
| 同步阻塞 | 异步非阻塞 |
| 高带宽消耗 | 数据驱动传输 |
| 线性操作流 | 复杂交互可能性 |
1.4 V8 引擎发布
V8由Google开发,2008年随Chrome发布,目的是提升JavaScript的执行速度。之前的JS引擎多是解释执行,效率较低,而V8引入了JIT编译技术,将JS代码直接编译成机器码,大大提高了性能。
工作流程
分为编译阶段和执行阶段。编译阶段包括词法分析、语法分析生成AST,然后生成字节码。执行阶段则通过解释器Ignition执行字节码,同时利用TurboFan进行优化编译,将热点代码编译成机器码。
graph TD
A[源码] --> B(解析器)
B --> C[AST]
C --> D[字节码]
D --> E{热点检测}
E --> F[机器码生成]
内存管理机制
垃圾回收策略
sequenceDiagram
participant 主线程
participant 新生代堆
主线程->>新生代堆: 对象分配
新生代堆->>新生代堆: 空间不足时触发GC
Note right of 新生代堆: 1. 标记存活对象
新生代堆->>新生代堆: 2. 复制到toSpace
新生代堆->>新生代堆: 3. 交换空间角色
通过字节码生成和分层编译策略,V8 成功将 JavaScript 执行速度提升 10 倍以上,为现代 Web 应用提供了性能保障。其创新性的隐藏类和内联缓存机制,更是解决了动态类型语言的速度瓶颈问题。
1.5 Node.js 诞生
Node.js由Ryan Dahl在2009年创建,核心是将V8引擎引入服务器端,让JavaScript能进行后端开发。是一个开源的、跨平台的 JavaScript 运行环境
JavaScript解析器只是JavaScript代码运行的一种环境,浏览器是JavaScript运行的一种环境,浏览器为JavaScript提供了操作DOM对象和window对象等的接口。Node.js也是JavaScript运行的一种环境,Node.js为JavaScript提供了操 作文件、创建HTTP服务、 创建TCP/UDP服务等的接口,所以Node.js可以完成其他后台语言(Python、PHP等)能完成的工作。
核心架构
事件循环机制
// 典型事件循环流程
const eventLoop = {
phases: [
'timers', // setTimeout/setInterval
'pending', // 系统级回调
'poll', // I/O 事件
'check' // setImmediate
]
};
Node.js 的出现打破了前端与后端的界限,开创了「JavaScript 全栈」的新纪元,其事件驱动模型更成为现代云原生架构的重要基石
1.6 ES6 发布
graph TD
A[ES6] --> B(语法增强)
A --> C(异步处理)
A --> D(模块化)
B --> E[箭头函数]
B --> F[解构赋值]
C --> G[Promise]
C --> H[async/await]
D --> I[import/export]
核心特性解析
一、块级作用域(let/const)
// 传统 var 的问题
function varExample() {
var x = 10;
if (true) {
var x = 20; // 变量覆盖
}
console.log(x); // 20
}
// let/const 解决方案
function letExample() {
let x = 10;
if (true) {
let x = 20; // 独立作用域
const y = 30; // 常量声明
}
console.log(x); // 10
}
二、箭头函数(Arrow Functions)
// 传统函数 vs 箭头函数
const obj = {
value: 42,
traditional: function() {
setTimeout(function() {
console.log(this.value); // undefined
}, 100);
},
arrow: function() {
setTimeout(() => {
console.log(this.value); // 42
}, 100);
}
};
三、Promise 异步处理
// 异步流程控制对比
// 回调
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
// ...
});
});
});
// Promise 链式调用
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.catch(error => console.error(error));
// async/await 语法糖
async function process() {
try {
const a = await getData();
const b = await getMoreData(a);
return b;
} catch (error) {
console.error(error);
}
}
四、Class 语法糖
// ES5 原型继承
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
// ES6 Class 实现
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi, I'm ${this.name}`);
}
}
五、模块化(Modules)
// CommonJS vs ESM
// CommonJS (Node.js)
const { func1 } = require('./module');
// ES Modules
import { func1 } from './module.js';
export const apiKey = '123';
// 动态导入
button.addEventListener('click', async () => {
const module = await import('./module.js');
module.doSomething();
});
这些特性从根本上改变了 JavaScript 的开发范式,使代码更易维护、更具表现力。结合 V8 引擎的优化,ES6 为现代 Web 应用开发奠定了坚实基础。
二、Js的执行机制
graph TD
A[源码加载] --> B(词法分析)
B --> C[生成Token]
C --> D[语法分析]
D --> E[构建AST]
E --> F[生成字节码]
F --> G[编译执行]
JavaScript 的执行机制可以理解为「菜谱烹饪」的过程,我们用实际的代码示例来理解:
var txt = '外层变量-->你好呀';
function fn() {
console.log(txt); // 这里会输出什么?
if (true) {
var txt = '内层变量-->hello';
}
}
fn();
- 源码加载 :就像把菜谱从书柜拿到厨房,浏览器/Node.js 把 JS 文件读入内存
- 词法分析 :把代码拆成「食材」
// 拆解结果示例:
["var", "txt", "=", "'外层变量-->你好呀'", ";", "function", "fn", "(", ")", ...]
- 语法分析 :检查菜谱步骤是否合理
// 构建的 AST 结构类似:
Program
├─ VariableDeclaration (var)
│ └─ txt = '外层变量-->你好呀'
└─ FunctionDeclaration
└─ fn()
├─ IfStatement
│ └─ Block
│ └─ VariableDeclaration (var)
│ └─ txt = '内层变量-->hello'
└─ ExpressionStatement
└─ console.log(txt)
- 生成字节码 :翻译成厨师能理解的步骤
// 伪字节码示例:
LOAD_GLOBAL 'console'
LOAD_ATTR 'log'
LOAD_FAST 'txt'
CALL_FUNCTION 1
- 编译执行 :分阶段烹饪
// 执行过程解析:
// 阶段一:准备食材(变量提升)
var txt; // 提升声明
function fn() {} // 整体提升
// 阶段二:正式做菜(逐行执行)
txt = '外层变量-->你好呀';
fn(); → 进入函数作用域
// 函数内变量提升:var txt 提升到函数顶部
console.log(txt); // 输出 undefined(此时内层 txt 还未赋值)
if 代码块执行:
txt = '内层变量-->hello'; // 修改的是函数作用域的 txt
最终输出结果会是 undefined ,因为函数内的 var txt 在函数作用域内提升,但赋值发生在 console.log 之后。这就是经典的变量提升现象,也是理解 JS 执行机制的关键案例。
三、总结:
JavaScript 的执行机制就像一场精心编排的舞台剧:从源码加载的「剧本拆解」到词法分析的「台词标注」,再到语法分析的「场景编排」,最终通过字节码生成实现「舞台调度」。V8 引擎如同资深导演,在编译阶段悄悄调整变量位置(提升),又在执行阶段精准控制每个环节的时序,这种「预编译+即时执行」的双阶段模式,正是 JS 既能保持灵活性又能高效运行的核心秘密。
下篇预告 : 我们将深入解剖「变量提升的魔术」,通过作用域链的「寻宝地图」,揭示 var 与 let/const 在作用域迷宫中的不同命运。届时会通过「时间旅行调试法」,带你看穿函数执行上下文中的变量穿越现象,敬请期待这场关于「代码时空」的奇幻之旅!