与功能混合(Functional Mixins)实战指南)
教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载本篇指南围绕 JS Tips 仓库中第 75 条提示what-is-a-functional-inheritance展开系统讲解 JavaScript 中一种被低估的对象复用方式功能继承Functional Inheritance。你将从定义、闭包私有数据、动态对象扩展三个维度理解其原理并通过Animal/Cat的完整示例掌握编写与组合功能混合Functional Mixins的实战技能进而与原型继承、new运算符等经典方案做横向对比在合适的场景中做出更优设计决策。什么是功能继承功能继承Functional Inheritance是通过向对象实例应用一个增强函数augmenting function来继承特性的过程。它不依赖class、extends或prototype链而是基于 JavaScript 的两个天然能力闭包Closure增强函数内部形成一个私有作用域可以用来保存只有该对象自己知道的数据动态对象扩展在运行时向已存在的对象实例追加新的属性与方法。原文档给出的核心定义如下Functional inheritance is the process of inheriting features by applying an augmenting function to an object instance. The function supplies a closure scope which you can use to keep some data private. The augmenting function uses dynamic object extension to extend the object instance with new properties and methods.用一句话概括工作流先创建一个基础对象再通过调用一个或多个增强函数把新能力注入到该对象上。这与类—子类的静态继承模型截然不同继承发生在实例层面而非类型层面。基础示例从 Animal 到 Cat原文档提供了一个最小但完整的实现。我们先看基础构造函数// Base object constructor function function Animal(data) { var that {}; // Create an empty object that.name data.name; // Add it a name property return that; // Return the object };注意这里没有任何new关键字也没有this赋值。Animal只是一个工厂函数它创建空对象、填充属性、显式返回该对象。这避免了new带来的隐性语义——仓库中另一条提示 Return Values with the new Operator 详细分析了new的行为当构造函数返回原始值时会忽略返回值并返回this实例而当返回对象等非原始值时则会覆盖this实例。功能继承直接绕过这些规则始终通过显式return控制产出行为一目了然。接下来定义子对象Cat它继承并扩展Animal// Create a child object, inheriting from the base Animal function Cat(data) { // Create the Animal object var that Animal(data); // Extend base object that.sayHello function() { return Hello, I\m that.name; }; return that; }; // Usage var myCat Cat({ name: Rufi }); console.log(myCat.sayHello()); // Output: Hello, Im Rufi运行结果Hello, Im Rufi这段代码展示了功能继承的两个关键动作继承Cat内部调用Animal(data)拿到一个已具备name属性的对象增强通过that.sayHello function() {...}在实例上动态追加方法。由于Cat返回的正是扩展后的that调用方拿到的是一个完整拥有name与sayHello的对象。闭包让数据真正私有在上述Cat中sayHello通过that.name读取数据。而功能继承更强大的用法是利用闭包为对象保存调用者无法直接访问的私有数据。回顾仓库中的 Closures inside loops 提示闭包会捕获其词法作用域中的变量函数即使在外层执行完毕后仍能访问这些变量。功能继承正是利用这一点——增强函数被调用时其局部变量被闭包记住而外部代码没有任何句柄能触及它们。改写示例以展示私有状态function Counter(data) { var that {}; var count data.start || 0; // count 是闭包内的私有变量 that.get function() { return count; }; that.increment function() { count 1; return that; }; // 返回 that 支持链式调用 that.decrement function() { count - 1; return that; }; return that; } var c Counter({ start: 10 }); c.increment().increment(); console.log(c.get()); // 12 console.log(c.count); // undefined —— 外部无法访问私有变量这里count只能通过get/increment/decrement三个闭包方法读写外部读取c.count得到undefined。这与 Return objects to enable chaining of functions 提示中的思路一致——让方法返回对象本身以支持链式调用同时结合闭包保证了状态的封装性。动态对象扩展继承发生在实例层面功能继承中的扩展不需要修改任何类定义或原型对象只需给实例追加属性var pet Animal({ name: Tom }); pet.meow function() { return this.name says meow; };由于 JavaScript 对象是动态的参见仓库 Advanced Properties 提示对属性机制的讨论这种追加不需要任何声明。多个增强函数可以先后作用于同一个实例这就是下一节功能混合的组合基础。从传递机制上看这里还有一个容易被忽略的细节Cat拿到的是Animal返回对象的引用。根据仓库提示 Know the passing mechanismJavaScript 对对象采用引用作为值传递call-by-sharing——函数内直接修改传入对象的属性会反映到原对象上。因此Cat内部对that.name的读取以及追加sayHello的操作都直接作用于Animal创建的那个对象实例继承是就地完成的而非复制。功能混合Functional Mixins像流水线一样组装对象原文档随后给出一个关键概念Functional mixins are composable factory functions that add properties and behaviors to objects like stations in an assembly line.功能混合Functional Mixins就是可组合的工厂函数它们像装配流水线上的工位一样逐个为对象添加属性和行为。核心模式是每个 mixin 接收一个对象返回增强后的对象多个 mixin 依次调用能力便逐层叠加。// 每个 mixin 都是工厂函数接收对象 - 增强 - 返回 function withName(obj, name) { obj.name name; return obj; } function withGreeting(obj) { obj.sayHello function() { return Hello, I\m this.name; }; return obj; } function withAge(obj, age) { obj.age age; obj.getAge function() { return this.age; }; return obj; } // 流水线式组装 var cat withAge( withGreeting( withName({}, Rufi) ), 3 ); console.log(cat.sayHello()); // Hello, Im Rufi console.log(cat.getAge()); // 3也可以借助 IIFE立即执行函数表达式参见仓库 Using immediately invoked function expression 提示将若干 mixin 合并为一个组装函数function Cat(data) { return (function(obj) { withName(obj, data.name); withGreeting(obj); withAge(obj, data.age); return obj; })({}); } var myCat Cat({ name: Rufi, age: 3 }); myCat.sayHello(); // Hello, Im Rufi组合的关键约束是mixin 的叠加顺序决定最终对象的行为。若两个 mixin 定义了同名方法后应用者覆盖先应用者——这正是流水线比喻的准确之处。与原型继承、new运算符的对比功能继承与经典原型继承prototype/class/extends的差异可通过下表概括维度原型继承Prototypal功能继承Functional继承发生位置类型/原型层面共享实例层面逐个注入数据共享原型方法共享实例数据各自持有每个实例由工厂独立创建天然隔离私有数据需借助闭包或约定_前缀闭包内变量天然私有扩展新能力修改原型或使用Object.assign调用增强函数即可组合方式多重继承受限需 mixin 或组合多个 mixin 顺序叠加天然可组合方法存储方法存于原型内存高效每个实例持有自己的方法闭包内存开销更高new运算符的隐性规则在功能继承中完全不存在。正如 Return Values with the new Operator 所揭示的用new调用时构造函数返回原始值会被忽略返回this返回对象则会踩踏stompthis实例。功能继承的工厂函数显式return that行为可预期不依赖new的魔法。此外功能继承中子类通过调用父工厂实现继承这与柯里化Currying参见仓库 what-is-a-currying-function 提示在思想上同源都是通过函数层层包装、逐级组合来构造结果而不是通过类型层级。实战私有状态 组合 链式调用的完整案例综合以上所有要素给出一个可运行的综合示例// 基础工厂 function Animal(data) { var that {}; that.name data.name; return that; } // 私有能量闭包实现 function withEnergy(animal, level) { var energy level || 100; // 私有变量 animal.getEnergy function() { return energy; }; animal.run function() { if (energy 0) { energy - 10; } return animal; // 链式调用 }; return animal; } // 组合两个能力 function Dog(data) { var that Animal(data); withEnergy(that, data.energy); that.bark function() { return that.name barks!; }; return that; } var dog Dog({ name: Rex, energy: 50 }); dog.run().run(); console.log(dog.getEnergy()); // 30 —— 外部无法直接修改 energy console.log(dog.bark()); // Rex barks!要点回顾Animal提供基础数据withEnergy以 mixin 形式注入带私有状态的run/getEnergyDog按流水线顺序组合基础工厂与 mixin再追加自己的bark所有方法返回animal支持链式调用呼应 Return objects to enable chaining of functions。优缺点与适用场景优点实现简单直观不依赖new、this、prototype等隐式机制学习成本低闭包提供真正的数据私有性无需命名约定如_count来假装私有Mixin 天然可组合、可复用适合横向能力注入如日志、事件、校验每个实例独立创建状态隔离清晰不受原型共享污染。局限每个实例都各自持有方法闭包内存占用高于原型共享方法动态追加的属性无法通过instanceof判定类型不存在类型层级方法覆盖顺序依赖组合顺序调用方需理解流水线语义与工具函数的序列化、深拷贝等操作配合时闭包内私有状态不可枚举。适用场景需要为对象注入横向能力mixin、需要真正私有状态、或希望规避this/new语义陷阱的小型对象模型。对于需要大量实例、强类型层级和高性能方法的场景原型继承仍更合适。小结功能继承的本质是**增强函数 闭包 动态扩展三者的组合**增强函数接收并返回对象实例闭包负责保存私有数据动态扩展负责注入属性和方法。基于它演化出的功能混合Functional Mixins以工厂函数为单元像流水线一样逐级组装对象能力是 JavaScript 中一种简洁、灵活且无隐式魔法new、this、prototype的对象复用范式。当你需要可组合的横向能力注入或严格的私有状态时它值得成为你工具箱中的第一选择。本文对应的原始文档位于 what-is-a-functional-inheritance仓库中与之配套的闭包、传递机制、new运算符、链式调用与柯里化等提示closures-inside-loops、know-the-passing-mechanism、return-values-with-the-new-operator可帮助你继续深入底层原理。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐JavaScript Mixins 混入详解用方法复制绕开单继承限制Modern JavaScript Tutorial 实战JavaScript Mixins 混入详解用方法复制绕开单继承限制Modern JavaScript Tutorial 实战 JavaScript 的对文档/教程前端30-seconds-of-interviews 面试题精讲JavaScript 原型继承Prototypal Inheritance与经典继承Classical Inheritance对比30 seconds of interviews 面试题精讲JavaScript 原型继承Prototypal Inheritance与经典继承Clas教程前端TypeScript 混入Mixins实战指南以构造函数组合突破单继承限制TypeScript 混入Mixins实战指南以构造函数组合突破单继承限制 在 TypeScript以及 JavaScript中类只支持严格的单继承教程上一篇DDrawCompat终极指南3步让经典游戏在Windows 10/11完美运行下一篇B站视频转换终极指南3步实现m4s到MP4的无损快速转换创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考