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

Angular中無處不在的注解(當然,更準確地說,注解是屬于TypeScript而非Angular,只是通過Angular體現(xiàn)),它為何產生,又有何妙用?
注解的產生背景和意義——給你想要處理的程序打個標
上一篇文章中我介紹了“依賴注入”,說到了依賴注入框架,其實是個巨大的Map。那么,它如何記錄他的鍵值對呢,比如一個在src/app/car/carService
的服務要告訴框架,它需要被管理,被在應用啟動時就被構造出來,以Java為例,需要放在配置文件中,類似這樣:

其中的bean的id就是鍵,class的路徑,就是要讓框架管理的全路徑(唯一)的對象。
這樣的作法當需要依賴注入框架管理的對象越來越多時,每加一個bean都記錄它的全路徑,填在這里,很復雜,這也是10年前的人難以入門JavaWeb的原因,過多的配置、以及引如jar包、排除jar包沖突等事情要做,自學的話,把項目啟動起來都費勁。
而Java在JDK 1.5版本中剛好提供了注解
的能力。設計它的本意是一種注釋機制
,比如,我的一個方法過時了,不希望別人再用了,可以這樣寫

這樣別人調用getReadedDirPath這個方法時,就會有編譯警告,IDE上也有相應提示,如圖

也就是說,注解的實質是標注,作用是可以在程序運行的任何時刻獲取你標注的類、方法、屬性的信息
,而借助這個能力,很多框架開始優(yōu)化它的配置文件,以Spring為例,它是一個依賴注入框架,既然要管理的對象需要知道它的全路徑,是否可以以注解
的手段代替它,進而省去繁雜的配置文件?當然是可以的,于是Spring這么做了,Hibernate、Mybatis等等所有現(xiàn)在還會被使用到的JavaWeb開發(fā)框架,都這么做了。于是,代碼就有了這樣的變化。

別的不說,至少代碼清晰多了,要配置的內容也少了。
來都來了,只打個標嗎——代碼增強
上面介紹了Java中的注解,可以實現(xiàn)打標的能力,那么打標有什么好的使用場景嗎?
有的。那就是代碼增強。
既然可以在程序運行過程中拿到打標的內容,我們可以在這個時機加入一些我們的邏輯,就是代碼增強。舉個例子,比如你需要在所有Java的控制器方法中加入日志,最佳實踐是這樣做:

在某些代碼中打標

找到打標內容,執(zhí)行增強的代碼



上面的例子說明,在Java中,注解的作用就是打標,它的最佳實踐是給代碼增強“打輔助”,它不能單獨完成代碼增強
, 那么,在TypeScript中,注解的使用又是怎樣的?
TS中的注解怎么理解——打標+代碼增強,我全都要
我相信看完上面的涵蓋注解的Java代碼,前端的同學都會覺得過于復雜了,其實它復雜的原因只有一個——Java是不能把函數(shù)作為參數(shù)的
。打過的標,要怎么處理,需要另外引入一個類(類似上個例子中最后一段很長的代碼),將他們關聯(lián)起來處理。那么,在靈活的JS/TS中,函數(shù)可以作為參數(shù),它寫起代碼來是怎么樣的呢?
舉個很經(jīng)典的例子:我需要用兩個??
的表情去包裹某個屬性的值,當然,可以手動去改,只是如果要改的地方很多,可能比較麻煩。如果你將它看作是一種增強,可以這樣寫:

將注解打標和代碼增強合起來,寫這樣一個方法:

這樣寫代碼的好處往小了說,就是修改所有被@Emoji()修飾的表情時,很方便,比如需要把??
替換成??
的時候,只需要改上述一處代碼即可。簡單來說,TS中的注解,實現(xiàn)了打標+代碼增強的能力,可以把它看成一個函數(shù),它也可以接收函數(shù)參數(shù)。
。既然可以接收函數(shù)參數(shù),也用一個經(jīng)典的例子演示下:
需求:假如你的前端代碼有些重要業(yè)務,現(xiàn)在要讓他們在觸發(fā)前都出現(xiàn)一個二次確認是否繼續(xù)的彈窗。
實現(xiàn):通過注解增強

添加它的相應方法

