javascript

examples

examples.js
/**
 * =====================================================
 * 7.2 OBJECT METHODS - EXAMPLES
 * =====================================================
 * Comprehensive examples demonstrating object methods in JavaScript
 */

// =====================================================
// EXAMPLE 1: Defining Methods - Traditional vs Shorthand
// =====================================================
console.log('=== Example 1: Defining Methods ===');

// Traditional function expression (ES5)
const calculatorTraditional = {
  value: 0,

  add: function (num) {
    this.value += num;
    return this.value;
  },

  subtract: function (num) {
    this.value -= num;
    return this.value;
  },
};

// ES6 Method shorthand (preferred)
const calculatorModern = {
  value: 0,

  add(num) {
    this.value += num;
    return this.value;
  },

  subtract(num) {
    this.value -= num;
    return this.value;
  },
};

console.log('Traditional:', calculatorTraditional.add(10)); // 10
console.log('Modern:', calculatorModern.add(10)); // 10

// =====================================================
// EXAMPLE 2: The `this` Keyword in Methods
// =====================================================
console.log('\n=== Example 2: The `this` Keyword ===');

const user = {
  firstName: 'John',
  lastName: 'Doe',
  age: 30,

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  greet() {
    console.log(
      `Hello, I'm ${this.getFullName()} and I'm ${this.age} years old.`
    );
  },

  haveBirthday() {
    this.age++;
    console.log(`Happy birthday! Now ${this.age} years old.`);
  },
};

console.log(user.getFullName()); // "John Doe"
user.greet(); // "Hello, I'm John Doe and I'm 30 years old."
user.haveBirthday(); // "Happy birthday! Now 31 years old."

// =====================================================
// EXAMPLE 3: `this` Context is Dynamic
// =====================================================
console.log('\n=== Example 3: Dynamic `this` Context ===');

const person1 = {
  name: 'Alice',
  introduce() {
    console.log(`Hi, I'm ${this.name}`);
  },
};

const person2 = {
  name: 'Bob',
};

// Method borrowing - `this` depends on what's before the dot
person1.introduce(); // "Hi, I'm Alice"

// Assign the method to person2
person2.introduce = person1.introduce;
person2.introduce(); // "Hi, I'm Bob"

// Extract the method - loses context
const intro = person1.introduce;
// intro();  // Would be: "Hi, I'm undefined" (in strict mode)

// =====================================================
// EXAMPLE 4: Preserving `this` with bind()
// =====================================================
console.log('\n=== Example 4: Preserving `this` with bind() ===');

const counter = {
  count: 0,

  increment() {
    this.count++;
    console.log(`Count: ${this.count}`);
  },
};

// Without bind - loses context
const incrementFn = counter.increment;
// incrementFn();  // Would fail or give wrong result

// With bind - preserves context
const boundIncrement = counter.increment.bind(counter);
boundIncrement(); // "Count: 1"
boundIncrement(); // "Count: 2"

// =====================================================
// EXAMPLE 5: call() and apply()
// =====================================================
console.log('\n=== Example 5: call() and apply() ===');

function introduce(greeting, punctuation) {
  console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}

const person = { name: 'Charlie' };

// call() - pass arguments individually
introduce.call(person, 'Hello', '!'); // "Hello, I'm Charlie!"

// apply() - pass arguments as array
introduce.apply(person, ['Hi', '.']); // "Hi, I'm Charlie."

// Another example with object method
const mathHelper = {
  multiply(a, b) {
    return a * b * this.factor;
  },
};

const context1 = { factor: 2 };
const context2 = { factor: 10 };

console.log(mathHelper.multiply.call(context1, 3, 4)); // 24 (3 * 4 * 2)
console.log(mathHelper.multiply.call(context2, 3, 4)); // 120 (3 * 4 * 10)

// =====================================================
// EXAMPLE 6: Arrow Functions vs Regular Methods
// =====================================================
console.log('\n=== Example 6: Arrow Functions vs Regular Methods ===');

