自由度這個概念在不一樣領域有不一樣的定義,咱們借鑑數學中構成一個空間的維數來表達其自由度的作法,在此指的是:解決同一個問題彼此不相關的設計方法學數量。javascript
例如,解決一個好比商品打折的問題,如何設計順序、提取函數,具體的思路可能有不少,可是這可能都是從面向過程(OP)的角度,一樣解決這個問題,若是另外一門語言還支持面向對象(OO)的設計方法,那麼咱們認爲後者的自由度要多一些,由於OO提供了幾乎徹底從另外一個角度解決問題的能力。html
既然自由度能夠借鑑「維數」的定義,咱們來嘗試分析一下計算機語言的「維數」,在此以前,咱們有必要簡單分析一下語言是怎樣一步一步變得複雜的。java
本文關注的重點是命令式風格的計算機語言。git
第一步,出現告終構體(數據結構)、常量、變量、算符、順序、分支、循環等這些體現「命令」的基本方面;github
第二步,例程的出現,包括函數、過程等;web
第三步,宏的出現,包括宏、模板、泛型;編程
第四步,對客觀世界在結構化上抽象能力出現,包括OO等;數據結構
第五步,元編程能力的出現,如註釋、反射等等; …框架
從計算機語言歷史來看,以上步驟不必定按照時間順序展開,咱們更關注的是語言能力提高帶來的意義。其中,第二步的完成,標誌着結構化程序設計方法的出現,對大型軟件工程提供了較好的支持,第三步是對第二步的進一步抽象,第四步所表明的意義更加劇大,其中很是重要的一點,意味着終於能夠支持實現「層次化」,能夠實現將「內核」與「外圍」作分離,將相對穩定與潛在變化的部分分開,也就是說,編碼所表達的內容再也不只能扁平化,終於進化出了「階級」。函數
從本質上來說,以上演進反映了語言自身抽象能力的不斷提高。
這裏很是有意思的一個現象是,抽象化的不斷提高,會使得語言的維度提高至某個分數維——抽象的本質是在空間上提供了某種自類似的遞歸映射,從而體現出「分形」的結構形式,分形結構表現出在原有空間中增長了分數維,可是獲得一個新的整數維是困難的,即好比1維能夠提高至1.5維,可是沒法到達2維。
因此,目前絕大部分計算機高級語言的維度是1.X。
可是第五步,意味着語言開始真正走向一個更高的維度。
事實上,實現元編程有多種方式,從語言自己來說,能夠分爲兩類:加強型API與新的語法實現,前者的表明是反射,後者的表明爲Annotation。
咱們來看一個例子:
public class TestCase{
@Before
public void setUp() throws Exception{}
@After
public void tearDown() throws Exception{}
@Test
public void add() {}
}
複製代碼
上面是Java語言中使用Annotation類型定義了一個單元測試的三個階段,在這裏: @Before、@After、@Test用「變量」定義了「變量」,同時定義了執行的順序,這裏是「對編碼再進行編碼」的過程,是元編程的一種典型的實現。
咱們固然也能夠經過加強型API(反射或者用設計約束(好比摸版方法))來解決,可是不管哪種,都不如Annotation的方式要簡單直接明瞭。
根本的緣由,在於加強型API的實現方式與原有代碼這兩個表達邏輯的維度存在過多的「相關性」,即1.X維的,但Annotation的方式在相關性上大大減小,兩個維度更加解耦,因此後者的自由度更高。
以下JS基於Mocha的單元測試代碼:
describe('測試過程1', function() {
it('1+1', function() {
expect(fn_add(1, 1)).to.be.equal(2);
});
});
複製代碼
咱們指望以下編程風格:
'@test(step=測試過程1,name=1+1,expect=2';
var step0 = function(){
return fn_add(1, 1);
}
複製代碼
###JS實現基於註釋的元編程
咱們嘗試將Annotation的機制引入JS,以下:
'@Log(level=info,dateFormat=YYYY-MM-DD HH:mm)’;
var logInfo = function(_msg){
console.log(_msg);
}
複製代碼
複雜的場景,考慮多個註釋的相關性:
'@Start';
var serverStart = function(){}
'@Rule(fileType=.(html|htm))';
var proHtml = function(_req,_res){}
'@Rule(fileType=.(jpg|gif|webp))';
var proPic = function(_req,_res){}
'@Finish';
var serverFinish = function(){}
複製代碼
基於以上想法,咱們實現了At-js框架並開源,At-js的實現思路很是簡單,在Node.js端,經過覆蓋運行時JS文件加載機制實現對Annotation類型的識別判斷並對原生文件進行Enhance處理,爲性能考慮,At-js採用了正則掃描而非AST的方式。
At-js使用方法包括:定義註釋與使用註釋。
定義註釋:
require('at-js').define('helloworld',{//annotation's name
scope: 'var', build: function () {//the scope of it's effected
return "return function(_msg) {console.log('[helloworld]'+_msg);};"//the real script
}
})
複製代碼
使用註釋:
'@helloworld';
var sayHello = function(){}
sayHello('here')
複製代碼
運行效果:
[Helloworld]here
如下代碼描述了一個單元測試過程(https://github.com/CheMingjun/at-test):
'@test.start';
var start = function () {
ds = {};
}
'@test.step(timeout=2000)';
var test0 = function* () {
ds.test0 = 'finish';
var rtn = yield (function(){
return function(_next){
setTimeout(function(){
_next(null,3);
},2000)
}
})();
assert.equal(rtn,3);
}
'@test.step';
var test1 = function () {
ds.test1 = 'finish';
return ds;
}
'@test.finish';
var fh = function () {
ds = null;
}
複製代碼
At-js支持Var級及File不一樣級別的註釋定義,上例屬於File級別複雜的註釋定義,二者的API以下:
Var型註釋定義:
{
scope:'var',
build:function(_ctx, _argAry){
//_ctx
{
filePath,//應該該註釋的文件位置
name,//註釋名稱
desc,//註釋中的變量表(key-value)
refName,//被註釋的變量名稱
refType//被註釋的變量類型(undefined|function|generator|object)
}
//_aryAry 被註釋變量簽名中的參數列表
return //返回該變量被替換以後的代碼
}
}
複製代碼
File型註釋定義:
{
return {
which: {//針對改組annotation中的每一項作處理
'test.start': function (_ctx, _argAry) {
//_ctx 與 _argAry 同上定義
//處理邏輯
}
}, script: function () {
return //返回該文件追加的代碼
}
}
}
複製代碼
在實際生產過程當中,以下一套註釋實現了ORM:
'@dao.column';
var id;
'@dao.column(name=name)';
var name;
'@dao.column(name=status)';
var status;
'@dao.column(name=creator_id)';
var creatorId;
'@dao.column(name=creator_name)';
var creatorName;
'@dao.column(name=gmt_create)';
var createTime = function (_time) {
var mm = require('moment');
return mm(_time).format("YYYY-MM-DD HH:mm:ss");
}
'@dao.column(name=gmt_update)';
var updateTime = function (_time) {
var mm = require('moment');
return mm(_time).format("YYYY-MM-DD HH:mm:ss");
}
'@dao.column(name=type)';
var type;
複製代碼
本文給出了語言自由度的簡單定義,並在此基礎上論述了在語言發展過程當中呈現的不一樣複雜性,並探討了元編程是如何從根本上增長語言的自由度的。在第二部分,咱們嘗試在JS語言基礎上增長原生的元編程能力並介紹了該思路的實現:At-js框架。