Shopify/batman — pull request #720 by hornairs (2013-05-15 01:56:07+00:00)
Title: Make Properties act as their own event instead of being EventEmitters

Long ago during the development of Batman we realized that Properties needed to be constructed on top of events. We realized that observation really was only a special case of event firing callbacks if we create the notion of a `change` event on properties. So, `Property` started mixing in `EventEmitter` to it's prototype, and as such got functions like `fire`, `on`, and the handy function `event(someKey)` for creating/accessing events on the property.

This is great for structure and logic purposes: the `change` event ended up being a nice abstraction we could use to let other things trigger accessor re-computations, however its form before this change, ends up requiring a silly-large amount of objects. For the thing returned by a simple `Batman(foo: 1)` we have:

```
Batman.Object instance:
_batman: _Batman instance
_properties: SimpleHash instance
_objectStorage:
foo: Property Instance
value: 1
cached: true
events: SimpleHash instance
_objectStorage: POJO
change: Event instance
handlers: Array
preventCount: 0
```

There is more concerning the root but I think the part of the tree starting at the `Property` is accurate. `Property` is an `EventEmitter` with the potential to have many events, and where each event is represented as its own object in a tracking hash (`events`) on the parent.

This change aims to reduce the number of objects allocated for each `Property`. Instead of making `Property` a bona fide `EventEmitter`, this makes `Property` *subclass* `Event`. That means that the thing which fires handlers is the `Property` its self. Instead of the array of handlers to fire residing in an object two hops away, the handlers array lives right on the `Property` instance. Instead of calling the `@event('change')` function all the time to access the event from code inside `Property`, or even calling the optimized `@changeEvent()` version, we do away with any event access and start calling `fire` on `this`, since the Property is now the event.

The results of the change:

##### Instantiating a simple BatmanObject with 0 keys
```coffee
suite.add 'vanilla object creation', () ->
new Batman.Object() for i in [0..1000]
true
```
master in light blue, branch in dark blue
![screen shot 2013-05-14 at 9 06 38 pm](https://f.cloud.github.com/assets/158950/504912/3dc6fd60-bd01-11e2-98bc-10d2798e0930.png)

##### Instantiating a simple BatmanObject subclass with 3 properties

```coffee
suite.add 'clunk creation', () ->
new Clunk({i}) for i in [0..200]
true
```
master in light blue, branch in dark blue
![screen shot 2013-05-14 at 9 06 47 pm](https://f.cloud.github.com/assets/158950/504910/3dbc7566-bd01-11e2-94bd-36a5192b91d5.png)

#### Accessing accessors with 0, 1, and 3 sources
```coffee
do ->
clunks = (new Clunk({i}) for i in [0..1000])
suite.add 'getting with no sources', ->
for clunk in clunks
clunk.property('i').cached = false
clunk.get('i')
true

do ->
class ClunkWithAccessor extends Clunk
@accessor 'bar', ->
@get('i') + 1

clunks = (new ClunkWithAccessor({i}) for i in [0..1000])

suite.add 'getting with 1 source', ->
for clunk in clunks
clunk.property('bar').cached = false
clunk.get('bar')
true

do ->
class ClunkWithAccessor extends Clunk
@accessor 'bar', ->
@get('foo')
@get('baz')
@get('i') + 1

clunks = (new ClunkWithAccessor({i}) for i in [0..1000])

suite.add 'getting with 3 sources', ->
for clunk in clunks
clunk.property('bar').cached = false
clunk.get('bar')
true
```

master in dark blue, branch in light blue

![screen shot 2013-05-14 at 9 06 03 pm](https://f.cloud.github.com/assets/158950/504914/3dcbb864-bd01-11e2-802c-b24787b0c9e5.png)
![screen shot 2013-05-14 at 9 06 12 pm](https://f.cloud.github.com/assets/158950/504913/3dcae1f0-bd01-11e2-905e-fe464e055ca7.png)
![screen shot 2013-05-14 at 9 06 24 pm](https://f.cloud.github.com/assets/158950/504911/3dc6f4c8-bd01-11e2-95ed-86d67ca5a0b7.png)

So, instantiation is up to twice as fast, and property value computation is faster, especially when you have many sources.

So I think we should merge this. It removes some functionality, for example you couldn't do `someProperty.event('otherKey')` any more, and there just might be some stuff that breaks despite green tests. The code also gets a bit more complicated, and the happy wonderful abstraction of `Property` emitting events is gone for something ... muddier.

Thoughts @kristianpd @nickjs @matthelm @ibawt ?