ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

core-js 中 Function.prototype.demethodize 提案详解:方法解绑的语义与 uncurryThis 实现原理

core-js 中 Function.prototype.demethodize 提案详解:方法解绑的语义与 uncurryThis 实现原理 core-js 中 Function.prototype.demethodize 提案详解方法解绑的语义与 uncurryThis 实现原理【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js本文围绕 core-js 对 ECMAScript 提案Function.prototype.demethodize的实现展开讲解该方法如何把一个以this接收目标对象的实例方法解绑成可直接以对象为第一参数调用的普通函数并深入 esnext.function.demethodize 模块 与 function-demethodize 内部实现 的源码细节。读完本文你将掌握该 API 的签名、core-js 提供的全部入口点、典型借用法如借用Array.prototype.slice写法以及其底层基于uncurryThis的实现原理。提案背景与核心语义Function.prototype.demethodize是 ECMAScript 的一个提案proposal目标是提供一种标准化的方法解绑手段。在 JavaScript 中很多方法依赖this指向调用者例如Array.prototype.slice必须通过array.slice()或fn.call(array, ...)的形式调用。当我们需要把这类方法当作独立函数复用时常见做法是const slice Array.prototype.slice; // 直接解构会丢失 this 绑定 slice([1, 2, 3], 1); // TypeError: slice called on null or undefineddemethodize的核心语义正是解决这个痛点它返回一个与原始方法逻辑相同、但不再依赖this的新函数新函数的第一个参数即目标对象。这正是该提案的原始动机。在 core-js 中该功能归属于esnext.*命名空间属于提案阶段特性其提案依据为 js-choi 的proposal-function-demethodize对应模块为 packages/core-js/modules/esnext.function.demethodize.js。需要注意提案阶段特性尚未完全定稿细节可能随提案演进而变化。API 签名与行为约定根据 docs/web/docs/features/proposals/function-prototype-demethodize.md 中给出的类型签名class Function { demethodize(): Function; }关键行为约定零参数demethodize不接受任何参数测试中也通过assert.arity(demethodize, 0)验证了这一点返回值是函数调用后返回一个新的可调用函数语义等价返回函数的第一个参数将作为原方法的this剩余参数原样透传非枚举属性测试断言assert.nonEnumerable(Function.prototype, demethodize)即该属性不会被for...in枚举到。从源码层面看packages/core-js/internals/function-demethodize.js 完整实现了上述契约use strict; var uncurryThis require(../internals/function-uncurry-this); var aCallable require(../internals/a-callable); module.exports function demethodize() { return uncurryThis(aCallable(this)); };core-js 中的入口点原文档列出了该功能在 core-js 中可用的三类入口点它们分别服务于不同的引入场景入口点适用场景core-js/proposals/function-demethodize按提案维度批量引入proposals 入口core-js(-pure)/full/function/demethodize以命名导出方式引入单个方法core-js(-pure)/full/function/virtual/demethodize引入虚拟原型方法供Function.prototype调用各入口点的实际文件均可在此仓库中找到提案入口 packages/core-js/proposals/function-demethodize.js内部直接require(../modules/esnext.function.demethodize)函数入口 packages/core-js/full/function/demethodize.js通过entryUnbind(Function, demethodize)导出可脱离全局原型直接调用的函数虚拟原型入口 packages/core-js/full/function/virtual/demethodize.js通过getBuiltInPrototypeMethod(Function, demethodize)获取原型方法再由 packages/core-js/full/instance/demethodize.js 统一分发。其中full/instance/demethodize.js是一个分发器当目标对象是Function.prototype本身或其原型链成员时返回虚拟方法否则返回对象自有的demethodize属性保证在未污染全局原型的core-js-pure场景下也能正常工作。使用示例原文档给出的核心示例是借用数组的slice方法const slice Array.prototype.slice.demethodize(); slice([1, 2, 3], 1); // [2, 3]与传统的Array.prototype.slice.call(arr, 1)相比demethodize让方法先被固定后续调用更加简洁。结合 demethodize 的语义还可以举一反三// 解绑 map 后对类数组对象使用 const map Array.prototype.map.demethodize(); map(abc, ch ch.toUpperCase()); // [A, B, C] // 解绑后配合函数式工具使用 const toFixed Number.prototype.toFixed.demethodize(); toFixed(3.14159, 2); // 3.14需要说明的是demethodize返回的新函数对this完全不敏感无论以何种方式调用其第一个参数都会被当作目标对象处理这正是解绑demethodize与绑定bind互为镜像的原因。源码实现深度解析模块注册packages/core-js/modules/esnext.function.demethodize.js 是标准的核心模块注册文件use strict; var $ require(../internals/export); var demethodize require(../internals/function-demethodize); // Function.prototype.demethodize method // https://github.com/js-choi/proposal-function-demethodize $({ target: Function, proto: true, forced: true }, { demethodize: demethodize });其中target: Function表示挂载到Function上proto: true表示挂载到原型Function.prototypeforced: true表示无条件覆盖写入core-js 对提案阶段特性通常采用强制注入以保证行为一致性。底层解绑工具uncurryThis实现的关键在 packages/core-js/internals/function-uncurry-this.js。这个通用工具用于反柯里化把一个依赖this的方法转换为以目标对象为首参的普通函数。其实现分为两条路径var uncurryThisWithBind NATIVE_BIND FunctionPrototype.bind.bind(call, call); module.exports NATIVE_BIND ? uncurryThisWithBind : function (fn) { return function () { return call.apply(fn, arguments); }; };原生bind可用时NATIVE_BIND为真见 internals/function-bind-native.js直接利用Function.prototype.bind.bind(call, call)一次性构造出高性能的解绑工厂这是最优雅且开销最低的路径无原生bind的环境如部分老旧引擎退化为运行时包装函数通过Function.prototype.call.apply(fn, arguments)将传入的首参作为this完成调用。这一底层工具也是 core-js 内部广泛复用的基础设施demethodize正是在它之上构建的语法糖。调用者类型断言aCallable在解绑之前packages/core-js/internals/a-callable.js 会对this做强制类型检查module.exports function (argument) { if (isCallable(argument)) return argument; throw new $TypeError(tryToString(argument) is not a function); };它借助isCallable判断目标是否可调用函数或可调用对象否则抛出TypeError。这意味着对非函数调用demethodize例如str.demethodize()会得到语义明确的类型错误而不是静默失败。测试与行为验证仓库为该方法提供了全局版与纯版两套单元测试可直接作为行为契约参考全局版 tests/unit-global/esnext.function.demethodize.js 断言了存在性、参数个数为 0、函数名为demethodize、非枚举、function () { return 42; }.demethodize()()返回 42以及Array.prototype.slice.demethodize()([1, 2, 3], 1)得到[2, 3]纯版 tests/unit-pure/esnext.function.demethodize.js 验证了core-js-pure模式下通过import demethodize from core-js-pure/full/function/demethodize导入后以demethodize(fn)(obj, ...args)形式调用同样得到一致结果。这两套测试恰好对应上述两条入口链全局原型注入 vs 命名函数导出是理解两种消费方式行为差异的最佳示例。使用注意与边界提案阶段特性demethodize位于esnext命名空间语义以提案为准后续可能调整。引入时建议按入口点显式引入避免无意中启用未定稿的全局 API性能考量每次调用demethodize()都会创建一个新函数。在热路径中建议像示例那样先解绑一次并复用结果而不是反复解绑core-js-pure的区别纯版不会污染Function.prototype而是通过模块导出函数如full/function/demethodize提供等价的demethodize(fn)调用形式二者的行为由对应测试保持一致与其他方法的组合由于返回函数不依赖this它可以被安全地传入map、reduce、setTimeout等会改变this绑定的高阶场景这正是该提案在函数式编程风格中的主要价值。总而言之Function.prototype.demethodize让借方法从手写fn.call的模式中解放出来而 core-js 的实现以极小的代码量复用了成熟的uncurryThis基础设施并配以完整的入口点与测试覆盖是研究提案特性如何在标准库中落地的一个理想样本。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表