jjzjj

javascript - 扩展挑战 : preprocessor function macros and class-like oop

coder 2024-07-26 原文

背景

我一直在使用 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

创建一个src和一个build目录,并将.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 怎么样?

所以这让我想到了一个问题......我们如何将 EXTENDS 实现为一个宏来包装通常的“克隆原型(prototype),复制构造函数属性”js 原型(prototype)继承?除了要求 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(){}}())

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

最佳答案

我想我刚刚完成了自己的挑战。我已经为正在声明的类的父类(super class)的 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 和代码重用的有用方法吗?如果我遗漏了什么,请告诉我。

关于javascript - 扩展挑战 : preprocessor function macros and class-like oop,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3329094/

有关javascript - 扩展挑战 : preprocessor function macros and class-like oop的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

    我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

  3. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  4. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  5. ruby - 如何在 ruby​​ 中复制目录结构,不包括某些文件扩展名 - 2

    我想编写一个ruby​​脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"

  6. ruby - 扩展类和实例 - 2

    这个问题有两个部分。在RubyProgrammingLanguage一书中,有一个使用模块扩展字符串对象和类的示例(第8.1.1节)。第一个问题。为什么如果您使用新方法扩展类,然后创建该类的对象/实例,则无法访问该方法?irb(main):001:0>moduleGreeter;defciao;"Ciao!";end;end=>nilirb(main):002:0>String.extend(Greeter)=>Stringirb(main):003:0>String.ciao=>"Ciao!"irb(main):004:0>x="foobar"=>"foobar"irb(main):

  7. ruby - 动态扩展现有方法或覆盖 ruby​​ 中的发送方法 - 2

    假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入

  8. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  9. ruby-on-rails - 如何扩展 Ruby Test::Unit 断言以包含 assert_false? - 2

    显然在Test::Unit中没有assert_false。您将如何通过扩展断言并添加文件config/initializers/assertions_helper.rb来添加它?这是最好的方法吗?我不想修改test/unit/assertions.rb。顺便说一句,我不认为这是多余的。我使用的是assert_equalfalse,something_to_evaluate。这种方法的问题是很容易意外使用assertfalse,something_to_evaluate。这将始终失败,不会引发错误或警告,并且会在测试中引入错误。 最佳答案

  10. ruby-on-rails - 无法构建 gem native 扩展 (mkmf (LoadError)) - Ubuntu 12.04 - 2

    这个问题在这里已经有了答案:Unabletoinstallgem-Failedtobuildgemnativeextension-cannotloadsuchfile--mkmf(LoadError)(17个答案)关闭9年前。嘿,我正在尝试在一台新的ubuntu机器上安装rails。我安装了ruby​​和rvm,但出现“无法构建gemnative扩展”错误。这是什么意思?$sudogeminstallrails-v3.2.9(没有sudo表示我没有权限)然后它会输出很多“获取”命令,最终会出现这个错误:Buildingnativeextensions.Thiscouldtakeawhi

随机推荐