iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >Javascript装饰器原理分析
  • 319
分享到

Javascript装饰器原理分析

2024-04-02 19:04:59 319人浏览 薄情痞子
摘要

这篇文章将为大家详细讲解有关javascript装饰器原理分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一个以@开头的描述性词语。英语的decorator动词是dec

这篇文章将为大家详细讲解有关javascript装饰器原理分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

一个以@开头的描述性词语。英语的decorator动词是decorate,装饰的意思。其中词根dek(dec发音)原始印欧语系中意思是“接受”。即,原来的某个事物接受一些新东西(而变得更好)。

从另外一个角度描述,装饰器主要是在被装饰对象的外部起作用,而非入侵其内部发生什么改变。装饰器模式同时也是一种开发模式,其地位虽然弱于mvcioc等,但不失为一种优秀的模式。

JavaScript的装饰器可能是借鉴自python也或许是Java。较为明显的不同的是大部分语言的装饰器必须是一行行分开,而js的装饰器可以在一行中。

装饰器存在的意义

举个例子:我拿着员工卡进入公司总部大楼。因为每个员工所属的部门、级别不同,并不能进入大楼的任何房间。每个房间都有一扇门;那么,公司需要安排每个办公室里至少一个人关于验证来访者的工作:

  1. 先登记来访者

  2. 验证是否有权限进入,如果没有则要求其离开

  3. 记录其离开时间

还有一个选择方式,就是安装电子门,门锁只是将员工卡的信息传输给机房,由特定的程序验证。

前者暂且称之为笨模式,代码如下:

function A101(who){  
record(who,new Date(),'enter');  
if (!permission(who)) {    
record(who,new Date(),'no permission')    
return void;  
}  // 继续执行  
doSomeWork();  
record(who,new Date(),'leave')}
function A102(who){record(who,new Date(),'enter');  
if (!permission(who)) {    
record(who,new Date(),'no permission')    
return void;  }  
// 继续执行  
doSomeWork();  
record(who,new Date(),'leave')}
// ...

有经验的大家肯定第一时间想到了,把那些重复语句封装为一个方法,并统一调用。是的,这样可以解决大部分问题,但是还不够“优雅”。同时还有另外一个问题,如果“房间”特别多,又或者只有大楼奇数号房间要验证偶数不验证,那岂不是很“变态”?如果使用装饰器模式来做,代码会如下面这样的:

@verify(who)class Building {  
@verify(who)  A101(){}  
@verify(who)  A102(){}  
//...}

verify是验证的装饰器,而其本质就是一组函数。

JavaScript装饰器

正如先前的那个例子,装饰器其实本身就是一个函数,它在执行被装饰的对象之前先被执行。

在JavaScript中,装饰器的类型有:

  • 存取方法(属性的get和set)

  • 字段

  • 方法

  • 参数

由于目前装饰器概念还处于提案阶段,不是一个正式可用的js功能,所以想要使用这个功能,不得不借助翻译工具,例如Babel工具或者typescript编译JS代码转后才能被执行。我们需要先搭建运行环境,配置一些参数。(以下过程,假设已经正确安装了nodejs开发环境以及包管理工具)

cd project && npm initnpm i -D @babel/cli @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/preset-env babel-plugin-parameter-decorator

创建一个.babelrc配置文件,如下:

{  "presets": ["@babel/preset-env"],  "plugins": [    
["@babel/plugin-proposal-decorators", { "legacy": true }],    
["@babel/plugin-proposal-class-properties", { "loose": true }],    
"babel-plugin-parameter-decorator"  ]}

利用下面的转换命令,我们可以得到ES5的转换程序:

npx babel source.js --out-file target.js

类装饰器

创建一个使用装饰器的JS程序decorate-class.js

@classDecoratorclass Building {  
constructor() {    
this.name = "company";  
}}
const building = new Building();
function classDecorator(target) {  
console.log("target", target);}

以上是最最简单的装饰器程序,我们利用babel将其“翻译”为ES5的程序,然后再美化一下后得到如下程序。

"use strict";
var _class;
function _classCallCheck(instance, Constructor) {  
if (!(instance instanceof Constructor)) {    
throw new TypeError("Cannot call a class as a function");  
}}
var Building =  classDecorator(    
(_class = function Building() {      
_classCallCheck(this, Building);
      this.name = "company";    
      })  ) || _class;
var building = new Building();
function classDecorator(target) {  
console.log("target", target);}

第12行就是在类生成过程中,调用函数形态的装饰器,并将构造函数(类本身)送入其中。同样揭示了装饰器的第一个参数是类的构造函数的由来。

方法 (method)装饰器

稍微修改一下代码,依旧是尽量保持最简单:

class Building {  constructor() {    
this.name = "company";  
}  
@methodDecorator  
openDoor() {    
console.log("The door being open");  
}}
const building = new Building();
function methodDecorator(target, property, descriptor) {  
console.log("target", target);  
if (property) {    
console.log("property", property);  
}  
if (descriptor) {    
console.log("descriptor", descriptor);  
}  
console.log("=====end of decorator=========");
}

然后转换代码,可以发现,这次代码量突然增大了很多。排除掉_classCallCheck、_defineProperties和_createClass三个函数,关注_applyDecoratedDescriptor函数:

function _applyDecoratedDescriptor(  target,  property,  decorators,  descriptor,  context) {  
var desc = {};  
Object.keys(descriptor).forEach(function (key) {    
desc[key] = descriptor[key];  
});  
desc.enumerable = !!desc.enumerable;  
desc.configurable = !!desc.configurable;  
if ("value" in desc || desc.initializer) {    
desc.writable = true;  
}  
desc = decorators    .slice()    .reverse()    .reduce(function (desc, decorator) {      
return decorator(target, property, desc) || desc;    
}, desc);  
if (context && desc.initializer !== void 0) {    
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;    
desc.initializer = undefined;  
}  
if (desc.initializer === void 0) {    
Object.defineProperty(target, property, desc);    
desc = null;  
}  
return desc;}

它在生成构造函数之后,执行了这个函数,特别注意,这个装饰器函数是以数组形式的参数传递的。然后到上述代码的17~22行,将装饰器逐个应用,其中对装饰器的调用就在第21行。

它发送了3个参数,target指类本身。property指方法名(或者属性名),desc是可能被先前装饰器被处理过的descriptor,如果是第一次循环或只有一个装饰器,那么就是方法或属性本身的descriptor。

存取器(accessor)装饰

JS关于类的定义中,支持get和set关键字针对设置某个字段的读写操作逻辑,装饰器也同样支持这类方法的操作。

class Building {  
constructor() {    
this.name = "company";  
}  
@propertyDecorator  get roomNumber() {    
return this._roomNumber;  
}
  _roomNumber = "";  
  openDoor() {    
  console.log("The door being open");  
  }}

有心的读者可能已经发现了,存取器装饰的代码与上面的方法装饰代码非常接近。关于属性 get和set方法,其本身也是一种方法的特殊形态。所以他们之间的代码就非常接近了。

属性装饰器

继续修改源代码:

class Building {  
constructor() {    
this.name = "company";  
}  
@propertyDecorator  roomNumber = "";
}
const building = new Building();
function propertyDecorator(target, property, descriptor) {  
console.log("target", target);  
if (property) {    
console.log("property", property);  
}  
if (descriptor) {    
console.log("descriptor", descriptor);  
}  
console.log("=====end of decorator=========");
}

转换后的代码,还是与上述属性、存取器的代码非常接近。但除了_applyDecoratedDescriptor外,还多了一个_initializerDefineProperty函数。这个函数在生成构造函数时,将声明的各种字段绑定给对象。

参数装饰器

参数装饰器的使用位置较之前集中装饰器略有不同,它被使用在行内。

class Building {  
constructor() {    
this.name = "company";  
}  
openDoor(@parameterDecorator num, @parameterDecorator zoz) {    
console.log(`${num} door being open`);  
}}
const building = new Building();
function parameterDecorator(target, property, key) {  
console.log("target", target);  
if (property) {    
console.log("property", property);  
}  
if (key) {    
console.log("key", key);  
}  
console.log("=====end of decorator=========");
}

转换后的代码区别就比较明显了,babel并没有对其生成一个特定的函数对其进行特有的操作,而只在创建完类(构造函数)以及相关属性、方法后直接调用了开发者自己编写的装饰器函数:

var Building = function () {  
function Building() {    
_classCallCheck(this, Building);
    this.name = "company";  
    }
  _createClass(Building, [{    key: "openDoor",    value: function openDoor(num, zoz) {      console.log("".concat(num, " door being open"));    }  }]);
  parameterDecorator(Building.prototype, "openDoor", 1);  parameterDecorator(Building.prototype, "openDoor", 0);  return Building;}();

装饰器应用

使用参数——闭包

以上所有的案例,装饰器本身均没有使用任何参数。然实际应用中,经常会需要有特定的参数需求。我们再回到一开头的例子中verify(who),其中需要传入一个身份变量。哪又怎么做?我们少许改变一下类装饰器的代码:

const who = "Django";@classDecorator(who)class Building {
  constructor() { 
     this.name = "company"; 
      }}

转换后得到

// ...var who = "DjanGo";var Building =  ((_dec = classDecorator(who)),  _dec(    
(_class = function Building() {      
_classCallCheck(this, Building);
      this.name = "company";    
      })  
      ) || _class);
      // ...

