搜索
首页web前端js教程JavaScript中猴子补丁的务实用途

Pragmatic Uses of Monkey Patching in JavaScript

核心要点

  • JavaScript中的猴子补丁(MP)是一种技术,允许程序员覆盖、扩展或抑制代码段的默认行为,而无需更改其原始源代码。
  • 虽然MP通常被认为是不好的实践,但它可以作为修改第三方代码以适应特定需求的有用工具,尤其是在无法更改原始源代码的情况下。
  • MP可用于用自定义行为覆盖现有函数,通过在原始代码之前或之后添加自定义行为来增强方法,或拦截Ajax调用以修改其行为。
  • 应该谨慎使用MP,因为它可能导致代码中出现不可预测的行为和冲突,特别是如果在代码库的其他地方使用了已修补的方法。
  • 尽管存在潜在风险,但MP仍然可以成为测试、调试和实施修复的强大工具,它突出了JavaScript作为编程语言的灵活性和动态性。

Pragmatic Uses of Monkey Patching in JavaScript

本文由Moritz Kröger和Tom Greco审核。感谢所有SitePoint的同行评审员,使SitePoint的内容达到最佳状态!

您是否曾经使用过第三方代码,除了一个让您抓狂的小问题外,其他都运行良好?创建者为什么忘记删除那些讨厌的控制台日志?如果那个API调用可以多做一件事,那不是很好吗?如果是这样,那么您就会知道,让维护者实施您的更改可能很困难(或不可能)。但是,自己更改代码呢?如果您没有源代码并且不想自己托管它们,该如何操作?欢迎来到JavaScript猴子补丁的世界之旅!

在本文中,我们将了解什么是猴子补丁,并逐步完成一些不同的示例,使用它来更改第三方小部件的功能以适应我们的需求。

什么是猴子补丁?

猴子补丁(以下简称MP)是一种技术,用于覆盖、扩展甚至抑制代码段的默认行为,而无需更改其原始源代码。这是通过用修复版本替换原始行为来实现的。

本文将使用现有的反馈框小部件,该小部件显示一个简单的、可滑动的弹出窗口(如下图所示),其中包含反馈表单。

Pragmatic Uses of Monkey Patching in JavaScript

源代码已修改为包含用作MP目标的用例。目标是指我们将要修补的特定功能、特性或最低级别的使用方法。

我做的另一个修改是删除了围绕代码的立即调用函数表达式(IIFE)。这样做是为了专注于MP的技术。

您可以在Plunker中找到整个示例,包括本文中讨论的猴子补丁。

猴子补丁不是一种不好的实践吗?

在开始之前,让我们先明确一点:是的,MP被认为是一种不好的实践——邪恶的eval、命令式编程、可变数据结构、双向绑定等等也是如此。

如果您使用其中任何一种,很可能会有相当大的一群人告诉您您做错了,应该更改此或那项以适应更好的条件。但一如既往,有不同的工具和技术可用,它们在特定场景下的适用性各不相同。有时,看起来极端、疯狂或根本不好的东西可能是特定情况下的最后手段。不幸的是,由于某些实践被认为是不好的,您甚至找不到很多文章来描述如何以正确的方式做错事

此处描述的情况可能是不自然的,用虚假的小部件将其推向极端,以显示您的选择。然后,作为读者,您必须决定是否喜欢您看到的内容。如果没有什么别的,在阅读本文之后,您将有更好的理解,以便反对MP。

猴子补丁的目标

在我们深入研究这些技术之前,让我们首先检查一下我们想要实现的目标。修改后的窗口部件有一些代码异味,我们想解决这些问题。

硬编码的背景颜色

第一个是名为toggleError的方法,该方法应该根据布尔参数更改元素的背景颜色

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.css("background-color", "darkgrey");
  } else {
    obj.css("background-color", "");
  }
}

如您所见,它通过jQuery方法css设置background-color属性。这是一个问题,因为我们希望通过样式表规则指定它。

讨厌的控制台日志

在开发小部件时,使用控制台日志来向开发人员提示当前正在执行的内容。在开发过程中这可能是一种不错的方法,但在生产使用中肯定不是最好的方法。因此,我们需要找到一种方法来删除所有这些调试语句。

拦截广告服务器调用

该小部件很棒,但它有一个奇怪的行为。每次初始化脚本时,它都会向一个奇怪的广告服务器发出请求,并在我们的页面上显示不必要的膨胀内容。