// DON'T DO THIS - Arrow function as method
const badExample = {
  name: 'Bad Object',
  greet: () => {
    // `this` is NOT badExample, it's the outer scope's this!
    console.log(`Hello from ${this?.name || 'undefined'}`);
  },
};

badExample.greet(); // "Hello from undefined"

// DO THIS - Regular method
const goodExample = {
  name: 'Good Object',
  greet() {
    console.log(`Hello from ${this.name}`);
  },
};

goodExample.greet(); // "Hello from Good Object"

// =====================================================
// EXAMPLE 7: Arrow Functions INSIDE Methods (Good!)
// =====================================================
console.log('\n=== Example 7: Arrow Functions Inside Methods ===');

const timer = {
  seconds: 0,

  start() {
    console.log('Timer started');

    // Arrow function preserves outer `this`
    const interval = setInterval(() => {
      this.seconds++;
      console.log(`Seconds: ${this.seconds}`);

      if (this.seconds >= 3) {
        clearInterval(interval);
        console.log('Timer stopped');
      }
    }, 100); // Fast for demo
  },
};

// timer.start();  // Uncomment to run

// =====================================================
// EXAMPLE 8: Method Chaining
// =====================================================
console.log('\n=== Example 8: Method Chaining ===');

const stringBuilder = {
  value: '',

  append(str) {
    this.value += str;
    return this; // Return this for chaining
  },

  prepend(str) {
    this.value = str + this.value;
    return this;
  },

  uppercase() {
    this.value = this.value.toUpperCase();
    return this;
  },

  lowercase() {
    this.value = this.value.toLowerCase();
    return this;
  },

  trim() {
    this.value = this.value.trim();
    return this;
  },

  build() {
    return this.value;
  },
};

const result = stringBuilder
  .append('  Hello ')
  .append('World  ')
  .trim()
  .uppercase()
  .build();

console.log(result); // "HELLO WORLD"

// =====================================================
// EXAMPLE 9: Fluent API Pattern
// =====================================================
console.log('\n=== Example 9: Fluent API Pattern ===');

const queryBuilder = {
  _table: '',
  _fields: '*',
  _conditions: [],
  _orderBy: '',
  _limit: null,

  reset() {
    this._table = '';
    this._fields = '*';
    this._conditions = [];
    this._orderBy = '';
    this._limit = null;
    return this;
  },

  select(fields) {
    this._fields = Array.isArray(fields) ? fields.join(', ') : fields;
    return this;
  },

  from(table) {
    this._table = table;
    return this;
  },

  where(condition) {
    this._conditions.push(condition);
    return this;
  },

  orderBy(field, direction = 'ASC') {
    this._orderBy = `${field} ${direction}`;
    return this;
  },

  limit(n) {
    this._limit = n;
    return this;
  },

  build() {
    let query = `SELECT ${this._fields} FROM ${this._table}`;

    if (this._conditions.length > 0) {
      query += ` WHERE ${this._conditions.join(' AND ')}`;
    }

    if (this._orderBy) {
      query += ` ORDER BY ${this._orderBy}`;
    }

    if (this._limit !== null) {
      query += ` LIMIT ${this._limit}`;
    }

    return query;
  },
};

const sql = queryBuilder
  .reset()
  .select(['id', 'name', 'email'])
  .from('users')
  .where('age > 18')
  .where("status = 'active'")
  .orderBy('name', 'ASC')
  .limit(10)
  .build();

console.log(sql);
// SELECT id, name, email FROM users WHERE age > 18 AND status = 'active' ORDER BY name ASC LIMIT 10

// =====================================================
// EXAMPLE 10: Computed Method Names
// =====================================================
console.log('\n=== Example 10: Computed Method Names ===');

const prefix = 'get';
const suffix = 'Value';

