serializeAttributes

24 views
Skip to first unread message

stephanos

unread,
Nov 3, 2012, 4:47:55 PM11/3/12
to chapl...@googlegroups.com
Hi,

I had some problems with 'getTemplateData': it would not pick up my model by default - it was always empty.
I did a big of debugging and it seems the problem is in 'serializeAttributes'.

# Private helper function for serializing attributes recursively,
# creating objects which delegate to the original attributes
# when a property needs to be overwritten.
serializeAttributes = (model, attributes, modelStack) ->
  # Create a delegator on initial call
  unless modelStack
    delegator = utils.beget attributes <-- (A)
    modelStack = [model]
  else
    # Add model to stack
    modelStack.push model
  [...]
  # Return the delegator if it was created, otherwise the plain attributes
  delegator or attributes <!-- (B)

The serialize() function calls serializeAttributes() with 2 arguments, and it appears as if the delegator is then always initialized (see (A)), and then returned (see (B)).

I don't know if I'm doing something wrong but 'utils.beget attributes' seems to always return an empty object instead of undefined/null?


Regards
Stephan

Mathias Schäfer

unread,
Nov 3, 2012, 6:45:45 PM11/3/12
to chapl...@googlegroups.com
Hello,

Am 03.11.12 21:47, schrieb stephanos:
> I don't know if I'm doing something wrong but '/utils.beget
> attributes'/ seems to always return an empty object instead of
> undefined/null?

utils.beget returns an object that is empty BUT it has the given object
as its prototype. It’s a simple way to set up prototypal delegation
(sometimes called prototypal inheritance).

http://javascript.crockford.com/prototypal.html

In ECMAScript-5-capable browsers, utils.beget just uses Object.create
which is a built-in function for setting up prototypal inheritance.

https://developer.mozilla.org/de/docs/JavaScript/Reference/Global_Objects/Object/create

The returned object is an empty wrapper which allows to access all
properties of the prototype:

var o1 = { prop: 1 };
var o2 = Object.create(o1);
// o2 has no own properties:
alert(_.isEmpty(o1) + ' ' + _.isEmpty(o2));
// -> true false
alert(o1.hasOwnProperty('prop') + ' ' + o2.hasOwnProperty('prop'));
// -> true false
// But you can read inherited properties:
alert(o1.prop + ' ' + o2.prop);
// -> 1 1

So the object o2 is empty but all of o1’s properties can be accessed by
their name.

We’re using this approach in getTemplateData so you can add and
overwrite some properties for the template safely without changing the
model attributes (create a “view model” out of the bare model
attributes). The standard Backbone approach is to create a copy of the
model attributes but this is significantly slower and does not scale.

In practice, you shouldn’t see a difference between the actual model
attributes and the wrapper that delegates to the model data.

Just make sure that you don’t skip inherited properties when looping
over all object properties.

// Usually a loop performs a hasOwnProperty check:
for (var propName in o2) {
if (o2.hasOwnProperty(propName)) {
alert(propName + ': ' + o2[propName]);
}
}
// -> Won’t alert anything

// Without the check:
for (var propName in o2) {
alert(propName + ': ' + o2[propName]);
}
// -> prop: 1, because it walks up the prototype chain

Helpers like Underscore’s _.each, jQuery’s $.each etc. perform this check.

But usually a template doesn’t need to loop over all properties.

Regards
Mathias

stephanos

unread,
Nov 4, 2012, 4:20:02 AM11/4/12
to chapl...@googlegroups.com
Thank you for taking the time to explain it! I appreciate it.

I only looked at the object and thought it just had an empty '__proto__' object.
I'm using a custom handlebars function 'eachKeyValue()' which uses _.each internally ...

PS: I really like Chaplin :)

Regards
Stephan
Reply all
Reply to author
Forward
0 new messages