EXTENDS challenge: preprocessor function macros and class-like oop

后端 未结 1 1677
执笔经年
执笔经年 2020-12-29 14:17

Background

I\'ve been using the C preprocessor to manage and \"compile\" semi-large javascript projects with multiple files and build targets. This

相关标签:
1条回答
  • 2020-12-29 14:57

    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.

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

    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.

    0 讨论(0)
提交回复
热议问题