【问题标题】:EXTENDS challenge: preprocessor function macros and class-like oopEXTENDS 挑战:预处理器函数宏和类 oop
【发布时间】:2011-03-20 17:39:47
【问题描述】:

背景

我一直在使用 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 和一些#ifdefs 的预处理器的东西,但我开始想,有一些方便的宏不是很好吗?实验随之而来。

#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

让我们从一个 NAMESPACE 宏和一个不起眼但有用的 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(){}}())

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

【问题讨论】:

    标签: javascript shell macros preprocessor prototypal-inheritance


    【解决方案1】:

    我想我刚刚完成了自己的挑战。我在 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 中实现类 OOP 和代码重用的有用方法吗?如果我遗漏了什么,请告诉我。

    【讨论】:

    • 与 GWT 相比有哪些优势?
    • 嗯,这仍然是 javascript,只是用宏扩展。 GWT 代码用 java 编写并“编译”为 javascript。因此,如果您熟悉 javascript,并且想要一种方便的方式来使用熟悉的类类设计,如名称空间、内部带有构造函数的类声明、继承等......所有这些都可以在 javascript 中模拟,这些宏只是让更方便。
    猜你喜欢
    • 1970-01-01
    • 2015-03-03
    • 2010-11-11
    • 2013-05-13
    • 2013-03-14
    • 2017-10-23
    • 2013-05-08
    • 2023-03-31
    相关资源
    最近更新 更多