object Pattern: Base of Doh Instances
The object pattern is the base pattern or root pattern that all other Doh Patterns implicitly inherit from. It provides the functionality required for object instance creation (AsyncNew() as standard, or New() for synchronous-only cases) and management, including inheritance resolution, lifecycle phase management, and the automatic building of child objects (sub-object-building).
This guide covers:
object pattern as a base.object base pattern.object pattern.object Pattern in ContextThe object pattern sits at the intersection of several key Doh systems:
object is the root pattern that all other patterns ultimately extendobject provides the basic inheritance mechanismsobject establishes the phase execution systemobject implements the auto-building functionalityThis integration creates a foundation for Doh's component architecture.
In Doh, objects are created from "ideas," which are plain JavaScript object literals. This transformation occurs through AsyncNew(), which is the standard and recommended approach. It enables async phases in the construction lifecycle for all objects and their children:
// Define an idea
let buttonIdea = {
text: 'Click Me',
onClick: function() {
console.log('Button clicked!');
}
};
// Standard approach: Create a Doh object using AsyncNew()
let buttonObject = await AsyncNew('object', buttonIdea);
// AsyncNew() enables async phases for all objects and children
// Works with both synchronous and asynchronous patterns
This process involves:
object_phase and builder_phase)After construction, a Doh object has internal properties:
// Internal structure (simplified)
buttonObject = {
// User-defined properties from the idea
text: 'Click Me',
onClick: function() { console.log('Button clicked!'); },
// Internal properties (normally not enumerable)
inherits: ['object'],
inherited: {
object: { /* reference to object pattern */ },
idea: { /* reference to original idea */ }
},
moc: { /* melded MOC definitions */ },
machine: function(phase) { /* phase control function */ }
object_phase: function() { /* melded method executor */ }
builder_phase: function() { /* melded method executor */ }
}
The object pattern establishes the basic Melded Object Composition (MOC) structure that governs how properties are composed:
moc: {
object_phase: 'phase',
builder_phase: 'phase'
}
This definition specifies that object_phase and builder_phase should use the 'phase' melding type—a special method melding behavior for lifecycle phases.
The object pattern implements the core object lifecycle phase execution engine through its machine function, which:
// Advancing an object to a specific phase
myObject.machine('html_phase');
The object_phase is the initial setup phase for all Doh objects:
object_phase: function() {
// Handle static properties
for (let prop_name in this.moc) {
if (this.moc[prop_name] === 'STATIC') {
// Connect to pattern statics
for (pattern in this.inherited) {
if (NotUndefined(this.inherited[pattern][prop_name])) {
Doh.mimic(this, prop_name, this.inherited[pattern], prop_name);
break;
}
}
}
}
}
Immediate reads after writes
Doh.mimic inside object construction or phases, mirrored values update on the microtask queue. If you set a value and need to read the synchronized counterpart immediately (common in tests), flush first:await Doh.microtask();
See /docs/core/data-binding#timing-and-microtasks.
Asynchronous Object Phase:
When using AsyncNew(), the object_phase can be asynchronous for patterns that need to perform async initialization:
// Pattern for a database-connected component
Pattern('DatabaseComponent', {
async object_phase() {
// Initialize database connection asynchronously
this.connection = await this.connectToDatabase();
// Load initial data
this.userData = await this.connection.query('SELECT * FROM users WHERE active = 1');
// Set up caching
this.cache = new Map();
this.userData.forEach(user => this.cache.set(user.id, user));
console.log(`DatabaseComponent initialized with ${this.userData.length} active users`);
// Return values get merged onto this object
return {
ready: true,
userCount: this.userData.length,
lastInit: new Date()
};
},
async connectToDatabase() {
// Simulate async database connection
await new Promise(resolve => setTimeout(resolve, 100));
return { query: async (sql) => [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] };
}
});
// Must use AsyncNew for patterns with async phases
const dbComponent = await AsyncNew('DatabaseComponent');
console.log(dbComponent.ready); // true
console.log(dbComponent.userCount); // 2
This phase handles essential object setup, including:
Because of how phases work, patterns can all have their own 'object_phase' and all the different methods will be stitched together, making it a proper behavioral init path.
The builder_phase is responsible for automated child object construction, integrating tightly with the Sub-Object-Builder system:
builder_phase: function() {
// Collect all sub-object-buildable properties into this.built
Doh.collect_buildable_ideas(this, this.moc, this, '');
// Build all child objects
if (this.built) {
this.machine_built = function(phase) {
// Implementation that builds child objects
};
this.machine_built(this.machine_built_to);
}
}
This phase is crucial for:
A key feature enabled by the object pattern is its integrated sub-object-building system, which automatically constructs child objects from property definitions.
builder_phase, Doh scans the object for properties with a pattern attributebuilt objectNew() (or AsyncNew() when the parent uses AsyncNew())AsyncNew() is used for the parent, children can also use async phases in their construction lifecycle// Before builder_phase
myComponent = {
header: { pattern: 'html', tag: 'header' }
};
// After builder_phase
myComponent = {
header: /* Fully built Doh object */
};
Properties should be defined in the idea object or pattern definition (before the builder_phase runs):
// Best practice: Define sub-object-buildable properties in the idea
// Use AsyncNew() as standard - enables async phases for all objects and children
let pageObject = await AsyncNew('object', {
header: {
pattern: 'html',
tag: 'header',
html: 'Page Title'
},
content: {
pattern: 'html',
tag: 'main',
html: 'Page content goes here'
}
});
The object pattern establishes a parent-child relationship system:
Builder Reference: Each auto-built object gets a builder property pointing to its parent
// Parent object with child objects
// Use AsyncNew() as standard - enables async phases for all objects and children
let form = await AsyncNew('object', {
submitButton: {
pattern: 'Button',
text: 'Submit',
// Child object can access parent methods
onClick: function() {
this.builder.submitForm();
}
},
// Parent method
submitForm: function() {
console.log('Form submitted!');
}
});
The inheritance system in Doh is flexible, allowing for multiple ways to specify inheritance relationships.
Doh's inheritance system accepts the inherits property in multiple formats:
This flexibility appears throughout the system:
// PATTERN DEFINITION: Multiple ways to specify inheritance
// 1. Single inheritance as second argument
Pattern('ButtonPattern', 'UIComponent', {
// Button-specific properties
});
// 2. Multiple inheritance as array in second argument
Pattern('DropdownButton', ['Button', 'Dropdown'], {
// Combined functionality
});
// 3. Complex inheritance through inherits property
Pattern('AdvancedComponent', {
inherits: {
BaseComponent: true, // Hard inheritance
OptionalComponent: false, // Soft inheritance
ConditionalComponent: { // Conditional inheritance
DependencyComponent: true
}
},
// Component properties
});
// OBJECT CREATION: Similarly flexible inheritance
// Use AsyncNew() as standard - enables async phases for all objects and children
// 1. Direct inheritance through pattern name
let button = await AsyncNew('ButtonPattern', {
label: 'Click Me'
});
// 2. Multiple inheritance as first argument
let specialButton = await AsyncNew(['Button', 'Draggable'], {
label: 'Drag Me'
});
// 3. Inheritance through inherits property in idea
let customObject = await AsyncNew('object', {
inherits: ['BasePattern', 'MixinPattern'],
// Custom properties
});
// 4. Combining approaches
let complexComponent = await AsyncNew(['BaseComponent', 'UIComponent'], {
inherits: {
'SpecialFeature': true,
'OptionalFeature': false
},
// Additional properties
});
The inheritance system is designed to be flexible and convenient, allowing you to:
// Ad-hoc composition example
// Use AsyncNew() as standard - enables async phases for all objects and children
let specialButton = await AsyncNew(['Draggable', 'Resizable', 'Button'], {
label: 'Special Button',
});
When multiple patterns are inherited, Doh resolves effective order by first expanding all branches of the inheritance tree, then flattening to a list that respects prerequisites and explicit ordering. Keep in mind:
Method, Array, Object). If a property's moc type is set to 'Override', or if it has no melder defined, it is replaced by the most specific later application (often the idea). This makes 'Override' the explicit way to enforce replacement behavior and prevent melding. See the MOC documentation for complete melding rules.This inheritance system is central to Doh's pattern-based composition, enabling clean, reusable, and flexible code structures.
Explicit Inheritance: When creating patterns, explicitly inherit from 'object' or another pattern.
MOC Definitions: Always define MOC types for properties that need special handling.
Phase Methods: Place initialization code in the appropriate phase methods.
Sub-Object-Building: Define child objects in the idea or pattern definition, not in phase methods.
Parent-Child Relationships: Use the builder chain navigation methods to maintain loose coupling.
For more detailed information on related topics, see: