JavaScript 核心特性综合实战 —— 从函数到对象的深度应用

JavaScript 核心特性综合实战 —— 从函数到对象的深度应用

这是一篇偏向中高级的实战向总结,重点在于把 JavaScript 最核心的几个概念(函数、闭包、this、原型、对象、ES6+ 新特性)通过真实场景代码串联起来,帮助你真正“用起来”而不是只停留在理论。

一、核心概念速览(带着问题阅读)

概念关键一句话理解实战中最容易出错的地方现代解决方案 / 推荐写法
函数是一等公民可以赋值、传参、返回this 指向混乱箭头函数 + 明确绑定
闭包函数 + 它能访问的外部词法环境内存泄漏、循环中的闭包陷阱模块模式、IIFE、块级作用域 + let/const
this动态绑定,取决于调用方式事件回调、setTimeout、对象方法丢失 this箭头函数、bind、class + 自动绑定
原型与继承对象通过 __proto__ 链接到原型链原型污染、for…in 遍历到原型属性Object.create、class、组合优于继承
对象字面量最常用的创建对象方式属性名冲突、方法中 this 丢失简写语法、计算属性名、Symbol
ES6+ 对象增强解构、展开、Object.assign、Object.fromEntries浅拷贝问题、属性描述符丢失结构化克隆、深拷贝库、Proxy

二、综合实战案例:实现一个“任务管理器”模块

我们通过一个小型任务管理系统,把上面所有核心特性串起来。

// =============================================
// 1. 使用闭包 + 模块模式 封装私有数据
// =============================================
const createTaskManager = () => {
  // 私有变量(外部无法直接访问)
  let tasks = [];
  let taskIdCounter = 0;
  const listeners = new Set(); // 观察者模式 - 事件订阅

  // 私有辅助函数
  const generateId = () => ++taskIdCounter;

  // 对外暴露的 API 对象
  return {
    // 添加任务(闭包访问私有变量)
    addTask(title, priority = 1) {
      const task = {
        id: generateId(),
        title,
        priority,
        completed: false,
        createdAt: new Date(),
      };
      tasks.push(task);
      this.notify('taskAdded', task);
      return task;
    },

    // 删除任务(使用 findIndex + splice)
    removeTask(id) {
      const index = tasks.findIndex(t => t.id === id);
      if (index !== -1) {
        const removed = tasks.splice(index, 1)[0];
        this.notify('taskRemoved', removed);
        return removed;
      }
      return null;
    },

    // 切换完成状态(对象方法中的 this)
    toggleComplete(id) {
      const task = tasks.find(t => t.id === id);
      if (task) {
        task.completed = !task.completed;
        this.notify('taskToggled', task);
      }
    },

    // 获取所有任务(返回副本防止外部修改原数据)
    getAllTasks() {
      return [...tasks]; // 浅拷贝
    },

    // 按优先级排序(函数作为参数)
    getSortedTasks(sortBy = 'priority') {
      return [...tasks].sort((a, b) => {
        if (sortBy === 'priority') return b.priority - a.priority;
        if (sortBy === 'created') return b.createdAt - a.createdAt;
        return 0;
      });
    },

    // 订阅事件(闭包保存 listeners)
    subscribe(callback) {
      listeners.add(callback);
      return () => listeners.delete(callback); // 返回取消订阅函数
    },

    // 通知所有订阅者
    notify(event, data) {
      listeners.forEach(cb => cb(event, data));
    }
  };
};

// =============================================
// 2. 使用方式 - 体现 this、闭包、原型链
// =============================================
const manager = createTaskManager();

// 订阅变化(闭包 + 高阶函数)
const unsubscribe = manager.subscribe((event, task) => {
  console.log(`事件:${event}`, task);
  renderTasks(); // 假设有 UI 更新函数
});

// 添加任务
manager.addTask("完成周报", 3);
manager.addTask("修复 Bug #123", 1);
manager.toggleComplete(1);

// 3秒后自动移除高优先级任务(this 丢失经典场景)
setTimeout(() => {
  // 错误写法:this 指向 window / undefined
  // manager.getAllTasks().forEach(task => {
  //   if (task.priority === 1) manager.removeTask(task.id);
  // });

  // 正确写法1:箭头函数保留 this
  manager.getAllTasks().forEach(task => {
    if (task.priority === 1) manager.removeTask(task.id);
  });

  // 正确写法2:bind
  // manager.getAllTasks().forEach(function(task) {
  //   if (task.priority === 1) this.removeTask(task.id);
  // }.bind(manager));
}, 3000);

// 取消订阅
// unsubscribe();

// =============================================
// 3. 扩展:使用 class 语法 + 原型继承风格
// =============================================
class AdvancedTaskManager extends Object {
  constructor() {
    super();
    this.tasks = [];
    this.idCounter = 0;
    this.listeners = new Set();
  }

  addTask(title, priority = 1) {
    const task = {
      id: ++this.idCounter,
      title,
      priority,
      completed: false,
      createdAt: new Date(),
      // 方法定义简写 + this 自动绑定(class 特性)
      getInfo() {
        return `${this.title} (优先级 ${this.priority})`;
      }
    };
    this.tasks.push(task);
    this.notify('taskAdded', task);
    return task;
  }

  // ... 其他方法类似
}

// =============================================
// 4. 现代写法对比:使用 Proxy 实现响应式任务
// =============================================
function reactiveTasks(initialTasks = []) {
  const handlers = {
    set(target, prop, value) {
      const result = Reflect.set(target, prop, value);
      console.log(`任务列表变化:${prop} 被修改`);
      // 可以在这里触发 UI 更新
      return result;
    }
  };

  return new Proxy(initialTasks, handlers);
}

const reactiveList = reactiveTasks();
reactiveList.push({ id: 1, title: "测试 Proxy" }); // 会自动打印变化

三、核心考察点总结(面试/实战常问)

  1. 闭包最经典的两个问题
  • for var i 循环中的 setTimeout
  • 内存泄漏(未释放的引用)
  1. this 绑定规则优先级(ES6 前)
  2. new 绑定
  3. 显式绑定(call/apply/bind)
  4. 隐式绑定(obj.method())
  5. 默认绑定(window / undefined)
  6. 对象拷贝的多种方式及深浅区别
  • { ...obj } / Object.assign() → 浅拷贝
  • structuredClone()(现代浏览器推荐)
  • JSON.parse(JSON.stringify(obj)) → 局限性大
  • lodash / clone-deep 等库
  1. 如何实现一个简易的继承?
  • Object.create + Object.assign
  • class + extends + super
  • 组合寄生式继承(最推荐)

四、进阶练习题(建议动手写)

  1. 用闭包实现一个计数器(支持 increment、decrement、reset)
  2. 用 Proxy 实现一个简单的双向绑定(类似 Vue2 的响应式)
  3. 实现一个 new 操作符的模拟函数
  4. 用 ES6+ 语法重写一个“经典 for var i 的 setTimeout 闭包问题”

如果你想针对上面某一个点深入展开代码实现、或者需要某个具体场景的完整代码(防抖节流、发布订阅、深拷贝、事件委托等),可以直接告诉我,我会继续提供详细写法和原理说明。

文章已创建 4580

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部