javascript

examples

examples.js
/**
 * =====================================================
 * 7.4 OBJECT STATIC METHODS - EXAMPLES
 * =====================================================
 * Comprehensive examples of Object static methods
 */

// =====================================================
// EXAMPLE 1: Object.keys(), Object.values(), Object.entries()
// =====================================================
console.log('=== Example 1: keys(), values(), entries() ===');

const user = {
  name: 'Alice',
  age: 30,
  email: 'alice@example.com',
  role: 'admin',
};

// Object.keys() - get all enumerable property names
const keys = Object.keys(user);
console.log('Keys:', keys); // ["name", "age", "email", "role"]

// Object.values() - get all enumerable property values
const values = Object.values(user);
console.log('Values:', values); // ["Alice", 30, "alice@example.com", "admin"]

// Object.entries() - get [key, value] pairs
const entries = Object.entries(user);
console.log('Entries:', entries);
// [["name", "Alice"], ["age", 30], ["email", "alice@example.com"], ["role", "admin"]]

// Iterate with destructuring
console.log('\nIterating with entries:');
for (const [key, value] of Object.entries(user)) {
  console.log(`  ${key}: ${value}`);
}

// =====================================================
// EXAMPLE 2: Object.assign()
// =====================================================
console.log('\n=== Example 2: Object.assign() ===');

// Merge objects
const defaults = { theme: 'dark', language: 'en', notifications: true };
const userPrefs = { theme: 'light', fontSize: 16 };

const settings = Object.assign({}, defaults, userPrefs);
console.log('Merged settings:', settings);
// { theme: "light", language: "en", notifications: true, fontSize: 16 }

// Clone an object (shallow)
const original = { a: 1, b: { nested: true } };
const clone = Object.assign({}, original);
console.log('Clone:', clone);

// Note: Shallow copy - nested objects are still referenced
clone.b.nested = false;
console.log('Original affected:', original.b.nested); // false

// Multiple sources
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const obj3 = { c: 3, a: 99 }; // Later overrides earlier

const combined = Object.assign({}, obj1, obj2, obj3);
console.log('Combined:', combined); // { a: 99, b: 2, c: 3 }

// =====================================================
// EXAMPLE 3: Object.fromEntries()
// =====================================================
console.log('\n=== Example 3: Object.fromEntries() ===');

// Create object from entries array
const entriesArray = [
  ['name', 'Bob'],
  ['age', 25],
  ['city', 'NYC'],
];

const person = Object.fromEntries(entriesArray);
console.log('From entries:', person); // { name: "Bob", age: 25, city: "NYC" }

// Create object from Map
const map = new Map([
  ['x', 10],
  ['y', 20],
  ['z', 30],
]);

const coords = Object.fromEntries(map);
console.log('From Map:', coords); // { x: 10, y: 20, z: 30 }

// Transform object using entries + fromEntries
const prices = { apple: 1.5, banana: 0.75, cherry: 2.0 };

// Double all prices
const doubledPrices = Object.fromEntries(
  Object.entries(prices).map(([key, value]) => [key, value * 2])
);
console.log('Doubled prices:', doubledPrices);

// Filter properties
const expensiveItems = Object.fromEntries(
  Object.entries(prices).filter(([, value]) => value >= 1)
);
console.log('Expensive items:', expensiveItems); // { apple: 1.5, cherry: 2.0 }

// =====================================================
// EXAMPLE 4: Object.create()
// =====================================================
console.log('\n=== Example 4: Object.create() ===');

// Create object with specific prototype
const animalProto = {
  speak() {
    console.log(`${this.name} says ${this.sound}`);
  },
};

const dog = Object.create(animalProto);
dog.name = 'Rex';
dog.sound = 'Woof!';
dog.speak(); // "Rex says Woof!"

// Create object with null prototype (no inherited properties)
const pureDict = Object.create(null);
pureDict.key = 'value';
console.log('Pure dict toString:', pureDict.toString); // undefined

