8

バックグラウンド

私は C プリプロセッサを使用して、複数のファイルとビルド ターゲットを含むセミ大規模な JavaScript プロジェクトを管理および「コンパイル」しています。これにより、javascript 内から 、 、 など#include#defineCプリプロセッサ ディレクティブに完全にアクセスできます。#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秒間必要だったのですが、便利なマクロもいくつかあるといいのではないかと考えました。実験が続いた。

#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 をマクロとして実装して、通常の「プロトタイプのクローンを作成し、コンストラクターのプロパティをコピーする」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(){}}())

ヘルプ、アドバイス、または活発な議論をよろしくお願いします。:)

4

1 に答える 1

2

私は自分自身の挑戦を完了したと思います。宣言されているクラスのスーパークラスの CLASS 宣言マクロに 2 番目の (オプションの) 引数を追加しました。

私の最初の実装では、コンストラクターの周りに大量のインライン ジャンクが作成されたので、冗長性を避けるために、いくつかの便利な関数をマクロ ヘルパー オブジェクトにラップすることにしました。

私のクラスのような 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 でのコードの再利用に対する有用なアプローチだと思いますか? 何か見逃した場合はお知らせください。

于 2010-07-26T19:03:51.260 に答える