EXTENDS挑战:预处理器函数宏和类类oop [英] EXTENDS challenge: preprocessor function macros and class-like oop

查看:100
本文介绍了EXTENDS挑战:预处理器函数宏和类类oop的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我一直在使用C预处理器来管理和编译包含多个文件和构建的半大型JavaScript项目目标。这样就可以完全访问C预处理程序指令,例如 #include #define #ifdef <来自javascript内的/ code>等。这是一个示例构建脚本,因此您可以测试示例代码:

I've been using the C preprocessor to manage and "compile" semi-large javascript projects with multiple files and build targets. This gives full access to C preprocessor directives like #include, #define, #ifdef, etc. from within javascript. Here's a sample build script so you can test the example code:

#!/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 和一个 build 目录,并将.js文件放在 src 中。

Make a src and a build directory, and put the .js files in src.

便利宏

最初,我只想要预处理器的东西 #include ,也许还有一些 #ifdef s,但我想,有一些不是很好便利宏呢?随后进行了实验。

Originally, I just wanted the preprocessor stuff for #include and maybe a few #ifdefs, but I got to thinking, wouldn't it be nice to have some convenience macros too? Experimentation ensued.

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

很酷,现在我可以这样写:

Cool, so now I can write something like this:

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

它将扩展为:

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

foreach怎么样?

How about 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 条件内,所以它不会打扰应该跟随此宏调用的花括号。

Notice how we sneak v=o[k] inside the if condition so it doesn't disturb the curly braces that should follow the invocation of this macro.

类似OOP

让我们从一开始NAMESPACE宏和一个不起眼但有用的js模式......

Let's start with a NAMESPACE macro and an obscure but useful js pattern...

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

new function(){...} 做一些整洁的东西。它将匿名函数作为构造函数调用,因此最后不需要额外的()来调用它,并且在其中 this 指的是构造函数创建的对象,换句话说,就是命名空间本身。这也允许我们在名称空间中嵌套名称空间。

new function(){ ... } does some neat stuff. It calls an anonymous function as a constructor, so it doesn't need an extra () at the end to call it, and within it this refers to the object being created by the constructor, in other words, the namespace itself. This also allows us to nest namespaces within namespaces.

这是我的全套类OOP宏:

Here is my full set of class-like OOP macros:

#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

如您所见,这些宏在变量对象(为方便起见)和(必要时)。以下是一些示例代码:

As you can see, these macros define many things both in the Variable Object (for convenience) and in this (from necessity). Here's some example code:

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或它没用。只要它们给出相同的结果,可以随意更改其他宏。

So this brings me to the question... how can we implement EXTENDS as a macro to wrap the usual "clone the prototype, copy constructor properties" js prototype inheritance? I haven't found a way to do it outside of requiring the EXTENDS to appear after the class definition, which is silly. This experiment needs EXTENDS or it's useless. Feel free to change the other macros as long as they give the same results.

编辑 - 这些可能对EXTENDS有用;为了完整性将它们列在这里。

Edit - These might come in handy for EXTENDS; listing them here for completeness.

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

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

Thanks in advance for any help, advice, or lively discussion. :)

推荐答案

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

I think I just completed my own challenge. I've added a second (optional) argument to the CLASS declaration macro for the superclass of the class being declared.

我的原始实现创建了很多内联垃圾构造函数,所以我决定在宏辅助对象中包含一些便利函数以避免冗余。

My original implementation created a lot of inline junk around the constructor, so I decided to wrap some convenience functions up in a macro helper object to avoid redundancy.

以下是我类类OOP宏的当前版本:

Here are the current incarnations of my class-like OOP macros:

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

})

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

... here's a test namespace ...

//#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 = [];
      }
    }

  }
}

...这里是输出...

... and here's the output ...

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中的代码重用的有用方法吗?如果我错过了什么,请告诉我。

Inheritance, internal classes, and nested namespaces seem to work fine. What do you think, is this a useful approach to class-like OOP and code reuse in js? Let me know if I've missed anything.

这篇关于EXTENDS挑战:预处理器函数宏和类类oop的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