JavaScript进阶篇——第九章 异常、this 与性能优化全解(终)

发布于:2025-07-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

1. 异常处理

2. this 指向详解

3. 改变this指向的方法

4. 性能优化技术

5. 防抖与节流对比

6. 复习要点速查表


这是JavaScript最后的技术,也是JavaScript的最后一个章节了。作者本人不厉害,只是整理了一下笔记,方便自己观看和查阅,顺便也能帮到在学JavaScript不想做笔记的人。我可能会时不时观看查阅,看有没有改进的地方。

本文系统介绍了JavaScript的核心概念和技术要点,主要内容包括:

  1. 异常处理机制
  • throw主动抛出异常,推荐使用Error对象
  • try/catch捕获处理异常,finally确保执行关键操作
  • debugger调试技巧
  1. this指向规则
  • 普通函数的this取决于调用者
  • 箭头函数继承定义时的外层this
  • 改变this的三种方法:call/apply/bind
  1. 性能优化技术
  • 防抖(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;
}

执行流程:

  1. 先执行 try 代码块

  2. 若出错,跳转至 catch 块

  3. 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));

调试技巧:

  1. 在开发者工具的 Sources 面板查看

  2. 可查看当前作用域变量

  3. 支持单步执行(Step Over/Into/Out)

  4. 观察调用栈(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 注意事项

  1. 箭头函数没有自己的this:使用外层this

  2. 无法通过call/apply/bind改变:this在定义时已确定

  3. 多层嵌套时的查找规则:向外层作用域一层层查找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的特点:

  1. 返回新函数,原函数不变

  2. 永久绑定this,无法再次更改

  3. 可预设参数(柯里化)

// 参数预设
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() 不执行 参数列表 新函数

性能优化技术对比

技术 核心思想 适用场景
防抖 只执行最后一次 搜索框输入、窗口大小调整结束
节流 单位时间只执行一次 滚动事件、鼠标移动、高频点击

最佳实践

  1. 异常处理

    • 关键操作使用 try/catch

    • 抛出有意义的错误信息

    • 使用 finally 清理资源

  2. this指向

    • 对象方法使用普通函数

    • 回调函数使用箭头函数

    • 避免在箭头函数中使用 this

  3. 性能优化

    • 输入类事件用防抖

    • 高频触发事件用节流

    • 复杂计算考虑 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绑定返新函"
"防抖节流性能优,高频操作不发愁"


网站公告

今日签到

点亮在社区的每一天
去签到