const dynamicMethods = {
  _name: 'Dynamic Object',
  _value: 42,

  // Computed method names using template literals
  [`${prefix}Name`]() {
    return this._name;
  },

  [`${prefix}${suffix}`]() {
    return this._value;
  },

  [`set${suffix}`](val) {
    this._value = val;
  },
};

console.log(dynamicMethods.getName()); // "Dynamic Object"
console.log(dynamicMethods.getValue()); // 42
dynamicMethods.setValue(100);
console.log(dynamicMethods.getValue()); // 100

// =====================================================
// EXAMPLE 11: Methods from Symbol Keys
// =====================================================
console.log('\n=== Example 11: Symbol Method Keys ===');

const LOG = Symbol('log');
const VALIDATE = Symbol('validate');

const obj = {
  data: 'Hello',

  [LOG]() {
    console.log(`[LOG] Data: ${this.data}`);
  },

  [VALIDATE]() {
    return this.data.length > 0;
  },

  process() {
    if (this[VALIDATE]()) {
      this[LOG]();
      return 'Processed successfully';
    }
    return 'Validation failed';
  },
};

console.log(obj.process()); // [LOG] Data: Hello \n "Processed successfully"

// Symbol methods are not enumerable
console.log(Object.keys(obj)); // ["data", "process"]

// =====================================================
// EXAMPLE 12: Methods with Default Parameters
// =====================================================
console.log('\n=== Example 12: Methods with Default Parameters ===');

const formatter = {
  format(value, options = {}) {
    const {
      prefix = '',
      suffix = '',
      uppercase = false,
      truncate = null,
    } = options;

    let result = String(value);

    if (truncate && result.length > truncate) {
      result = result.substring(0, truncate) + '...';
    }

    if (uppercase) {
      result = result.toUpperCase();
    }

    return `${prefix}${result}${suffix}`;
  },
};

console.log(formatter.format('hello'));
// "hello"

console.log(formatter.format('hello', { uppercase: true }));
// "HELLO"

console.log(
  formatter.format('hello world', { prefix: '>>> ', suffix: ' <<<' })
);
// ">>> hello world <<<"

console.log(formatter.format('This is a very long message', { truncate: 10 }));
// "This is a ..."

// =====================================================
// EXAMPLE 13: Rest Parameters in Methods
// =====================================================
console.log('\n=== Example 13: Rest Parameters in Methods ===');

const mathOperations = {
  sum(...numbers) {
    return numbers.reduce((total, n) => total + n, 0);
  },

  average(...numbers) {
    if (numbers.length === 0) return 0;
    return this.sum(...numbers) / numbers.length;
  },

  max(...numbers) {
    return Math.max(...numbers);
  },

  min(...numbers) {
    return Math.min(...numbers);
  },

  stats(...numbers) {
    return {
      sum: this.sum(...numbers),
      average: this.average(...numbers),
      max: this.max(...numbers),
      min: this.min(...numbers),
      count: numbers.length,
    };
  },
};

console.log(mathOperations.sum(1, 2, 3, 4, 5)); // 15
console.log(mathOperations.average(1, 2, 3, 4, 5)); // 3
console.log(mathOperations.stats(1, 2, 3, 4, 5));
// { sum: 15, average: 3, max: 5, min: 1, count: 5 }

// =====================================================
// EXAMPLE 14: Private Methods Pattern (using closures)
// =====================================================
console.log('\n=== Example 14: Private Methods Pattern ===');

function createBankAccount(initialBalance) {
  // Private variable
  let balance = initialBalance;

  // Private function
  function validateAmount(amount) {
    return typeof amount === 'number' && amount > 0;
  }

  function logTransaction(type, amount) {
    console.log(`[${type}] Amount: $${amount}, Balance: $${balance}`);
  }

  // Return object with public methods
  return {
    deposit(amount) {
      if (!validateAmount(amount)) {
        throw new Error('Invalid amount');
      }
      balance += amount;
      logTransaction('DEPOSIT', amount);
      return this;
    },

    withdraw(amount) {
      if (!validateAmount(amount)) {
        throw new Error('Invalid amount');
      }
      if (amount > balance) {
        throw new Error('Insufficient funds');
      }
      balance -= amount;
      logTransaction('WITHDRAW', amount);
      return this;
    },

    getBalance() {
      return balance;
    },
  };
}

