JavaScript进阶篇——第九章 异常、this 与性能优化全解(终)
目录
1. 异常处理
2. this 指向详解
3. 改变this指向的方法
4. 性能优化技术
5. 防抖与节流对比
6. 复习要点速查表
这是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 = '';// 箭头函数保持组件实例的thisdocument.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绑定返新函"
"防抖节流性能优,高频操作不发愁"