// Create with property descriptors
const immutableConfig = Object.create(null, {
  apiUrl: {
    value: 'https://api.example.com',
    writable: false,
    enumerable: true,
    configurable: false,
  },
  timeout: {
    value: 5000,
    writable: false,
    enumerable: true,
    configurable: false,
  },
});

console.log('Immutable config:', immutableConfig);
immutableConfig.apiUrl = 'changed';
console.log('After attempted change:', immutableConfig.apiUrl); // unchanged

// =====================================================
// EXAMPLE 5: Object.is()
// =====================================================
console.log('\n=== Example 5: Object.is() ===');

// Regular comparisons work like ===
console.log('5 === 5:', Object.is(5, 5)); // true
console.log("'a' === 'a':", Object.is('a', 'a')); // true
console.log('{} === {}:', Object.is({}, {})); // false (different refs)

// But handles edge cases differently
console.log('\nEdge cases:');
console.log('NaN === NaN with ===:', NaN === NaN); // false
console.log('NaN === NaN with Object.is:', Object.is(NaN, NaN)); // true!

console.log('+0 === -0 with ===:', +0 === -0); // true
console.log('+0 === -0 with Object.is:', Object.is(+0, -0)); // false!

// Practical use: Check for NaN
const value = 0 / 0; // NaN
if (Object.is(value, NaN)) {
  console.log('Value is NaN');
}

// =====================================================
// EXAMPLE 6: Object.hasOwn()
// =====================================================
console.log('\n=== Example 6: Object.hasOwn() ===');

const obj = {
  name: 'Test',
  value: 42,
};

// Check own properties
console.log("Has 'name':", Object.hasOwn(obj, 'name')); // true
console.log("Has 'value':", Object.hasOwn(obj, 'value')); // true
console.log("Has 'toString':", Object.hasOwn(obj, 'toString')); // false (inherited)

// Safe even with overridden hasOwnProperty
const dangerousObj = {
  data: 'important',
  hasOwnProperty: function () {
    return 'gotcha!';
  },
};

console.log('\nSafe check with hasOwn:');
console.log(Object.hasOwn(dangerousObj, 'data')); // true

// Works with null prototype objects
const nullProtoObj = Object.create(null);
nullProtoObj.key = 'value';
console.log("Null proto has 'key':", Object.hasOwn(nullProtoObj, 'key')); // true

// =====================================================
// EXAMPLE 7: Object.getOwnPropertyNames() vs Object.keys()
// =====================================================
console.log('\n=== Example 7: getOwnPropertyNames() vs keys() ===');

const example = { visible: true };

Object.defineProperty(example, 'hidden', {
  value: 'secret',
  enumerable: false,
});

console.log('Object.keys():', Object.keys(example));
// ["visible"]

console.log('getOwnPropertyNames():', Object.getOwnPropertyNames(example));
// ["visible", "hidden"]

// Built-in array properties example
const arr = [1, 2, 3];
console.log('\nArray keys:', Object.keys(arr));
// ["0", "1", "2"]
console.log('Array getOwnPropertyNames:', Object.getOwnPropertyNames(arr));
// ["0", "1", "2", "length"]

// =====================================================
// EXAMPLE 8: Object.getOwnPropertySymbols()
// =====================================================
console.log('\n=== Example 8: getOwnPropertySymbols() ===');

const sym1 = Symbol('id');
const sym2 = Symbol('secret');

const symbolObj = {
  name: 'Regular property',
  [sym1]: 12345,
  [sym2]: 'hidden data',
};

console.log('Regular keys:', Object.keys(symbolObj));
// ["name"]

console.log('Symbol keys:', Object.getOwnPropertySymbols(symbolObj));
// [Symbol(id), Symbol(secret)]

// Access symbol values
console.log('Symbol value:', symbolObj[sym1]); // 12345

// =====================================================
// EXAMPLE 9: Reflect.ownKeys() - All Keys
// =====================================================
console.log('\n=== Example 9: Reflect.ownKeys() ===');

const sym = Symbol('mySymbol');

