目录
硬编码的背景颜色
讨厌的控制台日志
拦截广告服务器调用
JavaScript中的猴子补丁的概念是什么?
JavaScript和Python中的猴子补丁有何不同?
猴子补丁在JavaScript中被认为是一种好的实践吗?
猴子补丁的潜在风险是什么?
如何在JavaScript中干净地猴子补丁一个函数?
猴子补丁可以用于测试和调试吗?
原型在JavaScript中的猴子补丁中起什么作用?
猴子补丁如何影响JavaScript中的性能?
猴子补丁可以用来扩展内置的JavaScript对象吗?
JavaScript中猴子补丁的一些替代方案是什么?
首页 web前端 js教程 JavaScript中猴子补丁的务实用途

JavaScript中猴子补丁的务实用途

Feb 17, 2025 pm 12:52 PM

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

热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

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

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1655
14
CakePHP 教程
1413
52
Laravel 教程
1306
25
PHP教程
1252
29
C# 教程
1226
24
前端热敏纸小票打印遇到乱码问题怎么办? 前端热敏纸小票打印遇到乱码问题怎么办? Apr 04, 2025 pm 02:42 PM

前端热敏纸小票打印的常见问题与解决方案在前端开发中,小票打印是一个常见的需求。然而,很多开发者在实...

神秘的JavaScript:它的作用以及为什么重要 神秘的JavaScript:它的作用以及为什么重要 Apr 09, 2025 am 12:07 AM

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

谁得到更多的Python或JavaScript? 谁得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript开发者的薪资没有绝对的高低,具体取决于技能和行业需求。1.Python在数据科学和机器学习领域可能薪资更高。2.JavaScript在前端和全栈开发中需求大,薪资也可观。3.影响因素包括经验、地理位置、公司规模和特定技能。

如何实现视差滚动和元素动画效果,像资生堂官网那样?
或者:
怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? 如何实现视差滚动和元素动画效果,像资生堂官网那样? 或者: 怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? Apr 04, 2025 pm 05:36 PM

实现视差滚动和元素动画效果的探讨本文将探讨如何实现类似资生堂官网(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript的演变:当前的趋势和未来前景 JavaScript的演变:当前的趋势和未来前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? 如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? Apr 04, 2025 pm 05:09 PM

如何在JavaScript中将具有相同ID的数组元素合并到一个对象中?在处理数据时,我们常常会遇到需要将具有相同ID�...

JavaScript引擎:比较实施 JavaScript引擎:比较实施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

前端开发中如何实现类似 VSCode 的面板拖拽调整功能? 前端开发中如何实现类似 VSCode 的面板拖拽调整功能? Apr 04, 2025 pm 02:06 PM

探索前端中类似VSCode的面板拖拽调整功能的实现在前端开发中,如何实现类似于VSCode...

See all articles