const account = createBankAccount(100);
account
  .deposit(50) // [DEPOSIT] Amount: $50, Balance: $150
  .withdraw(30); // [WITHDRAW] Amount: $30, Balance: $120
console.log(`Final balance: $${account.getBalance()}`); // $120

// =====================================================
// EXAMPLE 15: Methods That Return New Objects
// =====================================================
console.log('\n=== Example 15: Methods Returning New Objects ===');

const Vector = {
  create(x, y) {
    return {
      x,
      y,

      add(other) {
        return Vector.create(this.x + other.x, this.y + other.y);
      },

      subtract(other) {
        return Vector.create(this.x - other.x, this.y - other.y);
      },

      scale(factor) {
        return Vector.create(this.x * factor, this.y * factor);
      },

      magnitude() {
        return Math.sqrt(this.x ** 2 + this.y ** 2);
      },

      normalize() {
        const mag = this.magnitude();
        return mag === 0 ? Vector.create(0, 0) : this.scale(1 / mag);
      },

      toString() {
        return `Vector(${this.x}, ${this.y})`;
      },
    };
  },
};

const v1 = Vector.create(3, 4);
const v2 = Vector.create(1, 2);

console.log(v1.toString()); // Vector(3, 4)
console.log(v1.magnitude()); // 5
console.log(v1.add(v2).toString()); // Vector(4, 6)
console.log(v1.scale(2).toString()); // Vector(6, 8)
console.log(v1.normalize().toString()); // Vector(0.6, 0.8)

// =====================================================
// EXAMPLE 16: Async Methods
// =====================================================
console.log('\n=== Example 16: Async Methods ===');

const apiClient = {
  baseUrl: 'https://api.example.com',

  async get(endpoint) {
    console.log(`Fetching ${this.baseUrl}${endpoint}`);
    // Simulated API response
    return { data: { id: 1, name: 'Example' } };
  },

  async post(endpoint, data) {
    console.log(`Posting to ${this.baseUrl}${endpoint}`, data);
    return { success: true, id: Date.now() };
  },

  async fetchUser(id) {
    const response = await this.get(`/users/${id}`);
    return response.data;
  },

  async createUser(userData) {
    const response = await this.post('/users', userData);
    return response;
  },
};

// Using async methods
(async () => {
  const user = await apiClient.fetchUser(1);
  console.log('Fetched user:', user);

  const created = await apiClient.createUser({ name: 'New User' });
  console.log('Created:', created);
})();

// =====================================================
// EXAMPLE 17: Generator Methods
// =====================================================
console.log('\n=== Example 17: Generator Methods ===');

const range = {
  start: 0,
  end: 10,
  step: 1,

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i += this.step) {
      yield i;
    }
  },

  *filter(predicate) {
    for (const value of this) {
      if (predicate(value)) {
        yield value;
      }
    }
  },

  *map(transform) {
    for (const value of this) {
      yield transform(value);
    }
  },

  toArray() {
    return [...this];
  },
};

console.log(range.toArray()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Using generator methods
const evenNumbers = [...range.filter((n) => n % 2 === 0)];
console.log('Even:', evenNumbers); // [0, 2, 4, 6, 8, 10]

const squares = [...range.map((n) => n * n)];
console.log('Squares:', squares); // [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

// =====================================================
// EXAMPLE 18: Method Decorators Pattern
// =====================================================
console.log('\n=== Example 18: Method Decorators Pattern ===');

// Simple logging decorator
function withLogging(obj, methodName) {
  const original = obj[methodName];

  obj[methodName] = function (...args) {
    console.log(`[CALL] ${methodName}(${args.join(', ')})`);
    const result = original.apply(this, args);
    console.log(`[RETURN] ${methodName} => ${JSON.stringify(result)}`);
    return result;
  };

  return obj;
}

// Memoization decorator
function withMemoization(obj, methodName) {
  const original = obj[methodName];
  const cache = new Map();

  obj[methodName] = function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      console.log(`[CACHE HIT] ${methodName}(${args.join(', ')})`);
      return cache.get(key);
    }
    const result = original.apply(this, args);
    cache.set(key, result);
    return result;
  };

  return obj;
}