FeedbackBox.prototype.init = function() {
  // 我们想要跳过的广告服务器调用
  $.ajax('vendor/service.json', {
    method: 'GET'
  }).then(function(data) {
    console.log("FeedbackBox: AdServer contacted");
  });

  ...

注意:演示代码针对Plunker中的JSON文件来模拟传出的Ajax请求,但我希望您明白这一点。

覆盖方法

MP的关键概念之一是获取现有函数并使用自定义行为在调用原始代码之前或之后对其进行增强。但调用原始实现并非总是必要的,因为有时您只想用自定义操作替换它。这种方法非常适合帮助我们解决硬编码的背景颜色问题。

应用MP的位置必须在加载并提供原始实现之后。通常,您应该努力使更改尽可能接近目标,但请记住,目标的实现可能会随着时间的推移而发生变化。至于我们的示例,初始化以及MP将进入文件main.js。

查看小部件实现,我们可以看到有一个FeedbackBox对象作为小部件的根。稍后,将在其原型上实现toggleError函数。

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.css("background-color", "darkgrey");
  } else {
    obj.css("background-color", "");
  }
}

由于JavaScript是一种动态语言,其对象可以在运行时修改,因此我们最终将做的只是用我们的自定义方法替换toggleError。唯一需要注意的是保持签名(名称和传递的参数)相同。

FeedbackBox.prototype.init = function() {
  // 我们想要跳过的广告服务器调用
  $.ajax('vendor/service.json', {
    method: 'GET'
  }).then(function(data) {
    console.log("FeedbackBox: AdServer contacted");
  });

  ...

新的实现现在只需向给定的元素添加一个错误类,从而允许我们通过css设置背景颜色。

增强方法

在前面的示例中,我们看到了如何通过提供我们自己的方法来覆盖原始实现。另一方面,处理控制台日志应该只是过滤掉特定的调用并抑制它们。成功的关键是检查您嵌入的代码并尝试理解其工作流程。通常,这是通过启动您选择的浏览器中的开发者控制台并在加载的资源中窥视、添加断点和调试目标代码部分来完成的,以便了解它的功能。但是,这一次,您只需在另一个选项卡中打开名为vendor/jquery.feedBackBox.js的Plunker示例中的实现即可。

通过查看调试消息,我们可以看到它们中的每一个都以FeedbackBox:开头。因此,实现我们想要的目标的一种简单方法是拦截原始调用,检查要写入的提供的文本,并且仅当它不包含调试提示时才调用原始方法。

为此,让我们首先将原始console.log存储到一个变量中以供以后使用。然后,我们再次用我们的自定义实现覆盖原始实现,该实现首先检查提供的属性文本是否为字符串类型,如果是,则检查它是否包含子字符串FeedbackBox:。如果是,我们将什么也不做,否则我们将通过调用其apply方法来执行原始控制台代码。

请注意,此方法将上下文作为第一个参数,这意味着应该在该对象上调用该方法,以及一个神奇的arguments变量。后者是最初传递给原始console.log调用的所有参数的数组。

function FeedbackBox(elem, options) {
  this.options = options;  
  this.element = elem;  
  this.isOpen = false;
}

FeedbackBox.prototype.toggleError = function(obj, isError) {
  ...
}

注意:您可能想知道为什么我们没有简单地转发text属性。好吧,console.log实际上可以用无限的参数调用,最终这些参数将连接到单个文本输出。因此,与其定义所有这些参数(对于无限的可能性来说可能非常困难),我们只需转发所有传入的内容。

拦截Ajax调用

最后但并非最不重要的一点是,让我们看看如何解决广告服务器问题。让我们再次查看小部件的init函数:

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.css("background-color", "darkgrey");
  } else {
    obj.css("background-color", "");
  }
}

第一个想法可能是打开浏览器并搜索如何覆盖jQuery插件。根据您的搜索技能的好坏,您可能会或可能不会找到合适的答案。但是,让我们停下来思考一下这里到底发生了什么。无论jQuery对其ajax方法做了什么,它最终都会在某个时候创建一个本机XMLHttpRequest。

让我们看看它在幕后是如何工作的。在MDN上找到的最简单的示例向我们展示了这一点:

FeedbackBox.prototype.init = function() {
  // 我们想要跳过的广告服务器调用
  $.ajax('vendor/service.json', {
    method: 'GET'
  }).then(function(data) {
    console.log("FeedbackBox: AdServer contacted");
  });

  ...

我们看到创建了一个新的XMLHttpRequest实例。它有一个onreadystatechange方法,我们实际上并不关心,然后是open和send方法。太好了。所以我们的想法是猴子补丁send方法并告诉它不要执行对特定URL的调用。

function FeedbackBox(elem, options) {
  this.options = options;  
  this.element = elem;  
  this.isOpen = false;
}

FeedbackBox.prototype.toggleError = function(obj, isError) {
  ...
}

好吧,事实证明您无法从对象本身获取目标URL。糟糕。那我们该怎么办?我们将其放在对象上。寻找获取URL的第一个机会,我们可以看到open方法将其作为第二个参数接受。为了使URL在对象本身可用,让我们首先MP open方法。

和以前一样,我们将原始open方法存储在一个变量中以供以后使用。然后我们用自定义实现覆盖原始实现。由于我们可以使用JavaScript(一种动态语言),因此我们可以随时创建一个新属性并将其命名为_url,该属性将设置为传入参数的值。

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.addClass("error");
  } else {
    obj.removeClass("error");
  }
};