代碼增強到底是什么——持續(xù)優(yōu)化
上面我提到很多次代碼增強,那么到底代碼增強是什么,還有前面舉得Java代碼的例子,熟悉Java的同學都知道,它叫AOP,它的本質又是什么?
可以從一個很簡單的例子做解釋:
我相信很多同學走上程序員這條路的第二步(第一步是打印helloworld)都是編寫這樣一道題:三角形打印
題目類似這樣
給定一個n,打印n階三角形(類似下圖,n=5)

考慮到題目的嚴謹性,這個n應該有個范圍,比如是1-5,那么這道題我們完全可以使用窮舉法,把每種n的取值對應的結果直接通過print之類的函數(shù)打出來就行了。但是為什么大家都不這樣寫?
因為太麻煩了,而且,沒有什么樂趣,反之,如果你觀察到它的規(guī)律
,發(fā)現(xiàn)根據(jù)n的值可以組合空格和星號排列,這樣寫出的代碼,很簡練,也易于在需求變化時修改

同樣的道理,如果你寫了很多業(yè)務代碼,舉個例子,很多業(yè)務都需要寫查詢數(shù)據(jù)庫的代碼,如果之前的代碼都是直接查詢數(shù)據(jù)庫的,而現(xiàn)在鼓勵把緩存當數(shù)據(jù)庫,查詢數(shù)據(jù)庫之前先查緩存,相當于要在原本的代碼基礎上通過修改做個增加,當然,你可以把所有業(yè)務對象的代碼都改下,亦或者觀察它的規(guī)律
,對于所有同樣要增加的操作
,是不是可以通過更簡練的方式
增強它的能力,而不是每個業(yè)務代碼的多次復制粘貼?如下圖,所有的原本業(yè)務之前之后可以統(tǒng)一的增強。

這就是標準的AOP(面向切面編程),其中切面的意思就是,把你的業(yè)務想象成一個類似“漢堡”的結構,通過縱切,可以看清里面的每一層都是什么

觀察整個漢堡的切面,就相當于在觀察你的代碼邏輯了,如果有個邏輯,需要添加到你的多處業(yè)務中,不妨試試使用注解去增強你的代碼。
沒有最好,只有最適合你的
總結一下:這篇文章主要想給大家介紹注解的用途、原理,
注解的最佳實踐就是打標加代碼增強。實現(xiàn)這個的步驟往往是:
找到注解打標的代碼;
找到要執(zhí)行增強代碼的時機;
織入增強代碼;
其中“找到要執(zhí)行增強代碼的時機”這一步,往往源于你對業(yè)務的理解,比如“縱切”你的代碼,通過觀察切面尋找規(guī)律,進而通過注解引起上述三個動作的觸發(fā)。
不過,以我最開始舉的使用注解替代配置文件的例子來說,使用注解是比配置文件簡化很多,但是同樣它多了耦合。因為配置文件本來是跟代碼分離的,使用注解則是將這個關系耦合到代碼中了,所以,不是任何時刻注解都優(yōu)于配置文件的
。同樣,后面介紹的AOP,本質上是對代碼的增強,這個可以理解為一種模式,我覺得這種模式是值得大家了解的,但不是任何時候都必須要使用模式,沒有必要生搬硬套
。就像前兩天我們團隊的同學在討論,個別設計模式有什么異同,其實我感覺并不需要區(qū)分的那么清楚,知道每種模式為什么那么寫代碼就好。比如責任鏈模式,它的特點是能寫出類似obj.handle().handle()
的代碼,那么可以不關注一個場景是不是責任鏈模式,而是在你像寫出類似代碼的時候,知道需要通過繼承,實現(xiàn)handle方法,并返回obj對象,即可,這樣就能保證你的鏈式調用繼續(xù)下去。你可以反模式,可以用你喜歡的方式去解決你遇到的問題,尋找最適合你的方式去“野蠻生長”。說到模式,Rx.js中倒是有不少設計模式,下一篇文章我們會給大家簡單介紹下Rx.js如何理解,以及它的設計理念。
最后
如果你對這些WEB前沿技術也有興趣,歡迎你對我們的文章一鍵三聯(lián),以及關注我們接下來的開源項目————OpenTiny。歡迎微信搜索我們的小助手:opentiny-official
,拉你進群,了解它最新的動態(tài)。