Javascript元编程之Annotation

言语的自由度

自由度这个概念在差别范畴有差别的定义,我们自创数学中组成一个空间的维数来表达其自由度的做法,在此指的是:处置惩罚同一个题目相互不相关的设想要领学数目。

比方,处置惩罚一个比方商品打折的题目,如何设想递次、提取函数,详细的思绪能够有许多,然则这能够都是从面向历程(OP)的角度,一样处置惩罚这个题目,假如另一门言语还支撑面向对象(OO)的设想要领,那末我们以为后者的自由度要多一些,由于OO供应了险些完整从另一个角度处置惩罚题目的才能。

既然自由度能够自创“维数”的定义,我们来尝试剖析一下计算机言语的“维数”,在此之前,我们有必要简朴剖析一下言语是如何一步一步变得庞杂的。

本文关注的重点是敕令式作风的计算机言语。

第一步,涌现了构造体(数据构造)、常量、变量、算符、递次、分支、轮回等这些表现“敕令”的基础方面;

第二步,例程的涌现,包含函数、历程等;

第三步,宏的涌现,包含宏、模板、泛型;

第四步,对客观天下在构造化上笼统才能涌现,包含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框架并开源,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…):

'@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框架。

    原文作者:cloudsinger
    原文地址: https://segmentfault.com/a/1190000013041008
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