【前端】【JavaScript】【总复习】四万字详解JavaScript知识体系
JavaScript 前端知识体系
📌 说明:本大纲从基础到高级、从语法到应用、从面试到实战,分层级讲解 JavaScript 的核心内容。
一、JavaScript 基础语法
1.1 基本概念
1.1.1 JavaScript 的发展史与用途
1. 发展简史
- 1995 年:JavaScript 由 Netscape 工程师 Brendan Eich 在 10 天 内创建,最初叫 LiveScript,后更名为 JavaScript。
- 早期用途:仅用于网页中的简单表单校验和交互动画。
- 1997 年:被 ECMA 国际标准化组织采纳,形成 ECMAScript 标准。
- 2009 年:Node.js 横空出世,JS 从浏览器进入服务端。
- 2015 年:ES6(ECMAScript 2015)发布,成为 JS 的重大飞跃。
2. 现代用途
JavaScript 已不再只是“网页脚本语言”,现在几乎无所不能:
- 浏览器交互开发(HTML+CSS+JS)
- Web 应用开发(React/Vue/Angular)
- 服务端开发(Node.js)
- 移动端开发(React Native)
- 桌面应用开发(Electron)
- 游戏开发(Pixi.js、Three.js)
- 自动化测试、爬虫、AI 前端可视化等
🎯 一句话总结:JS 是一门“浏览器起家、全栈统治、无处不在”的语言。
1.1.2 浏览器中的 JS 与 Node.js 的区别
特性 | 浏览器中的 JS | Node.js |
---|---|---|
运行环境 | 浏览器(Chrome、Firefox 等) | 服务端(基于 V8 引擎) |
核心目标 | 实现用户交互、DOM 操作 | 搭建 Web 服务、处理后端逻辑 |
可访问的 API | DOM、BOM(window、document)等 | 文件系统(fs)、网络模块(http) |
模块系统 | ES Module(ES6后支持) | CommonJS |
全局对象 | window | global |
适合应用场景 | 网页开发、浏览器插件 | 接口服务、工具脚本、构建工具等 |
🚀 一句话记忆:浏览器 JS 用于“看得见的交互”,Node.js 用于“看不见的服务”。
1.1.3 动态类型语言 vs 静态类型语言
1. 类型系统简介
- 类型:变量可以表示的数据类型,例如字符串、数字、布尔值等。
- 类型系统:语言如何检测、限制这些数据类型的规则体系。
2. 对比分析
特性 | 动态类型语言(如 JS) | 静态类型语言(如 Java、C++) |
---|---|---|
声明变量 | 不需指定类型,运行时才知道 | 声明时必须指定类型 |
类型检查 | 在运行时进行 | 在编译阶段进行 |
灵活性 | 高,变量可变类型 | 低,但可提升可靠性 |
出错时间点 | 运行时才报错 | 编译时就能发现类型错误 |
开发体验 | 快速开发,适合原型迭代 | 安全稳定,适合大型项目 |
3. 示例对比
// JS - 动态类型
let x = 10;
x = "hello"; // 合法,x 类型变为 string
// Java - 静态类型
int x = 10;
x = "hello"; // ❌ 报错,类型不匹配
💡 总结一句话:JS 是“你想放什么我都装”,Java 是“你不给我指定我不干活”。
1.2 数据类型
JavaScript 中的数据类型分为两大类:原始类型(Primitive Type) 和 引用类型(Reference Type)。
1.2.1 原始类型(7 种)
原始类型是不可变值,每个变量直接存储值本身,保存在栈内存中。
类型 | 示例值 | 说明 |
---|---|---|
String | "hello" | 表示文本 |
Number | 42 , 3.14 , NaN | 所有数字(包含整数和浮点数) |
Boolean | true , false | 逻辑值 |
Undefined | undefined | 未赋值变量的默认值 |
Null | null | 表示“无值” |
Symbol | Symbol("id") | 创建独一无二的标识符(ES6) |
BigInt | 12345678901234567890n | 表示任意精度整数(ES11) |
示例:
let name = "Alice"; // String
let age = 30; // Number
let isAdmin = true; // Boolean
let user; // Undefined
let empty = null; // Null
let key = Symbol("id"); // Symbol
let bigNumber = 123456789012345678901234567890n; // BigInt
🧠 注意:
typeof null === "object"
是历史遗留 bug,不代表 null 是引用类型!
1.2.2 引用类型(常见 5 类)
引用类型是可变对象,变量保存的是指向值的地址(引用),保存在堆内存中。
类型 | 示例 | 特点 |
---|---|---|
Object | {name: "Tom"} | 万物皆对象的基础类型 |
Array | [1, 2, 3] | 有序集合,索引访问 |
Function | function() {} | 可调用对象,函数是一等公民 |
Date | new Date() | 日期对象 |
RegExp | /\d+/ | 正则表达式对象 |
示例:
let person = { name: "Tom", age: 25 }; // Object
let numbers = [1, 2, 3]; // Array
let greet = function() { console.log("Hi"); }; // Function
let now = new Date(); // Date
let pattern = /\d+/; // RegExp
1.2.3 原始类型 vs 引用类型 对比总结
特性 | 原始类型 | 引用类型 |
---|---|---|
存储方式 | 栈内存,值拷贝 | 堆内存,引用拷贝(指针) |
是否可变 | 不可变(每次修改都创建新值) | 可变(修改对象本身) |
比较方式 | 值比较(===) | 引用地址比较 |
复制行为 | 复制值 | 复制引用,多个变量指向同一对象 |
示例比较:
let a = 100;
let b = a;
b = 200;
console.log(a); // 100(原始类型,值独立)let obj1 = { x: 1 };
let obj2 = obj1;
obj2.x = 99;
console.log(obj1.x); // 99(引用类型,地址共享)
📌 一句话总结:原始类型像“复制粘贴”,引用类型像“共享文件夹”。
1.3 变量声明
1.3.1 var
、let
、const
的区别
特性 | var | let | const |
---|---|---|---|
声明方式 | ES5,函数级作用域 | ES6,块级作用域 | ES6,块级作用域 |
变量提升 | ✅ 有提升,值为 undefined | ✅ 有提升但不初始化 | ✅ 有提升但不初始化 |
允许重复声明 | ✅ 允许 | ❌ 报错 | ❌ 报错 |
可重新赋值 | ✅ 可以 | ✅ 可以 | ❌ 不可重新赋值 |
是否必须初始化 | ❌ 不需要 | ❌ 不需要 | ✅ 必须初始化 |
示例:
// var
console.log(a); // undefined(已提升)
var a = 10;// let
console.log(b); // ReferenceError(暂时性死区)
let b = 20;// const
const c = 30;
c = 40; // ❌ 报错,不能重新赋值
🔒 记忆口诀:
var
会提升,let
会保护,const
定值不可改。
1.3.2 作用域与变量提升(Hoisting)
- 函数作用域(Function Scope):
var
声明的变量只在函数内部有效。 - 块级作用域(Block Scope):
let
和const
声明的变量只在当前代码块{}
内有效。
function test() {if (true) {var x = 10;let y = 20;}console.log(x); // ✅ 输出 10console.log(y); // ❌ 报错
}
- 变量提升(Hoisting):JavaScript 在运行前会“预处理”变量和函数声明,使它们“看起来”被提升到作用域顶部。
console.log(a); // undefined
var a = 5;
1.4 运算符与表达式
1.4.1 算术、比较、逻辑运算符
- 算术运算符:
+
-
*
/
%
**
- 比较运算符:
>
<
>=
<=
==
===
!=
!==
- 逻辑运算符:
&&
||
!
2 ** 3 // 8,幂运算
3 > 2 // true
true && false // false
1.4.2 其他运算符
- 三元运算符:
条件 ? 值1 : 值2
let result = score >= 60 ? "及格" : "不及格";
- 位运算符:
&
|
^
~
<<
>>
(适用于底层优化,如权限控制、性能压缩) - 空值合并运算符(
??
):仅在左值为null
或undefined
时使用右值。
let name = userName ?? "默认用户";
1.5 流程控制
1.5.1 条件判断
- if…else
if (score >= 90) {console.log("优秀");
} else if (score >= 60) {console.log("及格");
} else {console.log("不及格");
}
- switch…case
let color = "green";
switch (color) {case "red":console.log("红色");break;case "green":console.log("绿色");break;default:console.log("未知颜色");
}
1.5.2 循环结构
- for 循环
for (let i = 0; i < 3; i++) {console.log(i);
}
- while / do…while
let i = 0;
while (i < 3) {console.log(i);i++;
}do {console.log(i);i--;
} while (i > 0);
- for…in:遍历对象的键
let obj = { a: 1, b: 2 };
for (let key in obj) {console.log(key); // "a", "b"
}
- for…of:遍历可迭代对象的值(如数组、字符串)
for (let value of [10, 20, 30]) {console.log(value); // 10, 20, 30
}
🔁 记忆总结:
for...in
用来遍历“对象键名”,for...of
用来遍历“数组值”。
二、函数与作用域
2.1 函数定义方式
2.1.1 函数声明(Function Declaration)
function sayHi(name) {return `Hello, ${name}`;
}
- ✅ 支持提升:可以在函数声明之前调用
- ✅ 语义清晰,适合通用工具函数
- 📌 函数体内 this 指向调用者
greet(); // ✅ 输出 "Hi there!"function greet() {console.log("Hi there!");
}
2.1.2 函数表达式(Function Expression)
const sayHi = function(name) {return `Hello, ${name}`;
};
- ❌ 不支持提升:调用必须在定义之后
- ✅ 更灵活:可作为参数传递、闭包使用
- ✅ 命名 or 匿名:支持匿名函数
// 报错:Cannot access 'greet' before initialization
greet();const greet = function () {console.log("Hello");
};
2.1.3 箭头函数(Arrow Function)
const sayHi = (name) => {return `Hello, ${name}`;
};
- ✅ 语法简洁
- ❗ 没有自己的
this
、arguments
- ❌ 不能当构造函数使用
简写形式(单参数、单表达式可省略括号与 return):
const double = x => x * 2;
this 对比示例:
const obj = {normal: function() {console.log(this); // 指向 obj},arrow: () => {console.log(this); // 指向定义时外部的 this(可能是 window 或 undefined)}
};
🧠 一图记忆:
类型 | 是否提升 | 是否有 this | 是否能 new | 是否简洁 |
---|---|---|---|---|
函数声明 | ✅ | ✅ | ✅ | ❌ |
函数表达式 | ❌ | ✅ | ✅ | ❌ |
箭头函数 | ❌ | ❌(继承) | ❌ | ✅✅✅ |
2.2 作用域与闭包
2.2.1 词法作用域(Lexical Scope)
定义:词法作用域是指变量的作用范围由代码书写位置决定,而非函数的调用方式。
function outer() {const a = 10;function inner() {console.log(a); // 可以访问 a}inner();
}
outer();
- 📌 函数定义在哪里,就决定了它能访问哪些变量。
- ✅ 内部函数可以访问其外部函数作用域中的变量,这就是闭包的基础。
💡 记忆口诀:作用域查找看“定义位置”,不是“调用位置”。
2.2.2 闭包原理与应用场景
📘 什么是闭包?
闭包(Closure)是函数与其词法环境的组合。当函数在其定义的作用域外被调用时,它仍然能够记住并访问其定义时的作用域链。
function createCounter() {let count = 0;return function () {count++;return count;};
}const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
- 🔁
count
没有被销毁,因为返回的函数一直引用它,这就是闭包。 - 💡 JS 的函数是一等对象,可以被返回、赋值、传参,而闭包能让这些函数带上“记忆”。
📌 闭包的常见应用场景
场景 | 示例 |
---|---|
数据私有 | 封装内部变量不暴露到全局作用域 |
函数工厂 | 动态生成具有“私有数据”的函数 |
防抖节流 | 利用闭包保存定时器引用等状态 |
记忆函数 | 缓存函数执行结果(Memoization) |
// 数据私有:模拟私有变量
function Secret() {let secret = "密码123";return {get: () => secret,set: (val) => secret = val};
}
const s = Secret();
console.log(s.get()); // 密码123
🧠 一句话总结:闭包 = 函数 + 定义时的作用域链,是 JS 中实现“私有状态”和“记忆能力”的核心机制。
2.3 this 指向与 call/apply/bind
2.3.1 this 在不同上下文的指向规则
this
在 JavaScript 中非常灵活,它的指向是动态的,依赖于函数的调用方式。我们可以将它分为几种主要的调用方式进行分类讲解。
一、隐式绑定(Implicit Binding)
隐式绑定指的是 在对象的方法中调用函数时,this
指向调用该方法的对象。
const person = {name: "Alice",greet() {console.log(this.name);}
};
person.greet(); // "Alice"
- 在这个例子中,
this
指向person
对象。
二、显式绑定(Explicit Binding)
显式绑定是通过 call
、apply
和 bind
等方法显式地指定 this
的指向。
-
call()
:立即调用函数,传入this
指定的对象和后续参数。function greet() {console.log(`Hello, ${this.name}`); } const person = { name: "Bob" }; greet.call(person); // "Hello, Bob"
-
apply()
:类似call()
,但接收一个数组作为参数。greet.apply(person); // "Hello, Bob"
-
bind()
:返回一个新函数,绑定了this
,但不立即执行。const boundGreet = greet.bind(person); boundGreet(); // "Hello, Bob"
- 总结:
call
和apply
立即执行,而bind
返回一个新的函数。
三、默认绑定(Default Binding)
当函数被直接调用(例如普通函数调用时),this
的默认指向规则会根据执行环境有所不同:
- 在 非严格模式下,
this
会指向全局对象(浏览器中是window
,Node 中是global
)。 - 在 严格模式下,
this
为undefined
。
function greet() {console.log(this);
}greet(); // 在非严格模式下,指向 window;严格模式下,指向 undefined
四、构造函数绑定(Constructor Binding)
当函数作为构造函数通过 new
关键字调用时,this
会指向新创建的实例对象。
function Person(name) {this.name = name;
}const person1 = new Person("Alice");
console.log(person1.name); // "Alice"
new
关键字使得this
指向新创建的实例对象。
五、箭头函数(Arrow Function)
箭头函数没有自己的 this
,它会继承外层上下文的 this
。也就是说,箭头函数的 this
是在定义时就确定的,而不是在调用时确定。
const obj = {name: "Alice",greet() {const arrowFunc = () => {console.log(this.name);};arrowFunc(); // "Alice"}
};obj.greet(); // "Alice"
arrowFunc
的this
指向的是外部的greet
方法中的this
,即obj
。
🎯 总结:
调用方式 | this 指向 |
---|---|
隐式绑定 | 调用方法的对象 |
显式绑定 | call 、apply 、bind 参数指定的对象 |
默认绑定 | 非严格模式下为 window ,严格模式下为 undefined |
构造函数绑定 | 新创建的对象(实例) |
箭头函数 | 继承外部作用域的 this |
2.3.2 手动改变 this 指向的方法
有三个方法可以手动改变函数内部的 this 指向:
🧩 1)call()
立即调用函数,传入第一个参数作为 this
,后续参数依次传给函数本体。
function greet(who) {console.log(`Hello, ${who}, from ${this.name}`);
}
const person = { name: "Alice" };
greet.call(person, "Bob"); // Hello, Bob, from Alice
🧩 2)apply()
与 call
类似,但参数必须用数组传入:
greet.apply(person, ["Charlie"]); // Hello, Charlie, from Alice
🧩 3)bind()
不会立即调用,而是返回一个新的函数,绑定了 this
。
const boundGreet = greet.bind(person, "Diana");
boundGreet(); // Hello, Diana, from Alice
🎯 对比总结:
方法 | 是否立即执行 | 参数传递方式 | 返回值 |
---|---|---|---|
call | ✅ 是 | 普通参数列表 | 函数执行结果 |
apply | ✅ 是 | 参数数组 | 函数执行结果 |
bind | ❌ 否 | 参数列表(可预设) | 新函数 |
🧠 记忆口诀:
call
马上叫、apply
数组搞、bind
等你叫。
三、对象与原型链
3.1 对象创建与属性操作
3.1.1 对象的创建方式
-
字面量法
使用大括号{}
直接定义对象,简单直观。const person = {name: "Alice",age: 30 };
-
构造函数法
使用new Object()
或通过自定义构造函数创建对象。const person = new Object(); person.name = "Bob"; person.age = 25;
-
Object.create()
使用指定的原型对象创建一个新对象,适用于原型链继承。const personProto = {greet() {console.log(`Hello, ${this.name}`);} };const person = Object.create(personProto); person.name = "Charlie"; person.greet(); // Hello, Charlie
-
class
关键字
使用 ES6 中的class
语法创建对象及其构造函数。class Person {constructor(name, age) {this.name = name;this.age = age;} } const person = new Person("David", 28);
3.1.2 属性描述符与 Object.defineProperty()
属性描述符定义了对象属性的特性(如是否可写、可枚举等)。
- 数据描述符:包含
value
和writable
(是否可修改)。 - 访问器描述符:包含
get
和set
。
Object.defineProperty()
方法允许你直接设置属性的描述符,并能够控制属性的行为(例如是否能修改、是否能枚举等)。
const person = {};
Object.defineProperty(person, "name", {value: "Eve",writable: false, // 不可修改enumerable: true, // 可枚举configurable: true // 可配置
});console.log(person.name); // Eve
person.name = "John"; // 不会修改
console.log(person.name); // Eve
writable: false
使得name
属性不可修改。configurable: false
防止删除该属性或修改其特性。
3.2 原型与原型链
3.2.1 __proto__
vs prototype
-
prototype
每个函数对象都有一个prototype
属性,指向该函数的原型对象。构造函数的实例会继承该原型对象上的属性和方法。function Person(name) {this.name = name; }const person = new Person("Alice"); console.log(person.__proto__ === Person.prototype); // true
-
__proto__
__proto__
是每个对象的内部属性,指向该对象的构造函数的原型对象。它指示了对象的原型链的“父级”。const obj = {}; console.log(obj.__proto__ === Object.prototype); // true
prototype
是函数的属性,而__proto__
是对象的属性。
3.2.2 原型链查找机制
当访问对象的属性时,JS 引擎会先在对象自身查找,如果没有找到,再沿着原型链向上查找,直到找到该属性或到达 null
为止。
- 每个对象都有一个
__proto__
属性,它指向该对象的构造函数的原型对象。 - 原型对象也有
__proto__
,形成一个链条,最终链条的尽头是Object.prototype
,它的__proto__
为null
。
const obj = { name: "Alice" };
console.log(obj.name); // "Alice"
console.log(obj.toString()); // 调用 Object.prototype.toString
obj
没有toString
方法,它会向原型链上的Object.prototype
查找toString
方法。
3.3 继承方式
3.3.1 ES5 原型继承
在 ES5 中,原型继承是通过构造函数和 prototype
属性实现的。基本的继承方式是通过让子类的 prototype
指向父类的 prototype
。
function Animal(name) {this.name = name;
}Animal.prototype.sayHello = function() {console.log(`Hello, I am a ${this.name}`);
};function Dog(name) {Animal.call(this, name); // 继承属性
}Dog.prototype = Object.create(Animal.prototype); // 继承方法
Dog.prototype.constructor = Dog; // 修复构造函数指向const dog = new Dog("Buddy");
dog.sayHello(); // Hello, I am a Buddy
Object.create()
用于创建一个新的对象,将原型链指向父类的原型对象,从而实现继承。
3.3.2 组合继承
组合继承(又叫伪经典继承)是 构造函数继承 和 原型继承 的组合,它解决了原型继承的缺点:子类会继承父类的所有实例属性,但每个子类实例都会重复父类的实例属性。
function Animal(name) {this.name = name;
}Animal.prototype.sayHello = function() {console.log(`Hello, I am a ${this.name}`);
};function Dog(name, breed) {Animal.call(this, name); // 继承实例属性this.breed = breed;
}Dog.prototype = new Animal(); // 继承方法
Dog.prototype.constructor = Dog;const dog = new Dog("Buddy", "Golden Retriever");
dog.sayHello(); // Hello, I am a Buddy
console.log(dog.breed); // Golden Retriever
- 缺点:构造函数
Animal.call(this)
被调用了两次。new Animal()
会创建父类的实例,并将父类的属性赋给子类原型。
3.3.3 类似寄生继承的方式
寄生继承 是通过 借用构造函数 的方式继承父类的属性,但不改变原型链。
function Animal(name) {this.name = name;
}Animal.prototype.sayHello = function() {console.log(`Hello, I am a ${this.name}`);
};function Dog(name, breed) {Animal.call(this, name); // 继承属性this.breed = breed;
}Dog.prototype = Object.create(Animal.prototype); // 继承方法
Dog.prototype.constructor = Dog;const dog = new Dog("Buddy", "Golden Retriever");
dog.sayHello(); // Hello, I am a Buddy
console.log(dog.breed); // Golden Retriever
- 这个方式在继承父类方法时,依然保持了父类原型链的正确性,解决了构造函数重复调用的问题。
3.3.4 寄生组合继承
寄生组合继承(parasitic combination inheritance)是一种优化的方式,它结合了 寄生继承 和 组合继承 的优点。通过 Object.create()
继承父类的方法,并通过构造函数继承父类的实例属性,避免了重复调用父类构造函数。
function Animal(name) {this.name = name;
}Animal.prototype.sayHello = function() {console.log(`Hello, I am a ${this.name}`);
};function Dog(name, breed) {Animal.call(this, name); // 继承属性this.breed = breed;
}// 使用寄生组合继承来避免重复调用父类构造函数
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;const dog = new Dog("Buddy", "Golden Retriever");
dog.sayHello(); // Hello, I am a Buddy
console.log(dog.breed); // Golden Retriever
- 优化点:只调用一次父类的构造函数,通过
Object.create()
来继承父类的原型方法,避免了组合继承的性能问题。
3.3.5 ES6 class 继承 (extends
与 super
)
ES6 引入了 class
和 extends
语法,使得继承更加简洁和直观。super
用于调用父类的构造函数或方法。
class Animal {constructor(name) {this.name = name;}sayHello() {console.log(`Hello, I am a ${this.name}`);}
}class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed = breed;}bark() {console.log("Woof!");}
}const dog = new Dog("Buddy", "Golden Retriever");
dog.sayHello(); // Hello, I am a Buddy
dog.bark(); // Woof!
extends
用于实现继承。super
用于调用父类的构造函数或方法。
3.3 总结
继承方式 | 描述 | 优缺点 |
---|---|---|
原型继承 | 通过设置子类原型为父类原型的实例来实现继承 | 缺点:父类实例属性会被所有子类实例共享 |
组合继承 | 结合构造函数继承和原型继承 | 缺点:构造函数被调用两次 |
寄生继承 | 通过借用构造函数继承父类实例属性,不改变原型 | 适用于不需要创建新对象的场景 |
寄生组合继承 | 结合了寄生继承与组合继承的优点,优化了性能 | 解决了组合继承的缺点,避免了重复调用构造函数 |
ES6 class | 使用 class 和 extends 语法简化继承 | 语法简洁,易于理解,但仍然是基于原型链的继承 |
四、数组与内置对象
4.1 数组操作
4.1.1 创建与转换
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
Array.of(...items) | 新数组(包含所有参数) | 否 | 创建一个包含所有参数的数组 |
Array.from(obj) | 新数组(类数组转数组) | 否 | 将类数组或可迭代对象转换为数组 |
arr.toString() | 字符串(元素用逗号连接) | 否 | 将数组元素转换为字符串,用逗号分隔 |
arr.join(sep) | 字符串(自定义分隔符) | 否 | 将数组元素连接成字符串,使用指定的分隔符 |
4.1.2 增删元素
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
push(...items) | 新长度 | 是 | 在数组末尾添加一个或多个元素 |
pop() | 被删除的元素 | 是 | 删除并返回数组的最后一个元素 |
unshift(...items) | 新长度 | 是 | 在数组开头添加一个或多个元素 |
shift() | 被删除的元素 | 是 | 删除并返回数组的第一个元素 |
splice(start, del, ...items) | 被删除的元素数组 | 是 | 从指定位置删除指定数量的元素,并可以插入新元素 |
slice(start, end) | 新数组(部分元素) | 否 | 返回数组的一个浅拷贝(部分) |
4.1.3 查找与过滤
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
indexOf(item) | 首个匹配索引(-1 表示不存在) | 否 | 查找元素首次出现的位置 |
lastIndexOf(item) | 最后匹配索引(-1 表示不存在) | 否 | 查找元素最后一次出现的位置 |
includes(item) | 布尔值(是否包含) | 否 | 判断数组是否包含某个元素 |
find(fn) | 首个匹配元素(undefined 表示不存在) | 否 | 查找并返回第一个满足条件的元素 |
findIndex(fn) | 首个匹配索引(-1 表示不存在) | 否 | 查找并返回第一个满足条件的元素索引 |
filter(fn) | 新数组(所有匹配元素) | 否 | 返回一个包含所有满足条件元素的新数组 |
4.1.4 遍历与转换
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
forEach(fn) | undefined | 否(但可修改元素) | 遍历数组,执行给定的函数,适用于副作用操作 |
map(fn) | 新数组(每个元素处理后) | 否 | 返回一个新的数组,每个元素经过给定函数处理 |
flat(depth) | 新数组(扁平化后) | 否 | 将嵌套的数组结构“拉平”至指定深度 |
flatMap(fn) | 新数组(先 map 再 flat) | 否 | 先对数组进行 map 操作,再进行扁平化 |
4.1.5 排序与反转
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
sort(fn) | 原数组(排序后) | 是 | 对数组进行排序,默认按字符顺序排列 |
reverse() | 原数组(反转后) | 是 | 将数组元素的顺序颠倒 |
4.1.6 合并与拆分
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
concat(...arrays) | 新数组(合并后) | 否 | 合并多个数组或值到一个新数组 |
split(sep) | 字符串 → 数组 | 否(操作字符串) | 将字符串分割为数组 |
join(sep) | 数组 → 字符串 | 否 | 将数组元素连接成一个字符串,使用指定的分隔符 |
4.1.7 归约方法
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
reduce(fn, init) | 累计值 | 否 | 对数组元素执行累加操作,返回累计值 |
reduceRight(fn, init) | 累计值(从右到左) | 否 | 从右到左对数组元素执行累加操作,返回累计值 |
4.1.8 判定方法
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
every(fn) | 布尔值(所有元素满足) | 否 | 判断数组中的每个元素是否都满足条件 |
some(fn) | 布尔值(至少一个满足) | 否 | 判断数组中是否至少有一个元素满足条件 |
isArray(value) | 布尔值(是否为数组) | 否(静态方法) | 判断给定值是否为数组 |
4.1.9 其他方法
方法 | 返回值 | 是否修改原数组 | 作用 |
---|---|---|---|
fill(value, start, end) | 原数组(填充后) | 是 | 用指定的值填充数组的某部分元素 |
copyWithin(target, start, end) | 原数组(复制后) | 是 | 将数组中的一部分复制到同一数组的指定位置 |
entries() | 迭代器(索引-值对) | 否 | 返回一个数组的遍历器对象,包含索引和对应值 |
keys() | 迭代器(索引) | 否 | 返回一个包含数组索引的遍历器对象 |
values() | 迭代器(值) | 否 | 返回一个包含数组值的遍历器对象 |
记忆技巧
- 修改原数组的方法:
push/pop/unshift/shift/splice/sort/reverse/fill/copyWithin
(口诀:增删改查排序反转填充复制) - 返回新数组的方法:
slice/map/filter/concat/flat/flatMap
(口诀:切片映射过滤合并扁平化) - 归约与判定:
reduce/every/some/find/findIndex/includes
(口诀:累计判定查找包含)
4.2 常用内置对象
4.2.1 String 对象
1. 字符操作
方法 | 返回值 | 作用 |
---|---|---|
charAt(index) | 字符 | 返回指定位置的字符 |
charCodeAt(index) | 数字 | 返回指定位置字符的 Unicode 编码 |
codePointAt(index) | 数字 | 返回指定位置字符的 Unicode 代码点 |
fromCharCode(...codes) | 字符串 | 从 Unicode 编码返回字符 |
fromCodePoint(...codePoints) | 字符串 | 从 Unicode 代码点返回字符 |
2. 查找与替换
方法 | 返回值 | 作用 |
---|---|---|
includes(searchString) | 布尔值 | 判断字符串是否包含指定的子串 |
indexOf(searchValue) | 索引 | 返回子串首次出现的位置 |
lastIndexOf(searchValue) | 索引 | 返回子串最后一次出现的位置 |
match(regexp) | 数组 | 匹配正则表达式并返回结果 |
replace(searchValue, newValue) | 新字符串 | 替换匹配的子字符串 |
3. 大小写转换
方法 | 返回值 | 作用 |
---|---|---|
toLowerCase() | 小写字符串 | 返回将所有字符转换为小写的新字符串 |
toUpperCase() | 大写字符串 | 返回将所有字符转换为大写的新字符串 |
4. 切割与连接
方法 | 返回值 | 作用 |
---|---|---|
slice(start, end) | 子字符串 | 返回字符串的一个部分 |
split(separator) | 数组 | 按照指定分隔符拆分字符串 |
concat(...strings) | 字符串 | 连接多个字符串并返回新字符串 |
join(sep) | 字符串 | 数组元素连接为字符串 |
4.2.2 Date 对象
1. 获取日期与时间
方法 | 返回值 | 作用 |
---|---|---|
getFullYear() | 年份 | 返回完整的年份(4位) |
getMonth() | 月份 | 返回月份(0-11) |
getDate() | 日期 | 返回一个月中的日期(1-31) |
getDay() | 星期几 | 返回星期几(0-6,0为星期天) |
getHours() | 小时 | 返回小时(0-23) |
getMinutes() | 分钟 | 返回分钟(0-59) |
getSeconds() | 秒数 | 返回秒数(0-59) |
getMilliseconds() | 毫秒 | 返回毫秒数(0-999) |
2. 设置日期与时间
方法 | 返回值 | 作用 |
---|---|---|
setFullYear(year) | 设置年份 | 设置年份(4位) |
setMonth(month) | 设置月份 | 设置月份(0-11) |
setDate(day) | 设置日期 | 设置日期(1-31) |
setHours(hours) | 设置小时 | 设置小时(0-23) |
setMinutes(minutes) | 设置分钟 | 设置分钟(0-59) |
setSeconds(seconds) | 设置秒数 | 设置秒数(0-59) |
setMilliseconds(milliseconds) | 设置毫秒 | 设置毫秒(0-999) |
4.2.3 Math 对象
1. 数学常用方法
方法 | 返回值 | 作用 |
---|---|---|
Math.abs(x) | 数值 | 返回 x 的绝对值 |
Math.ceil(x) | 数值 | 返回大于或等于 x 的最小整数 |
Math.floor(x) | 数值 | 返回小于或等于 x 的最大整数 |
Math.round(x) | 数值 | 返回四舍五入后的值 |
Math.random() | 数值 | 返回一个 0 到 1 之间的随机数 |
Math.sqrt(x) | 数值 | 返回 x 的平方根 |
2. 极值与范围
方法 | 返回值 | 作用 |
---|---|---|
Math.max(...values) | 数值 | 返回一组数中的最大值 |
Math.min(...values) | 数值 | 返回一组数中的最小值 |
Math.pow(x, y) | 数值 | 返回 x 的 y 次方 |
Math.PI | 数值 | 返回圆周率常量 π |
4.2.4 JSON 对象
1. JSON 解析与字符串化
方法 | 返回值 | 作用 |
---|---|---|
JSON.parse(text) | 对象/数组 | 将 JSON 字符串解析为 JavaScript 对象 |
JSON.stringify(value) | JSON 字符串 | 将 JavaScript 对象转换为 JSON 字符串 |
4.2.5 RegExp 对象
1. 正则表达式方法
方法 | 返回值 | 作用 |
---|---|---|
test(str) | 布尔值 | 测试正则表达式是否匹配字符串 |
exec(str) | 数组 | 返回正则表达式与字符串匹配的结果(如果有) |
五、异步编程与事件机制
5.1 异步基础
5.1.1 同步 vs 异步
- 同步(Synchronous):任务按顺序逐行执行,前一个任务不完成,后一个任务无法开始。
- 异步(Asynchronous):某些操作可在“等待结果”的同时继续执行其他任务。
📌 示例:
console.log('A');
setTimeout(() => console.log('B'), 1000);
console.log('C');
// 输出顺序:A → C → B
5.1.2 回调函数与 Callback Hell
- 回调函数:将一个函数作为参数传给另一个函数,用于异步任务完成后执行。
- 回调地狱(Callback Hell):多个嵌套回调,导致代码结构混乱、难以维护。
📌 示例:
doSomething(function(result1) {doSomethingElse(result1, function(result2) {doThirdThing(result2, function(result3) {console.log('All done!');});});
});
5.2 Promise 与 async/await
5.2.1 Promise 构造与链式调用(进阶版)
Promise
是异步编程的核心机制,表示一个可能现在、将来或永不完成的值。- 状态只能从
pending
→fulfilled
或pending
→rejected
,不可逆。
✅ 一、创建 Promise 实例
const p = new Promise((resolve, reject) => {const data = getData();if (data) resolve(data);else reject(new Error("获取失败"));
});
resolve(value)
:表示成功,进入.then()
分支reject(error)
:表示失败,进入.catch()
分支
✅ 二、链式调用(then / catch / finally)
p.then(result => {console.log("成功:", result);return result + "!";
}).then(modified => {console.log("链式处理:", modified);
}).catch(err => {console.error("捕获错误:", err);
}).finally(() => {console.log("无论成功失败都会执行");
});
.then()
可以返回新值传递给下一个.then()
.catch()
捕获任意前面出现的错误.finally()
不处理值,仅用于收尾动作(如关闭 loading)
✅ 三、错误传播机制
若
.then()
中抛出错误,会直接被后面的.catch()
捕获。
p.then(() => {throw new Error("出错了");
}).catch(err => {console.log("捕获到错误", err.message);
});
✅ 四、Promise 嵌套与避免回调地狱
getUserInfo().then(user => {return getPostsByUser(user.id);
}).then(posts => {return getCommentsForPost(posts[0].id);
});
- 通过链式结构代替回调嵌套,实现逻辑扁平化
- 若返回的是一个新的 Promise,则自动等待其执行完成
✅ 五、常见错误使用案例(警示)
// ⚠️ 不要这样写
p.then(res => {doSomething(res, function(result) {// 回调地狱又来了});
});
应改写为:
p.then(res => doSomethingAsync(res)).then(next => console.log(next));
非常好,Promise 除了构造函数和链式调用外,还有一组非常实用的静态方法(类方法),适用于多个异步任务的管理与控制。以下是完整补充,按照清晰的结构归类呈现:
🔹 5.2.2 Promise 所有方法汇总
✅ 一、构造函数实例方法
方法 | 作用 | 特点 |
---|---|---|
new Promise(fn) | 创建一个新的 Promise 实例 | 传入 resolve 和 reject 两个函数参数 |
.then(onFulfilled) | 注册成功回调函数 | 支持链式调用 |
.catch(onRejected) | 注册失败回调函数 | 是 .then(null, onRejected) 的语法糖 |
.finally(fn) | 无论成功/失败都会执行 | 不影响返回值传递 |
✅ 二、静态方法(类方法)
1. Promise.resolve(value)
- 返回一个状态为
fulfilled
的 Promise - 如果传入的是一个 Promise,会直接返回
Promise.resolve(42).then(console.log); // 42
2. Promise.reject(error)
- 返回一个状态为
rejected
的 Promise - 通常用于封装异常
Promise.reject("失败").catch(console.error); // 失败
3. Promise.all([p1, p2, …])
- 等待所有 Promise 成功,才
resolve
,否则立即reject
- 返回值是所有结果的数组(按顺序)
Promise.all([p1, p2]).then(([r1, r2]) => {console.log(r1, r2);
});
🧠 常用于:并发请求,必须都成功
4. Promise.race([p1, p2, …])
- 谁先完成(成功或失败),就采用谁的结果
- 竞速场景:如加载动画 vs 请求超时
Promise.race([fetchData(),timeoutPromise(3000)
]).then(console.log).catch(console.error);
5. Promise.allSettled([p1, p2, …])
- 等待所有 Promise 都结束(无论成功或失败)
- 每一项返回
{ status, value }
或{ status, reason }
Promise.allSettled([p1, p2]).then(results => {results.forEach(r => console.log(r.status));
});
🧠 常用于:统计、批处理,不能因为一个失败而中断
6. Promise.any([p1, p2, …])
- 谁先成功就
resolve
,全部失败才reject
- ES2021 新增
Promise.any([Promise.reject("失败1"),Promise.resolve("成功"),Promise.reject("失败2")
]).then(console.log); // 输出:"成功"
🧠 常用于:只需一个成功即可,如镜像 CDN 请求
✅ 三、对比总结
方法 | 成功策略 | 失败策略 | 典型应用 |
---|---|---|---|
Promise.all | 全部成功 | 任意失败立即中止 | 并行任务且都要成功 |
Promise.race | 谁先返回 | 谁先返回 | 超时控制 |
Promise.allSettled | 不关心 | 不关心 | 全部结果分析 |
Promise.any | 任意一个成功即可 | 全部失败才失败 | 多镜像请求、降级处理 |
✨ 小贴士:手写模拟 Promise.all(核心思维训练)
function promiseAll(promises) {return new Promise((resolve, reject) => {let result = [], count = 0;promises.forEach((p, i) => {Promise.resolve(p).then(val => {result[i] = val;count++;if (count === promises.length) resolve(result);}).catch(reject);});});
}
5.2.3 async/await
async
声明函数返回一个 Promise。await
暂停异步函数执行,等待 Promise 结果。
📌 示例:
async function fetchData() {try {const data = await getData();console.log(data);} catch (error) {console.error('Error:', error);}
}
5.3 事件循环机制(Event Loop)
5.3.1 宏任务 vs 微任务
类型 | 示例 | 执行时机 |
---|---|---|
宏任务 | setTimeout 、setInterval 、I/O 、setImmediate (Node) | 每轮事件循环开始时调度 |
微任务 | Promise.then 、queueMicrotask 、MutationObserver (浏览器) | 当前宏任务执行完立即执行所有微任务 |
📌 执行顺序示例:
console.log('start');setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve().then(() => console.log('promise'));
queueMicrotask(() => console.log('microtask'));
console.log('end');// 输出:start → end → promise → microtask → setTimeout
5.3.2 浏览器中的事件循环流程
- 执行全局同步代码(主线程 → 调用栈)
- 执行所有微任务队列
- 执行一个宏任务队列中的任务
- 重复步骤 2 → 3,直到所有任务完成
5.3.3 Node.js 中的事件循环阶段
Node.js 的事件循环更复杂,包含 6 个阶段(基于 libuv):
阶段 | 描述 |
---|---|
timers | 执行 setTimeout 、setInterval 回调 |
pending callbacks | 执行一些系统操作的回调(如 TCP 错误) |
idle/prepare | 内部使用 |
poll | 处理 I/O 事件,如果没有则可能进入 check 阶段或等待 |
check | 执行 setImmediate() 的回调 |
close callbacks | 执行如 socket.on('close', fn) 等关闭回调 |
✅ 每个阶段之间都会清空微任务队列(process.nextTick & Promise)。
📌 微任务优先级:
process.nextTick > Promise.then > 宏任务(setTimeout、setImmediate)
📌 Node 示例:
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));// 输出顺序:nextTick → promise → timeout/immediate(取决于系统)
✅ 总结:执行顺序记忆口诀
- 浏览器中:同步 → 微任务 → 宏任务
- Node.js 中:同步 → nextTick → Promise → 各阶段宏任务
💡提示:Node.js 中的
setImmediate
可能比setTimeout(fn, 0)
更快执行,但不保证一致顺序。
六、DOM 与 BOM 操作
6.1 DOM 基础(全面分类)
6.1.1 节点获取与遍历
方法 | 描述 | 返回类型 |
---|---|---|
getElementById(id) | 根据 ID 获取节点 | 单个元素 |
getElementsByClassName(class) | 根据类名获取 | 类数组 |
getElementsByTagName(tag) | 根据标签名获取 | 类数组 |
querySelector(selector) | CSS 选择器,获取首个匹配节点 | 单个元素 |
querySelectorAll(selector) | CSS 选择器,获取全部匹配节点 | NodeList(类数组) |
parentNode / childNodes / nextSibling | DOM 树节点关系遍历 | 节点对象 |
6.1.2 节点创建、插入、删除、克隆
操作 | 方法 | 示例 |
---|---|---|
创建元素 | createElement(tag) | let div = document.createElement('div') |
插入 | appendChild() 、append() 、insertBefore() | parent.appendChild(child) |
删除 | removeChild() 、remove() | parent.removeChild(el) |
替换 | replaceChild(newEl, oldEl) | 替换节点 |
克隆 | cloneNode(true) | 复制节点 |
6.1.3 节点内容与属性操作
操作 | 方法 / 属性 | 示例 |
---|---|---|
获取/设置文本 | innerText / textContent | el.textContent = 'Hello' |
获取/设置 HTML | innerHTML | el.innerHTML = '<b>Hi</b>' |
获取/设置属性 | getAttribute() / setAttribute() | el.setAttribute('href', '#') |
移除属性 | removeAttribute() | el.removeAttribute('title') |
操作类名 | classList.add/remove/toggle/contains | el.classList.toggle('show') |
操作样式 | style.property | el.style.color = 'red' |
6.1.4 元素位置与尺寸获取
属性 / 方法 | 描述 | 示例 |
---|---|---|
offsetTop / offsetLeft | 元素相对 offsetParent 的偏移 | el.offsetTop |
offsetWidth / offsetHeight | 包含 padding 和 border | el.offsetHeight |
clientWidth / clientHeight | 包含 padding 不含 border | el.clientWidth |
scrollTop / scrollLeft | 滚动距离 | el.scrollTop |
getBoundingClientRect() | 获取元素相对视口的位置与尺寸 | el.getBoundingClientRect() |
6.1.5 常用事件分类
📌 一)鼠标事件
事件名 | 说明 |
---|---|
click / dblclick | 点击 / 双击 |
mousedown / mouseup | 按下 / 弹起 |
mousemove | 鼠标移动 |
mouseenter / mouseleave | 进入 / 离开元素(不冒泡) |
mouseover / mouseout | 进入 / 离开(冒泡) |
contextmenu | 右键菜单 |
📌 二)键盘事件
事件名 | 说明 |
---|---|
keydown | 键盘按下 |
keyup | 键盘松开 |
keypress | 输入字符(已废弃) |
📌 三)表单事件
事件名 | 说明 |
---|---|
submit | 表单提交 |
change | 表单值变化(如 select) |
input | 输入变化(推荐用于 text 输入) |
focus / blur | 聚焦 / 失焦 |
📌 四)窗口事件
事件名 | 说明 |
---|---|
load | 页面加载完成 |
resize | 窗口尺寸变化 |
scroll | 页面或元素滚动 |
beforeunload | 页面关闭前 |
6.1.6 事件监听与代理
操作 | 示例 |
---|---|
绑定事件 | el.addEventListener('click', fn) |
移除事件 | el.removeEventListener('click', fn) |
阻止默认行为 | event.preventDefault() |
阻止冒泡 | event.stopPropagation() |
事件代理 | 绑定父级,判断 e.target |
list.addEventListener('click', e => {if (e.target.tagName === 'LI') {console.log('点击了第', e.target.innerText);}
});
6.1.7 拖拽与监听移动
功能 | 方法 |
---|---|
拖拽事件 | dragstart / dragover / drop |
鼠标监听移动 | 结合 mousedown 、mousemove 、mouseup 实现 |
示例伪代码实现拖动:
let isDragging = false;
el.onmousedown = () => isDragging = true;
document.onmousemove = (e) => {if (isDragging) el.style.left = e.clientX + 'px';
};
document.onmouseup = () => isDragging = false;
6.2 事件模型(浏览器)
6.2.1 事件传播机制:捕获 & 冒泡
事件传播分为 三个阶段:
阶段顺序 | 阶段名称 | 描述 |
---|---|---|
① | 捕获阶段(Capture Phase) | 从 window 自顶向下,沿着 DOM 树传递到目标元素 |
② | 目标阶段(Target Phase) | 实际目标元素上触发的事件 |
③ | 冒泡阶段(Bubble Phase) | 从目标元素沿 DOM 树向上传递回 window |
📌 示例:
element.addEventListener('click', handler, true); // 第三个参数 true 表示捕获阶段监听
element.addEventListener('click', handler, false); // false 表示冒泡阶段监听(默认)
6.2.2 阻止事件传播
方法 | 作用 |
---|---|
event.stopPropagation() | 阻止事件继续冒泡或捕获 |
event.stopImmediatePropagation() | 阻止同一元素上后续所有事件监听器执行 |
event.preventDefault() | 阻止默认行为(如表单提交、a 链接跳转) |
6.2.3 自定义事件(CustomEvent)
用于手动触发和传递自定义数据的事件。
1️⃣ 创建并触发事件:
const event = new CustomEvent('myEvent', {detail: { name: 'ChatGPT', level: 99 }
});
element.dispatchEvent(event);
2️⃣ 监听事件:
element.addEventListener('myEvent', function(e) {console.log(e.detail.name); // 输出:ChatGPT
});
6.2.4 事件对象 Event
当事件触发时,监听函数会自动接收一个事件对象:
属性/方法 | 描述 |
---|---|
event.target | 实际触发事件的元素 |
event.currentTarget | 当前绑定事件的元素 |
event.type | 事件类型(如 click) |
event.timeStamp | 触发事件的时间戳 |
event.defaultPrevented | 是否已调用 preventDefault() |
event.bubbles | 该事件是否支持冒泡 |
event.cancelable | 是否可以取消默认操作 |
6.2.5 补充:事件委托(推荐实践)
通过把事件绑定在父节点上,提高性能和可维护性。
ul.addEventListener('click', function(e) {if (e.target.tagName === 'LI') {console.log('点击了:', e.target.innerText);}
});
✅ 好处:
- 减少事件监听数量
- 支持动态添加子元素的事件响应
6.2.6 事件绑定优先级顺序
在浏览器中,当同一元素绑定了多种事件方式,它们的触发顺序如下:
✅ 优先级顺序(由高到低):
- 内联绑定(HTML 属性):如
<button onclick="alert(1)">
- DOM0 绑定(传统方式):如
element.onclick = fn
- DOM2 绑定(推荐方式):如
element.addEventListener('click', fn)
⚠️ 示例说明:
<button id="btn" onclick="console.log('inline')">点击</button>
const btn = document.getElementById('btn');
btn.onclick = () => console.log('DOM0');
btn.addEventListener('click', () => console.log('DOM2'));
输出顺序:
inline
DOM0
DOM2
6.2.7 DOM 0 / DOM 2 级事件模型差异
比较项 | DOM 0 级事件(传统) | DOM 2 级事件(标准) |
---|---|---|
绑定方式 | element.onclick = fn | addEventListener('click', fn, useCapture) |
是否支持多个监听器 | ❌ 只能绑定一个 | ✅ 可绑定多个 |
是否支持捕获阶段 | ❌ 不支持 | ✅ 支持(通过第三个参数) |
是否兼容 IE 低版本 | ✅ | IE9+ |
是否标准推荐 | ❌ | ✅ W3C 推荐标准方式 |
6.2.8 React 合成事件机制(SyntheticEvent)
React 并不直接绑定原生事件,而是通过自己的事件系统(合成事件)实现更高效的管理。
📌 特点:
特性 | 描述 |
---|---|
合成封装 | 对原生事件进行封装,统一不同浏览器差异 |
自动绑定 | 自动使用事件委托,绑定在根节点(提高性能) |
统一池化 | 使用事件池提升性能(v17 前),需注意事件异步访问 |
命名风格 | 使用驼峰命名:如 onClick 、onChange |
✅ 示例:
<button onClick={handleClick}>点击</button>function handleClick(e) {console.log(e.nativeEvent); // 原生事件console.log(e); // 合成事件
}
⚠️ 注意事项:
- 异步中访问事件属性需要调用
e.persist()
(在 React17 以前) - React 17+ 不再使用事件池,不再需要
e.persist()
6.3 BOM 操作
6.3.1 window
对象
- 浏览器的全局对象,所有全局变量和函数都是其属性或方法。
- 也是 BOM 的顶层对象。
常见属性/方法:
属性/方法 | 作用 |
---|---|
window.innerWidth / innerHeight | 获取窗口内容区域的宽/高(不含滚动条) |
window.open(url) | 打开新窗口或标签页 |
window.alert() / confirm() / prompt() | 浏览器弹窗 |
window.scrollTo(x, y) | 滚动到指定位置 |
6.3.2 location
对象
- 用于获取或修改当前页面的 URL。
属性/方法 | 说明 |
---|---|
location.href | 当前完整 URL,可读取或赋值跳转 |
location.protocol | 协议,如 https: |
location.host / hostname / port | 主机、主机名、端口号 |
location.pathname | 路径部分 |
location.search | 查询字符串(如 ?id=1 ) |
location.reload() | 重新加载页面 |
location.assign(url) | 跳转到新 URL(有历史记录) |
location.replace(url) | 替换当前页面(无历史记录) |
6.3.3 navigator
对象
- 描述用户浏览器的信息。
属性 | 说明 |
---|---|
navigator.userAgent | 浏览器/设备详细信息 |
navigator.platform | 操作系统平台(如 Win32) |
navigator.language | 当前浏览器语言 |
navigator.onLine | 当前是否联网(布尔值) |
6.3.4 history
对象
- 用于操作浏览器历史记录。
方法 | 说明 |
---|---|
history.back() | 返回上一页(等同于点击后退) |
history.forward() | 前进一页 |
history.go(n) | 前进或后退 n 步 |
history.pushState(state, title, url) | 添加历史记录(不会刷新页面) |
history.replaceState(...) | 替换当前历史记录 |
注意:
pushState
和replaceState
是 HTML5 的新特性,常用于 SPA 前端路由。
6.3.5 定时器:setTimeout
与 setInterval
方法 | 作用 | 返回值 |
---|---|---|
setTimeout(fn, delay) | 延迟执行一次 | 返回定时器 ID |
setInterval(fn, delay) | 每隔一段时间重复执行 | 返回定时器 ID |
clearTimeout(id) | 取消 setTimeout | |
clearInterval(id) | 取消 setInterval |
示例:
const id = setTimeout(() => console.log("一次性延迟"), 1000);
clearTimeout(id);const loopId = setInterval(() => console.log("每秒执行"), 1000);
clearInterval(loopId);
⚠️ 注意内存泄漏风险:组件销毁/页面离开应清理定时器。
好的,以下是对 6.3 BOM 操作 的进一步补充,涵盖浏览器窗口通信、全局错误处理,以及实用 Web API 技巧,帮助你建立更加系统的 JavaScript 浏览器编程知识体系。
6.3.6 浏览器窗口通信:postMessage
✅ 场景:
- 不同窗口/iframe 之间传递数据(甚至跨域);
- 通常用于父页面与子页面之间的数据通信。
💡 基本语法:
// 发送方(通常是父窗口或 iframe)
otherWindow.postMessage(message, targetOrigin);// 接收方
window.addEventListener("message", function(event) {// event.data 是传递过来的数据// event.origin 是消息来源的域名
}, false);
✅ 参数说明:
参数 | 说明 |
---|---|
message | 发送的数据(可以是对象) |
targetOrigin | 接收方的 origin(例如:"https://example.com")用于安全校验-w41hk4oxqyc3z0h1aq79f/) |
📌 示例:
// 子页面向父页面发送消息
window.parent.postMessage({ type: "resize", height: 600 }, "https://yourdomain.com");// 父页面接收子页面消息
window.addEventListener("message", (event) => {if (event.origin !== "https://yourdomain.com") return; // 安全校验console.log("子页面消息:", event.data);
});
6.3.7 全局错误处理机制
✅ window.onerror
用于捕获运行时错误,防止页面崩溃时无反馈。
window.onerror = function (message, source, lineno, colno, error) {console.error("捕获错误:", message, "位置:", source, lineno, colno);// 可上传日志服务器return true; // 阻止默认报错行为
};
✅ window.addEventListener('error')
更强大,可以捕获资源加载错误(如图片、脚本加载失败):
window.addEventListener("error", function (e) {if (e.target instanceof HTMLImageElement) {console.warn("图片加载失败:", e.target.src);}
}, true); // 第三个参数设为 true 才能捕获资源加载错误
✅ window.addEventListener('unhandledrejection')
用于捕获未被 .catch()
捕获的 Promise 错误:
window.addEventListener("unhandledrejection", (event) => {console.error("未处理的 Promise 错误:", event.reason);
});
6.3.8 Web API 实用技巧
✅ 剪贴板 API
// 复制文本到剪贴板
navigator.clipboard.writeText("复制的内容").then(() => alert("已复制")).catch(err => console.error("复制失败", err));// 读取剪贴板内容(需 HTTPS 环境+用户触发)
navigator.clipboard.readText().then(text => console.log("读取到剪贴板内容:", text));
⚠️ 注意安全性:大多浏览器要求用户手势触发(如点击)
✅ 页面可见性 API(Page Visibility)
判断页面是否处于活跃(当前标签页是否可见),适合用于:
- 暂停动画、视频播放;
- 控制数据轮询行为等。
document.addEventListener("visibilitychange", () => {if (document.visibilityState === "hidden") {console.log("页面不可见,暂停轮询");} else {console.log("页面可见,恢复轮询");}
});
✅ 屏幕尺寸与滚动监听
// 获取滚动位置
window.scrollY; // 垂直滚动距离
window.scrollX; // 水平滚动距离// 监听页面滚动
window.addEventListener("scroll", () => {console.log("滚动中...", window.scrollY);
});
七、模块化与工具链
7.1 模块化发展历程
模块化是前端工程化的核心。它的演化反映了前端开发复杂度的提升和工具生态的进化。
7.1.1 IIFE(立即执行函数表达式)
最原始的模块化方式,用闭包封装变量避免污染全局作用域。
(function () {var name = "模块内部变量";console.log(name);
})();
- ✅ 优点:避免全局变量污染
- ❌ 缺点:无模块复用能力、缺乏依赖管理
7.1.2 CommonJS(Node.js 中使用)
// a.js
module.exports = {sayHi: () => console.log("Hi"),
};// b.js
const a = require("./a.js");
a.sayHi();
- ✅ 特点:同步加载,适用于服务器端
- ❌ 浏览器不支持,需要打包工具(如 Webpack)转换
7.1.3 AMD(Asynchronous Module Definition)
浏览器端模块化规范,代表库:RequireJS
define(["moduleA"], function (moduleA) {moduleA.doSomething();
});
- ✅ 特点:异步加载,适合浏览器
- ❌ 缺点:语法繁琐、可读性差
7.1.4 UMD(Universal Module Definition)
兼容 CommonJS、AMD 和浏览器全局变量
(function (root, factory) {if (typeof define === "function" && define.amd) {define([], factory);} else if (typeof exports === "object") {module.exports = factory();} else {root.myModule = factory();}
})(this, function () {return {};
});
7.1.5 ES6 模块化(现代主流)
// module.js
export const name = "JS模块";
export default function greet() {console.log("Hello ES Module");
}// main.js
import greet, { name } from "./module.js";
greet();
- ✅ 静态加载、编译时可分析依赖
- ✅ 浏览器原生支持(需 type=“module”)
- ✅ 与打包工具完美结合
7.2 前端开发工具链概览
从代码撰写 → 转译兼容 → 打包构建 → 代码规范 → 性能优化,全流程涉及以下关键工具:
7.2.1 构建与打包工具
工具 | 主要用途 | 特点 |
---|---|---|
Webpack | 模块打包器 | 配置灵活、插件强大、学习曲线略高 |
Vite | 新一代构建工具 | 极速启动、基于原生 ESM、现代开发优选 |
Rollup | 打包库的首选工具 | 构建体积小,Tree-shaking 效果好 |
7.2.2 代码转译与语法支持
工具 | 作用 |
---|---|
Babel | 将 ES6+ 代码转为向后兼容的 JavaScript |
TypeScript | 增加类型系统、增强开发体验 |
7.2.3 代码质量与风格规范
工具 | 用途 |
---|---|
ESLint | 静态代码检查,防止潜在错误 |
Prettier | 统一代码格式,提升团队协作效率 |
✅ 推荐配合 IDE 插件 + Git Hooks 实现自动检查 + 修复
7.2.4 常见工具集成方式
- 项目初始化:
npm init vite@latest
/create-react-app
- 构建命令:
npm run build
- 开发模式:
npm run dev
(通常开启热更新) - 检查格式:
eslint src/
、prettier --write .
🔧 补充建议
✅ 强烈建议配合 husky + lint-staged 实现提交前校验:
npx husky-init && npm install
npx husky add .husky/pre-commit "npx lint-staged"
八、ES6+ 新特性
ES6 是 ECMAScript 的重大升级版本,后续 ES7+ 持续增强语法和内置能力,使 JavaScript 更现代、更强大。以下按功能模块系统归纳。
8.1 语法增强与数据结构
8.1.1 解构赋值
快速提取对象或数组中的值
const { name, age } = person;
const [a, b] = [1, 2];
- ✅ 默认值、嵌套解构
- ✅ 可用于函数参数
8.1.2 模板字符串
多行字符串 & 插值表达式
const msg = `Hello, ${user.name}!`;
8.1.3 扩展与收集运算符(...
)
-
展开:将数组/对象拆开
const arr2 = [...arr1]; const obj2 = { ...obj1 };
-
收集:函数剩余参数
function fn(...args) {}
8.1.4 Symbol(独一无二的值)
创建唯一键,适合定义私有属性
const sym = Symbol('key');
obj[sym] = 'value';
8.1.5 Set & Map(全新数据结构)
特性 | Set | Map |
---|---|---|
存储 | 值的集合 | 键值对 |
是否重复 | 否 | 否(键唯一) |
常用方法 | add , has , delete | set , get , has , delete |
const s = new Set([1, 2, 2]); // 去重
const m = new Map([["a", 1]]);
8.1.6 可迭代对象与 for…of
可使用
for...of
、...展开符
的对象:Array、Map、Set、字符串等
for (const item of set) {}
8.2 Class 与模块系统
8.2.1 class
类定义与继承
class Person {constructor(name) {this.name = name;}greet() {console.log(`Hi, ${this.name}`);}
}
- ✅
extends
实现继承 - ✅
super()
调用父类构造器 - ✅ 私有属性:
#privateName
8.2.2 模块系统(import
/ export
)
// 导出
export const name = "Tom";
export default function greet() {}// 导入
import greet, { name } from "./module.js";
- 支持:默认导出 / 命名导出 / 重命名 / 整体导入
8.3 常用内置 API 与语法糖
8.3.1 新增 API
方法 | 作用 |
---|---|
Object.entries(obj) | 返回键值对数组 |
Object.values(obj) | 返回值数组 |
Array.flat(depth) | 扁平化嵌套数组 |
Array.includes(val) | 判断是否包含 |
Promise.allSettled() | 所有 Promise 返回后统一处理,无论成功失败 |
8.3.2 Nullish Coalescing(??
)
只有
null
或undefined
才触发右侧默认值
const val = input ?? "默认值";
- ❌ 不会因
''
或0
而触发
8.3.3 Optional Chaining(?.
)
安全读取深层属性,避免报错
const city = user?.address?.city;
🔍 记忆建议:
- ✅ 解构 & 展开:多写函数参数和对象操作
- ✅ Symbol:对象私有成员
- ✅ Set/Map:处理去重与映射优于 Object
- ✅ ?? 与 ?.:null 安全写法,高频出现于项目中
九、浏览器通信与网络
现代前端开发离不开浏览器与服务器之间的高效通信,本章将系统梳理 AJAX、跨域机制、本地存储方案与 Web 通信能力。
9.1 浏览器请求方式
9.1.1 XMLHttpRequest 基础(XHR)
- 原始的 AJAX 技术核心
- 支持事件监听与状态码处理
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {console.log(xhr.responseText);}
};
xhr.send();
9.1.2 Fetch API
更现代的异步请求方式,基于 Promise
fetch('/api/data').then(res => res.json()).then(data => console.log(data)).catch(err => console.error(err));
- ✅ 更简洁
- ✅ 默认不携带 cookie(需配置)
- ✅ 不会自动 reject 4xx/5xx(需手动判断
res.ok
)
9.1.3 封装 Fetch 常用方法
async function request(url, options = {}) {const res = await fetch(url, options);if (!res.ok) throw new Error('网络错误');return res.json();
}
9.2 跨域处理策略
9.2.1 同源策略
协议 + 域名 + 端口号 三者相同才算同源
不同源会受到限制(如 DOM 操作、AJAX 请求)
9.2.2 解决方式对比
方法 | 原理 | 优缺点 |
---|---|---|
CORS | 设置响应头 Access-Control-Allow-Origin | 推荐,标准方案 |
JSONP | 利用 <script> 标签不受同源限制 | 仅支持 GET,请求安全性差 |
代理转发 | 本地服务器转发请求,绕过浏览器限制 | 需后端支持,常见于开发环境 |
PostMessage | 窗口间通信(iframe 或新窗口) | 跨域数据安全通信 |
9.3 本地存储方式对比
9.3.1 Cookie
- 每次请求自动携带,常用于身份验证
- 有大小限制(~4KB)
- 支持设置过期时间、路径、域
document.cookie = "token=123;path=/;max-age=3600";
9.3.2 localStorage
- 永久存储(直到手动清除)
- 单域名下最大约 5MB
- 仅支持字符串
localStorage.setItem("key", "value");
localStorage.getItem("key");
9.3.3 sessionStorage
- 页面会话级别,标签页关闭即清除
- API 与 localStorage 相同
✅ 对比总结:
特性 | Cookie | localStorage | sessionStorage |
---|---|---|---|
是否随请求发送 | ✅ | ❌ | ❌ |
生命周期 | 可设置 | 永久 | 会话 |
大小限制 | 4KB | 5MB+ | 5MB+ |
跨标签页共享 | ✅ | ✅ | ❌ |
9.4 实用通信机制补充
9.4.1 浏览器窗口通信:postMessage
用于主窗口与 iframe / 子窗口 / 弹窗间安全通信
window.postMessage("数据", "https://other.com");window.addEventListener("message", (event) => {console.log(event.origin, event.data);
});
9.4.2 全局错误监听
- 捕获 JS 报错信息,便于上报与监控
window.onerror = function (msg, url, line, col, error) {console.error('捕获错误:', msg, error);
};window.addEventListener('error', (event) => {console.log('资源加载错误:', event.target);
});
9.4.3 实用 Web API 技巧
API | 功能 | 示例 |
---|---|---|
navigator.clipboard.writeText() | 写入剪贴板 | 复制文本 |
document.visibilityState | 页面可见性检测 | visibilitychange 监听 |
navigator.onLine | 网络状态检测 | 离线/在线判断 |
Performance API | 页面性能分析 | performance.now() |
好的,下面是第九章:浏览器通信与网络的进一步补充,涵盖更深入的网络通信机制及相关 API,包括 WebSocket、服务端事件(SSE)、网络状态检测、离线缓存等内容。
9.5 实时通信技术
9.5.1 WebSocket 长连接
一种在单个 TCP 连接上进行全双工通信的协议,适合实时聊天、推送通知等场景。
使用示例:
const socket = new WebSocket('wss://example.com/socket');socket.onopen = () => {socket.send('Hello Server!');
};socket.onmessage = (event) => {console.log('收到消息:', event.data);
};socket.onerror = (err) => console.error('连接错误:', err);
socket.onclose = () => console.log('连接关闭');
- ✅ 持久连接,适合高频交互
- ❌ 需服务端配套支持,维护成本高
9.5.2 Server-Sent Events(SSE)
浏览器从服务端接收单向推送数据(基于 HTTP)
const source = new EventSource('/api/events');source.onmessage = function (event) {console.log('服务端推送:', event.data);
};
- ✅ 简洁、自动重连、支持事件命名
- ❌ 只支持单向、部分浏览器支持
9.6 网络状态与页面生命周期
9.6.1 网络状态检测
console.log(navigator.onLine); // true or falsewindow.addEventListener('online', () => console.log('网络恢复'));
window.addEventListener('offline', () => console.log('网络断开'));
9.6.2 页面可见性检测
document.addEventListener('visibilitychange', () => {if (document.visibilityState === 'hidden') {console.log('页面隐藏');} else {console.log('页面可见');}
});
- 可用于暂停动画、视频、轮询请求
9.7 离线缓存与存储技术
9.7.1 Cache API(Service Worker)
配合 Service Worker 实现离线缓存、离线访问页面内容
caches.open('v1').then(cache => {cache.addAll(['/index.html', '/styles.css']);
});
- 与 fetch 结合,可拦截请求并返回缓存
- 需 HTTPS 环境下注册 Service Worker
9.7.2 IndexedDB
浏览器提供的事务型数据库,可存储结构化数据
const request = indexedDB.open('myDB', 1);
request.onsuccess = (event) => {const db = event.target.result;console.log('数据库打开成功');
};
- 支持索引、事务、高容量数据
- 常用于大型离线 Web 应用(PWA)
9.8 网络调优与安全
9.8.1 请求优化技巧
- 开启
Keep-Alive
复用连接 - 使用 CDN 缓存静态资源
- 图片懒加载 + 资源压缩
- 合理使用缓存头部(Cache-Control、ETag)
9.8.2 网络安全机制
- HTTPS 加密传输
- 防 XSS/CSRF 攻击
- 输入校验与输出转义
- 设置
Content-Security-Policy
安全策略头
十、性能优化与安全
10.1 性能优化
10.1.1 资源懒加载
将不在视口内的资源延迟加载,减少初始加载时间。
- 图片懒加载:
<img data-src="image.jpg" class="lazyload" alt="Lazy Load Image">
- JavaScript 懒加载:
const script = document.createElement('script');
script.src = 'path/to/your/script.js';
document.body.appendChild(script);
- 适用场景: 图片、视频、JavaScript 等
10.1.2 事件节流与防抖
节流 (Throttling) 和 防抖 (Debouncing) 用于控制频繁触发的事件,如滚动、输入等。
- 防抖:
function debounce(fn, delay) {let timer;return function (...args) {clearTimeout(timer);timer = setTimeout(() => fn(...args), delay);};
}
- 节流:
function throttle(fn, delay) {let last = 0;return function (...args) {const now = Date.now();if (now - last >= delay) {last = now;fn(...args);}};
}
10.1.3 DOM 优化
- 减少 DOM 操作: 批量更新、避免频繁重排与重绘(例如使用
DocumentFragment
) - 缓存查询结果: 存储对 DOM 的查询结果,避免重复查找
const button = document.getElementById('myButton');
// 使用变量缓存 DOM 查找
10.1.4 异步加载与延迟加载
- 异步加载: 使用
async
和defer
属性来异步加载 JavaScript 文件,避免阻塞渲染。
<script src="script.js" async></script>
<script src="script.js" defer></script>
- 分块加载: 使用 Webpack 等工具进行代码分块,按需加载,提升初次加载速度。
10.1.5 缓存策略
缓存能够显著提高性能,减少重复的网络请求。
- Service Worker + Cache API: 在浏览器端缓存资源,离线访问。
- HTTP 缓存: 使用
Cache-Control
和ETag
头部进行缓存控制。
// 在 Service Worker 中缓存请求资源
caches.open('v1').then((cache) => {cache.add('/index.html');
});
- LocalStorage / IndexedDB: 在本地存储应用数据,避免频繁请求数据。
10.1.6 图片与视频优化
- 图片格式优化: 使用现代格式(如 WebP)来减少图片大小。
- SVG 图标替代位图图标:SVG 比 PNG/JPG 更加灵活且文件更小。
- 懒加载: 只在用户滚动到页面时才加载图片。
10.2 前端安全
10.2.1 XSS(跨站脚本攻击)
攻击者通过注入恶意脚本,窃取用户数据或篡改网页内容。
- 防御方法:
- 输入验证与输出编码: 对用户输入进行严格的验证和转义,避免脚本执行。
- 使用 CSP(内容安全策略) 来限制加载的脚本源。
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
- 避免内联脚本: 使用外部脚本文件,禁止内联 JavaScript 代码。
10.2.2 CSRF(跨站请求伪造)
攻击者伪造用户请求,造成未授权操作。
- 防御方法:
- Token 验证: 每次提交请求时,附加一个唯一的 CSRF Token(在表单或请求头中传递),服务器验证是否匹配。
- SameSite Cookies: 使用
SameSite
属性,限制 Cookie 在跨站请求时是否随请求一起发送。
document.cookie = "token=your_token; SameSite=Strict;";
10.2.3 Clickjacking(点击劫持)
攻击者通过透明 iframe 诱使用户点击其并非意图点击的内容。
- 防御方法:
- 使用
X-Frame-Options
头部阻止页面被嵌套在 iframe 中。
- 使用
<meta http-equiv="X-Frame-Options" content="DENY">
10.2.4 内容安全策略(CSP)
CSP 是一种防止 XSS 攻击的安全机制,限制浏览器加载资源的源。
- 基本 CSP 配置:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline';">
- 策略设计:
default-src
:指定默认的加载源script-src
:限制脚本的来源style-src
:限制样式的来源
10.2.5 其他前端安全防护
- HTTPOnly Cookie: 防止 JavaScript 访问 Cookie,增强安全性。
- HSTS(HTTP 严格传输安全): 强制浏览器与服务器之间使用 HTTPS 通信。
// 在服务器上启用 HSTS
Strict-Transport-Security: max-age=31536000; includeSubDomains
- 两步验证: 通过发送验证码或短信来加强用户的身份验证。
十一、项目实战与面试要点
11.1 项目实践
- Todo List、画板、购物车、小型 SPA 实现
- 调试技巧与错误捕获(Try/Catch、window.onerror)
11.2 面试高频题
- 手写 Promise、深拷贝、节流/防抖
- 输出题、闭包题、原型继承题
十二、进阶方向推荐
- TypeScript 基础与应用
- React / Vue / Svelte 框架精通
- 前端架构、微前端、性能监控
- WebAssembly、Serverless、PWA