请注意第4第5行,它先执行了装饰器,然后再用返回值将类(构造函数)送入。相对应的,我们就应该将构造函数写成下面这样:

function classDecorator(people) {  
console.log(`hi~ ${people}`);  
return function (target) {    
console.log("target", target);  
};
}

同样的,方法、存取器、属性和参数装饰器均是如此。

装饰器包裹方法

到此,我们已经可以将装饰器参数与目标对象结合起来,进行一些逻辑类的操作。那么再回到文章的开头的例子中:需求中要先验证来访者权限,然后记录,最后在来访者离开时再做一次记录。此时需要监管对象方法被调用的整个过程。

请大家留意那个方法装饰器的descriptor,我们可以利用这个对象来“重写”这个方法。

class Building {  
constructor() {    
this.name = "company";  
}
  @methodDecorator("Gate")  
  openDoor(firstName, lastName) {    
  return `The door will be open, when ${firstName} ${lastName} is walking into the ${this.name}.`;  
  }}
let building = new Building();console.log(building.openDoor("django", "xiang"));
function methodDecorator(door) {  
return function (target, property, descriptor) {    
let fn = descriptor.value;    
descriptor.value = function (...args) {      
let [firstName, lastName] = args;      
console.log(`log: ${firstName}, who are comming.`);      
// verify(firstName,lastName)      
let result = Reflect.apply(fn, this, [firstName, lastName]);      
console.log(`log: ${result}`);  
    console.log(`log: ${firstName}, who are leaving.`);      
    return result;    
    };    
    return descriptor;  
    };}

代码第17行,将原方法暂存;18行定义一个新的方法,20~25行,记录、验证和记录离开的动作。

log: Django, who are comming.log: The door will be open, when Django Xiang is walking in to the company.log: Django, who are leaving.The door will be open, when Django Xiang is walking in to the company

装饰顺序

通过阅读转换后的代码,我们知道装饰器工作的时刻是在类被实例化之前,在生成之中完成装饰函数的动作。那么,如果不同类型的多个装饰器同时作用,其过程是怎样的?我们将先前的案例全部整合到一起看看:

const who = "Django";@classDecorator(who)class Building {  
constructor() {    
this.name = "company";  
}
  @propertyDecorator  roomNumber = "";
  @methodDecorator  openDoor(@parameterDecorator num) {    
  console.log(`${num} door being open`);  
  }
  @accessorDecorator  get roomNumber() {    
  return this._roomNumber;  
  }}
const building = new Building();
function classDecorator(people) {  
console.log(`class decorator`);  
return function (target) {    
console.log("target", target);  
};}
function methodDecorator(target, property, descriptor) {  
console.log("method decorator");}
function accessorDecorator(target, property, descriptor) {  
console.log("accessor decorator");}
function propertyDecorator(target, property, descriptor) {  
console.log("property decoator");}
function parameterDecorator(target, property, key) {  
console.log("parameter decorator");}
  1. class decorator

  2. parameter decorator

  3. property decoator

  4. method decorator

  5. accessor decorator

还可以通过阅读转换后的源代码得到执行顺序:

  1. 类装饰器(在最外层)

  2. 参数装饰器(在生成构造函数最里层)

  3. 按照出现的先后顺序的:属性、方法和存取器

关于“Javascript装饰器原理分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

--结束END--

本文标题: Javascript装饰器原理分析