除此之外,我们调用原始open方法,不做任何其他操作。

重新审视我们的send MP,现在很明显如何解决条件检查。以下是修改后的版本:

var originalConsoleLog = console.log;
console.log = function(text) {
  if (typeof text === "string" && text.indexOf("FeedbackBox:") === 0) {
    return;
  }

  originalConsoleLog.apply(console, arguments);
}

结论

我们在这里看到的是关于使用猴子补丁在运行时更改代码行为的简短介绍。但更重要的是,我希望这篇文章能够让您了解如何处理猴子补丁问题。虽然补丁本身通常很简单,但重要的是如何在运行时调整代码的想法。

此外,我希望无论您对猴子补丁有何看法,您都有机会看到使用动态语言的美妙之处,它允许您动态地在运行时更改甚至本机实现。

关于实用猴子补丁的常见问题解答 (FAQ)

JavaScript中的猴子补丁的概念是什么?

JavaScript中的猴子补丁是一种技术,其中内置对象或用户定义对象的行为被修改,通常是通过添加、修改或更改对象的原型来实现的。这是一种扩展或更改代码行为而不更改原始源代码的方法。此技术可用于实施修复、增强现有函数,甚至用于测试和调试目的。

JavaScript和Python中的猴子补丁有何不同?

虽然JavaScript和Python中的猴子补丁的概念相同——修改或扩展对象的行为——但由于语言本身的差异,实现方式有所不同。在JavaScript中,猴子补丁通常是通过修改对象的原型来完成的,而在Python中,它是通过添加或更改类或实例方法来完成的。这两种语言的灵活性都允许进行猴子补丁,但应谨慎使用此技术,以避免出现意外行为。

猴子补丁在JavaScript中被认为是一种好的实践吗?

猴子补丁是一个强大的工具,但它并非没有争议。虽然它可以快速修改或扩展功能而无需更改原始源代码,但它也可能导致不可预测的行为和冲突,尤其是在过度使用或使用不当时。因此,通常建议谨慎和负责任地使用猴子补丁,并始终考虑对整个代码库的潜在影响。

猴子补丁的潜在风险是什么?

猴子补丁的主要风险是它可能导致代码中出现不可预测的行为和冲突。因为它修改了现有对象的行为,所以如果在代码库的其他地方使用了已修补的方法,它可能会破坏代码。它还可能导致其他开发人员感到困惑,他们可能不知道这些修改。因此,务必清晰而全面地记录任何猴子补丁。

如何在JavaScript中干净地猴子补丁一个函数?

要在JavaScript中干净地猴子补丁一个函数,您可以围绕原始函数创建一个包装器。此包装器函数将调用原始函数,然后根据需要添加或修改行为。这样,原始函数保持不变,附加行为清晰地分开,使代码更易于理解和维护。

猴子补丁可以用于测试和调试吗?

是的,猴子补丁可以作为测试和调试的有用工具。通过修改或扩展函数或方法的行为,您可以模拟不同的场景、注入错误或添加日志来跟踪代码的执行。但是,重要的是在生产代码中删除或隔离这些补丁,以避免任何意外的副作用。

原型在JavaScript中的猴子补丁中起什么作用?

在JavaScript中,原型在猴子补丁中起着至关重要的作用。由于JavaScript是一种基于原型的语言,因此每个对象都有一个原型,它从中继承属性和方法。通过修改对象的原型,您可以更改该对象所有实例的行为。这是JavaScript中猴子补丁的基础。

猴子补丁如何影响JavaScript中的性能?

猴子补丁对JavaScript性能的影响通常很小。但是,过度或不当使用猴子补丁可能会导致性能问题。例如,如果在代码中频繁使用已修补的方法,则附加行为可能会减慢执行速度。因此,务必谨慎使用猴子补丁并定期监控性能。

猴子补丁可以用来扩展内置的JavaScript对象吗?

是的,猴子补丁可以用来扩展内置的JavaScript对象。通过修改内置对象的原型,您可以添加新的方法或属性,这些方法或属性将可用于该对象的所有实例。但是,应谨慎执行此操作,以避免与未来版本的JavaScript发生冲突,这些版本可能会引入相同的方法或属性。

JavaScript中猴子补丁的一些替代方案是什么?

JavaScript中猴子补丁有几种替代方案。一种常见的方法是使用组合,您创建一个包含原始对象并添加或覆盖行为的新对象。另一种方法是使用继承,您创建一个从原始类继承并覆盖方法的新类。这些方法可以提供与猴子补丁类似的灵活性,但具有更好的封装性和更少的冲突风险。

以上是JavaScript中猴子补丁的务实用途的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

node.js流带打字稿node.js流带打字稿Apr 30, 2025 am 08:22 AM

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python vs. JavaScript:性能和效率注意事项Python vs. JavaScript:性能和效率注意事项Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript的起源:探索其实施语言JavaScript的起源:探索其实施语言Apr 29, 2025 am 12:51 AM

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能