Episode 1 — Fundamentals / 1.23 — Objects

1.23.b — Key-Value Pairs

In one sentence: Every object property is a key-value pair where the key is always a string (or Symbol) and the value can be any type — including functions, which turn properties into methods.

Navigation: ← 1.23 Overview · 1.23.c — Accessing Properties →


1. Properties — keys and values

An object is essentially a bag of properties. Each property has:

  • A key (also called a property name) — always coerced to a string unless it is a Symbol.
  • A value — can be any JavaScript type.
const product = {
  name: "Wireless Mouse",   // key: "name",  value: string
  price: 29.99,             // key: "price", value: number
  inStock: true,            // key: "inStock", value: boolean
  tags: ["electronics"],    // key: "tags", value: array
  manufacturer: null,       // key: "manufacturer", value: null
};

Keys are always strings (internally)

Even if you use a number as a key, JavaScript converts it to a string:

const obj = { 1: "one", 2: "two" };
console.log(obj["1"]); // "one"
console.log(obj[1]);   // "one" — 1 is coerced to "1"

// Proof:
console.log(Object.keys(obj)); // ["1", "2"] — strings!

2. Property shorthand

When a variable name matches the key you want, use shorthand notation to avoid repetition:

const name = "Alice";
const age = 25;
const role = "admin";

// Without shorthand — repetitive
const user1 = { name: name, age: age, role: role };

// With shorthand — clean and idiomatic
const user2 = { name, age, role };

console.log(user2); // { name: "Alice", age: 25, role: "admin" }

You can mix shorthand and regular properties:

const email = "alice@example.com";
const user = {
  email,            // shorthand
  username: "alice", // regular
  isActive: true,    // regular
};

When to use shorthand

ScenarioRecommendation
Variable name matches desired keyUse shorthand — less noise
Variable name differs from desired keyUse regular key: value syntax
Building objects from function parametersShorthand shines — return { name, age }

3. Computed property names

Wrap a key in square brackets [] to compute it from an expression:

const field = "email";
const user = {
  name: "Alice",
  [field]: "alice@example.com",  // key is "email"
};
console.log(user.email); // "alice@example.com"

Dynamic keys from expressions

const prefix = "user";
const id = 42;

const obj = {
  [`${prefix}_${id}`]: "Alice",  // key is "user_42"
};
console.log(obj["user_42"]); // "Alice"

Common use case — building objects from data

function buildLookup(items, keyProp) {
  const lookup = {};
  for (const item of items) {
    lookup[item[keyProp]] = item;  // dynamic key assignment
  }
  return lookup;
}

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
];

const byId = buildLookup(users, "id");
console.log(byId[1]); // { id: 1, name: "Alice" }

Using computed property names inside the literal:

function createPair(key, value) {
  return { [key]: value };
}

console.log(createPair("color", "blue")); // { color: "blue" }

4. String keys vs identifier keys — when quotes are needed

Most keys are written as bare identifiers (no quotes). You need quotes when the key:

  • Contains spaces or special characters
  • Starts with a number
  • Is a reserved word (though modern JS allows most reserved words as keys)
  • Contains hyphens (common in CSS-like properties or HTTP headers)
const obj = {
  name: "Alice",             // valid identifier — no quotes needed
  "full name": "Alice J.",   // space — quotes required
  "content-type": "json",   // hyphen — quotes required
  "123start": true,          // starts with number — quotes required
  class: "A",                // reserved word — works without quotes in modern JS
};

// Access quoted keys with bracket notation
console.log(obj["full name"]);     // "Alice J."
console.log(obj["content-type"]);  // "json"
Key formatQuotes needed?Access
nameNoobj.name or obj["name"]
full nameYesobj["full name"] only
content-typeYesobj["content-type"] only
123abcYesobj["123abc"] only
classNo (modern JS)obj.class or obj["class"]

5. Symbol keys

Symbols are unique, immutable identifiers. They create "hidden" properties that do not collide with string keys and do not appear in for...in or Object.keys:

const SECRET = Symbol("secret");
const ID = Symbol("id");

const user = {
  name: "Alice",
  [SECRET]: "classified-data",
  [ID]: 42,
};

console.log(user.name);     // "Alice"
console.log(user[SECRET]);  // "classified-data"
console.log(user[ID]);      // 42

// Symbol keys are NOT enumerated by default
console.log(Object.keys(user));    // ["name"]
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(secret), Symbol(id)]

When to use Symbol keys

  • Library / framework internals — avoid collision with user-defined keys
  • Well-known symbolsSymbol.iterator, Symbol.toPrimitive, etc.
  • Truly private-like metadata — not visible to serialization or casual iteration

For day-to-day code, string keys are the standard. Symbols are an advanced tool.


6. Values can be any type

Object values have no type restrictions:

const demo = {
  // Primitives
  str: "hello",
  num: 42,
  bool: true,
  nothing: null,
  missing: undefined,
  big: 9007199254740991n,    // BigInt
  sym: Symbol("demo"),       // Symbol

  // Reference types
  arr: [1, 2, 3],
  nested: { inner: "value" },
  fn: function() { return "I'm a function"; },
  arrow: () => "I'm an arrow function",
  date: new Date(),
  regex: /pattern/gi,
};

