JavaScript装饰者模式:从基础实现到AOP扩展与应用

作者:小括号

时间:2026-10-23

浏览:551

本文深入解析JavaScript中的装饰者模式,探讨如何在保持原有函数逻辑不变的前提下动态添加新功能。通过对比传统对象装饰与基于Function.prototype的AOP实现(before/after方法),详细阐述其执行顺序与this指向处理,并结合用户数据上报场景提供完整代码示例,帮助开发者降低耦合度并提升代码可维护性。

文章目录

装饰者模式概述与问题背景

在JavaScript函数开发过程中,若需为现有函数增加与其核心功能无关的新特性,常规做法往往是在原函数内部直接追加相关代码。这种做法虽能实现功能,但由于新增逻辑与原有逻辑缺乏内在关联,会导致后期维护成本显著上升,并引发函数结构臃肿的问题。

更为优雅的方案是引入装饰器模式。该模式的核心在于保持现有函数及其内部实现代码的完整性,将新增功能封装为独立的函数,并通过包装机制与原函数协同执行,从而实现功能的动态扩展。

首先,我们来看一个基础的JavaScript版装饰者模式实现示例。

基础对象装饰器实现


var Plane = function(){}

Plane.prototype.fire = function(){
 console.log('发射普通子弹');
}

//增加两个装饰类,导弹类和原子弹类
var MissileDecorator = function(plane){
 this.plane = plane;
}
MissileDecorator.prototype.fire = function(){
 this.plane.fire();
 console.log('发射导弹');
}

var AtomDecorator = function(plane){
 this.plane = plane;
}
AtomDecorator.prototype.fire = function(){
 this.plane.fire();
 console.log('发射原子弹');
}

var plane = new Plane();
console.log(plane);
plane = new MissileDecorator(plane);
console.log(plane);
plane = new AtomDecorator(plane);
console.log(plane);

plane.fire();

/*
发射普通子弹
发射导弹
发射原子弹
*/

接下来展示一种升级版的装饰器模式实现方式,该方法通过为JavaScript的Function构造函数添加实例方法before和after来达成目标。

Function.prototype.before和Function.prototype.after方法接收一个函数作为参数,该参数即为承载新增功能代码的新函数。

在方法内部,首先保存当前的this引用。由于Function是JavaScript中所有函数的构造器,因此Function.prototype中的this指向具体的实例函数,保存该引用有助于后续正确调用原函数。

随后,返回一个代理函数。需要注意的是,此处的“代理”仅指结构上的相似性,并不承担控制对象访问等传统代理职责。其核心工作是将请求分别转发给新添加的函数和原函数,并确保执行顺序:前置装饰(Function.prototype.before)确保新函数在原函数之前执行,后置装饰(Function.prototype.after)则相反,以此实现动态装饰效果。

基于Function原型的AOP装饰器


// AOP 装饰函数
Function.prototype.before = function(beforefn){
 var _self = this;  //保存原函数的引用
 return function(){  //返回包含了原函数和新函数的‘代理'函数
  beforefn.apply(this, arguments);  //先执行新函数,且保证this不会被劫持,新函数接受的参数也会原封不动的传入原函数,新函数在原函数之前执行
  return _self.apply(this, arguments); //再执行原函数并返回原函数的执行结果,并保证this不被劫持
 }
}

Function.prototype.after = function(afterfn){
 var _self = this;  //保存原函数的引用
 return function(){  //返回包含了原函数和新函数的‘代理'函数
  var ret = _self.apply(this, arguments); //先执行原函数并返回原函数的执行结果,并保证this不被劫持,原函数执行的结果会赋值给ret变量,交由'代理'函数最后return
  afterfn.apply(this, arguments);   //再执行新函数,且保证this不会被劫持,新函数接受的参数也会原封不动的传入原函数,新函数在原函数之前执行  
  return ret; 
 }
}

//定义原函数
var print = function(){
 console.log('打印原函数执行结果');
}

print = print.before(function(){
 console.log('打印前置装饰函数的执行结果');
})

print = print.after(function(){
 console.log('打印后置装饰函数的执行结果');
})

//执行装饰后的print函数,为原函数print函数添加的装饰器对用户来说看起来是透明的
print();

//打印结果
/*
打印前置装饰函数的执行结果
打印原函数执行结果
打印后置装饰函数的执行结果
*/

上述AOP装饰器通过在Function.prototype上直接添加before和after方法实现。然而,直接污染函数原型在某些场景下并非最佳实践。一种变通方案是将原函数和新函数均作为参数传入before和after方法中,从而避免原型污染。

非原型污染的实现变通


var before = function(fn, beforefn){
 return function(){
  beforefn.apply(this, arguments)
  return fn.apply(this, arguments)
 }
}

var after = function(fn, agterfn){
 return function(){
  var ret = fn.apply(this, arguments)
  agterfn.apply(this, arguments)
  return ret;
 }
}

var a = function(){
 console.log('原函数执行结果');
}

a = before(a, function(){
 console.log('前置装饰函数执行结果');
})

a = after(a, function(){
 console.log('后置装饰函数执行结果');
})

a()
/*
前置装饰函数执行结果
原函数执行结果
后置装饰函数执行结果
*/

最后,通过一个具体的装饰器模式应用实例来巩固理解。

在实际开发中,用户数据上报是一个常见需求。通常在项目开发后期,此类需求才会陆续提出。若此时直接在对应业务函数中嵌入数据上报代码,不仅修改繁琐,还会增加开发与测试成本。此时,利用装饰器模式将上报函数装饰到原有业务函数上,是更为理想的解决方案。

装饰器模式在数据上报中的应用


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>装饰者模式应用数据上报</title>
</head>
<body>
 <button type="button" id="btn">点击登录并上报数据</button>
</body>
<script>
var showDialog = function(){
 console.log('显示登录弹窗');
}

var log = function(){
 console.log('计数上报');
}

var after = function(fn, afterFn){
 return function(){
  var ret = fn.apply(this, arguments)
  afterFn.apply(this, arguments)
  return ret;
 }
}

showDialog = after(showDialog, log)

document.getElementById('btn').onclick = showDialog;

</script>
</html>

以上内容即为本文的全部讲解,希望能为读者的学习提供有益帮助,也欢迎大家继续支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章