7

背景EXTENDS挑战:预处理功能宏和类像接力

我一直使用C预处理程序来管理和“编译”与多个文件半大型的JavaScript项目和构建目标。这使得可以在JavaScript中完全访问C预处理器指令,如#include#define,#ifdef等。下面是一个示例构建脚本,所以你可以测试示例代码:

#!/bin/bash 
export OPTS="-DDEBUG_MODE=1 -Isrc" 
for FILE in `find src/ | egrep '\.js?$'` 
do 
    echo "Processing $FILE" 
    cat $FILE \ 
    | sed 's/^\s*\/\/#/#/' \ 
    | cpp $OPTS \ 
    | sed 's/^[#:<].*// ; /^$/d' \ 
    > build/`basename $FILE`; 
done 

做一个srcbuild目录,并把.js文件中src


很方便的宏

本来我只是想预处理器时钟为#include,也许几#ifdef S,但我开始思考,是不是很高兴有一些很方便的宏太?随后进行了实验。

#define EACH(o,k)  for (var k in o) if (o.hasOwnProperty(k)) 

酷,所以现在我可以写这样的事:

EACH (location, prop) { 
    console.log(prop + " : " location[prop]); 
} 

而且它将扩大到:

for (var prop in location) if (location.hasOwnProperty(prop)) { 
    console.log(prop + " : " location[prop]); 
} 

怎么样的foreach?

#define FOREACH(o,k,v) var k,v; for(k in o) if (v=o[k], o.hasOwnProperty(k)) 
// ... 
FOREACH (location, prop, val) { console.log(prop + " : " + val) } 

注意我们是如何潜入v=o[k]if条件内,因此不会打扰应该遵循这个宏的调用花括号。


类样OOP

让我们先从一个命名空间宏观和一个不起眼但非常有用的js模式...

#define NAMESPACE(ns) var ns = this.ns = new function() 

new function(){ ... }做一些奇妙的东西。它调用一个匿名函数作为构造函数,所以它最后不需要额外的()来调用它,而在它内部,this引用由构造函数创建的对象,换句话说,就是名称空间本身。这也允许我们在命名空间中嵌套命名空间。

这里是我的全套类样OOP宏:

#define NAMESPACE(ns) var ns=this.ns=new function() 

#define CLASS(c)  var c=this;new function() 

#define CTOR(c)  (c=c.c=this.constructor=$$ctor).prototype=this;\ 
         function $$ctor 

#define PUBLIC(fn) this.fn=fn;function fn 
#define PRIVATE(fn) function fn 
#define STATIC(fn) $$ctor.fn=fn;function fn 

正如你所看到的,这些宏在Variable Object(为方便起见)定义很多事情都和this(从必要性)。下面是一些示例代码:

NAMESPACE (Store) { 

    CLASS (Cashier) { 

    var nextId = 1000; 

    this.fullName = "floater"; 

    CTOR (Cashier) (fullName) { 
     if (fullName) this.fullName = fullName; 
     this.id = ++nextId; 
     this.transactions = 0; 
    } 

    PUBLIC (sell) (item, customer) { 
     this.transactions += 1; 
     customer.inventory.push(item); 
    } 

    STATIC (hire) (count) { 
     var newCashiers = []; 
     for (var i=count; i--;) { 
     newCashiers.push(new Cashier()); 
     } 
     return newCashiers; 
    } 
    } 

    CLASS (Customer) { 

    CTOR (Customer) (name) { 
     this.name = name; 
     this.inventory = []; 
     this.transactions = 0; 
    } 

    PUBLIC (buy) (item, cashier) { 
     cashier.sell(this, item); 
    } 
    } 
} 

约EXTENDS什么?

因此,这带来了我的问题...我们如何实现EXTENDS作为一个宏来包装通常的“克隆原型,复制构造函数属性”js原型继承?我还没有找到办法做到这一点之外,要求EXTENDS在之后出现这个类定义,这很愚蠢。这个实验需要EXTENDS或者没用。随意更改其他宏,只要他们给出相同的结果。

编辑 - 这些可能适用于EXTENDS;在这里列出他们的完整性。

#define EACH(o,k) for(var k in o)if(o.hasOwnProperty(k)) 
#define MERGE(d,s) EACH(s,$$i)d[$$i]=s[$$i] 
#define CLONE(o) (function(){$$C.prototype=o;return new $$C;function $$C(){}}()) 

在此先感谢您的帮助,建议或热烈讨论。 :)

回答

2

我想我只是完成了我自己的挑战。我已经为CLASS声明宏添加了第二个(可选)参数,用于声明该类的超类。

我的原始实现在构造函数周围创建了大量内联垃圾,因此我决定将一些便利函数包装在宏助手对象中以避免冗余。

这里是我的类样OOP宏目前的化身:

// class-like oo 

#ifndef BASE 
    #define BASE $$_ 
#endif 

#define COLLAPSE(code)  code 

#define NAMESPACE(ns)  var ns=BASE._ns(this).ns=new function() 

#define CLASS(c,__ARGS...) var c=[BASE._class(this),[__ARGS][0]]; \ 
          new function() 

#define CTOR(c)    BASE._extend($$_##c,c[1],this); \ 
          c=c[0].c=$$_##c; function $$_##c 

#define PUBLIC(fn)   BASE._public(this).fn=fn;function fn 

#define PRIVATE(fn)   function fn 

#define STATIC(fn)   BASE._static(this).fn=fn;function fn 

// macro helper object 

COLLAPSE(var BASE=new function(){ 

    function Clone(){}; 

    function clone (obj) { 
    Clone.prototype=obj; return new Clone; 
    }; 

    function merge (sub, sup) { 
    for (var p in sup) if (sup.hasOwnProperty(p)) sub[p]=sup[p]; 
    }; 

    this._extend = function (sub, sup, decl) { 
    if (sup) { 
     merge(sub, sup); 
     sub.prototype=clone(sup.prototype); 
     sub.prototype.constructor=sub; 
    }; 
    if (decl) { 
     merge(sub.prototype, decl); 
     decl._static=sub; 
     decl._public=sub.prototype; 
    }; 
    }; 

    this._static=this._ns=this._class=function (obj) { 
    return (obj._static || obj); 
    }; 

    this._public=function (obj) { 
    return (obj._public || obj); 
    }; 

}) 

...这是一个测试命名空间...

//#include "macros.js" 

NAMESPACE (Store) { 

    CLASS (Cashier) { 

    var nextId = 1000; 

    this.fullName = "floater"; 

    CTOR (Cashier) (fullName) { 
     if (fullName) this.fullName = fullName; 
     this.id = ++nextId; 
     this.transactions = 0; 
    } 

    PUBLIC (sell) (item, customer) { 
     this.transactions += 1; 
     customer.inventory.push(item); 
    } 

    STATIC (hire) (count) { 
     var newCashiers = []; 
     for (var i=count; i--;) { 
     newCashiers.push(new Cashier()); 
     } 
     return newCashiers; 
    } 
    } 

    // Customer extends Cashier, just so we can test inheritance 

    CLASS (Customer, Cashier) { 

    CTOR (Customer) (name) { 
     this.name = name; 
     this.inventory = []; 
     this.transactions = 0; 
    } 

    PUBLIC (buy) (item, cashier) { 
     cashier.sell(this, item); 
    } 

    CLASS (Cart) { 

     CTOR (Cart) (customer) { 
     this.customer = customer; 
     this.items = []; 
     } 
    } 

    } 
} 

...这里是输出..

var $$_=new function(){ function Clone(){}; function clone (obj) { Clone.prototype=obj; return new Clone; }; function merge (sub, sup) { for (var p in sup) if (sup.hasOwnProperty(p)) sub[p]=sup[p]; }; this._extend = function (sub, sup, decl) { if (sup) { merge(sub, sup); sub.prototype=clone(sup.prototype); sub.prototype.constructor=sub; }; if (decl) { merge(sub.prototype, decl); decl._static=sub; decl._public=sub.prototype; }; }; this._static=this._ns=this._class=function (obj) { return (obj._static || obj); }; this._public=function (obj) { return (obj._public || obj); }; } 
var Store=$$_._ns(this).Store=new function() { 
    var Cashier=[$$_._class(this),[][0]]; new function() { 
    var nextId = 1000; 
    this.fullName = "floater"; 
    $$_._extend($$_Cashier,Cashier[1],this); Cashier=Cashier[0].Cashier=$$_Cashier; function $$_Cashier (fullName) { 
     if (fullName) this.fullName = fullName; 
     this.id = ++nextId; 
     this.transactions = 0; 
    } 
    $$_._public(this).sell=sell;function sell (item, customer) { 
     this.transactions += 1; 
     customer.inventory.push(item); 
    } 
    $$_._static(this).hire=hire;function hire (count) { 
     var newCashiers = []; 
     for (var i=count; i--;) { 
     newCashiers.push(new Cashier()); 
     } 
     return newCashiers; 
    } 
    } 
    var Customer=[$$_._class(this),[Cashier][0]]; new function() { 
    $$_._extend($$_Customer,Customer[1],this); Customer=Customer[0].Customer=$$_Customer; function $$_Customer (name) { 
     this.name = name; 
     this.inventory = []; 
     this.transactions = 0; 
    } 
    $$_._public(this).buy=buy;function buy (item, cashier) { 
     cashier.sell(this, item); 
    } 
    var Cart=[$$_._class(this),[][0]]; new function() { 
     $$_._extend($$_Cart,Cart[1],this); Cart=Cart[0].Cart=$$_Cart; function $$_Cart (customer) { 
     this.customer = customer; 
     this.items = []; 
     } 
    } 
    } 
} 

继承,内部类和嵌套命名空间似乎工作正常。你认为,这是一种类似于面向对象的代码重用和js代码重用的有用方法吗?如果我错过了任何东西,请告诉我。

+0

与GWT相比有哪些好处? – user123444555621 2010-07-27 09:15:37

+0

那么,这仍然是JavaScript,只是扩展与宏。 GWT代码是用java编写的,'编译'为javascript。所以如果你熟悉javascript并且想要一种方便的方式来使用像命名空间这样类似熟悉的类设计,带有构造函数的类声明,继承等等......所有这些都可以在javascript中模拟,这些宏只是使它更方便。 – 2010-07-27 09:48:51