const mixedObj = {
  regular: 'value',
};

Object.defineProperty(mixedObj, 'hidden', {
  value: 'non-enumerable',
  enumerable: false,
});

mixedObj[sym] = 'symbol value';

console.log('Reflect.ownKeys():', Reflect.ownKeys(mixedObj));
// ["regular", "hidden", Symbol(mySymbol)]

// =====================================================
// EXAMPLE 10: Object.freeze()
// =====================================================
console.log('\n=== Example 10: Object.freeze() ===');

const frozenConfig = Object.freeze({
  appName: 'MyApp',
  version: '1.0.0',
  features: ['feature1', 'feature2'],
});

console.log('Is frozen:', Object.isFrozen(frozenConfig)); // true

// Cannot modify
frozenConfig.appName = 'ChangedApp';
console.log('appName after attempt:', frozenConfig.appName); // "MyApp"

// Cannot add
frozenConfig.newProp = 'new';
console.log('Has newProp:', 'newProp' in frozenConfig); // false

// Cannot delete
delete frozenConfig.version;
console.log('version still exists:', frozenConfig.version); // "1.0.0"

// WARNING: Nested arrays/objects are NOT frozen
frozenConfig.features.push('feature3');
console.log('Features modified:', frozenConfig.features); // Has 3 items!

// =====================================================
// EXAMPLE 11: Object.seal()
// =====================================================
console.log('\n=== Example 11: Object.seal() ===');

const sealedObj = Object.seal({
  name: 'Sealed',
  count: 0,
});

console.log('Is sealed:', Object.isSealed(sealedObj)); // true

// CAN modify values
sealedObj.name = 'Modified';
sealedObj.count = 100;
console.log('After modification:', sealedObj); // { name: "Modified", count: 100 }

// CANNOT add properties
sealedObj.newProp = 'test';
console.log('newProp exists:', 'newProp' in sealedObj); // false

// CANNOT delete properties
delete sealedObj.name;
console.log('name still exists:', 'name' in sealedObj); // true

// =====================================================
// EXAMPLE 12: Object.preventExtensions()
// =====================================================
console.log('\n=== Example 12: Object.preventExtensions() ===');

const limitedObj = Object.preventExtensions({
  existing: 'I exist',
});

console.log('Is extensible:', Object.isExtensible(limitedObj)); // false

// CAN modify existing
limitedObj.existing = 'Modified';
console.log('After modification:', limitedObj.existing);

// CAN delete
delete limitedObj.existing;
console.log('After delete:', limitedObj); // {}

// CANNOT add new
limitedObj.newProp = 'test';
console.log('Has newProp:', 'newProp' in limitedObj); // false

// =====================================================
// EXAMPLE 13: Object.getPrototypeOf() / setPrototypeOf()
// =====================================================
console.log('\n=== Example 13: Prototype Methods ===');

const animal = {
  type: 'animal',
  speak() {
    console.log('Some sound');
  },
};

const cat = {
  name: 'Whiskers',
  meow() {
    console.log('Meow!');
  },
};

// Get prototype
console.log('cat prototype:', Object.getPrototypeOf(cat) === Object.prototype); // true

// Set prototype (use sparingly - performance impact)
Object.setPrototypeOf(cat, animal);
console.log('cat prototype now:', Object.getPrototypeOf(cat) === animal); // true

cat.speak(); // "Some sound"
console.log('cat type:', cat.type); // "animal"

// Better: Use Object.create() from the start
const betterCat = Object.create(animal);
betterCat.name = 'Felix';
betterCat.meow = function () {
  console.log('Meow!');
};

// =====================================================
// EXAMPLE 14: Object.getOwnPropertyDescriptor(s)
// =====================================================
console.log('\n=== Example 14: getOwnPropertyDescriptor(s) ===');

const product = {
  name: 'Widget',
  get price() {
    return this._price;
  },
  set price(value) {
    this._price = value;
  },
};
product._price = 99.99;

