This document discusses attributes in YUI and their advantages over plain object properties. It explains that attributes provide encapsulation of state and configuration through features like getters, setters, default values, and change events. It describes how attributes are implemented using the Y.AttributeCore and Y.AttributeObservable modules to provide this magic functionality. The document addresses some costs of using attributes, like additional processing during object initialization and property access compared to plain properties. It provides guidance on when it may make sense to use attributes versus properties.
3. You know this stuff
model.get(“id”);
overlay.set(“visible”, true);
var drag = new Y.DD.Drag({
node: “#dragme”
});
4. You know this stuff
Bakery.ATTRS = {
flour: {
value: “pastry”
},
...
};
5. What is an attribute?
Encapsulation of a value state and set of
feature configurations for customizing logic
associated with the mutation and storage of
that state, while providing opportunities to
monitor changes.
35. value and valueFn
• Most expensive
• lazyAdd = true to defer cost
• valueFn always called at instantiation
36. lazyAdd
• Minimal setup, no set()
• Full setup on first call to get() or set()
• Some might never get fully setup (good)
• Y.BaseCore defaults lazyAdd = true for all
(override with prototype._lazyAddAttrs)
• Beware lazyAdd + setter
37. setter for robustness
• Use for
a) Input normalization, or
b) Tightly coupled state related logic
• Not for side effects (use events)
• setter + lazyAdd gotcha
38. setter vs validator
• validator is called before setter
• If you have a setter, validate inline
_setFlour: function (val) {
return (typeof val === “string”) ?
val.toLowerCase() :
Y.Attribute.INVALID_VALUE;
40. cloneDefaultValue
• {undefined, true, “deep”}, “shallow”, false
• Y.Base defaults Y.clone() behavior
• Always set or use a valueFn instead
flour: {
value: { grain: ‘fine’, gluten: false, ... },
cloneDefaultValue: true,
41. Change events
• For decoupling systems
• Why custom events have on() and after()
• Assigning initial value does NOT fire event
• Change event published on first set()
42. Ad hoc attributes
• From Y.BaseCore
• Create dynamic objects with Attribute API
• prototype._allowAdHocAttrs = true
• static _NON_ATTRS_CFG to blacklist
49. Attribute setup
1. Creates State object
2. Depth 2 copy of configs
3. Gets value from construction values,
valueFn, or value
50. Attribute setup
1. Creates State object
2. Depth 2 copy of configs
3. Gets value from construction values,
valueFn, or value
4. lazyAdd attributes are stowed in State
51. Attribute setup
1. Creates State object
2. Depth 2 copy of configs
3. Gets value from construction values,
valueFn, or value
4. lazyAdd attributes are stowed in State
5. non-lazy populate State and set(value)
56. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
57. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
1. class extension constructors
58. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
1. class extension constructors
2. addAttrs with filtered list from ATTRS aggregate
59. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
1. class extension constructors
2. addAttrs with filtered list from ATTRS aggregate
3. initializer
60. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
1. class extension constructors
2. addAttrs with filtered list from ATTRS aggregate
3. initializer
4. class extension initializers
61. Base setup
1. Creates State object
2. Aggregates ATTRS up superclass chain
3. for each class call...
1. class extension constructors
2. addAttrs with filtered list from ATTRS aggregate
3. initializer 2.5. addAttrs(ad-hocs)
4. class extension initializers
62. get()
1. Checks if attribute is lazy, inits if necessary
2. Gets the value and getter from State
3. Returns value or result of calling getter
69. Is the magic worth it?
Should you use a property or an attribute?
70. Property
PROs CONs
• Fast • Dumb
• Terse • Can’t migrate to
attribute w/o API
• Nested structs are breakage
the same
• Magic allowed if
environment
mandated
71. Attribute
PROs CONs
• Stable abstraction • Slow
• Code organization • Nested structs are
awkward
• Consistent API (subattributes--)
• Easy loose coupling
• More typing
with events
• Abstraction
performance can
improve invisibly
72. Property or Attribute?
• Will it be accessed a lot? Like, really A LOT.
• Will there be lots of instances? (see #1)
• Will it be public, or interesting outside the
object?
• Might the class be extended? Plugged?
• Would it amount to a micro-optimization?
73. Property or Attribute?
Justify the use of properties
rather than
justifying the use of attributes
(It’s less likely to bite you in the ass)
You can also use obj.set(‘unknown’, val) and setAttrs({ ... }); but they will be magicless\n
\n
\n
\n
\n
\n
“Be conservative in what you send, liberal in what you accept”\nsetter code creep = serving multiple masters\n
\n
You can find this in the API docs, but I want to strongly recommend\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
cloneDefaultValue for obj values during aggregation\naddAttrs() called with the aggregated configs for that class\naddAttrs() applies user values - Attribute ignores user values for unknown attrs\naddAttrs() calls Y.merge() on user value hash, so once for each level\n
\n
\n
\n
\n
\n
\n
\n
\n
Similarly, if you capture the object returned from get(‘nested’), you can set its properties whenever.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
It’s not just a numbers game - maintenance, future proofing\n
\n
and remember, you’re talking about giving up all those yummy features\n
\n
\n
result in the cost being replaced by kittens making you pancakes\n
result in the cost being replaced by kittens making you pancakes\n
result in the cost being replaced by kittens making you pancakes\n
result in the cost being replaced by kittens making you pancakes\n