const mathService = {
  factorial(n) {
    if (n <= 1) return 1;
    return n * this.factorial(n - 1);
  },
};

// Apply decorators
withLogging(mathService, 'factorial');
withMemoization(mathService, 'factorial');

console.log(mathService.factorial(5)); // 120 (with logging)
console.log(mathService.factorial(5)); // 120 (from cache)

// =====================================================
// EXAMPLE 19: State Machine with Methods
// =====================================================
console.log('\n=== Example 19: State Machine with Methods ===');

const trafficLight = {
  states: ['red', 'yellow', 'green'],
  currentIndex: 0,

  get currentState() {
    return this.states[this.currentIndex];
  },

  next() {
    this.currentIndex = (this.currentIndex + 1) % this.states.length;
    console.log(`Light changed to: ${this.currentState}`);
    return this;
  },

  reset() {
    this.currentIndex = 0;
    console.log(`Light reset to: ${this.currentState}`);
    return this;
  },

  canGo() {
    return this.currentState === 'green';
  },

  shouldStop() {
    return this.currentState === 'red';
  },
};

console.log(`Current: ${trafficLight.currentState}`); // red
trafficLight.next(); // yellow
trafficLight.next(); // green
console.log(`Can go: ${trafficLight.canGo()}`); // true
trafficLight.next(); // red
console.log(`Should stop: ${trafficLight.shouldStop()}`); // true

// =====================================================
// EXAMPLE 20: Object with Validation Methods
// =====================================================
console.log('\n=== Example 20: Validation Methods ===');

const userValidator = {
  errors: [],

  reset() {
    this.errors = [];
    return this;
  },

  validateName(name) {
    if (!name || name.trim().length < 2) {
      this.errors.push('Name must be at least 2 characters');
    }
    return this;
  },

  validateEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!email || !emailRegex.test(email)) {
      this.errors.push('Invalid email format');
    }
    return this;
  },

  validateAge(age) {
    if (typeof age !== 'number' || age < 0 || age > 150) {
      this.errors.push('Age must be a number between 0 and 150');
    }
    return this;
  },

  validatePassword(password) {
    if (!password || password.length < 8) {
      this.errors.push('Password must be at least 8 characters');
    }
    if (!/[A-Z]/.test(password)) {
      this.errors.push('Password must contain uppercase letter');
    }
    if (!/[0-9]/.test(password)) {
      this.errors.push('Password must contain a number');
    }
    return this;
  },

  isValid() {
    return this.errors.length === 0;
  },

  getErrors() {
    return [...this.errors];
  },

  validate(user) {
    return this.reset()
      .validateName(user.name)
      .validateEmail(user.email)
      .validateAge(user.age)
      .validatePassword(user.password);
  },
};

// Test validation
const validUser = {
  name: 'John Doe',
  email: 'john@example.com',
  age: 30,
  password: 'SecurePass123',
};

userValidator.validate(validUser);
console.log('Valid user valid?', userValidator.isValid()); // true

const invalidUser = {
  name: 'J',
  email: 'invalid',
  age: 200,
  password: 'weak',
};

userValidator.validate(invalidUser);
console.log('Invalid user valid?', userValidator.isValid()); // false
console.log('Errors:', userValidator.getErrors());

console.log('\n=== All Examples Complete ===');
Examples - JavaScript Tutorial | DeepML