Learn shorthand properties, computed property names, and method definitions in modern object syntax.
ES6 enhanced object literals revolutionize how we create and work with objects in JavaScript. These enhancements provide cleaner syntax, dynamic property creation, and more powerful method definitions that make object-oriented programming more elegant and expressive.
Beyond simple property assignments, enhanced object literals enable computed property names, concise method syntax, and seamless integration with modern JavaScript patterns like destructuring and spread operations. They're fundamental to modern frameworks and libraries.
In this lesson, you'll explore shorthand property syntax, computed property names, enhanced method definitions, and learn to combine these features to create more maintainable and readable JavaScript code in both functional and object-oriented programming paradigms.
ES6 allows concise syntax when property names match variable names and provides shorthand method definitions for cleaner object creation.
1. Property Value Shorthand
1// Traditional object property assignment
2const name = 'Alice';
3const age = 30;
4const city = 'New York';
5
6const traditionalUser = {
7 name: name,
8 age: age,
9 city: city
10};
11
12// ES6 shorthand - property name matches variable name
13const modernUser = {
14 name,
15 age,
16 city
17};
18
19console.log(modernUser); // { name: 'Alice', age: 30, city: 'New York' }
20
21// Mixed shorthand and traditional
22const email = '[email protected]';
23const userProfile = {
24 name,
25 age,
26 email,
27 isActive: true, // Traditional syntax when needed
28 lastLogin: new Date()
29};2. Method Definition Shorthand
1// Traditional method definition
2const traditionalCalculator = {
3 add: function(a, b) {
4 return a + b;
5 },
6 subtract: function(a, b) {
7 return a - b;
8 }
9};
10
11// ES6 method shorthand
12const modernCalculator = {
13 add(a, b) {
14 return a + b;
15 },
16
17 subtract(a, b) {
18 return a - b;
19 },
20
21 multiply(a, b) {
22 return a * b;
23 },
24
25 // Arrow functions as methods (be careful with 'this')
26 divide: (a, b) => b !== 0 ? a / b : null,
27
28 // Async method
29 async fetchData(url) {
30 const response = await fetch(url);
31 return response.json();
32 },
33
34 // Generator method
35 *generateNumbers(max) {
36 for (let i = 0; i < max; i++) {
37 yield i;
38 }
39 }
40};
41
42console.log(modernCalculator.add(5, 3)); // 8
43console.log([...modernCalculator.generateNumbers(3)]); // [0, 1, 2]3. Constructor Function Pattern
1// Factory function with enhanced object literals
2function createUser(name, email, role = 'user') {
3 const id = Math.random().toString(36).substr(2, 9);
4 const createdAt = new Date();
5
6 return {
7 // Shorthand properties
8 id,
9 name,
10 email,
11 role,
12 createdAt,
13
14 // Method definitions
15 getDisplayName() {
16 return `${this.name} (${this.role})`;
17 },
18
19 updateProfile(updates) {
20 Object.assign(this, updates);
21 this.updatedAt = new Date();
22 return this;
23 },
24
25 hasPermission(permission) {
26 const rolePermissions = {
27 admin: ['read', 'write', 'delete'],
28 editor: ['read', 'write'],
29 user: ['read']
30 };
31
32 return rolePermissions[this.role]?.includes(permission) || false;
33 },
34
35 toJSON() {
36 const { name, email, role, createdAt } = this;
37 return { name, email, role, createdAt };
38 }
39 };
40}
41
42const user = createUser('John Doe', '[email protected]', 'editor');
43console.log(user.getDisplayName()); // "John Doe (editor)"
44console.log(user.hasPermission('write')); // trueComputed property names allow dynamic property creation using expressions in square brackets, enabling flexible object construction.
1. Basic Computed Properties
1// Dynamic property names
2const propertyName = 'dynamicProp';
3const prefix = 'user_';
4
5const obj = {
6 staticProp: 'static value',
7 [propertyName]: 'dynamic value',
8 [`${prefix}name`]: 'John',
9 [`${prefix}id`]: 123,
10 [Symbol.iterator]: function* () {
11 yield* Object.values(this);
12 }
13};
14
15console.log(obj.dynamicProp); // 'dynamic value'
16console.log(obj.user_name); // 'John'
17console.log([...obj]); // ['static value', 'dynamic value', 'John', 123]2. Function-based Computed Properties
1// Using functions for property names
2function getPropertyName(type, index) {
3 return `${type}_${index}`;
4}
5
6function createConfig(environments) {
7 const config = {};
8
9 environments.forEach((env, index) => {
10 Object.assign(config, {
11 [getPropertyName('env', index)]: env,
12 [getPropertyName('url', index)]: `https://${env}.api.example.com`,
13 [getPropertyName('timeout', index)]: (index + 1) * 5000
14 });
15 });
16
17 return config;
18}
19
20const envConfig = createConfig(['dev', 'staging', 'production']);
21console.log(envConfig);
22// {
23// env_0: 'dev', url_0: 'https://dev.api.example.com', timeout_0: 5000,
24// env_1: 'staging', url_1: 'https://staging.api.example.com', timeout_1: 10000,
25// env_2: 'production', url_2: 'https://production.api.example.com', timeout_2: 15000
26// }3. Advanced Computed Patterns
1// Using computed properties for method names
2const operations = ['add', 'subtract', 'multiply', 'divide'];
3
4const calculator = operations.reduce((calc, op) => ({
5 ...calc,
6 [op](a, b) {
7 switch (op) {
8 case 'add': return a + b;
9 case 'subtract': return a - b;
10 case 'multiply': return a * b;
11 case 'divide': return b !== 0 ? a / b : null;
12 default: return 0;
13 }
14 }
15}), {});
16
17console.log(calculator.add(5, 3)); // 8
18console.log(calculator.multiply(4, 6)); // 24
19
20// Dynamic API endpoints
21function createAPI(baseURL, endpoints) {
22 return endpoints.reduce((api, endpoint) => ({
23 ...api,
24 [`get${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}`](id) {
25 return fetch(`${baseURL}/${endpoint}/${id}`);
26 },
27 [`create${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}`](data) {
28 return fetch(`${baseURL}/${endpoint}`, {
29 method: 'POST',
30 body: JSON.stringify(data),
31 headers: { 'Content-Type': 'application/json' }
32 });
33 }
34 }), {});
35}
36
37const api = createAPI('https://api.example.com', ['users', 'posts', 'comments']);
38// Creates methods: getUsers, createUsers, getPosts, createPosts, etc.4. Computed Properties with Symbols
1// Using symbols for computed properties
2const idSymbol = Symbol('id');
3const metadataSymbol = Symbol('metadata');
4
5function createSecureObject(data, metadata) {
6 return {
7 ...data,
8 [idSymbol]: Math.random().toString(36),
9 [metadataSymbol]: metadata,
10
11 getId() {
12 return this[idSymbol];
13 },
14
15 getMetadata() {
16 return this[metadataSymbol];
17 },
18
19 [Symbol.toString]() {
20 return `SecureObject(${this[idSymbol]})`;
21 },
22
23 [Symbol.toPrimitive](hint) {
24 if (hint === 'string') return this.toString();
25 if (hint === 'number') return this[idSymbol].length;
26 return this[idSymbol];
27 }
28 };
29}
30
31const secureObj = createSecureObject(
32 { name: 'Important Data' },
33 { created: new Date(), version: '1.0' }
34);
35
36console.log(secureObj.getId()); // Random ID
37console.log(String(secureObj)); // "SecureObject(randomId)"Enhanced object literals support various method definition patterns and super references for prototype-based inheritance.
1. Different Method Definition Styles
1const obj = {
2 // Regular method
3 regularMethod() {
4 return 'regular method';
5 },
6
7 // Async method
8 async asyncMethod() {
9 await new Promise(resolve => setTimeout(resolve, 100));
10 return 'async method completed';
11 },
12
13 // Generator method
14 *generatorMethod() {
15 yield 1;
16 yield 2;
17 yield 3;
18 },
19
20 // Async generator method
21 async *asyncGeneratorMethod() {
22 for (let i = 0; i < 3; i++) {
23 await new Promise(resolve => setTimeout(resolve, 100));
24 yield `async value ${i}`;
25 }
26 },
27
28 // Arrow function property (no 'this' binding)
29 arrowFunction: () => 'arrow function',
30
31 // Traditional function property
32 traditionalFunction: function() {
33 return `traditional function with this: ${this.constructor.name}`;
34 }
35};
36
37// Usage examples
38console.log(obj.regularMethod()); // 'regular method'
39console.log([...obj.generatorMethod()]); // [1, 2, 3]
40
41(async () => {
42 console.log(await obj.asyncMethod()); // 'async method completed'
43
44 for await (const value of obj.asyncGeneratorMethod()) {
45 console.log(value); // 'async value 0', 'async value 1', 'async value 2'
46 }
47})();2. Prototype Chain and Super References
1// Base object with methods
2const animal = {
3 name: 'Unknown',
4
5 speak() {
6 return `${this.name} makes a sound`;
7 },
8
9 move() {
10 return `${this.name} moves`;
11 }
12};
13
14// Derived object using super
15const dog = {
16 __proto__: animal,
17 name: 'Dog',
18
19 speak() {
20 return `${super.speak()} - Woof!`;
21 },
22
23 move() {
24 return `${super.move()} by running`;
25 },
26
27 wagTail() {
28 return `${this.name} wags tail`;
29 }
30};
31
32const cat = {
33 __proto__: animal,
34 name: 'Cat',
35
36 speak() {
37 return `${super.speak()} - Meow!`;
38 },
39
40 move() {
41 return `${super.move()} by walking gracefully`;
42 },
43
44 purr() {
45 return `${this.name} purrs contentedly`;
46 }
47};
48
49console.log(dog.speak()); // "Dog makes a sound - Woof!"
50console.log(cat.move()); // "Cat moves by walking gracefully"3. Mixin Pattern with Enhanced Object Literals
1// Mixin for logging functionality
2const LoggingMixin = {
3 log(message, level = 'info') {
4 const timestamp = new Date().toISOString();
5 console.log(`[${timestamp}] [${level.toUpperCase()}] ${this.constructor.name || 'Object'}: ${message}`);
6 },
7
8 logError(error) {
9 this.log(error.message, 'error');
10 },
11
12 logDebug(message) {
13 if (this.debugMode) {
14 this.log(message, 'debug');
15 }
16 }
17};
18
19// Mixin for event handling
20const EventMixin = {
21 addEventListener(event, handler) {
22 if (!this._events) this._events = {};
23 if (!this._events[event]) this._events[event] = [];
24 this._events[event].push(handler);
25 },
26
27 removeEventListener(event, handler) {
28 if (!this._events?.[event]) return;
29 const index = this._events[event].indexOf(handler);
30 if (index > -1) this._events[event].splice(index, 1);
31 },
32
33 emit(event, data) {
34 this._events?.[event]?.forEach(handler => {
35 try {
36 handler(data);
37 } catch (error) {
38 this.logError?.(error);
39 }
40 });
41 }
42};
43
44// Create object with multiple mixins
45function createComponent(name) {
46 return {
47 // Own properties
48 name,
49 debugMode: true,
50
51 // Mixin methods using spread
52 ...LoggingMixin,
53 ...EventMixin,
54
55 // Own methods
56 initialize() {
57 this.log(`Component ${this.name} initialized`);
58 this.emit('initialized', { name: this.name });
59 },
60
61 destroy() {
62 this.log(`Component ${this.name} destroyed`);
63 this.emit('destroyed', { name: this.name });
64 this._events = {};
65 }
66 };
67}
68
69const component = createComponent('UserProfile');
70component.addEventListener('initialized', (data) =>
71 console.log(`Component ${data.name} is ready!`)
72);
73component.initialize();Modern JavaScript leverages enhanced object literals in sophisticated patterns for configuration, factories, and architectural design.
1. Configuration Objects with Validation
1// Configuration factory with validation
2function createConfig(userConfig = {}) {
3 const defaultConfig = {
4 timeout: 5000,
5 retries: 3,
6 debug: false
7 };
8
9 // Validation rules
10 const validators = {
11 timeout: (value) => typeof value === 'number' && value > 0,
12 retries: (value) => typeof value === 'number' && value >= 0,
13 debug: (value) => typeof value === 'boolean'
14 };
15
16 // Validate user config
17 for (const [key, value] of Object.entries(userConfig)) {
18 if (validators[key] && !validators[key](value)) {
19 throw new Error(`Invalid configuration for ${key}: ${value}`);
20 }
21 }
22
23 return {
24 // Merge configurations
25 ...defaultConfig,
26 ...userConfig,
27
28 // Computed properties
29 [Symbol.toStringTag]: 'Configuration',
30
31 // Methods
32 validate() {
33 return Object.entries(this).every(([key, value]) =>
34 !validators[key] || validators[key](value)
35 );
36 },
37
38 update(updates) {
39 return createConfig({ ...this, ...updates });
40 },
41
42 toJSON() {
43 const { timeout, retries, debug } = this;
44 return { timeout, retries, debug };
45 },
46
47 // Getters for computed values
48 get isProduction() {
49 return !this.debug;
50 },
51
52 get timeoutMessage() {
53 return `Request will timeout after ${this.timeout}ms`;
54 }
55 };
56}
57
58const config = createConfig({ timeout: 10000, debug: true });
59console.log(config.timeoutMessage); // "Request will timeout after 10000ms"
60console.log(config.isProduction); // false2. Builder Pattern with Fluent Interface
1// Query builder using enhanced object literals
2function createQueryBuilder() {
3 let query = {
4 select: [],
5 from: '',
6 where: [],
7 orderBy: [],
8 limit: null
9 };
10
11 return {
12 // Fluent interface methods
13 select(...fields) {
14 query.select.push(...fields);
15 return this;
16 },
17
18 from(table) {
19 query.from = table;
20 return this;
21 },
22
23 where(condition) {
24 query.where.push(condition);
25 return this;
26 },
27
28 orderBy(field, direction = 'ASC') {
29 query.orderBy.push(`${field} ${direction}`);
30 return this;
31 },
32
33 limit(count) {
34 query.limit = count;
35 return this;
36 },
37
38 // Build final query
39 build() {
40 const parts = [];
41
42 // SELECT
43 if (query.select.length > 0) {
44 parts.push(`SELECT ${query.select.join(', ')}`);
45 } else {
46 parts.push('SELECT *');
47 }
48
49 // FROM
50 if (query.from) {
51 parts.push(`FROM ${query.from}`);
52 }
53
54 // WHERE
55 if (query.where.length > 0) {
56 parts.push(`WHERE ${query.where.join(' AND ')}`);
57 }
58
59 // ORDER BY
60 if (query.orderBy.length > 0) {
61 parts.push(`ORDER BY ${query.orderBy.join(', ')}`);
62 }
63
64 // LIMIT
65 if (query.limit) {
66 parts.push(`LIMIT ${query.limit}`);
67 }
68
69 return parts.join(' ');
70 },
71
72 // Reset builder
73 reset() {
74 query = { select: [], from: '', where: [], orderBy: [], limit: null };
75 return this;
76 },
77
78 // Clone builder
79 clone() {
80 const cloned = createQueryBuilder();
81 cloned.query = JSON.parse(JSON.stringify(query));
82 return cloned;
83 }
84 };
85}
86
87const query = createQueryBuilder()
88 .select('name', 'email')
89 .from('users')
90 .where('active = 1')
91 .where('age > 18')
92 .orderBy('name')
93 .limit(10)
94 .build();
95
96console.log(query);
97// "SELECT name, email FROM users WHERE active = 1 AND age > 18 ORDER BY name LIMIT 10"3. State Machine with Enhanced Objects
1// State machine using enhanced object literals
2function createStateMachine(initialState, states, transitions) {
3 let currentState = initialState;
4 const listeners = new Set();
5
6 const stateMachine = {
7 // Current state getter
8 get state() {
9 return currentState;
10 },
11
12 // State checking methods
13 is(state) {
14 return currentState === state;
15 },
16
17 can(action) {
18 return transitions[currentState]?.[action] !== undefined;
19 },
20
21 // State transition
22 transition(action, data) {
23 if (!this.can(action)) {
24 throw new Error(`Cannot perform action "${action}" in state "${currentState}"`);
25 }
26
27 const previousState = currentState;
28 const nextState = transitions[currentState][action];
29
30 // Execute exit handler if exists
31 states[currentState]?.exit?.(data, previousState, nextState);
32
33 currentState = nextState;
34
35 // Execute enter handler if exists
36 states[currentState]?.enter?.(data, previousState, nextState);
37
38 // Notify listeners
39 listeners.forEach(listener => {
40 try {
41 listener({ previousState, currentState, action, data });
42 } catch (error) {
43 console.error('State listener error:', error);
44 }
45 });
46
47 return this;
48 },
49
50 // Event listeners
51 onTransition(listener) {
52 listeners.add(listener);
53 return () => listeners.delete(listener);
54 },
55
56 // Convenience methods for each state
57 ...Object.keys(states).reduce((methods, state) => ({
58 ...methods,
59 [`is${state.charAt(0).toUpperCase() + state.slice(1)}`]() {
60 return this.is(state);
61 }
62 }), {}),
63
64 // Debugging
65 getAvailableActions() {
66 return Object.keys(transitions[currentState] || {});
67 },
68
69 toString() {
70 return `StateMachine(state: ${currentState})`;
71 }
72 };
73
74 // Initialize
75 states[currentState]?.enter?.();
76
77 return stateMachine;
78}
79
80// Usage: Create a door state machine
81const doorStates = {
82 closed: {
83 enter: () => console.log('Door is now closed'),
84 exit: () => console.log('Door is opening...')
85 },
86 open: {
87 enter: () => console.log('Door is now open'),
88 exit: () => console.log('Door is closing...')
89 },
90 locked: {
91 enter: () => console.log('Door is locked'),
92 exit: () => console.log('Door is unlocking...')
93 }
94};
95
96const doorTransitions = {
97 closed: { open: 'open', lock: 'locked' },
98 open: { close: 'closed' },
99 locked: { unlock: 'closed' }
100};
101
102const door = createStateMachine('closed', doorStates, doorTransitions);
103
104console.log(door.isClosed()); // true
105console.log(door.can('open')); // true
106door.transition('open');
107console.log(door.isOpen()); // trueCreate a sophisticated API client using all enhanced object literal features for real-world applications.
1// Modern API Client with Enhanced Object Literals
2class APIClient {
3 constructor(baseURL, options = {}) {
4 // Private properties using symbols
5 const _baseURL = Symbol('baseURL');
6 const _options = Symbol('options');
7 const _cache = Symbol('cache');
8 const _interceptors = Symbol('interceptors');
9
10 // Store private data
11 this[_baseURL] = baseURL;
12 this[_options] = {
13 timeout: 10000,
14 retries: 3,
15 headers: { 'Content-Type': 'application/json' },
16 ...options
17 };
18 this[_cache] = new Map();
19 this[_interceptors] = { request: [], response: [] };
20
21 // Dynamic endpoint methods
22 const endpoints = ['users', 'posts', 'comments', 'todos'];
23
24 endpoints.forEach(endpoint => {
25 // Create CRUD methods dynamically
26 Object.assign(this, {
27 // GET all
28 [`get${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}`](params = {}) {
29 return this.request('GET', endpoint, { params });
30 },
31
32 // GET by ID
33 [`get${endpoint.charAt(0).toUpperCase() + endpoint.slice(1, -1)}`](id, params = {}) {
34 return this.request('GET', `${endpoint}/${id}`, { params });
35 },
36
37 // POST create
38 [`create${endpoint.charAt(0).toUpperCase() + endpoint.slice(1, -1)}`](data) {
39 return this.request('POST', endpoint, { data });
40 },
41
42 // PUT update
43 [`update${endpoint.charAt(0).toUpperCase() + endpoint.slice(1, -1)}`](id, data) {
44 return this.request('PUT', `${endpoint}/${id}`, { data });
45 },
46
47 // DELETE
48 [`delete${endpoint.charAt(0).toUpperCase() + endpoint.slice(1, -1)}`](id) {
49 return this.request('DELETE', `${endpoint}/${id}`);
50 }
51 });
52 });
53
54 // Utility methods
55 Object.assign(this, {
56 // Get private values (using symbols)
57 getBaseURL() { return this[_baseURL]; },
58 getOptions() { return { ...this[_options] }; },
59 getCacheSize() { return this[_cache].size; },
60
61 // Cache management
62 clearCache() {
63 this[_cache].clear();
64 return this;
65 },
66
67 // Interceptor management
68 addRequestInterceptor(interceptor) {
69 this[_interceptors].request.push(interceptor);
70 return () => {
71 const index = this[_interceptors].request.indexOf(interceptor);
72 if (index > -1) this[_interceptors].request.splice(index, 1);
73 };
74 },
75
76 addResponseInterceptor(interceptor) {
77 this[_interceptors].response.push(interceptor);
78 return () => {
79 const index = this[_interceptors].response.indexOf(interceptor);
80 if (index > -1) this[_interceptors].response.splice(index, 1);
81 };
82 }
83 });
84 }
85
86 // Core request method
87 async request(method, endpoint, config = {}) {
88 const {
89 data,
90 params = {},
91 headers = {},
92 cache = false,
93 retries = this[Symbol.for('options')]?.retries || 3
94 } = config;
95
96 // Build URL
97 const url = new URL(endpoint, this[Symbol.for('baseURL')]);
98 Object.entries(params).forEach(([key, value]) => {
99 url.searchParams.append(key, value);
100 });
101
102 // Cache key
103 const cacheKey = `${method}:${url.toString()}`;
104
105 // Check cache
106 if (method === 'GET' && cache && this[Symbol.for('cache')].has(cacheKey)) {
107 return this[Symbol.for('cache')].get(cacheKey);
108 }
109
110 // Request configuration
111 let requestConfig = {
112 method,
113 headers: {
114 ...this[Symbol.for('options')].headers,
115 ...headers
116 }
117 };
118
119 if (data) {
120 requestConfig.body = JSON.stringify(data);
121 }
122
123 // Apply request interceptors
124 for (const interceptor of this[Symbol.for('interceptors')].request) {
125 requestConfig = await interceptor(requestConfig);
126 }
127
128 // Execute request with retries
129 for (let attempt = 0; attempt <= retries; attempt++) {
130 try {
131 const response = await fetch(url.toString(), requestConfig);
132
133 if (!response.ok) {
134 throw new Error(`HTTP ${response.status}: ${response.statusText}`);
135 }
136
137 let result = await response.json();
138
139 // Apply response interceptors
140 for (const interceptor of this[Symbol.for('interceptors')].response) {
141 result = await interceptor(result, response);
142 }
143
144 // Cache successful GET requests
145 if (method === 'GET' && cache) {
146 this[Symbol.for('cache')].set(cacheKey, result);
147 }
148
149 return result;
150
151 } catch (error) {
152 if (attempt === retries) {
153 throw error;
154 }
155
156 // Wait before retry
157 await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
158 }
159 }
160 }
161}
162
163// Create enhanced configuration factory
164const createAPIConfig = (environment) => {
165 const environments = {
166 development: {
167 baseURL: 'http://localhost:3000/api',
168 timeout: 30000,
169 debug: true
170 },
171 staging: {
172 baseURL: 'https://staging-api.example.com',
173 timeout: 15000,
174 debug: false
175 },
176 production: {
177 baseURL: 'https://api.example.com',
178 timeout: 5000,
179 debug: false
180 }
181 };
182
183 const envConfig = environments[environment] || environments.development;
184
185 return {
186 // Spread environment config
187 ...envConfig,
188
189 // Computed properties
190 [Symbol.toStringTag]: 'APIConfig',
191 [`is${environment.charAt(0).toUpperCase() + environment.slice(1)}`]: true,
192
193 // Methods
194 getEndpoint(path) {
195 return `${this.baseURL}/${path}`;
196 },
197
198 withAuth(token) {
199 return {
200 ...this,
201 headers: {
202 ...this.headers,
203 'Authorization': `Bearer ${token}`
204 }
205 };
206 },
207
208 // Logging method
209 log(message) {
210 if (this.debug) {
211 console.log(`[API Config - ${environment}] ${message}`);
212 }
213 }
214 };
215};
216
217// Usage Example
218console.log('=== Enhanced Object Literals API Client Demo ===\n');
219
220// Create API client with development config
221const devConfig = createAPIConfig('development');
222devConfig.log('Configuration created');
223
224const api = new APIClient(devConfig.baseURL, {
225 timeout: devConfig.timeout,
226 headers: { 'X-Environment': 'development' }
227});
228
229// Add interceptors using enhanced object patterns
230const requestLogger = api.addRequestInterceptor(async (config) => {
231 console.log(`Making ${config.method} request to ${config.url}`);
232 return {
233 ...config,
234 headers: {
235 ...config.headers,
236 'X-Request-Time': new Date().toISOString()
237 }
238 };
239});
240
241const responseLogger = api.addResponseInterceptor(async (data, response) => {
242 console.log(`Received response with ${Object.keys(data).length} keys`);
243 return data;
244});
245
246// Test the dynamic methods (simulate API calls)
247async function demonstrateAPI() {
248 try {
249 console.log('\n1. API Client Information:');
250 console.log(`Base URL: ${api.getBaseURL()}`);
251 console.log(`Options: ${JSON.stringify(api.getOptions(), null, 2)}`);
252 console.log(`Cache size: ${api.getCacheSize()}`);
253
254 console.log('\n2. Dynamic Methods Available:');
255 console.log('- getUsers(), getUser(id), createUser(data), updateUser(id, data), deleteUser(id)');
256 console.log('- getPosts(), getPost(id), createPost(data), updatePost(id, data), deletePost(id)');
257 console.log('- getComments(), getComment(id), createComment(data), updateComment(id, data), deleteComment(id)');
258 console.log('- getTodos(), getTodo(id), createTodo(data), updateTodo(id, data), deleteTodo(id)');
259
260 console.log('\n3. Configuration Patterns:');
261 const prodConfig = createAPIConfig('production');
262 console.log(`Production config: ${JSON.stringify(prodConfig, null, 2)}`);
263 console.log(`Is production: ${prodConfig.isProduction}`);
264
265 const authConfig = prodConfig.withAuth('jwt-token-123');
266 console.log(`Auth config headers: ${JSON.stringify(authConfig.headers, null, 2)}`);
267
268 console.log('\n4. Enhanced Object Features Demonstrated:');
269 console.log('✓ Shorthand properties and methods');
270 console.log('✓ Computed property names for dynamic API methods');
271 console.log('✓ Symbol properties for private data');
272 console.log('✓ Method definitions with async/await support');
273 console.log('✓ Spread operator for configuration merging');
274 console.log('✓ Template literals in computed properties');
275 console.log('✓ Object.assign for dynamic method assignment');
276
277 console.log('\n5. Real-world Patterns:');
278
279 // Factory pattern with enhanced literals
280 const createUser = (name, email, preferences = {}) => ({
281 // Shorthand properties
282 name,
283 email,
284
285 // Computed property with timestamp
286 [Symbol.for('created')]: new Date(),
287
288 // Merge preferences with defaults
289 preferences: {
290 notifications: true,
291 theme: 'light',
292 ...preferences
293 },
294
295 // Method definitions
296 getDisplayName() {
297 return this.name;
298 },
299
300 updatePreferences(updates) {
301 this.preferences = { ...this.preferences, ...updates };
302 return this;
303 },
304
305 // Computed getter
306 get isConfigured() {
307 return Object.keys(this.preferences).length > 0;
308 }
309 });
310
311 const user = createUser('Alice Johnson', '[email protected]', { theme: 'dark' });
312 console.log(`Created user: ${user.getDisplayName()}`);
313 console.log(`User preferences: ${JSON.stringify(user.preferences)}`);
314 console.log(`Is configured: ${user.isConfigured}`);
315
316 user.updatePreferences({ language: 'en', timezone: 'UTC' });
317 console.log(`Updated preferences: ${JSON.stringify(user.preferences)}`);
318
319 } catch (error) {
320 console.error('Demo error:', error.message);
321 }
322}
323
324// Run the demonstration
325demonstrateAPI();
326
327// Cleanup function with enhanced object
328const cleanup = {
329 // Method shorthand
330 removeInterceptors() {
331 requestLogger?.();
332 responseLogger?.();
333 },
334
335 clearCache() {
336 api.clearCache();
337 },
338
339 // Computed method name
340 [`cleanup${Date.now()}`]() {
341 this.removeInterceptors();
342 this.clearCache();
343 console.log('\n🧹 Cleanup completed');
344 }
345};
346
347// Execute cleanup after delay
348setTimeout(() => {
349 const cleanupMethod = Object.keys(cleanup).find(key => key.startsWith('cleanup'));
350 cleanup[cleanupMethod]?.();
351}, 100);
352
353console.log('\n=== Demo Complete ===');Your progress is saved in this browser, on this device only.