代码之家  ›  专栏  ›  技术社区  ›  Dagg Nabbit

扩展挑战:预处理器函数宏和类(如oop)

  •  7
  • Dagg Nabbit  · 技术社区  · 14 年前

    背景

    我一直在使用C预处理器来管理和“编译”具有多个文件和构建目标的半大型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
    

    做一个 src 和A build 目录,并将.js文件放入 SRC .


    便利宏

    最初,我只是想要预处理器的东西 包括: 也许还有几个 γIFIFF 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]);
    }
    

    前臂怎么样?

    #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 (为了方便)和 (出于必要)。下面是一些示例代码:

    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);
        }
      }
    }
    

    扩展呢?

    所以这让我想到了一个问题…我们如何实现扩展作为一个宏来包装通常的“克隆原型,复制构造函数属性”JS原型继承?我没有找到一种方法可以做到这一点,除了要求扩展出现 之后 类的定义,这是愚蠢的。这个实验需要扩展,否则就没用了。只要其他宏给出相同的结果,就可以随意更改它们。

    编辑-这些可能对扩展很有用;在这里列出它们是为了完整性。

    #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(){}}())
    

    提前感谢您的帮助、建议或热烈讨论。:)

    1 回复  |  直到 10 年前
        1
  •  2
  •   Dagg Nabbit    14 年前

    我想我刚刚完成了我自己的挑战。我在要声明的类的超类的类声明宏中添加了第二个(可选)参数。

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

    以下是我类的当前体现,如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 = [];
          }
        }
      }
    }
    

    继承、内部类和嵌套命名空间似乎工作正常。你认为这是一种很有用的方法,比如面向对象编程(OOP)和JS中的代码重用吗?如果我错过了什么,请告诉我。