一、JavaScript 基础语法
1.1 变量声明
关键字 | 作用域 | 提升 | 重复声明 | 暂时性死区 |
---|---|---|---|---|
var | 函数级 | ✅ | ✅ | ❌ |
let | 块级 | ❌ | ❌ | ✅ |
const | 块级 | ❌ | ❌ | ✅ |
1.1.1变量提升的例子
在 JavaScript 中,var
声明的变量会存在变量提升的现象,而 let
和 const
则不会。变量提升是指变量的声明会被提升到当前作用域的顶部,但赋值操作不会提升。
1. var
的变量提升
onsole.log(a); // 输出:undefined
var a = 10;
console.log(a); // 输出:10//重复声明特性
var b;
var b;
onsole.log(b); // 输出:undefined
var b = 10;
console.log(b); // 输出:10
解释:
- 虽然
var a = 10;
是在console.log(a);
之后声明的,但由于变量提升,var a
的声明会被提升到作用域的顶部。类似于var a;但是不进行赋值操作。 - 因此,第一次
console.log(a);
不会报错,而是输出undefined
(因为赋值操作a = 10
没有被提升)。 - 第二次
console.log(a);
输出10
,因为此时已经完成了赋值。
2. let
和 const
的暂时性死区
console.log(b); // 报错:ReferenceError: Cannot access 'b' before initialization
let b = 20;
console.log(b); // 输出:20
console.log(c); // 报错:ReferenceError: Cannot access 'c' before initialization
const c = 30;
console.log(c); // 输出:30
解释:
let
和const
声明的变量不会提升,且在声明之前访问会触发“暂时性死区”(Temporal Dead Zone, TDZ),导致报错。- 只有在声明之后,才能正常访问
let
和const
声明的变量。
1.1.2 变量提升的代码运行过程
1.var
的代码运行过程
// 实际运行过程
var a; // 声明被提升到作用域顶部
console.log(a); // 输出:undefined
a = 10; // 赋值操作
console.log(a); // 输出:10
2.let
和 const
的代码运行过程
// 实际运行过程
console.log(b); // 报错:ReferenceError
let b = 20; // 声明和赋值
console.log(b); // 输出:20// 若我们在上面自己声明好b,则也是报undefined。
let b
console.log(b); // 报错:undefined
b = 20; // 声明和赋值
console.log(b); // 输出:20//注意我们不能直接const b;这样子会报错的。const 在声明时必须立即初始化(即赋值),否则会报错。这是 const 的一个重要特性,也是它与 let 和 var 的主要区别之一。
1.1.3变量提升的总结
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | ✅ 声明提升,值为 undefined | ❌ 存在暂时性死区 | ❌ 存在暂时性死区 |
重复声明 | ✅ 允许重复声明 | ❌ 不允许重复声明 | ❌ 不允许重复声明 |
修改值 | ✅ 可以重新赋值 | ✅ 可以重新赋值 | ❌ 不能重新赋值(常量) |
1.2 数据类型
基本类型:
- Number
- String
- Boolean
- Undefined
- Null
- Symbol
- BigInt
引用类型:
- Object
- Array
- Function
/*
typeof 是一个一元操作符,用于检测一个值的基本数据类型。它返回一个表示数据类型的字符串
*/
// 类型检测
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (历史遗留问题)
console.log(typeof []); // "object"
console.log(typeof {}); // "object"
console.log(typeof function() {}); // "function"
console.log(typeof Symbol("id")); // "symbol"
console.log(typeof 123); // "bigint"
/*
instanceof 是一个二元操作符,用于检测一个对象是否是某个构造函数的实例。它可以用来检测对象的继承关系。
*/
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(function() {} instanceof Function); // trueclass Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true (继承关系)
console.log(dog instanceof Object); // true (所有对象都是 Object 的实例)
typeof null
返回"object"
,这是 JavaScript 的一个历史遗留问题。typeof
无法区分数组和普通对象,它们都返回"object"
。instanceof
返回一个布尔值:true
:对象是构造函数的实例。false
:对象不是构造函数的实例。
在这里我突然想知道symbol和bigint是什么东西?因为好像都没怎么用过,所以我上网搜了一下,总结了以下大概:
1.1 什么是 Symbol
?
Symbol
是一种原始数据类型,类似于number
、string
、boolean
等。- 每个
Symbol
值都是唯一的,即使它们的描述相同。 Symbol
值通常用作对象的属性名,以确保属性名不会冲突。
1.2 创建 Symbol
使用 Symbol()
函数可以创建一个 Symbol
值。可以传递一个可选的描述字符串,用于调试和区分不同的 Symbol
。
const sym1 = Symbol();
const sym2 = Symbol("description"); // 带描述的 Symbol
const sym3 = Symbol("description");console.log(sym1); // Symbol()
console.log(sym2); // Symbol(description)
console.log(sym2 === sym3); // false,即使描述相同,Symbol 也是唯一的
1.3. Symbol
的特性
1.4 唯一性
每个 Symbol
值都是唯一的,即使它们的描述相同。可能大家听不懂,直接上代码。
const sym1 = Symbol("id");
const sym2 = Symbol("id");console.log(sym1 === sym2); // false
1.5 不可变性
const sym = Symbol("id");
sym.description = "new description"; // 无效
console.log(sym.description); // "id"
2.3 不可枚举性
Symbol
属性不会出现在 for...in
或 Object.keys()
的遍历中。
const obj = {[Symbol("id")]: 123,name: "Alice"
};console.log(Object.keys(obj)); // ["name"]
for (let key in obj) {console.log(key); // 只输出 "name"
}
说到这里,还是要回归我们为什么要用symbol?
2.1 避免属性名冲突
Symbol
可以用作对象的属性名,确保属性名不会与其他属性冲突。
const id = Symbol("id");
const user = {name: "Alice",[id]: 123 ,// 使用 Symbol 作为属性名id:1
};
//语法要求:当使用Symbol作为属性名时,必须将其包裹在方括号[]中。这是因为方括号允许使用变量或表达式作为属性名,而Symbol变量属于这种情况。
console.log(user[id]); // 123
console.log(user.id); // 1
2.2 定义对象的私有属性
由于 Symbol
属性不会被常规方法遍历到,可以用来模拟私有属性。
const age = Symbol("age");
const person = {name: "Bob",[age]: 30
};console.log(person[age]); // 30
console.log(Object.keys(person)); // ["name"]
BigInt
是 JavaScript 中的一种新的基本数据类型(ES2020 引入),专门用来表示超大整数,解决传统 Number
类型无法精确处理大整数的问题。
3.1BigInt 的核心特点
-
表示超大整数:
Number
类型能精确表示的最大整数是2^53 - 1
(即9007199254740991
),超过这个值会丢失精度。BigInt
可以表示任意大的整数(理论上仅受内存限制)。
const maxNumber = 9007199254740991; // Number 的最大安全整数 const bigIntNum = 9007199254740992n; // BigInt 表示更大的整数 console.log(maxNumber + 1); // 9007199254740992 ✅ console.log(maxNumber + 2); // 9007199254740992 ❌ 精度丢失! console.log(bigIntNum + 1n); // 9007199254740993n ✅
-
语法:
- 在数字末尾加
n
(例如123n
)。 - 或通过
BigInt()
函数转换(例如BigInt(123)
)。
- 在数字末尾加
-
运算限制:
-
不能直接与
Number
混合运算,必须统一类型:console.log(1n + 2); // 报错:Cannot mix BigInt and other types console.log(1n + 2n); // 3n ✅
-
比较运算符(如 ==、>)可以直接使用:
console.log(1n < 2); // true ✅
-
-
用途:
- 处理金融、加密算法中的超大整数。
- 数据库中的大 ID(如 Twitter 的推文 ID)。
- 数学计算(如大数阶乘、质数验证)。
注意事项
- JSON 不支持:
JSON.stringify()
会忽略BigInt
属性,需要手动处理序列化。 - 兼容性:现代浏览器和 Node.js 支持,但旧环境需检查兼容性。
- 性能:
BigInt
运算比Number
稍慢,但可忽略不计。
示例
// 定义 BigInt
const big1 = 123456789012345678901234567890n;
const big2 = BigInt("987654321098765432109876543210");// 运算
const sum = big1 + big2; // 1111111110111111111011111111100n
const isEven = big1 % 2n === 0n; // 判断是否为偶数// 转换
console.log(big1.toString()); // "123456789012345678901234567890"
console.log(Number(big1)); // 1.2345678901234568e+29(可能丢失精度!)
好啦,扩展了这两个属性,下面继续我们的其他方面的学习~~
1.3 JavaScript 运算符详解
1. 算术运算符
let a = 10, b = 3;console.log(a + b); // 13 ➕
console.log(a - b); // 7 ➖
console.log(a * b); // 30 ✖️
console.log(a / b); // 3.333... ➗
console.log(a % b); // 1 取余
console.log(a ** 2); // 100 指数
console.log(a++); // 10 后置递增(先输出后计算)
console.log(++a); // 11 前置递增
2. 赋值运算符
let x = 5;
x += 3; // 等价于 x = x + 3 → 8
x **= 2; // 等价于 x = x ** 2,x = 5² → 25
3. 比较运算符
5 == '5' // true → 值相等(自动类型转换)
5 === '5' // false → 严格相等(值和类型)
NaN === NaN // false → 特殊:NaN不等于自身
NaN == NaN //false
Object.is(NaN, NaN) // true → ES6精准判断
4. 逻辑运算符
true && false // false → 与(短路特性)
0 || "default" // "default" → 或(返回第一个真值)0是false的
1 || "default" // 1
1 && 5 //5,因为两个都是真值,所以返回后面的5!!
!true // false → 非
5. 位运算符
核心规则:
- 位运算会将数值转换为 32 位二进制整数(超出 32 位的部分会被丢弃),然后对每一位进行运算。
- 运算后结果会转回十进制显示。
5 & 1 // 1 → 二进制 0101 & 0001 结果是 0001(1)
5 | 2 // 7 → 0101 | 0010 结果是0111(7)
5 << 1 // 10 → 左移1位(乘2) 0101向左移动一位就是 1010(10)
关键运算符
运算符 | 名称 | 规则(按位操作) |
---|---|---|
& | 按位与 | 会对每一位进行比较,如果两个对应的位都为 1 ,则结果为 1 ,否则为 0 。 |
| | 按位或 | 会对每一位进行比较,如果两个对应的位中至少有一个为 1 ,则结果为 1 ,否则为 0 。 |
<< | 左移 | 会将二进制数的所有位向左移动指定的位数,右边空出的位用 0 填充。 |
6. 三元运算符
const status = age >= 18 ? '成人' : '未成年';
7. 其他运算符
typeof 'hello' // 'string' → 类型检测
'hello' instanceof String // false(字面量非对象)
const arr = [...[1,2], ...[3,4]] // [1,2,3,4] → 展开运算符
-优先级金字塔(常见运算符)
优先级 | 运算符 | 示例 |
---|---|---|
20 | () 分组 | (1 + 2) * 3 |
18 | ++ -- 后置递增/减 | a++ |
17 | ! ~ 逻辑非/位非 | !false |
16 | ** 指数 | 2 ** 3 → 8 |
15 | * / % | 6 % 4 → 2 |
13 | + - | 3 + 5 → 8 |
10 | << >> 位移 | 4 << 1 → 8 |
9 | > < 比较 | 5 > 3 → true |
6 | == === 相等判断 | 5 === 5 → true |
5 | && 逻辑与 | a && b |
4 | ` | |
3 | ?: 三元 | a ? b : c |
2 | = 赋值 | x = 5 |
-高频考点解析
1. 严格相等陷阱
null == undefined // true(特殊规则)值都是没有
null === undefined // false(类型不同)
2. 逻辑运算符妙用
// 设置默认值
const port = config.port || 8080;// 条件执行
isLogin && showUserMenu();
3. 运算符优先级实战
2 + 3 * 4 // 14(乘法优先)
(2 + 3) * 4 // 20(括号改变优先级)
避坑指南
-
浮点运算精度
0.1 + 0.2 === 0.3 // false → 存储精度问题 // 解决方案:使用toFixed或放大为整数运算 const sum = 0.1 + 0.2; console.log(sum.toFixed(2)); // "0.30" (返回字符串) //2是保留2位小数,注意返回的是字符串,想要小数可以墙砖Number() // 转换为数字 console.log(Number(sum.toFixed(2))); // 0.3 console.log(+sum.toFixed(2)); // 0.3
-
短路运算边界条件
0 || 1 // 1(0为false值) "" && 5 // ""(空字符串为false)
-
类型转换陷阱 (很容易掉坑的地方!!)
"5" - 3 // 2(字符串转数字) "5" + 3 // "53"(数字转字符串拼接)
1.4JavaScript 流程控制语句详解
一、条件语句
1. if 语句
let age = 18;// 基础形式
if (age >= 18) {console.log("已成年");
}// if-else
if (age >= 18) {console.log("可以进入网吧");
} else {console.log("禁止进入");
}// 多条件分支
if (age < 13) {console.log("儿童");
} else if (age < 18) {console.log("青少年");
} else {console.log("成年人");
}
注意事项:
- 建议始终使用代码块
{}
包裹语句 - 条件表达式会自动转换布尔值
- 判断相等推荐使用
===
2. switch 语句
let day = 3;
let dayName;switch(day) {case 1:dayName = "星期一";break;case 2:dayName = "星期二";break;case 3:dayName = "星期三";break;default:dayName = "未知";
}console.log(dayName); // 星期三//特别易错点在break这里let day = 1;let dayName;switch (day) {case 1:dayName = "星期一";case 2:dayName = "星期二";case 3:dayName = "星期三";default:dayName = "未知";}console.log(dayName); // 未知
/*
这是因为在 switch 语句中,每个 case 后缺少了 break,导致代码产生了“穿透”(fall-through)现象。具体流程如下:
当 day = 1 时,会匹配第一个 case 1,此时 dayName 被正确赋值为“星期一”。
由于没有 break,代码不会跳出 switch,而是继续执行后续的 case 2,将 dayName 覆盖为“星期二”。
继续穿透到 case 3,再次覆盖为“星期三”。
最终穿透到 default,覆盖为“未知”。
*/
//把default换位置!!结果也是被星期三覆盖了let day = 1;let dayName;switch (day) {case 1:dayName = "星期一";case 2:dayName = "星期二";default:dayName = "未知";case 3:dayName = "星期三";}console.log(dayName); // 星期三
关键特性:
- 使用
===
严格比较 - 必须使用
break
阻止穿透 default
可放在任意位置
二、循环语句
1. while 循环
// 基础形式
let i = 0;
while (i < 5) {console.log(i);i++;
}
//0 1 2 4// 特殊用法
let count = 3;
while (count--) {console.log(`剩余次数:${count}`);
}
// 输出:
// 剩余次数:2
// 剩余次数:1
// 剩余次数:0
2. do-while 循环
let x = 0;
do {console.log(x);x++;
} while (x < 3);
// 保证至少执行一次
3. for 循环
// 经典形式
for (let i = 0; i < 5; i++) {console.log(i);
}// 特殊用法
for (let i = 0, j = 10; i < j; i++, j--) {console.log(i, j);
}
注意事项:
- 循环变量建议使用
let
声明 - 避免修改循环变量值
- 可省略任意表达式
for(;;)
三、循环控制
1. break 关键字
// 终止整个循环
for (let i = 0; i < 10; i++) {if (i === 5) {break;}console.log(i); // 输出 0-4
}
2. continue 关键字
// 跳过本次迭代
for (let i = 0; i < 5; i++) {if (i === 2) {continue;}console.log(i); // 输出 0,1,3,4
}
3. 标签语句(高级用法)
outerLoop:
for (let i = 0; i < 3; i++) {innerLoop:for (let j = 0; j < 3; j++) {if (i === 1 && j === 1) {break outerLoop; // 直接终止外层循环}console.log(i, j);}
}
// 输出:
// 0 0
// 0 1
// 0 2
// 1 0
四、特殊循环方法
1. for…of 循环
// 遍历可迭代对象
const colors = ["red", "green", "blue"];
for (const color of colors) {console.log(color);
}//换成let,var也是一样的
//red green blue
2. for…in 循环
// 遍历对象属性
const obj = {a:1, b:2, c:3};
for (const key in obj) {console.log(key, obj[key]);
}
//a 1
//b 2
//c 3
const obj = {
a: { name: 'a' },
b: { name: 'b' },
c: 3 };
for (const key in obj) {
console.log(key, obj[key]);
}
重要区别:
特性 | for…of | for…in |
---|---|---|
适用对象 | 可迭代对象(数组等) | 对象 |
返回值 | 元素值 | 属性名 |
原型链属性 | 不遍历 | 会遍历 |
五、常见错误示例
1. 死循环
// ❌ 错误示范
let n = 0;
while (n < 5) {console.log(n);// 忘记修改循环变量
}
2. 错误的作用域
// ❌ 错误示范
for (var i = 0; i < 3; i++) {setTimeout(() => {console.log(i); // 总是输出 3}, 100);
}// ✅ 正确写法
for (let i = 0; i < 3; i++) {setTimeout(() => {console.log(i); // 正确输出 0,1,2}, 100);
}
//这两个笔试的时候也会考到知识点哦!
- 作用域问题:
var
是函数作用域,循环中的i
被提升到全局(或外层函数作用域),所有异步回调共享同一个i
。 - 异步时序:当
setTimeout
回调执行时,循环早已结束,此时i
的值已变为 3。 - 块级作用域:
let
为每次循环创建一个新的块级作用域,每次迭代的i
都是独立的副本。 - 闭包捕获:每个
setTimeout
回调捕获的是对应迭代时的i
值,因此输出符合预期。
六、最佳实践建议
- 优先使用
for
循环 处理已知次数的迭代 - 慎用
while
处理不确定次数的循环时设置安全计数器 - 合理使用
break/continue
但避免过度使用 - 循环嵌套不超过3层 超过时应考虑拆分函数
- 及时释放资源 在循环结束后清理不需要的变量
// 性能优化示例
const bigArray = new Array(1000000).fill(0);// ❌ 低效写法
for (let i = 0; i < bigArray.length; i++) {// 每次循环都读取数组长度
}// ✅ 高效写法
const length = bigArray.length;
for (let i = 0; i < length; i++) {// 预先缓存数组长度
}
流程图示例:
二、DOM 与 BOM
终究是来到了我们的bom跟dom了。
一、什么是 DOM?
1. 基本定义
DOM(Document Object Model)是浏览器将 HTML/XML 文档解析成的树形结构模型。简单说:
👉 DOM = 网页的编程接口
👉 DOM = 操作网页内容的API集合
2.Dom树 直观图示
主要功能
功能类型 | 具体操作示例 |
---|---|
内容操作 | 修改文本、添加/删除元素 |
样式控制 | 修改颜色、大小、显示状态 |
事件响应 | 点击、滚动、键盘输入等交互处理 |
数据获取 | 获取表单数据、元素属性 |
<div id="box" class="container">内容</div>
// 节点操作
const box = document.getElementById('box');
const newDiv = document.createElement('div');
box.appendChild(newDiv);// 样式操作
box.style.backgroundColor = '#f00';
box.classList.add('active');// 属性操作
box.setAttribute('data-id', 123);
console.log(box.getAttribute('data-id')); // 123
(1) 获取元素
// 通过ID获取(返回单个元素)
const header = document.getElementById('header');
// 通过类名获取(返回集合)
const boxes = document.getElementsByClassName('box');
// 通过标签名获取(返回集合)
const imgs = document.getElementsByTagName('img');
// 现代选择器(返回第一个匹配元素)
const btn = document.querySelector('.submit-btn');
// 获取所有匹配元素
const items = document.querySelectorAll('.list-item');
(2) 修改内容
// 修改文本内容
const title = document.querySelector('h1');
title.textContent = '新标题';// 修改HTML内容(注意安全风险)
const container = document.getElementById('container');
container.innerHTML = '<p>动态内容</p>';// 安全插入内容
const safeDiv = document.createElement('div');
safeDiv.textContent = userInput; // 自动转义特殊字符
container.appendChild(safeDiv);
(3) 样式操作
const box = document.querySelector('.box');// 直接修改样式box.style
box.style.backgroundColor = '#f00';
box.style.fontSize = '20px';// 切换类名box.classList
box.classList.add('active');
box.classList.remove('old-class');
box.classList.toggle('hidden');// 获取计算样式
const computedStyle = window.getComputedStyle(box);
console.log(computedStyle.width);
3. 节点关系操作
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');// 父子关系
console.log(child.parentNode); // 获取父节点
console.log(parent.children); // 获取所有子元素// 兄弟关系
console.log(child.previousElementSibling); // 前一个兄弟元素
console.log(child.nextElementSibling); // 后一个兄弟元素
四、DOM 事件处理
1. 事件监听三要素
const btn = document.querySelector('#myButton');// 1. 事件类型:'click'
// 2. 回调函数:handleClick
// 3. 触发元素:btn
btn.addEventListener('click', handleClick);function handleClick(event) {console.log('按钮被点击了!');console.log(event.target); // 触发元素
}
2. 常用事件类型
事件类型 | 触发时机 |
---|---|
click | 鼠标点击 |
mouseover | 鼠标悬停 |
keydown | 键盘按下 |
submit | 表单提交 |
scroll | 滚动事件 |
load | 资源加载完成 |
五、DOM 操作性能优化
1. 高效操作建议
-
批量修改:使用文档片段减少重绘.
-
DocumentFragment 是一个轻量级的「虚拟DOM容器」,具有以下特点:
- 不在主DOM树中存在
- 可以临时存储DOM节点
- 批量插入时只触发一次重排(reflow)
// ❌ 低效写法(触发100次重排) for (let i = 0; i < 100; i++) {const div = document.createElement('div');document.body.appendChild(div); // 每次都会触发页面重排 } //✅ 高效写法 // 1. 创建文档片段(内存中的虚拟容器) const fragment = document.createDocumentFragment(); // 2. 批量创建元素(不会触发重排) for (let i = 0; i < 100; i++) {const div = document.createElement('div');div.textContent = `Item ${i}`;// 3. 将元素添加到片段(不在真实DOM中)fragment.appendChild(div); }// 4. 一次性插入DOM(仅触发1次重排) document.body.appendChild(fragment); // 操作完成后 fragment = null;
-
缓存查询结果:避免重复查询
查询缓存是指将 DOM 查询结果保存到变量中,避免重复执行相同的查询操作。就像去图书馆找书:
- ❌ 每次需要都重新查找(低效)
- ✅ 找到后记下位置,下次直接取用(高效)
// ❌ 错误方式 for (let i = 0; i < 10; i++) {document.querySelector('.item').style.color = 'red'; } //问题:执行了 10次完全相同的DOM查询,每次查询都要扫描整个文档树,性能损耗随循环次数指数级增长// ✅ 高效写法 // 1. 一次性查询所有元素(仅扫描DOM一次) const items = document.querySelectorAll('.item');// 2. 使用缓存的结果进行操作 items.forEach(item => {item.style.color = 'red'; // 直接使用已缓存元素 });
-
使用事件委托:减少事件监听器数量
一、什么是事件委托?
事件委托是一种利用事件冒泡机制的技术,通过将事件监听器设置在父元素上,来管理其所有子元素的事件响应。就像快递柜的运作方式:
- ❌ 传统方式:给每个快递格单独装监控(为每个子元素绑定事件)
- ✅ 事件委托:只在快递柜总台装监控(在父元素绑定事件)
二、为什么需要事件委托?
性能对比
方式 事件监听器数量 内存占用 动态元素支持 传统绑定 N个(子元素数) 高 需要重新绑定 事件委托 1个(父元素) 低 自动支持 document.querySelector('.list').addEventListener('click', (e) => {if (e.target.matches('.item')) {console.log('点击了列表项');} });
六、DOM vs BOM
特性 | DOM | BOM |
---|---|---|
作用对象 | 文档内容 | 浏览器窗口 |
核心对象 | document | window |
包含内容 | 页面元素操作 | 浏览器历史、地址栏、屏幕信息 |
标准化 | W3C 标准 | 浏览器厂商实现 |
一、什么是BOM?
BOM是浏览器提供的用于操作浏览器窗口的对象模型,与DOM(操作文档)不同,BOM主要控制:
- 浏览器窗口尺寸/位置
- 导航/历史记录
- 屏幕信息
- 弹窗对话框
- 定时器
- 本地存储等
二、核心对象及用法
1. window对象(全局对象)
// 获取窗口尺寸
const width = window.innerWidth;
const height = window.innerHeight;// 打开新窗口
const newWindow = window.open('https://baidu.com', '_blank');// 滚动控制
window.scrollTo(0, 500); // 滚动到指定位置
window.scrollBy(0, 100); // 相对滚动
2. location对象(地址栏控制)
// 获取当前URL信息
console.log(location.href); // 完整URL
console.log(location.hostname); // 域名
console.log(location.pathname); // 路径// 页面跳转
location.assign('https://new.com'); // 记录历史
location.replace('https://new.com'); // 不记录历史
location.reload(); // 重新加载// 修改URL参数
location.search = '?page=2'; // 添加查询参数
3. history对象(导航历史)
// 导航控制
history.back(); // 后退
history.forward(); // 前进
history.go(-2); // 后退2页// 添加历史记录
history.pushState({page: 1}, "title 1", "?page=1");
history.replaceState({page: 2}, "title 2", "?page=2");
4. navigator对象(浏览器信息)
// 检测浏览器类型
const isChrome = /Chrome/.test(navigator.userAgent);// 获取设备能力
console.log(navigator.onLine); // 是否联网
console.log(navigator.geolocation); // 地理位置API
console.log(navigator.clipboard); // 剪贴板API// 检测移动设备
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
5. screen对象(屏幕信息)
console.log(screen.width); // 屏幕宽度
console.log(screen.height); // 屏幕高度
console.log(screen.availWidth); // 可用宽度(除任务栏)
console.log(screen.colorDepth); // 颜色位数
三、实用功能详解
1. 弹窗控制
// 警告框
alert('警告信息');// 确认框
if (confirm('确定删除?')) {console.log('已删除');
}// 输入框
const name = prompt('请输入姓名', '张三');
2. 定时器
// 一次性定时器
const timer = setTimeout(() => {console.log('3秒后执行');
}, 3000);// 清除定时器
clearTimeout(timer);// 循环定时器
const interval = setInterval(() => {console.log('每秒执行');
}, 1000);// 清除循环
clearInterval(interval);
3. 本地存储
// localStorage(永久存储)
localStorage.setItem('theme', 'dark');
console.log(localStorage.getItem('theme')); // "dark"
localStorage.removeItem('theme');// sessionStorage(会话级存储)
sessionStorage.setItem('token', 'abc123');// Cookie操作
document.cookie = "username=John; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
四、现代BOM API
1. 地理位置API
navigator.geolocation.getCurrentPosition((pos) => {console.log(`纬度: ${pos.coords.latitude}`);console.log(`经度: ${pos.coords.longitude}`);},(err) => {console.error('获取位置失败:', err);}
);
2. 设备方向API
window.addEventListener('deviceorientation', (e) => {console.log(`Alpha: ${e.alpha}`); // 绕Z轴旋转console.log(`Beta: ${e.beta}`); // 绕X轴旋转console.log(`Gamma: ${e.gamma}`); // 绕Y轴旋转
});
3. 网络状态API
// 检测网络变化
window.addEventListener('online', () => {console.log('网络已连接');
});window.addEventListener('offline', () => {console.log('网络已断开');
});
六、注意事项
- 弹窗限制:现代浏览器会拦截非用户触发的弹窗
- 隐私API:如地理位置需要用户授权
- 兼容性问题:部分BOM特性在不同浏览器表现不同
- 内存泄漏:定时器需及时清除
三、JavaScript 内置对象全面解析
一、基础内置对象
1. Array 数组对象
// 创建数组
const fruits = ['Apple', 'Banana'];// 常用方法
fruits.push('Orange'); // 末尾添加
fruits.pop(); // 移除末尾
fruits.includes('Apple'); // 检查存在,返回布尔值
fruits.join(', '); // 转为字符串
fruit=fruits.push('Orange');
// 高阶函数
console.log([1,2,3].map(x=>x*2 )) // [2, 4, 6]
console.log([1,2,3].filter(x => x > 1))// [2, 3]
2. String 字符串对象
const str = 'Hello World';// 常用操作
str.indexOf('World'); // 6
str.slice(0, 5); // 'Hello'
str.replace('World', 'JS');// 'Hello JS'
str.split(' '); // ['Hello', 'World']// 模板字符串
const name = 'Ac;
console.log(`Hello ${name}`); // Hello Ac
3. Math 数学对象
Math.PI; // 3.141592653589793
Math.round(4.7); // 5
Math.random(); // 0-1随机数
Math.max(10, 20, 30); // 30// 生成区间随机数
function randomInRange(min, max) {return Math.floor(Math.random() * (max - min + 1)) + min;
}
//Math.random():生成一个0(包括)到1(不包括)之间的随机小数。乘以 (max - min + 1):将随机小数扩展到所需的范围长度。Math.floor():将结果向下取整,得到一个整数。加 min:将结果调整到所需的最小值 min。
/*
示例:
console.log( randomInRange(1,5))
可能返回 1, 2, 3, 4, 或 5。
randomInRange(10, 20) 可能返回 10 到 20 之间的任意整数。
*/
二、日期和时间对象
Date 对象
// 创建日期
const now = new Date();
const birthday = new Date('1995-12-17');// 日期操作
now.getFullYear(); // 当前年份
now.getMonth(); // 0-11 (0=一月)
now.toISOString(); // "2023-08-15T12:00:00.000Z"// 日期格式化
now.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric'
}); // "2023年8月15日"
三、键值对集合
1. Object 基础对象
const person = {name: 'John',age: 30,greet() {console.log(`Hello, ${this.name}`);}
};// 操作属性
person.name = 'Alice';
delete person.age;
Object.keys(person); // ['name', 'greet']
2. Map 与 Set
// Map(键值对集合)
const map = new Map();
map.set('name', 'John');
map.get('name'); // 'John'// Set(值唯一集合)
const set = new Set([1, 2, 3, 2]);
set.size; // 3 (自动去重)
四、JSON 处理
JSON 对象
const user = {name: "John",age: 30
};// 序列化与反序列化
const jsonStr = JSON.stringify(user);
const parsedObj = JSON.parse(jsonStr);// 格式化输出
JSON.stringify(user, null, 2);
/*
{"name": "John","age": 30
}
*/
五、数据类型对象
1. Number
// 数字常量
Number.MAX_SAFE_INTEGER; // 9007199254740991// 类型转换
Number('123'); // 123
(123).toString(); // '123'// 数值格式化
(12345.6789).toFixed(2); // "12345.68"
2. Boolean
// 显式转换
Boolean('text'); // true
Boolean(0); // false// 隐式转换规则
!!'hello'; // true
!!0; // false
六、特殊内置对象
1. RegExp 正则表达式
// 创建正则,下面第九节会清晰的讲解正则内容的
const regex = /\d+/g;// \d表示匹配数字// 使用方法
'123abc'.match(regex); // ['123']
regex.test('123'); // 这是测试返回true// 常用模式
/^[a-z]+$/i.test('Hello'); // 字母检查
/\d{4}-\d{2}-\d{2}/.test('2023-08-15'); // 日期格式检查
2. Error 错误对象
// 抛出错误
throw new Error('出错啦!');// 自定义错误
class ValidationError extends Error {constructor(message) {super(message);this.name = "ValidationError";}
}// 错误处理
try {// 可能出错的代码
} catch (err) {console.error(err.name, err.message);
}
七、现代新增对象
1. Promise
// 创建Promise
const fetchData = new Promise((resolve, reject) => {setTimeout(() => resolve('数据加载成功'), 1000);
});// 使用Promise
fetchData.then(data => console.log(data)).catch(err => console.error(err));// async/await
async function getData() {try {const data = await fetchData;console.log(data);} catch (err) {console.error(err);}
}
2. Proxy
// 创建代理
const target = {};
const handler = {get(obj, prop) {return prop in obj ? obj[prop] : 37;}
};const proxy = new Proxy(target, handler);
proxy.a = 1;
console.log(proxy.a, proxy.b); // 1, 37
八、对象关系图谱
九、最佳实践建议
- 优先使用现代API:如Map代替Object用于键值对集合
- 注意类型转换:明确使用String()/Number()等转换方法
- 善用高阶函数:reduce/filter/map等替代传统循环
- 合理使用扩展运算符:[…arr], {…obj}等语法糖
- 注意浏览器兼容性:如Proxy等新特性需考虑兼容
掌握这些内置对象,可以显著提升JavaScript开发效率和质量!
四、JavaScript 事件系统全面指南
一、事件基础概念
1. 什么是事件?
事件是发生在HTML元素上的"事情",比如:
- 用户操作(点击、滚动、按键)
- 浏览器行为(页面加载、窗口大小变化)
- 网络活动(AJAX请求完成)
2. 事件三要素
element.addEventListener('click', function(event) {// 1. 事件类型:'click'// 2. 事件目标:element// 3. 事件对象:event
});
二、事件绑定方式
1. HTML属性方式(不推荐)
<button onclick="handleClick()">点击</button>
2. DOM属性方式
document.getElementById('myBtn').onclick = function() {console.log('按钮被点击');
};
3. addEventListener(推荐)
const btn = document.querySelector('#myBtn');// 可以添加多个同类型事件
btn.addEventListener('click', handleClick);
btn.addEventListener('click', function() {console.log('第二个点击处理');
});
三、事件流机制
1. 三个阶段
2. 代码示例
document.querySelector('.outer').addEventListener('click', () => console.log('捕获阶段'),true // 使用捕获
);document.querySelector('.inner').addEventListener('click', () => console.log('冒泡阶段') // 默认冒泡阶段
);
四、事件对象(Event)
1. 常用属性
element.addEventListener('click', function(event) {event.target; // 触发事件的元素event.currentTarget; // 绑定事件的元素event.type; // 事件类型event.clientX; // 鼠标X坐标event.key; // 按键值
});
2. 常用方法
event.preventDefault(); // 阻止默认行为
event.stopPropagation(); // 停止事件传播
event.stopImmediatePropagation(); // 阻止其他监听器执行
五、常见事件类型
1. 鼠标事件
事件类型 | 触发条件 |
---|---|
click | 点击(按下并释放) |
dblclick | 双击 |
mousedown | 鼠标按下 |
mouseup | 鼠标释放 |
mousemove | 鼠标移动 |
mouseenter | 鼠标进入 |
mouseleave | 鼠标离开 |
2. 键盘事件
document.addEventListener('keydown', (e) => {console.log(`按下键: ${e.key}, 代码: ${e.code}`);
});
3. 表单事件
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {e.preventDefault(); // 阻止表单提交// 表单验证逻辑
});
4. 窗口事件
window.addEventListener('resize', () => {console.log(`窗口大小: ${window.innerWidth}x${window.innerHeight}`);
});window.addEventListener('scroll', () => {console.log(`滚动位置: ${window.scrollY}`);
});
六、事件委托模式
1. 传统方式的问题
// 为每个列表项绑定事件
document.querySelectorAll('.item').forEach(item => {item.addEventListener('click', handleClick);
});
2. 事件委托解决方案
// 只需一个事件监听器
document.querySelector('.list').addEventListener('click', (e) => {if (e.target.matches('.item')) {console.log('点击了项目:', e.target.dataset.id);}
});
七、自定义事件
1. 创建和触发
// 创建自定义事件
const event = new CustomEvent('myEvent', {detail: { message: 'Hello World' }
});// 监听事件
document.addEventListener('myEvent', (e) => {console.log(e.detail.message);
});// 触发事件
document.dispatchEvent(event);
2. 元素级自定义事件
const button = document.querySelector('button');button.addEventListener('alert', (e) => {alert(e.detail.message);
});button.dispatchEvent(new CustomEvent('alert', {detail: { message: '按钮事件触发!' }
}));
八、性能优化
1. 防抖(Debounce)
function debounce(func, delay) {let timer;return function() {clearTimeout(timer);timer = setTimeout(() => func.apply(this, arguments), delay);};
}window.addEventListener('resize', debounce(() => {console.log('窗口大小变化');
}, 200));
2. 节流(Throttle)
function throttle(func, limit) {let lastRun;return function() {if (!lastRun || (Date.now() - lastRun >= limit)) {func.apply(this, arguments);lastRun = Date.now();}};
}document.addEventListener('scroll', throttle(() => {console.log('滚动事件');
}, 100));
九、事件最佳实践
- 优先使用addEventListener:比on属性方式更灵活
- 及时移除事件监听:避免内存泄漏
function handleClick() {} element.addEventListener('click', handleClick); // 不再需要时 element.removeEventListener('click', handleClick);
- 合理使用被动事件:提升滚动性能
window.addEventListener('scroll', () => {}, { passive: true });
- 避免过度事件委托:深层嵌套结构可能影响性能
十、现代事件处理
1. AbortController
const controller = new AbortController();element.addEventListener('click', () => {console.log('点击事件');
}, { signal: controller.signal });// 取消所有通过该controller注册的事件
controller.abort();
2. 事件处理选项
// 只执行一次的事件
element.addEventListener('click', () => {console.log('只会执行一次');
}, { once: true });// 捕获阶段事件
element.addEventListener('click', () => {}, { capture: true });
五、AJAX 全面指南:现代前端数据交互技术
一、AJAX 基础概念
1. 什么是 AJAX?
AJAX(Asynchronous JavaScript and XML)是一种异步的 Web 开发技术,允许:
- 不刷新页面的情况下与服务器交换数据
- 局部更新网页内容
- 异步执行网络请求(不会阻塞页面)
2. 核心优势
传统方式 | AJAX 方式 |
---|---|
完整页面刷新 | 局部更新 |
同步请求 | 异步请求 |
用户体验中断 | 流畅交互 |
二、XMLHttpRequest 基础
1. 基本使用流程
const xhr = new XMLHttpRequest();xhr.open('GET', 'https://api.example.com/data', true); // 异步请求xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log(JSON.parse(xhr.responseText));}
};xhr.send();
2. readyState 状态码
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT | 代理被创建,但尚未调用 open() |
1 | OPENED | open() 方法已经被调用 |
2 | HEADERS_RECEIVED | send() 方法已被调用,响应头已接收 |
3 | LOADING | 下载响应体中 |
4 | DONE | 请求操作已完成 |
三、Fetch API(现代替代方案)
1. 基本用法
fetch('https://api.example.com/data').then(response => {if (!response.ok) {throw new Error('网络响应异常');}return response.json();}).then(data => console.log(data)).catch(error => console.error('请求失败:', error));
2. 带参数的请求
// POST 请求示例
fetch('https://api.example.com/data', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': 'Bearer token123'},body: JSON.stringify({username: 'john',password: 'secret'})
});
四、异步处理演进
1. 回调函数方式
function getData(callback) {const xhr = new XMLHttpRequest();xhr.onload = function() {callback(JSON.parse(xhr.responseText));};xhr.open('GET', 'api/data');xhr.send();
}
2. Promise 方式
function getData() {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.onload = () => resolve(JSON.parse(xhr.responseText));xhr.onerror = () => reject(xhr.statusText);xhr.open('GET', 'api/data');xhr.send();});
}
3. async/await(推荐)
async function loadData() {try {const response = await fetch('api/data');const data = await response.json();console.log(data);} catch (error) {console.error('加载失败:', error);}
}
五、实际应用场景
1. 表单提交
document.querySelector('form').addEventListener('submit', async (e) => {e.preventDefault();const formData = new FormData(e.target);const response = await fetch('/submit', {method: 'POST',body: formData});const result = await response.json();showNotification(result.message);
});
2. 无限滚动
window.addEventListener('scroll', throttle(async () => {if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {const newData = await loadMoreItems();appendItemsToDOM(newData);}
}, 200));
六、错误处理与调试
1. 全面的错误处理
async function fetchWithRetry(url, retries = 3) {try {const response = await fetch(url);if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);return await response.json();} catch (error) {if (retries <= 0) throw error;await new Promise(resolve => setTimeout(resolve, 1000));return fetchWithRetry(url, retries - 1);}
}
2. 调试技巧
// 查看请求详情
fetch('api/data').then(response => {console.log('Headers:', [...response.headers.entries()]);console.log('Status:', response.status);return response.json();});// 使用浏览器开发者工具
// Network 面板查看请求/响应
七、性能优化
1. 请求取消
const controller = new AbortController();fetch('api/data', {signal: controller.signal
}).catch(e => {if (e.name === 'AbortError') {console.log('请求被取消');}
});// 取消请求
controller.abort();
2. 数据缓存
const cache = new Map();async function getCachedData(url) {if (cache.has(url)) return cache.get(url);const data = await fetch(url).then(res => res.json());cache.set(url, data);return data;
}
八、安全实践
1. CSRF 防护
// 自动添加CSRF令牌
fetch('/api/data', {headers: {'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content}
});
2. CORS 处理
// 带凭证的请求
fetch('https://api.example.com', {credentials: 'include'
});// 预检请求处理
const response = await fetch('https://api.example.com', {method: 'POST',headers: {'Content-Type': 'application/json','X-Custom-Header': 'value'},body: JSON.stringify(data)
});
九、现代替代方案
1. Axios 库
import axios from 'axios';// 简洁的API
axios.get('/api/data').then(response => console.log(response.data));// 拦截器
axios.interceptors.response.use(response => response,error => {if (error.response.status === 401) {redirectToLogin();}return Promise.reject(error);}
);
2. GraphQL 请求
fetch('/graphql', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({query: `{user(id: "1") {nameemail}}`})
})
.then(res => res.json())
.then(data => console.log(data.data.user));
十、最佳实践总结
- 优先使用 Fetch API 或现代库(如 Axios)
- 始终处理错误 使用 try/catch 或 catch()
- 合理使用异步模式 async/await 提升可读性
- 考虑安全性 处理 CSRF、CORS 等问题
- 优化性能 实现缓存、请求取消等功能
- 保持代码整洁 封装通用请求函数
六、JavaScript 正则表达式完全指南
一、正则表达式基础
1. 创建正则表达式
// 字面量形式(推荐)
const regex1 = /pattern/flags;// 构造函数形式
const regex2 = new RegExp('pattern', 'flags');
2. 常用匹配方法
// test() 方法
/hello/.test('hello world'); // true// exec() 方法
const result = /world/.exec('hello world');
console.log(result[0]); // "world"
二、核心语法详解
1. 元字符
元字符 | 说明 | 示例 |
---|---|---|
. | 匹配任意单个字符 | /.a/ 匹配 “ba” |
\d | 数字 [0-9] | /\d/ 匹配 “5” |
\w | 单词字符 [A-Za-z0-9_] | /\w/ 匹配 “a” |
\s | 空白字符 | /\s/ 匹配 " " |
^ | 字符串开头 | /^A/ 匹配 "A"开头 |
$ | 字符串结尾 | /z$/ 匹配 "z"结尾 |
2. 量词
量词 | 说明 | 示例 |
---|---|---|
* | 0次或多次 | /a*/ 匹配 “aaa” |
+ | 1次或多次 | /a+/ 匹配 “aa” |
? | 0次或1次 | /a?/ 匹配 “” 或 “a” |
{n} | 恰好n次 | /a{2}/ 匹配 “aa” |
{n,} | 至少n次 | /a{2,}/ 匹配 “aaa” |
{n,m} | n到m次 | /a{2,4}/ 匹配 “aaa” |
三、实用技巧与示例
1. 分组与捕获
// 捕获分组
const phoneMatch = /(\d{3})-(\d{4})-(\d{4})/.exec('010-1234-5678');
console.log(phoneMatch[1]); // "010"// 非捕获分组
/(?:\d{3})-(\d{4})/.exec('010-1234');
2. 贪婪与非贪婪匹配
// 贪婪匹配(默认)
'<div>content</div>'.match(/<.*>/)[0]; // "<div>content</div>"// 非贪婪匹配
'<div>content</div>'.match(/<.*?>/)[0]; // "<div>"
3. 前瞻断言
// 正向肯定前瞻
'100 dollars'.match(/\d+(?= dollars)/)[0]; // "100"// 正向否定前瞻
'100 pesos'.match(/\d+(?! dollars)/)[0]; // "100"
四、字符串方法中的应用
1. String.prototype.match()
const str = 'The rain in Spain';
const matches = str.match(/ain/g);
console.log(matches); // ["ain", "ain"]
2. String.prototype.replace()
// 简单替换
'Hello World'.replace(/world/i, 'JavaScript'); // "Hello JavaScript"// 使用函数替换
'123-4567'.replace(/\d+/g, match => `(${match})`); // "(123)-(4567)"
3. String.prototype.split()
'2023-08-15'.split(/-/); // ["2023", "08", "15"]
五、常见实用正则
1. 邮箱验证
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
emailRegex.test('user@example.com'); // true
2. 手机号验证(中国大陆)
const mobileRegex = /^1[3-9]\d{9}$/;
mobileRegex.test('13812345678'); // true
3. URL 提取
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
urlRegex.test('https://www.example.com/path?query=string'); // true
六、性能优化
1. 预编译正则
// 需要多次使用的正则应该预编译
const precompiledRegex = /pattern/g;for (let i = 0; i < 1000; i++) {precompiledRegex.test(inputString);
}
2. 避免回溯灾难
// 糟糕的正则 - 可能导致性能问题
/(a+)*b/.test('aaaaaaaaaaaaaaaaaaaaac'); // 可能很慢// 改进版本
/a+b/.test('aaaaaaaaaaaaaaaaaaaaac'); // 效率更高
七、浏览器兼容性
1. ES6+ 新特性
特性 | Chrome | Firefox | Safari | Edge |
---|---|---|---|---|
u flag | 55 | 50 | 10.1 | 15 |
s flag | 62 | 78 | 11.1 | 79 |
命名捕获组 | 64 | 78 | 11.1 | 79 |
2. 兼容性写法
// 命名捕获组兼容方案
const namedGroupRegex = /(?<year>\d{4})-(?<month>\d{2})/;
const fallbackRegex = /(\d{4})-(\d{2})/;const match = (str.match(namedGroupRegex) || str.match(fallbackRegex));
const { year = match[1], month = match[2] } = match.groups || {};
八、调试工具与技巧
1. 可视化工具
- Regex101
- RegExr
2. 控制台调试
// 查看正则对象的属性
console.dir(/pattern/g);// 测试多个匹配
const regex = /\d+/g;
let match;
while ((match = regex.exec('123 456 789')) {console.log(match[0]);
}
九、最佳实践
-
添加注释:复杂正则使用
x
标志添加注释const regex = /^\d{3} # 前3位数字-\d{2} # 中间2位数字-\d{4}$ # 最后4位数字 /x;
-
优先可读性:复杂正则拆分为多个简单正则
-
充分测试:覆盖各种边界情况
-
性能考量:避免灾难性回溯
-
适时使用工具:可视化工具辅助编写复杂正则