Dag*_*bit 7 javascript shell macros preprocessor prototypal-inheritance
背景
我一直在使用C预处理器来管理和"编译"包含多个文件和构建目标的半大型JavaScript项目.这使全面进入C预处理指令一样#include,#define,#ifdef等从JavaScript中.这是一个示例构建脚本,因此您可以测试示例代码:
#!/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
Run Code Online (Sandbox Code Playgroud)
创建一个src和一个build目录,然后放入.js文件src.
便利宏
最初,我只是想要预处理器的东西#include,也许还有几个#ifdef,但我想,有一些方便的宏也不是很好吗?随后进行了实验.
#define EACH(o,k) for (var k in o) if (o.hasOwnProperty(k))
Run Code Online (Sandbox Code Playgroud)
很酷,所以现在我可以这样写:
EACH (location, prop) {
console.log(prop + " : " location[prop]);
}
Run Code Online (Sandbox Code Playgroud)
它将扩展到:
for (var prop in location) if (location.hasOwnProperty(prop)) {
console.log(prop + " : " location[prop]);
}
Run Code Online (Sandbox Code Playgroud)
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) }
Run Code Online (Sandbox Code Playgroud)
注意我们是如何潜入v=o[k]内部if条件,因此不会干扰应遵循这个宏的调用花括号.
类似OOP
让我们从一个NAMESPACE宏和一个不起眼但有用的js模式开始......
#define NAMESPACE(ns) var ns = this.ns = new function()
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这些宏在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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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(){}}())
Run Code Online (Sandbox Code Playgroud)
在此先感谢任何帮助,建议或热烈的讨论.:)
我想我刚刚完成了自己的挑战。我已经为所声明的类的超类的 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);
};
})
Run Code Online (Sandbox Code Playgroud)
...这是一个测试命名空间...
//#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 = [];
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
...这是输出...
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 = [];
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
继承、内部类和嵌套命名空间似乎工作得很好。您认为,这是一种在 js 中实现类似类 OOP 和代码重用的有用方法吗?如果我错过了什么,请告诉我。