本文链接: https://www.lsjlt.com/news/88953.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Javascript装饰器原理分析
    这篇文章将为大家详细讲解有关Javascript装饰器原理分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一个以@开头的描述性词语。英语的decorator动词是dec...
    99+
    2024-04-02
  • python装饰器原理源码示例分析
    目录前言一、什么是装饰器二、为什么要用装饰器三、简单的装饰器四、装饰器的语法糖五、装饰器传参六、带参数的装饰器七、类装饰器八、带参数的类装饰器九、装饰器的顺序前言 最近有人问我装饰器...
    99+
    2024-04-02
  • Python 装饰器工作原理解析
    #!/usr/bin/env python #coding:utf-8 """ 装饰器实例拆解 """ def login00(func):     print('00请通过验证用户!')     return func def ...
    99+
    2023-01-31
    工作原理 Python
  • JavaScript装饰器的实现原理详解
    目录装饰器的常见作用装饰类的属性装饰类注意实例应用最近在使用TS+Vue的开发模式,发现项目中大量使用了装饰器,看得我手足无措,今天特意研究一下实现原理,方便自己理解这块知识点。 装...
    99+
    2022-11-13
    JavaScript装饰器实现原理 JavaScript装饰器
  • python 多层装饰器分析
    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是1函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设...
    99+
    2023-01-30
    多层 python
  • Python装饰器实例分析
    这篇“Python装饰器实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python装饰器实例分析”文章吧。任务超时退...
    99+
    2023-06-27
  • python中装饰器的原理
    装饰器这玩意挺有用,当时感觉各种绕,现在终于绕明白了,俺滴个大爷,还是要慢慢思考才能买明白各种的真谛,没事就来绕一绕   def outer(func): def inner(): print("认证成功") ...
    99+
    2023-01-30
    原理 python
  • python装饰器的示例分析
    这篇“python装饰器的示例分析”除了程序员外大部分人都不太理解,今天小编为了让大家更加理解“python装饰器的示例分析”,给大家总结了以下内容,具有一定借鉴价值,内容详细步骤清晰,细节处理妥当,希望大家通过这篇文章有所收获,下面让我们...
    99+
    2023-06-06
  • python装饰器底层原理详解
    目录1 python装饰器的作用2 python装饰器的原理3 python装饰器的实现3.1 最简陋的装饰器3.2 给有返回值的函数加上装饰器3.3 给有返回值和参数的函数加上装饰...
    99+
    2024-04-02
  • 一文理解JavaScript装饰器模式
    装饰器模式想必大家并不陌生:它允许向一个现有的对象添加新的功能,同时又不改变其结构,属于结构型模式,它是作为现有的类的一个包装。 这种模式创建了一个装饰类,用来包装原有的类,并在保持...
    99+
    2024-04-02
  • Python装饰器原理——偷梁换柱
    本篇博客写于学完  金角大王Alex和海峰老师  于  老男孩2016年周末班S14期第4周03~08章节   装饰器  的相关内容,为对该部分视频的一个思考与补充。 下面举例子,代码如下: 例1,不使用语法糖(@xxx): 1 im...
    99+
    2023-01-31
    偷梁换柱 原理 Python
  • Java装饰器模式实例分析
    今天小编给大家分享一下Java装饰器模式实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。优点装饰类和被装饰类可以独立发...
    99+
    2023-06-29
  • JavaScript中装饰者模式与AOP的示例分析
    这篇文章将为大家详细讲解有关JavaScript中装饰者模式与AOP的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是装饰者模式当我们拍了一张照片准备发朋友圈...
    99+
    2024-04-02
  • JavaScript中Debugger的原理分析
    这篇文章给大家分享的是有关JavaScript中Debugger的原理分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。代码运行的原理是什么代码的运行方式可以分为直接执行和解释执...
    99+
    2024-04-02
  • python装饰器代码的示例分析
    这篇文章主要介绍python装饰器代码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.装饰器通用模型def wrapper(fn):    def&nbs...
    99+
    2023-06-29
  • vue中的.sync修饰符用法及原理分析
    目录.sync修饰符用法及原理例如.sync修饰符的用法总结需求描述解决方案.sync修饰符用法及原理 vue中我们经常会用v-bind(缩写为:)给子组件传入参数。 或者我们会给子...
    99+
    2024-04-02
  • JavaScript原始包装类型实例分析
    这篇文章主要讲解了“JavaScript原始包装类型实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript原始包装类型实例分析”吧!引出问题: 如下一段简单的代...
    99+
    2023-06-30
  • Python深入分析@property装饰器的应用
    目录什么是propertyproperty属性定义的两种方式@property属性值的限制什么是property 简单地说就是一个类里面的方法一旦被@property装饰,就可以像调...
    99+
    2024-04-02
  • PHP中装饰器模式的示例分析
    这篇文章主要介绍了PHP中装饰器模式的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Gof类图及解释GoF定义:将一个类的接口转换成客户希望的另外一个接口。Adapt...
    99+
    2023-06-20
  • JAVA装饰器模式应用实例分析
    本篇内容主要讲解“JAVA装饰器模式应用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JAVA装饰器模式应用实例分析”吧!什么是装饰器模式装饰器(Decorator)模式的定义: 指在不...
    99+
    2023-06-30
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作