ARTICLE DETAIL

资讯详情

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

在 JavaScript 教程仓库中为所有函数扩展 Function.prototype.defer:从延迟执行到装饰器封装

在 JavaScript 教程仓库中为所有函数扩展 Function.prototype.defer:从延迟执行到装饰器封装 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读本文基于《Modern JavaScript Tutorial》en.javascript.info仓库中「原生原型Native prototypes」一节的配套任务讲解如何通过修改Function.prototype为 JavaScript 中所有函数统一添加defer(ms)延迟执行能力。任务分为两档难度第一档用setTimeout(this, ms)实现最简单的延迟后调用原函数第二档要求返回一个装饰器wrapper把延迟与参数透传、this绑定融为一体。读完本文你将掌握原生原型修改的合法姿势、this在原型方法中的指向规律以及用f.apply(this, args)保持对象方法上下文的关键技巧并理解为什么修改原生原型在业界通常被视为危险操作、而 polyfill 是其唯一公认的合理场景。任务背景方法存储在原型里函数也不例外在开始动手写defer之前需要先明确一个底层事实JavaScript 所有内置构造函数都把自己的方法存放在prototype属性上对象实例本身只保存数据。正如仓库章节 native-prototypes 所阐述的Object.prototype处于所有原型链的最顶端它提供了toString等方法Object.prototype.__proto__为null数组实例继承Array.prototype再向上继承Object.prototype例如arr.__proto__ Array.prototype与arr.__proto__.__proto__ Object.prototype均为true函数是内置Function构造函数的对象它们的call/apply等方法都取自Function.prototype。可以用仓库中的验证代码印证函数的原型链function f() {} alert(f.__proto__ Function.prototype); // true alert(f.__proto__.__proto__ Object.prototype); // true, 函数最终也从对象继承下图展示了几个内置原型在链上的整体关系图来自 native-prototypes-classes.svg正因如此给所有函数加方法这件事有一个天然且统一的挂载点Function.prototype。只要往它上面添加一个属性全部函数都能立刻使用无需逐个函数打补丁——这正是本任务的核心思路。任务一为所有函数添加f.defer(ms)仓库中的任务文档 task.mdimportance 5属核心练习给出的需求非常明确在所有函数的原型上添加defer(ms)方法使其在ms毫秒之后执行该函数。期望的使用效果function f() { alert(Hello!); } f.defer(1000); // 1 秒后弹出 Hello!标准解法一行代码挂到 Function.prototype仓库配套的 solution.md 给出的解法简洁得近乎优雅Function.prototype.defer function(ms) { setTimeout(this, ms); }; function f() { alert(Hello!); } f.defer(1000); // shows Hello! after 1 sec剖析这行代码背后的this这段代码看起来只有三行却同时踩中了两个 JavaScript 核心机制原型方法中的this指向调用者。f.defer(1000)在调用时defer内部的this就是函数f本身——方法调用语法obj.method()会把obj作为this传入这里的obj是函数对象f。因此setTimeout(this, ms)等价于setTimeout(f, ms)。setTimeout的第一个参数可以直接接收函数引用。当ms毫秒计时结束后引擎会以普通函数调用的方式执行f弹窗如期出现。这里没有任何箭头函数或参数透传的负担因为原始函数f本身不接受参数。从原型链角度看Function.prototype.defer的查找过程是f自身没有defer属性 → 沿[[Prototype]]找到Function.prototype→ 命中新增的defer。这与仓库章节 prototype-inheritance 中读取属性时若对象自身缺失则自动从原型上取的规则完全一致。边界提醒该方法不接受参数第一版defer有一个明显短板如果原函数需要参数setTimeout(this, ms)无法把它们传递过去。例如function f(a, b) { alert(a b) }配合f.defer(1000)(1, 2)就会失败。这正是仓库中紧接着布置的进阶版任务要解决的问题。任务二把defer()升级为装饰器支持参数透传仓库紧接着的任务 2-defer-to-prototype-extended/task.mdimportance 4把需求升级为为所有函数的原型添加defer(ms)它返回一个包装函数wrapper把真实调用推迟ms毫秒。期望效果是链式调用function f(a, b) { alert( a b ); } f.defer(1000)(1, 2); // 1 秒后显示 3注意参数必须原封不动地传给原始函数而且调用时还不能丢失this。进阶解法rest 参数 f.apply 保持 this仓库配套的 solution.md 给出了完整实现Function.prototype.defer function(ms) { let f this; return function(...args) { setTimeout(() f.apply(this, args), ms); } }; // check it function f(a, b) { alert( a b ); } f.defer(1000)(1, 2); // shows 3 after 1 sec逐层拆解这条调用链可以看到它完成了三件事let f this先把原始函数保存下来。defer返回的是一个新的闭包函数如果不先把this暂存等到闭包真正执行时this早已不是原始函数了。保存f是装饰器模式的经典手法。function(...args)用 rest 参数收集调用时传入的所有实参。f.defer(1000)(1, 2)这一步中(1, 2)会被收集进args数组从而解决了第一版无法传参的问题。setTimeout(() f.apply(this, args), ms)双保险传参。外层箭头函数没有自己的this它会从包裹它的普通函数继承thisf.apply(this, args)再把收集到的参数数组展开传给原始函数f同时把当前的this原样传递。关键点为什么要用f.apply(this, args)仓库的官方解答特别强调了一句话我们在f.apply中使用this是为了让装饰器对对象方法也能正常工作。看这个官方给出的对象方法场景验证Function.prototype.defer function(ms) { let f this; return function(...args) { setTimeout(() f.apply(this, args), ms); } }; let user { name: John, sayHi() { alert(this.name); } } user.sayHi user.sayHi.defer(1000); user.sayHi(); // 1 秒后弹出 John这里的执行顺序值得细品user.sayHi.defer(1000)defer内部的this是user.sayHi函数本身被暂存为f返回值包装函数被重新赋回user.sayHi调用user.sayHi()时包装函数作为对象方法被调用因此它的this是user包装函数内部setTimeout(() f.apply(this, args), ms)中箭头函数捕获了包装函数的this即user于是f.apply(user, args)让原始sayHi在调用时this.name John。如果这里偷懒写成f.apply(null, args)或直接f(args)sayHi里的this就会丢失非严格模式下退化为全局对象输出将不再是John。正是apply(this, args)这一行让装饰器在普通函数与对象方法两种形态下都能正确工作这也是它与第一版解法最大的进阶点。原理纵深为什么可以把方法挂到 Function.prototype从仓库章节 F.prototype 可以知道每个函数天生都带一个prototype属性默认值为{ constructor: F }。但我们在这里并不是给某个构造函数设置原型而是直接往内置的Function.prototype对象上添加新属性所有函数包括构造函数、普通函数、箭头函数之外的具名函数等的[[Prototype]]都指向Function.prototype因此一次赋值Function.prototype.defer ...全库所有函数立即获得defer方法这正是原型共享方法、实例只存数据这一内存高效设计的直接红利从调用方视角看f.defer(1000)的查找路径是f自身 →Function.prototype命中新增方法。这一机制与仓库章节 native-prototypes 中String.prototype.show的例子同构——给String.prototype加方法后BOOM!.show()立即可用给Function.prototype加方法后所有函数立即可用。重要提醒修改原生原型是双刃剑任务在练习层面展示了修改原生原型的强大但仓库正文 native-prototypes 紧接着给出了严肃的工程警告切莫把练习当成生产环境的默认做法原型是全局的很容易产生冲突。如果两个库都添加了String.prototype.show其中一个就会覆盖另一个的方法。因此修改原生原型通常被认为是坏主意。同时正文也明确指出现代编程中唯一被认可的修改原生原型场景是 polyfill垫片——即规范中已定义、但当前引擎尚未实现的方法可以手动实现并挂到内置原型上让旧环境也能运行新语法。仓库给出的经典例子if (!String.prototype.repeat) { // 如果引擎还没有这个方法 String.prototype.repeat function(n) { // 按规范算法重复字符串 n 次 // 完整算法见规范这里的不完美实现通常也够用 return new Array(n 1).join(this); }; } alert( La.repeat(3) ); // LaLaLa把这一原则映射到本文的defer任务上setTimeout是早已普及的 API不存在 polyfill 的必要为Function.prototype添加自定义的defer属于扩展非标准方法会污染全局命名空间若与未来标准方法或第三方库同名即可能互相覆盖。在练习和教学演示中体验原型机制没有问题但在真实项目中更稳妥的做法是使用独立的工具函数或装饰器库避免触碰原生原型。与仓库其余部分的呼应本文的两个任务处于 03-native-prototypes 目录下与同章正文 article.md 的 Changing native prototypes 一节直接对应进阶版返回包装函数延迟调用的思想又与仓库中专门讲解函数包装的 09-call-apply-decorators 一章spy、delay、debounce、throttle 等装饰器一脉相承——Function.prototype.defer的进阶解法本身就是最精简的延迟装饰器原型。建议读者按顺序完成 任务一 与其 解答、任务二 与其 解答再回到正文巩固原生原型、polyfill 与原型修改的完整知识闭环。小结第一版解法Function.prototype.defer function(ms) { setTimeout(this, ms); }利用原型方法中this指向调用函数这一规则一行代码实现延迟执行但不支持传参。进阶版解法返回包装函数用let f this暂存原函数、...args收集实参、setTimeout(() f.apply(this, args), ms)同时完成延迟、参数透传与this保持可安全用于对象方法。底层原理函数的方法统一存放在Function.prototype改一处、全局生效属性查找沿原型链自动完成。工程红线修改原生原型易引发全局冲突除 polyfill 外不被推荐练习场景中理解机制生产环境中优先选用不污染全局的方案。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐Vue CLI 模式与环境变量机制MODE、.env 文件与 NODE_ENV 如何驱动 webpack 配置Vue CLI 模式与环境变量机制MODE、.env 文件与 NODE_ENV 如何驱动 webpack 配置 本篇技术指南围绕 Vue CLI 的“模式m文档/教程前端btop 终端资源监控快速上手指南CPU、内存、磁盘、网络与 GPU 一次看清btop 终端资源监控快速上手指南CPU、内存、磁盘、网络与 GPU 一次看清 btop 是一个 C 编写的终端资源监控器把处理器、内存、磁盘、网络和进CLI指标监控运维PyPTO Pro Kernel 核函数编程指南从 JIT 装饰器、执行域到 TilingKey 特化启动PyPTO Pro Kernel 核函数编程指南从 JIT 装饰器、执行域到 TilingKey 特化启动 Kernel 是 PyPTO Pro 中真正运行在人工智能编译器模型编译深度学习高性能计算CANNAscend上一篇快速上手VRM4U终极解决PMX动画重定向问题的完整指南下一篇APKMirror安卓应用下载工具完整使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表