目录
这是JavaScript最后的技术,也是JavaScript的最后一个章节了。作者本人不厉害,只是整理了一下笔记,方便自己观看和查阅,顺便也能帮到在学JavaScript不想做笔记的人。我可能会时不时观看查阅,看有没有改进的地方。
本文系统介绍了JavaScript的核心概念和技术要点,主要内容包括:
- 异常处理机制
- throw主动抛出异常,推荐使用Error对象
- try/catch捕获处理异常,finally确保执行关键操作
- debugger调试技巧
- this指向规则
- 普通函数的this取决于调用者
- 箭头函数继承定义时的外层this
- 改变this的三种方法:call/apply/bind
- 性能优化技术
- 防抖(debounce):高频操作只执行最后一次
- 节流(throttle):限制单位时间内的执行次数
- 对比分析适用场景和实现原理
文章通过代码示例和对比表格,清晰呈现了各技术要点的核心差异和使用场景,并提供了实用的记忆口诀,帮助开发者快速掌握JavaScript这些关键概念。
1. 异常处理
1.1 throw 抛异常
主动抛出错误,终止程序执行
function divide(a, b) {
if (b === 0) {
// 抛出错误对象(推荐)
throw new Error('除数不能为零');
// 也可以抛出简单信息(不推荐)
// throw '除数不能为零';
}
return a / b;
}
// 调用
try {
divide(10, 0);
} catch (error) {
console.log(error.message); // "除数不能为零"
}
核心要点:
throw
会立即终止程序执行推荐使用
Error
对象提供详细错误信息错误信息包含
message
属性和调用栈
1.2 try/catch/finally 捕获异常
捕获并处理可能的错误,避免程序崩溃
function loadData() {
try {
// 可能出错的代码
const data = JSON.parse(localStorage.getItem('userData'));
if (!data) {
throw new Error('用户数据不存在');
}
console.log('数据加载成功:', data);
} catch (error) {
// 处理错误
console.error('错误信息:', error.message);
// 可选:显示用户友好的提示
alert('加载数据失败,请重试');
// 可选:将错误上报到服务器
reportError(error);
return null;
} finally {
// 无论是否出错都会执行
console.log('数据加载过程结束');
// 清理资源等操作
}
return data;
}
执行流程:
先执行
try
代码块若出错,跳转至
catch
块finally
块始终执行(即使有return
)
1.3 debugger 断点调试
在代码中设置断点进行调试
function calculateTotal(prices) {
let total = 0;
prices.forEach(price => {
debugger; // 在此处暂停
total += price * 1.1; // 加10%税费
});
return total;
}
const prices = [100, 200, 300];
console.log(calculateTotal(prices));
调试技巧:
在开发者工具的 Sources 面板查看
可查看当前作用域变量
支持单步执行(Step Over/Into/Out)
观察调用栈(Call Stack)
2. this 指向详解
2.1 普通函数的 this 指向
规则:谁调用函数,this 就指向谁
// 全局调用 → this = window
function showThis() {
console.log(this);
}
showThis(); // window (严格模式下为undefined)
// 对象方法 → this = 调用对象
const user = {
name: '小明',
greet: function() {
console.log(`你好,我是${this.name}`);
}
};
user.greet(); // "你好,我是小明"
// DOM事件 → this = 触发事件的元素
button.addEventListener('click', function() {
console.log(this); // <button>元素
});
严格模式影响:
'use strict';
function test() {
console.log(this); // undefined
}
test();
2.2 箭头函数的 this 指向
规则:继承外层作用域的 this(定义时确定)
const obj = {
name: '小明',
traditional: function() {
console.log('传统函数:', this.name); // "小明"
},
arrow: () => {
console.log('箭头函数:', this.name); // undefined(外层this)
}
};
obj.traditional(); // 正常
obj.arrow(); // 异常(this指向window)
适用与不适用场景:
场景 | 适用性 | 说明 |
---|---|---|
对象方法 | ❌ 不适用 | this 指向外层作用域 |
DOM事件处理 | ❌ 不适用 | this 应为DOM元素 |
原型方法 | ❌ 不适用 | this 指向外层作用域 |
回调函数 | ✅ 适用 | 保持外层this |
定时器回调 | ✅ 适用 | 保持外层this |
// ✅ 适用场景:保持外层this
class SearchComponent {
constructor() {
this.searchTerm = '';
// 箭头函数保持组件实例的this
document.getElementById('search').addEventListener('input', (e) => {
this.searchTerm = e.target.value;
this.performSearch();
});
}
performSearch() {
// 使用this.searchTerm
}
}
2.3 注意事项
箭头函数没有自己的this:使用外层this
无法通过call/apply/bind改变:this在定义时已确定
多层嵌套时的查找规则:向外层作用域一层层查找this
3. 改变this指向的方法
3.1 call() 方法
立即调用函数,并指定this和参数
function introduce(lang, level) {
console.log(`我是${this.name},擅长${lang},水平${level}`);
}
const developer = { name: '小明' };
// 调用函数,this指向developer
introduce.call(developer, 'JavaScript', '高级');
// "我是小明,擅长JavaScript,水平高级"
参数说明:
第一个参数:this指向的对象
后续参数:函数的参数列表
3.2 apply() 方法
立即调用函数,并指定this和参数数组
function calculate(x, y, z) {
return (x + y + z) * this.factor;
}
const context = { factor: 2 };
// 使用数组传递参数
const result = calculate.apply(context, [10, 20, 30]);
console.log(result); // 120
// 实际应用:求数组最大值
const numbers = [5, 2, 8, 4];
const max = Math.max.apply(null, numbers); // 8
与call的区别:
call:参数逐个传递(func.call(obj, arg1, arg2))
apply:参数以数组传递(func.apply(obj, [arg1, arg2]))
3.3 bind() 方法
创建新函数,永久绑定this(不立即执行)
const user = {
name: '小明',
greet: function() {
console.log(`你好,我是${this.name}`);
}
};
// 创建绑定函数
const boundGreet = user.greet.bind(user);
// 延迟执行
setTimeout(boundGreet, 1000); // 1秒后:"你好,我是小明"
// 实际应用:事件处理函数
button.addEventListener('click', user.greet.bind(user));
bind的特点:
返回新函数,原函数不变
永久绑定this,无法再次更改
可预设参数(柯里化)
// 参数预设
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10 (2 * 5)
4. 性能优化技术
4.1 防抖(debounce)
单位时间内频繁触发,只执行最后一次(搜索框输入)
function debounce(func, delay) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer); // 清除上次定时器
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
// 使用示例
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(function() {
console.log('发送请求:', this.value);
}, 500));
执行过程:
准备开始执行
→ 触发
→ 取消上次,准备开始执行
→ 触发
→ 取消上次,准备开始执行
→ 触发
→ 执行结束
4.2 节流(throttle)
单位时间内频繁触发,只执行一次(滚动事件)
function throttle(func, interval) {
let lastTime = 0;
return function() {
const now = Date.now();
const context = this;
const args = arguments;
if (now - lastTime >= interval) {
func.apply(context, args);
lastTime = now;
}
};
}
// 使用示例
window.addEventListener('scroll', throttle(function() {
console.log('处理滚动:', window.scrollY);
}, 200));
执行过程:
真正开始执行
→ 触发
→ 执行结束
→ 触发
→ 执行结束
真正开始执行
→ 触发
→ 执行结束
→ 触发
→ 执行结束
之前已有,取消本次
→ 触发,前一个任务还在,取消触发
防抖与节流总结表
性能优化 | 核心原理 | 典型使用场景 |
---|---|---|
防抖 (Debounce) | 单位时间内频繁触发事件,只执行最后一次 | 搜索框输入、手机号/邮箱验证输入检测 |
节流 (Throttle) | 单位时间内频繁触发事件,只执行一次 | 鼠标移动(mousemove)、窗口缩放(resize)、滚动(scroll) |
5. 防抖与节流对比
5.1 核心区别
特性 | 防抖 | 节流 |
---|---|---|
执行时机 | 最后一次触发后延迟执行 | 固定时间间隔执行 |
响应速度 | 延迟响应 | 即时响应 |
适用事件 | 离散事件(输入、验证) | 连续事件(滚动、移动) |
用户感知 | 等待用户停止操作 | 即时反馈但限频 |
实现复杂度 | 相对简单 | 相对复杂 |
5.2 选择指南
使用防抖当:
只需最终结果(如搜索建议)
操作成本高(如API请求)
避免中间状态干扰
使用节流当:
需要即时反馈(如拖拽效果)
高频事件需要平滑处理(如动画)
保持响应但限制频率
终极选择指南:
"输入搜索用防抖,滚动动画用节流"
"高频事件先节流,关键操作后防抖"
"移动端延时要加长,组合使用效果优"
"性能优化无止境,用户体验是王道"
6. 复习要点速查表
异常处理要点
方法 | 作用 | 特点 |
---|---|---|
throw | 抛出错误 | 终止程序执行 |
try/catch | 捕获错误 | 防止程序崩溃 |
finally | 最终执行 | 无论是否出错都执行 |
this指向总结
函数类型 | this指向 | 是否可改变 |
---|---|---|
普通函数 | 调用者(window/对象) | ✅ 可改变 |
箭头函数 | 外层作用域的this | ❌ 不可改变 |
构造函数 | 新创建的实例 | ❌ 不可改变 |
事件处理 | 触发事件的元素 | ✅ 可改变 |
改变this的方法对比
方法 | 调用时机 | 参数形式 | 返回值 |
---|---|---|---|
call() | 立即执行 | 参数列表 | 函数结果 |
apply() | 立即执行 | 参数数组 | 函数结果 |
bind() | 不执行 | 参数列表 | 新函数 |
性能优化技术对比
技术 | 核心思想 | 适用场景 |
---|---|---|
防抖 | 只执行最后一次 | 搜索框输入、窗口大小调整结束 |
节流 | 单位时间只执行一次 | 滚动事件、鼠标移动、高频点击 |
最佳实践
异常处理:
关键操作使用 try/catch
抛出有意义的错误信息
使用 finally 清理资源
this指向:
对象方法使用普通函数
回调函数使用箭头函数
避免在箭头函数中使用 this
性能优化:
输入类事件用防抖
高频触发事件用节流
复杂计算考虑 Web Workers
// 综合示例:安全的API请求
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return processData(data);
} catch (error) {
console.error('请求失败:', error);
showErrorToast('数据加载失败');
throw error; // 继续向上抛出
} finally {
hideLoadingSpinner();
}
}
记忆口诀:
"异常处理三步走,try/catch/finally"
"this指向看调用,箭头函数看外层"
"call/apply立即调,bind绑定返新函"
"防抖节流性能优,高频操作不发愁"