// Single property descriptor
const nameDesc = Object.getOwnPropertyDescriptor(product, 'name');
console.log('name descriptor:', nameDesc);
/*
{
    value: "Widget",
    writable: true,
    enumerable: true,
    configurable: true
}
*/

// Accessor property descriptor
const priceDesc = Object.getOwnPropertyDescriptor(product, 'price');
console.log('price descriptor:', priceDesc);
/*
{
    get: [Function: get price],
    set: [Function: set price],
    enumerable: true,
    configurable: true
}
*/

// All descriptors
const allDesc = Object.getOwnPropertyDescriptors(product);
console.log('All descriptors keys:', Object.keys(allDesc));

// =====================================================
// EXAMPLE 15: Deep Clone with Descriptors
// =====================================================
console.log('\n=== Example 15: Deep Clone with Descriptors ===');

function deepCloneWithDescriptors(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (hash.has(obj)) return hash.get(obj);

  const clone = Object.create(
    Object.getPrototypeOf(obj),
    Object.getOwnPropertyDescriptors(obj)
  );

  hash.set(obj, clone);

  // Deep clone nested objects
  for (const key of Reflect.ownKeys(obj)) {
    const value = obj[key];
    if (value && typeof value === 'object') {
      clone[key] = deepCloneWithDescriptors(value, hash);
    }
  }

  return clone;
}

const original2 = {
  name: 'Original',
  nested: {
    value: 42,
  },
};

Object.defineProperty(original2, 'hidden', {
  value: 'secret',
  enumerable: false,
});

const deepClone = deepCloneWithDescriptors(original2);
deepClone.nested.value = 999;

console.log('Original nested value:', original2.nested.value); // 42
console.log('Clone nested value:', deepClone.nested.value); // 999
console.log('Clone has hidden:', Object.hasOwn(deepClone, 'hidden')); // true

// =====================================================
// EXAMPLE 16: Object Transformation Patterns
// =====================================================
console.log('\n=== Example 16: Object Transformation Patterns ===');

const data = {
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
  _private: 'secret',
  email: 'john@example.com',
};

// 1. Pick specific properties
const pick = (obj, ...keys) =>
  Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key)));
console.log('Pick:', pick(data, 'firstName', 'lastName'));

// 2. Omit specific properties
const omit = (obj, ...keys) =>
  Object.fromEntries(
    Object.entries(obj).filter(([key]) => !keys.includes(key))
  );
console.log('Omit:', omit(data, '_private', 'age'));

// 3. Map values
const mapValues = (obj, fn) =>
  Object.fromEntries(
    Object.entries(obj).map(([key, value]) => [key, fn(value, key)])
  );
console.log(
  'Map values (uppercase strings):',
  mapValues(data, (v) => (typeof v === 'string' ? v.toUpperCase() : v))
);

// 4. Filter by value
const filterByValue = (obj, predicate) =>
  Object.fromEntries(
    Object.entries(obj).filter(([key, value]) => predicate(value, key))
  );
console.log(
  'Filter strings:',
  filterByValue(data, (v) => typeof v === 'string')
);

// 5. Rename keys
const renameKeys = (obj, keyMap) =>
  Object.fromEntries(
    Object.entries(obj).map(([key, value]) => [keyMap[key] || key, value])
  );
console.log(
  'Rename keys:',
  renameKeys(data, { firstName: 'first', lastName: 'last' })
);

// =====================================================
// EXAMPLE 17: Object Comparison Utilities
// =====================================================
console.log('\n=== Example 17: Object Comparison Utilities ===');

// Shallow equality check
function shallowEqual(obj1, obj2) {
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);

  if (keys1.length !== keys2.length) return false;

  return keys1.every((key) => Object.is(obj1[key], obj2[key]));
}

const a = { x: 1, y: 2 };
const b = { x: 1, y: 2 };
const c = { x: 1, y: 3 };

console.log('a shallow equals b:', shallowEqual(a, b)); // true
console.log('a shallow equals c:', shallowEqual(a, c)); // false

