Фон
Я использую препроцессор 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
Создайте каталог 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
, чтобы оно не нарушало фигурные скобки, которые должны следовать за вызовом этого макроса.
Классоподобный ООП
Давайте начнем с макроса NAMESPACE и неясного, но полезного шаблона js ...
#define NAMESPACE(ns) var ns = this.ns = new function()
new function(){ ... }
делает некоторые полезные вещи.Он вызывает анонимную функцию в качестве конструктора, поэтому для его вызова не требуется дополнительных ()
в конце, а внутри него this
относится к объекту, создаваемому конструктором, другими словами, к самому пространству имен,Это также позволяет нам вкладывать пространства имен в пространства имен.
Вот мой полный набор классоподобных макросов ООП:
#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 какмакрос для переноса обычного «клонирования прототипа, копирования свойств конструктора» js наследование прототипа?Я не нашел способа сделать это, кроме как требовать, чтобы 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(){}}())
Заранее благодарим за любую помощь, совет или живое обсуждение.:)