欢迎光临散文网 会员登陆 & 注册

Angular/TypeScript中那些值得了解的知识——注解

2023-01-04 15:13 作者:OpenTiny社区  | 我要投稿


Angular中无处不在的注解(当然,更准确地说,注解是属于TypeScript而非Angular,只是通过Angular体现),它为何产生,又有何妙用?

注解的产生背景和意义——给你想要处理的程序打个标

上一篇文章中我介绍了“依赖注入”,说到了依赖注入框架,其实是个巨大的Map。那么,它如何记录他的键值对呢,比如一个在src/app/car/carService的服务要告诉框架,它需要被管理,被在应用启动时就被构造出来,以Java为例,需要放在配置文件中,类似这样:

其中的bean的id就是键,class的路径,就是要让框架管理的全路径(唯一)的对象。

这样的作法当需要依赖注入框架管理的对象越来越多时,每加一个bean都记录它的全路径,填在这里,很复杂,这也是10年前的人难以入门JavaWeb的原因,过多的配置、以及引如jar包、排除jar包冲突等事情要做,自学的话,把项目启动起来都费劲。

而Java在JDK 1.5版本中刚好提供了注解的能力。设计它的本意是一种注释机制,比如,我的一个方法过时了,不希望别人再用了,可以这样写

这样别人调用getReadedDirPath这个方法时,就会有编译警告,IDE上也有相应提示,如图

也就是说,注解的实质是标注,作用是可以在程序运行的任何时刻获取你标注的类、方法、属性的信息,而借助这个能力,很多框架开始优化它的配置文件,以Spring为例,它是一个依赖注入框架,既然要管理的对象需要知道它的全路径,是否可以以注解的手段代替它,进而省去繁杂的配置文件?当然是可以的,于是Spring这么做了,Hibernate、Mybatis等等所有现在还会被使用到的JavaWeb开发框架,都这么做了。于是,代码就有了这样的变化。

别的不说,至少代码清晰多了,要配置的内容也少了。

来都来了,只打个标吗——代码增强

上面介绍了Java中的注解,可以实现打标的能力,那么打标有什么好的使用场景吗?

有的。那就是代码增强。

既然可以在程序运行过程中拿到打标的内容,我们可以在这个时机加入一些我们的逻辑,就是代码增强。举个例子,比如你需要在所有Java的控制器方法中加入日志,最佳实践是这样做:

在某些代码中打标

找到打标内容,执行增强的代码

上面的例子说明,在Java中,注解的作用就是打标,它的最佳实践是给代码增强“打辅助”,它不能单独完成代码增强, 那么,在TypeScript中,注解的使用又是怎样的?

TS中的注解怎么理解——打标+代码增强,我全都要

我相信看完上面的涵盖注解的Java代码,前端的同学都会觉得过于复杂了,其实它复杂的原因只有一个——Java是不能把函数作为参数的。打过的标,要怎么处理,需要另外引入一个类(类似上个例子中最后一段很长的代码),将他们关联起来处理。那么,在灵活的JS/TS中,函数可以作为参数,它写起代码来是怎么样的呢?

举个很经典的例子:我需要用两个😂的表情去包裹某个属性的值,当然,可以手动去改,只是如果要改的地方很多,可能比较麻烦。如果你将它看作是一种增强,可以这样写:

将注解打标和代码增强合起来,写这样一个方法:

这样写代码的好处往小了说,就是修改所有被@Emoji()修饰的表情时,很方便,比如需要把😂替换成😎的时候,只需要改上述一处代码即可。简单来说,TS中的注解,实现了打标+代码增强的能力,可以把它看成一个函数,它也可以接收函数参数。 。既然可以接收函数参数,也用一个经典的例子演示下:

需求:假如你的前端代码有些重要业务,现在要让他们在触发前都出现一个二次确认是否继续的弹窗。

实现:通过注解增强

添加它的相应方法

代码增强到底是什么——持续优化

上面我提到很多次代码增强,那么到底代码增强是什么,还有前面举得Java代码的例子,熟悉Java的同学都知道,它叫AOP,它的本质又是什么?

可以从一个很简单的例子做解释:

我相信很多同学走上程序员这条路的第二步(第一步是打印helloworld)都是编写这样一道题:三角形打印

题目类似这样

给定一个n,打印n阶三角形(类似下图,n=5)

考虑到题目的严谨性,这个n应该有个范围,比如是1-5,那么这道题我们完全可以使用穷举法,把每种n的取值对应的结果直接通过print之类的函数打出来就行了。但是为什么大家都不这样写?

因为太麻烦了,而且,没有什么乐趣,反之,如果你观察到它的规律,发现根据n的值可以组合空格和星号排列,这样写出的代码,很简练,也易于在需求变化时修改

同样的道理,如果你写了很多业务代码,举个例子,很多业务都需要写查询数据库的代码,如果之前的代码都是直接查询数据库的,而现在鼓励把缓存当数据库,查询数据库之前先查缓存,相当于要在原本的代码基础上通过修改做个增加,当然,你可以把所有业务对象的代码都改下,亦或者观察它的规律,对于所有同样要增加的操作,是不是可以通过更简练的方式增强它的能力,而不是每个业务代码的多次复制粘贴?如下图,所有的原本业务之前之后可以统一的增强。

这就是标准的AOP(面向切面编程),其中切面的意思就是,把你的业务想象成一个类似“汉堡”的结构,通过纵切,可以看清里面的每一层都是什么

观察整个汉堡的切面,就相当于在观察你的代码逻辑了,如果有个逻辑,需要添加到你的多处业务中,不妨试试使用注解去增强你的代码。

没有最好,只有最适合你的

总结一下:这篇文章主要想给大家介绍注解的用途、原理,

注解的最佳实践就是打标加代码增强。实现这个的步骤往往是:

  1. 找到注解打标的代码;

  2. 找到要执行增强代码的时机;

  3. 织入增强代码;

其中“找到要执行增强代码的时机”这一步,往往源于你对业务的理解,比如“纵切”你的代码,通过观察切面寻找规律,进而通过注解引起上述三个动作的触发。

不过,以我最开始举的使用注解替代配置文件的例子来说,使用注解是比配置文件简化很多,但是同样它多了耦合。因为配置文件本来是跟代码分离的,使用注解则是将这个关系耦合到代码中了,所以,不是任何时刻注解都优于配置文件的。同样,后面介绍的AOP,本质上是对代码的增强,这个可以理解为一种模式,我觉得这种模式是值得大家了解的,但不是任何时候都必须要使用模式,没有必要生搬硬套。就像前两天我们团队的同学在讨论,个别设计模式有什么异同,其实我感觉并不需要区分的那么清楚,知道每种模式为什么那么写代码就好。比如责任链模式,它的特点是能写出类似obj.handle().handle() 的代码,那么可以不关注一个场景是不是责任链模式,而是在你像写出类似代码的时候,知道需要通过继承,实现handle方法,并返回obj对象,即可,这样就能保证你的链式调用继续下去。你可以反模式,可以用你喜欢的方式去解决你遇到的问题,寻找最适合你的方式去“野蛮生长”。说到模式,Rx.js中倒是有不少设计模式,下一篇文章我们会给大家简单介绍下Rx.js如何理解,以及它的设计理念。

最后

如果你对这些WEB前沿技术也有兴趣,欢迎你对我们的文章一键三联,以及关注我们接下来的开源项目————OpenTiny。欢迎微信搜索我们的小助手:opentiny-official,拉你进群,了解它最新的动态。

Angular/TypeScript中那些值得了解的知识——注解的评论 (共 条)

分享到微博请遵守国家法律