// Find differences between objects
function objectDiff(obj1, obj2) {
  const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
  const diff = {};

  for (const key of allKeys) {
    if (!Object.is(obj1[key], obj2[key])) {
      diff[key] = { old: obj1[key], new: obj2[key] };
    }
  }

  return diff;
}

const oldState = { name: 'Alice', age: 30, city: 'NYC' };
const newState = { name: 'Alice', age: 31, country: 'USA' };

console.log('Diff:', objectDiff(oldState, newState));
/*
{
    age: { old: 30, new: 31 },
    city: { old: "NYC", new: undefined },
    country: { old: undefined, new: "USA" }
}
*/

// =====================================================
// EXAMPLE 18: Object.groupBy() (ES2024)
// =====================================================
console.log('\n=== Example 18: Object.groupBy() (ES2024) ===');

// Note: Object.groupBy is ES2024, polyfill for older environments
const groupBy = (arr, keyFn) => {
  return arr.reduce((acc, item) => {
    const key = keyFn(item);
    (acc[key] ||= []).push(item);
    return acc;
  }, {});
};

const people = [
  { name: 'Alice', department: 'Engineering', level: 'Senior' },
  { name: 'Bob', department: 'Marketing', level: 'Junior' },
  { name: 'Charlie', department: 'Engineering', level: 'Junior' },
  { name: 'Diana', department: 'Marketing', level: 'Senior' },
];

// Group by department
const byDepartment = groupBy(people, (p) => p.department);
console.log('By department:', byDepartment);

// Group by level
const byLevel = groupBy(people, (p) => p.level);
console.log('By level:', byLevel);

// Group by first letter of name
const byFirstLetter = groupBy(people, (p) => p.name[0]);
console.log('By first letter:', byFirstLetter);

// =====================================================
// EXAMPLE 19: Creating Mixins with Object Methods
// =====================================================
console.log('\n=== Example 19: Creating Mixins ===');

const loggableMixin = {
  log(message) {
    console.log(`[${this.name || 'Object'}] ${message}`);
  },
};

const serializableMixin = {
  toJSON() {
    const obj = {};
    for (const key of Object.keys(this)) {
      if (!key.startsWith('_')) {
        obj[key] = this[key];
      }
    }
    return JSON.stringify(obj);
  },
  fromJSON(json) {
    Object.assign(this, JSON.parse(json));
    return this;
  },
};

const timestampMixin = {
  touch() {
    this._lastModified = new Date().toISOString();
  },
  get lastModified() {
    return this._lastModified;
  },
};

// Apply mixins to an object
function applyMixins(target, ...mixins) {
  return Object.assign(target, ...mixins);
}

const entity = applyMixins(
  { name: 'MyEntity', value: 42 },
  loggableMixin,
  serializableMixin,
  timestampMixin
);

entity.log('Hello!');
entity.touch();
console.log('Serialized:', entity.toJSON());
console.log('Last modified:', entity.lastModified);

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

function createValidatedObject(schema) {
  const data = {};

  return new Proxy(data, {
    set(target, property, value) {
      const validator = schema[property];
      if (validator && !validator(value)) {
        throw new TypeError(`Invalid value for ${property}: ${value}`);
      }
      target[property] = value;
      return true;
    },
    get(target, property) {
      return target[property];
    },
    ownKeys(target) {
      return Object.keys(target);
    },
    getOwnPropertyDescriptor(target, property) {
      return Object.getOwnPropertyDescriptor(target, property);
    },
  });
}

const userSchema = {
  name: (v) => typeof v === 'string' && v.length > 0,
  age: (v) => typeof v === 'number' && v >= 0 && v <= 150,
  email: (v) => typeof v === 'string' && v.includes('@'),
};

const validatedUser = createValidatedObject(userSchema);
validatedUser.name = 'Alice';
validatedUser.age = 30;
validatedUser.email = 'alice@example.com';

console.log('Validated user:', Object.assign({}, validatedUser));

try {
  validatedUser.age = -5; // Invalid!
} catch (e) {
  console.log('Validation error:', e.message);
}

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