EXTENDE o desafio: macros de função de pré-processador e oop de classe
fundo
Eu tenho usado o pré-processador C para gerenciar e "compilar" projetos javascript semi-grandes com vários arquivos e criar destinos. Isso fornece acesso total às diretrizes do pré-processador C, como#include
, #define
, #ifdef
etc. a partir do javascript. Aqui está um exemplo de script de construção para que você possa testar o código de exemplo:
#!/bin/bash
export OPTS="-DDEBUG_MODE=1 -Isrc"
for FILE in `find src/ | egrep '\.js?Faça umsrc
e umbuild
diretório e coloque os arquivos .js emsrc
.
Macros de conveniência
Originalmente, eu só queria o material do pré-processador para#include
e talvez alguns#ifdef
s, mas comecei a pensar: não seria legal ter algumas macros de conveniência também? Experimentação seguida.
#define EACH(o,k) for (var k in o) if (o.hasOwnProperty(k))
Legal, então agora eu posso escrever algo como isto:
EACH (location, prop) {
console.log(prop + " : " location[prop]);
}
E será expandido para:
for (var prop in location) if (location.hasOwnProperty(prop)) {
console.log(prop + " : " location[prop]);
}
Que tal 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) }
Observe como nos esgueiramosv=o[k]
dentro deif
para que não perturbe os chavetas que devem seguir a invocação dessa macro.
OOP de classe
Vamos começar com uma macro NAMESPACE e um padrão js obscuro, mas útil ...
#define NAMESPACE(ns) var ns = this.ns = new function()
new function(){ ... }
faz algumas coisas legais. Ele chama uma função anônima como construtor, portanto, não precisa de um extra()
no final para chamá-lo, e dentro delethis
refere-se ao objeto que está sendo criado pelo construtor, em outras palavras, o próprio espaço para nome. Isso também nos permite aninhar namespaces dentro de namespaces.
Aqui está o meu conjunto completo de macros OOP de classe:
#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
Como você pode ver, essas macros definem muitas coisas, tanto noVariable Object
(por conveniência) e emthis
(por necessidade). Aqui está um exemplo de código:
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);
}
}
}
E quanto a EXTENDS?
Então isso me leva à pergunta ... como podemos implementar EXTENDS como uma macro para agrupar o usual "clonar o protótipo, copiar propriedades do construtor" js herança do protótipo? Não encontrei uma maneira de fazer isso além de exigir que as EXTENDS apareçamdepois de a definição de classe, que é boba. Este experimento precisa de EXTENDS ou é inútil. Sinta-se livre para alterar as outras macros, desde que produzam os mesmos resultados.
Editar - pode ser útil para EXTENDS; listando-os aqui para verificar se estão completos.
#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(){}}())
Agradecemos antecipadamente por qualquer ajuda, conselho ou discussão animada. :)
`
do
echo "Processing $FILE"
cat $FILE \
| sed 's/^\s*\/\/#/#/' \
| cpp $OPTS \
| sed 's/^[#:<].*// ; /^$/d' \
> build/`basename $FILE`;
done
Faça umsrc
e umbuild
diretório e coloque os arquivos .js emsrc
.
Macros de conveniência
Originalmente, eu só queria o material do pré-processador para#include
e talvez alguns#ifdef
s, mas comecei a pensar: não seria legal ter algumas macros de conveniência também? Experimentação seguida.
#define EACH(o,k) for (var k in o) if (o.hasOwnProperty(k))
Legal, então agora eu posso escrever algo como isto:
EACH (location, prop) {
console.log(prop + " : " location[prop]);
}
E será expandido para:
for (var prop in location) if (location.hasOwnProperty(prop)) {
console.log(prop + " : " location[prop]);
}
Que tal 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) }
Observe como nos esgueiramosv=o[k]
dentro deif
para que não perturbe os chavetas que devem seguir a invocação dessa macro.
OOP de classe
Vamos começar com uma macro NAMESPACE e um padrão js obscuro, mas útil ...
#define NAMESPACE(ns) var ns = this.ns = new function()
new function(){ ... }
faz algumas coisas legais. Ele chama uma função anônima como construtor, portanto, não precisa de um extra()
no final para chamá-lo, e dentro delethis
refere-se ao objeto que está sendo criado pelo construtor, em outras palavras, o próprio espaço para nome. Isso também nos permite aninhar namespaces dentro de namespaces.
Aqui está o meu conjunto completo de macros OOP de classe:
#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
Como você pode ver, essas macros definem muitas coisas, tanto noVariable Object
(por conveniência) e emthis
(por necessidade). Aqui está um exemplo de código:
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);
}
}
}
E quanto a EXTENDS?
Então isso me leva à pergunta ... como podemos implementar EXTENDS como uma macro para agrupar o usual "clonar o protótipo, copiar propriedades do construtor" js herança do protótipo? Não encontrei uma maneira de fazer isso além de exigir que as EXTENDS apareçamdepois de a definição de classe, que é boba. Este experimento precisa de EXTENDS ou é inútil. Sinta-se livre para alterar as outras macros, desde que produzam os mesmos resultados.
Editar - pode ser útil para EXTENDS; listando-os aqui para verificar se estão completos.
#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(){}}())
Agradecemos antecipadamente por qualquer ajuda, conselho ou discussão animada. :)