console.log(demo.str);         // "hello"
console.log(demo.arr[0]);      // 1
console.log(demo.nested.inner); // "value"
console.log(demo.fn());        // "I'm a function"
console.log(demo.arrow());     // "I'm an arrow function"

7. Methods — functions as property values

When a property's value is a function, we call it a method:

const calculator = {
  result: 0,

  add: function(n) {
    this.result += n;
    return this;  // enables chaining
  },

  subtract: function(n) {
    this.result -= n;
    return this;
  },

  reset: function() {
    this.result = 0;
    return this;
  },

  getResult: function() {
    return this.result;
  },
};

calculator.add(10).add(5).subtract(3);
console.log(calculator.getResult()); // 12

8. Method shorthand vs function expression

ES6 introduced a shorthand for methods — omit : function:

// Function expression (ES5 style)
const userES5 = {
  name: "Alice",
  greet: function() {
    return "Hello, I'm " + this.name;
  },
};

// Method shorthand (ES6+ — preferred)
const userES6 = {
  name: "Alice",
  greet() {
    return `Hello, I'm ${this.name}`;
  },
};

console.log(userES5.greet()); // "Hello, I'm Alice"
console.log(userES6.greet()); // "Hello, I'm Alice"

Comparison table

StyleSyntaxthis bindingCan be constructor?Recommended?
Function expressiongreet: function() {}Dynamic (caller)Yes (new obj.greet())Legacy
Method shorthandgreet() {}Dynamic (caller)NoYes
Arrow functiongreet: () => {}Lexical (enclosing scope)NoNot for methods

Why NOT arrow functions for methods

const user = {
  name: "Alice",
  greet: () => {
    // `this` is NOT the object — it's the enclosing scope (e.g. `window` or `undefined`)
    return `Hello, I'm ${this.name}`;
  },
};

console.log(user.greet()); // "Hello, I'm undefined" — WRONG!

Arrow functions do not have their own this — they inherit it from the surrounding lexical scope. For object methods that need this, always use method shorthand or function expressions.


9. this inside methods — the basics

Inside a method, this refers to the object the method was called on:

const dog = {
  name: "Rex",
  breed: "Labrador",
  describe() {
    return `${this.name} is a ${this.breed}`;
  },
};

console.log(dog.describe()); // "Rex is a Labrador"

The key rule

this is determined by how the function is called, not where it is defined:

const dog = {
  name: "Rex",
  describe() {
    return `${this.name}`;
  },
};

// Called as a method — this === dog
console.log(dog.describe()); // "Rex"

// Extracted and called standalone — this is undefined (strict mode) or window
const fn = dog.describe;
// console.log(fn()); // TypeError or "undefined" — `this` is lost!

Deep dive on this is covered in functions and advanced topics. For now, remember: call a method on its object to keep this correct.


10. Real examples

Building a user object

function createUser(name, email, role = "viewer") {
  return {
    name,
    email,
    role,
    createdAt: new Date().toISOString(),
    isActive: true,

    deactivate() {
      this.isActive = false;
    },

    promote(newRole) {
      this.role = newRole;
    },

    summary() {
      return `${this.name} (${this.email}) — ${this.role}`;
    },
  };
}

const alice = createUser("Alice", "alice@example.com", "admin");
console.log(alice.summary()); // "Alice (alice@example.com) — admin"

alice.deactivate();
console.log(alice.isActive);  // false

Configuration object with methods

const appConfig = {
  settings: {
    theme: "light",
    language: "en",
    notifications: true,
  },

  get(key) {
    return this.settings[key];
  },

  set(key, value) {
    this.settings[key] = value;
  },

  reset() {
    this.settings = {
      theme: "light",
      language: "en",
      notifications: true,
    };
  },

  toJSON() {
    return JSON.stringify(this.settings, null, 2);
  },
};

appConfig.set("theme", "dark");
console.log(appConfig.get("theme")); // "dark"
console.log(appConfig.toJSON());
// {
//   "theme": "dark",
//   "language": "en",
//   "notifications": true
// }

Key takeaways

  1. Every property has a key (string or Symbol) and a value (any type).
  2. Property shorthand { name } avoids repetition when the variable name matches the key.
  3. Computed property names { [expr]: value } let you build keys dynamically.
  4. Keys that contain spaces, hyphens, or start with digits need quotes and bracket access.
  5. Symbol keys create non-enumerable, collision-free properties — mostly for libraries and internals.
  6. Methods are properties whose values are functions — use method shorthand greet() {} in ES6+.
  7. Arrow functions should not be used as methods because they do not bind their own this.
  8. this inside a method refers to the calling object — but only when called with dot notation on that object.

Explain-It Challenge

Explain without notes:

  1. What is property shorthand and when does it apply?
  2. Why would you use computed property names [expr]? Give a real example.
  3. Why should you avoid using arrow functions as object methods? What happens to this?

Navigation: ← 1.23 Overview · 1.23.c — Accessing Properties →