
文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载现代 JavaScript 早已不是简单的脚本语言纯函数、不可变数据、单向数据流、虚拟 DOM、Tree Shaking 等概念构成了 React、Vue、Redux、RxJS 等框架与工程化工具的底层语言。本文以本仓库 《现代 JavaScript 概览》 精读文章为核心骨架系统梳理这些概念的准确定义、彼此关联与现实落点并结合仓库内 Immutable 结构共享、前端数据流哲学、依赖注入简介 等系列精读给出纵深佐证。读完本文你可以建立一张完整的现代 JavaScript 概念地图并在实际框架选型、代码设计与面试准备中快速定位每个概念的应用场景。一、为什么要系统性梳理现代 JavaScript 概念现代 JavaScript 的概念和思想正在快速传播与扩展大量新概念高频出现在前端博客、框架文档与团队协作中但对很多前端开发者而言仍然陌生。本文原素材来自 Auth0 的两篇《Glossary of Modern JavaScript Concepts》系列文章其出发点是与其零散地遇到一个概念查一次不如把这些概念放在一起看它们在现代 JavaScript 库与应用中究竟是如何被使用的。仓库 readme.md 将本仓库定位为前端界的好文精读而本篇正是其中关于概念全景的经典一篇。下面按函数式编程 → 响应式编程 → 语言机制 → 框架架构 → 工程化编译的逻辑主线逐层展开。二、函数式编程的五个基础概念1. 纯函数与副作用理解纯函数之前必须先理解副作用side effect。副作用是指函数改变了其作用域外的状态例如调用了一个 API、操作了一个 DOM 节点、弹出了一个弹窗、或者改变了一条数据。纯函数pure function则是指函数的返回值仅仅由参数决定当传入同样的参数时返回值是固定的。纯函数没有副作用也不依赖外部状态因此具备可预测、可缓存、可并行执行的特性。仓库 前端数据流哲学 在讨论数据流管理时将副作用隔离列为通用准则并指出 Redux 之所以一步到位强制把副作用隔离掉正是因为纯函数是单向数据流模型Reducer 必须保持纯净得以成立的前提。2. Stateful 与 Stateless有状态与无状态Stateless无状态有点像纯函数不管理自己的数据或状态结果完全取决于传入的参数。Stateful有状态函数或组件自己持有运行状态并且可以修改自己的状态。在现代 JavaScript 开发中状态state处理显得尤为重要。这个概念的组件化版本就是后文将要提到的Dumb 组件无状态与 Smart 组件有状态而在 React 生态中从 Class 组件的this.state到 React Hooks 的useState本质上都是在解决有状态逻辑如何被更优雅地表达的问题。3. 可变对象与不可变对象概念本身很清晰可变对象指创建后值仍可以被改变的对象不可变对象指创建后值无法被改变的对象。但与其他语言相比JavaScript 中的这对概念更加模糊。当接触函数式编程时你会听到很多不可变对象的好处。在 JavaScript 中可以通过Object.freeze(obj)让一个对象变得不可变但需要注意这是浅层冻结——如果某个属性的值本身是一个对象那这个内层对象的属性依然可以被修改const user Object.freeze({ name: Alice, profile: { age: 18 } }); user.name Bob; // 严格模式下报错浅层冻结生效 user.profile.age 19; // 成功内层对象未被冻结因此在实践中现代 JavaScript 社区出现了deep-freeze、Immutable.js 等库帮助在 JavaScript 中实现真正的深度不可变。仓库 Immutable 结构共享 一文进一步剖析了 Immutable.js 的底层原理通过vector tries与hash maps tries树状结构将打平的引用建立深度减少引用操作次数以空间换时间实现结构共享使得大对象更新不再需要遍历十万级属性文中提到拥有 100,000 个属性的对象Object.assign操作耗时约 134ms。4. 命令式与声明式编程命令式编程Imperative描述一段代码的逻辑怎么被显式调用去改变程序的状态关注每一步的具体执行过程。声明式编程Declarative描述一段代码的逻辑是什么而不需要描述如何完成这段逻辑关注结果而非过程。JavaScript 可以同时用命令式和声明式两种风格编写但随着函数式编程的兴起声明式编程变得越来越普遍。典型例子用命令式写for循环逐个累加与用声明式写[1,2,3].map(x x * 2)后者描述意图前者描述步骤。5. 高阶函数函数是 JavaScript 的一等公民可以像普通数据类型一样被存储或者被作为值传参。高阶函数Higher-Order Function就是可以接收另外一个函数作为入参或者返回一个函数作为结果的函数。Array.prototype.map / filter / reduce、setTimeout(fn, ms)、React 的connect(mapStateToProps)(Component)都是高阶函数的日常体现。仓库 设计模式 系列中的 装饰器模式 与 策略模式在 JS 中往往也正是借助高阶函数实现。6. 函数式编程FP五个概念的收束上面了解的纯函数、无状态、不可变对象、声明式编程和高阶函数都是函数式编程的重要组成。函数式编程通过以下方式包含上述概念关键函数实现使用纯函数没有副作用数据不可变函数无状态用声明式代码管理副作用、执行命令式逻辑。简言之函数式编程的骨架就是纯函数 不可变数据 无状态 声明式的组合。三、响应式编程的三个概念1. Hot 与 Cold ObservablesObservables可观察对象和数组类似区别在于数组被保存在内存中而 Observables 的每一个元素是异步加入进来的。我们可以订阅这些 Observables。Hot Observables即使没有订阅它们也容易被执行。例如用户操作界面的按钮点击事件、鼠标移动、窗口大小改变这些事件源本身持续产生数据流与订阅者无关。Cold Observables需要我们去订阅并且会在订阅的那一刻开始执行每个订阅者都会获得自己独立的一次执行。这一对概念直接支撑了响应式编程库RxJS 等的行为模型。仓库 react-rxjs 精读即是在 React 中接入 RxJS 数据流的具体实践。2. 响应式编程RP响应式编程Reactive Programming可以看作是面向异步事件流的编程它是声明式的表述去做什么而不是怎么做。数据以流的形式流动通过操作符map、filter、merge 等对异步事件流进行组合与变换订阅者被动响应流的变化。仓库 前端数据流哲学 将常见数据流管理模式归纳为三类函数式不可变模式化Redux、响应式依赖追踪MobX、以流的形式实现的响应式RxJS、xstream——后两者正是响应式编程思想在前端状态管理中的落地。3. 函数式响应式编程FRP函数式响应式编程Functional Reactive Programming简而言之就是对事件或行为给予声明式的反馈。FRP 有两个很明显的特征函数或类型有明确的定义操作的是连续变化的值而非离散的事件快照。FRP 是函数式与响应式的交集响应式负责处理异步事件流函数式保证处理过程纯净、可组合、可预测。四、语言机制作用域与闭包闭包是最常见的面试题但很多资深前端开发者都解释不清楚闭包即使他们理解闭包。要讲清闭包先要理解作用域全局作用域在代码任何地方都能访问局部作用域函数作用域 / 块级作用域仅在其声明的函数或块内可访问。作用域是许多 JS 开发人员最开始学习的知识理解作用域对编写优秀代码至关重要。闭包的形成在于当一个在函数内声明的函数可以引用外部函数的局部变量时就形成了闭包。闭包让内部函数记住了其诞生时的词法环境function counter() { let count 0; return function () { count 1; // 内部函数引用外部函数的局部变量 count return count; }; } const c counter(); c(); // 1 c(); // 2闭包的价值在于封装私有状态、实现柯里化与高阶函数代价则是内存中保留外部环境引用使用不当可能造成内存泄漏。仓库 精读《This 带来的困惑》 从this绑定角度补充了函数调用上下文与作用域链的关系。五、框架架构数据流与变化侦测1. 单向数据流与双向数据流随着 SPA 框架的兴起理解数据流概念对现代 JS 开发者越来越重要。单向数据流One-way Data FlowReact 被认为是单向数据流的典范——使用 Model 作为唯一数据来源控制 View 的渲染View 层通过事件方式通知 Model 更新再反应到 View 层的变化上。数据沿一个方向流动UI 永远不会直接更新 Model而是通过事件或setState方法。双向数据绑定Two-way Data Binding数据在两个方向上流动JS 可以更新 Model 数据View 层也可以更新 Model 数据。AngularJS 1.x 是双向数据流的典型实现。早在 2009 年双向绑定是 Angular 最受欢迎的特性之一但 Angular 后来抛弃了这一特性。如今很多流行的框架和库都使用单向数据流React、Angular、Inferno、Redux 等倡导的是清晰的架构数据流动更加清晰、易管理。单向数据流牺牲了点 View 自动更新数据的便利但换来了清晰、可追踪的数据流。仓库 重新思考 Redux 与 React Router v6 等文章均是单向数据流思想在具体库中的延伸。2. JS 框架中的变化侦测脏检查、getter/setter、虚拟 DOM变化侦测对现代 SPA 应用来说很重要当用户更新一些内容时应用必须以一种方法感知这种变化并做出反应更新。主流方案有三类方案代表框架原理优缺点脏检查Dirty CheckingAngularJS 1.x对 View 中涉及到的 Model 进行深度比较简单、可预测不涉及 API 和对象变更但涉及大量比较效率较低getter / setterEmber、Backbone数据修改时触发变更事件精确到属性级变更但需要主动劫持属性读写虚拟 DOMVirtual DOMReact通过setState通知变更用虚拟 DOM 比较是否发生数据变化以树 diff 换取声明式渲染性能取决于 diff 算法仓库 DOM diff 原理详解 与 DOM diff 最长上升子序列 两篇精读深入剖析了 React 虚拟 DOM diff 的实现细节包括最长上升子序列算法在移动优化中的运用——这是虚拟 DOM 变化侦测方案在源码层面的直接证据。六、组件化Web Components 与 Smart/Dumb 组件1. Web ComponentsWeb Components 是 Web 平台上可复用的基础组件而 Web Components 规范定义了一些标准来实现这些可复用组件。规范包括自定义元素Custom ElementsHTML TemplateShadow DOMHTML imports 引入Web Components 本身并不能代替 SPA 框架的功能但它的想法和核心概念在很多 SPA 框架中都有体现。仓库 Web Components 的困境 一文专门讨论了这一规范的边界与演进困境可作延伸阅读。2. Smart 和 Dumb 组件现代 Web 开发严重依赖组件很多时候我们把组件分成两类Smart 组件智能组件 / 容器组件在组件内处理各种业务逻辑通常也管理 Dumb 组件并响应 Dumb 组件的事件。典型如 React 中连接 Redux 的容器组件。Dumb 组件哑组件 / 展示组件通常被写成纯函数依赖外部的数据和方法专注于展现数据。它们不持有业务状态因此天然可复用、易测试。这与本文第二节的有状态 / 无状态概念形成呼应Dumb 组件即无状态组件Smart 组件即持有状态与逻辑的组件。七、编译与构建JIT、AOT 与 Tree Shaking1. JIT 编译Just-In-TimeJIT即时编译指代码在运行时被编译成机器代码的过程。在 JavaScript 运行时JIT 能够找到代码的特定模式而这些模式可以让 JavaScript 更快速地执行。V8 引擎的优化编译器正是基于运行时收集的类型信息进行内联缓存与优化。仓库 V8 引擎特性带来的 JS 性能变化、JS 引擎基础之 Shapes and Inline Caches、V8 引擎 Lazy Parsing 三篇从引擎角度展示了 JIT 背后隐藏类 内联缓存的加速机制是对这一概念最直接的源码级佐证。2. AOT 编译Ahead-Of-TimeAOT预编译指编写的代码在运行之前就被翻译成机器代码或目标代码的过程。AOT 给 Tree Shaking 带来了可能使用 AOT 预编译对生产环境下的代码有以下好处更少的异步请求模板和样式内联在 JS 内更小的体积更早地检查到模板错误更好的安全性。Angular 的 AOT 编译、SolidJS 与 Svelte 的编译期优化都是这一思路的代表。3. Tree ShakingTree Shaking 指打包 JS 模块时通过对代码的静态分析排除掉不用的代码的机制。它建立在 ES2015 模块import/export之上支持导入特定的内容而不是整个库import { BehaviorSubject } from rxjs/BehaviorSubject;这样只导入了BehaviorSubject而没有导入整个 RxJS 库。Tree Shaking 能否生效的前提是模块系统是静态可分析的ESM 而非 CommonJS这也是 js 模块化发展、Webpack5 新特性 - 模块联邦 等仓库文章所讨论的构建链路基础。八、精读补充三个常被忽略但极其重要的概念原文档作者在梳理完上述概念后又从实践角度补充了三条1. 依赖注入Dependency Injection通过控制反转父级不需要关心子实现细节将子类可能用到的实例都初始化好由子类决定引入哪些依赖。依赖注入还有一个好处是维持了单实例——这一点在数据流中尤为重要如果 store 不是单例的数据流必然乱了套。既要传给子类使用又要维持单例依赖注入是很好的解决方案。仓库 依赖注入简介 对控制反转与注入方式构造注入、属性注入等做了系统讲解Nestjs 文档精读 则展示了依赖注入在 Node 服务端框架中的工程化应用。2. Symbol、Reflect、ProxySymbolES6 中加入的一种新的数据类型每一个 Symbol 都是独一无二的不与其它 Symbol 重复。它常被用作对象属性的唯一键以及定义内置行为如Symbol.iterator。Proxy通过new Proxy(target, handler)实现对对象的一层拦截提供一种机制代理对象的操作可以在读取、赋值、删除等操作上插入自定义逻辑。Reflect一个内置对象提供可拦截 JavaScript 操作的方法其方法与代理处理程序handler的方法一一对应如Reflect.get、Reflect.set便于在 Proxy 内部以标准语义转发操作。const target { name: x }; const proxy new Proxy(target, { get(obj, key) { return Reflect.get(obj, key) !; }, }); proxy.name; // x!仓库 react-easy-state 源码 剖析了如何用 Proxy 实现响应式状态对应本文getter/setter 变化侦测在现代框架中的升级版设计模式 - Proxy 代理模式 则从设计模式角度讲解代理的思想。3. Server rendering服务端渲染前端对后端渲染的热度降了很多主要是盲目跟风的氛围消停了真正需要的团队已经稳定地用起来了。服务端渲染的理念很新颖一定程度帮助 HTML 认识到自己的不足——就像 Angular、React、Vue 对 Web Components 的冲击一样或许未来十年可以用上 ECMAScript 标准提供的功能但业务不能等待技术唯有不断折腾直到被消灭或者招安。仓库 前后端渲染之争、在浏览器运行 serverRender 与 React Server Component 三篇分别从历史争论、降级方案与下一代服务端组件三个角度为这一概念提供了仓库内的完整讨论脉络。九、总结概念地图与学习路径伴随着各种框架的热度理解这些现代 JavaScript 概念变得越来越重要。把它们串成一张地图可以看到清晰的递进关系函数式基础层纯函数 / 副作用 → 无状态 / 有状态 → 不可变数据 → 声明式 → 高阶函数最终收束为函数式编程异步抽象层ObservablesHot / Cold→ 响应式编程 → 函数式响应式编程语言机制层作用域与闭包框架架构层单向 / 双向数据流、变化侦测脏检查、getter/setter、虚拟 DOM、Web Components、Smart / Dumb 组件工程化层JIT、AOT、Tree Shaking、依赖注入、Proxy / Reflect / Symbol、服务端渲染。你可以以此作为概览逐个深入学习。本仓库的 前沿技术 目录下对应概念的系列精读如 Immutable 结构共享、前端数据流哲学、DOM diff 原理详解 等正是继续深挖每个概念的绝佳素材。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐JavaScript核心概念深度解析33个必知必会的开发要点JavaScript核心概念深度解析33个必知必会的开发要点 前言为什么需要掌握这些JavaScript概念 在当今的前端开发领域JavaScript已教程前端文档PixiEditor 自定义笔刷完整指南如何做出三层渐变粒子效果PixiEditor 自定义笔刷完整指南如何做出三层渐变粒子效果 画一场日落山景时你多半想给角色身后撒一层星点状的粒子几个 2 像素的小点就够了难的是撒桌面应用图像处理Cocos Creator 场景引用查找指南删资源前先过一遍这 3 步Cocos Creator 场景引用查找指南删资源前先过一遍这 3 步 你删掉了一张看起来没人用的贴图结果三个场景当场加载失败控制台刷出一片资源缺失的游戏开发图形学3D渲染上一篇暗黑破坏神3终极按键助手D3KeyHelper完整使用教程与配置指南下一篇distribution 项目中 otlploggrpc 导出器实验特性全解析通过 OTEL_GO_X_OBSERVABILITY 开启导出器自观测创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考