Metaprogramming JavaScript

Adam McCrea adam@adamlogic.com

Making JavaScript Suck Less

Kicking Ass with JavaScript

an (extremely contrived ) example

Specification: - show state field when country is “United States”

Prototype makes it easy

…but then specs change: change state field to us-state, and add a province field show us-state field when country is "United States" show province field when country is "Canada" show Brutus when us-state is "Ohio" or "Michigan"

(because we can)

Metaprogramming to the rescue!

details

implementation

details “what”

implementation “how”

details “what”

implementation “how”

frequency of change

details “what” configuration file? xml? plain text?

implementation “how”

mini language? executable code?

details “what” configuration file? xml? plain text? mini language? executable code?

implementation “how”

DSL

A Rails Example

A Rails Example

These are just calls to class methods, what’s the big deal?

It just feels right.

A DSL for dynamic form behavior

Sounds great!
…but…

Where do we start?

start with the specs

start with the specs

start with the specs

language does not mirror the problem domain

let’s try again

let’s try again

Dependency

DependencyTrigger

now handle the change event

everything is “wired up”, now make it behave

It works!!!

but it needs cleaned up

let’s avoid top-level functions by creating our own namespace

Form.Behavior
DependencyManager

Dependency

DependencyTrigger

breaking encapsulation

check dependencies when page loads

Fix “domino” bug: when an element is shown/hidden, it may contain trigger fields; check dependencies on these fields

Fix “domino” bug: when an element is shown/hidden, it may contain trigger fields; check dependencies on these fields

Fix “domino” bug: when an element is shown/hidden, it may contain trigger fields; check dependencies on these fields

Fix “domino” bug: when an element is shown/hidden, it may contain trigger fields; check dependencies on these fields

Fix “domino” bug: when an element is shown/hidden, it may contain trigger fields; check dependencies on these fields

hidden fields should have no value – flag them here so we can identify them later

circular reference

DOM Element

JavaScript Object

circular reference: resolved

DOM Element

JavaScript Object

Our DSL is too rigid, let’s allow some variation:

or

resolves to this.show() or this.hide()

Extensibility

Form.Behavior
DependencyManager

Actions

extends

Dependency

DependencyTrigger

More duplication!

It must go.

mini-language mirroring problem domain separate “what” from “how”

Metaprogramming Review

easy to maintain reuseable self-documenting code

method chaining for sentence-like syntax keep it object-oriented

JavaScript Tools & Tips

incremental implementation don’t fear eval use with() and English-like method names

Thanks!
adam@adamlogic.com adamlogic.com