Jan 28, 2026 • 9 min read

objects

What I learned, what I’m still figuring out, and where I might go next.


Objects

JavaScript objects are an almost weirdly versatile collection type. Object literals are often used to store data in key-value pairs.

const apple = {
  name: "Apple",
  radius: 2,
  color: "red",
};

You can access properties stored on an object using the . operator:

console.log(apple.name); // prints "Apple"
console.log(apple.radius); // prints "2"
console.log(apple.color); // prints "red"

No Colon

The key: value syntax is the normal way to create key-value pairs in an object, but if you want a key to have the same name as an existing variable, you can omit the colon and the value.

const radius = 2;
const color = "red";
const apple = {
  radius, // same as radius: radius
  color: color, // set explicitly for demonstration
};

Updating Properties

You can update and add new keys (“property” and “key” can be used interchangeably) to an existing object using the . operator. If it exists, it’s updated; if it doesn’t, it’s created as a new property:

const apple = {
  name: "Apple",
  radius: 2,
  color: "red",
};

apple.numSeeds = 3; // new property
apple.color = "green"; // update property
// {"name":"Apple","radius":2,"color":"green","numSeeds":3}

Nesting Properties

Objects can contain other objects. Here we’ve nested two object literals within the tournament object:

const tournament = {
  referee: {
    name: "Sally",
    age: 25,
  },
  prize: {
    units: "dollars",
    value: 100,
  },
};

We can access nested properties the same way by chaining: tournament.referee.name

console.log(tournament.referee.name); // Sally
console.log(tournament.prize.value); // 100

Optional Chaining

When using the normal . operator, if the object on the left side of the . is null or undefined, you’ll get a TypeError at runtime. Thankfully, JavaScript has recently added a new operator to make dealing with this headache easier, the optional chaining operator?.

const tournament = {
  prize: {
    units: "dollars",
    value: 100,
  },
};

const h = tournament.referee.height;
// TypeError: Cannot read properties of undefined (reading 'height')

So, if you’re not sure whether the referee property exists (maybe it was sent to us over the network) we can use the optional chaining operator to avoid the error:

const tournament = {
  prize: {
    units: "dollars",
    value: 100,
  },
};

const h = tournament.referee?.height;
// h is simply undefined, no error is thrown

Object Methods

JavaScript objects can have methods, just like classes in Python or structs in Go. Objects are interesting in JavaScript because they play the role of dictionaries and classes in other languages.

Methods are functions that are defined on an object. They can access and change the properties of the object in question. In the context of an object method, the this keyword refers to the object itself, like self in Python.

const person = { firstName: “John”, lastName: “Smith”, getFullName() { return this.firstName + ” ” + this.lastName; }, };

console.log(person.getFullName()); // John Smith

Methods Mutate

Methods can change the properties of their objects as well:

const tree = {
  height: 256,
  color: "green",
  cut() {
    this.height /= 2;
  },
};

tree.cut();
console.log(tree.height);
// prints 128

tree.cut();
console.log(tree.height);
// prints 64
> In JavaScript, the `const` keyword doesn't stop you from _changing the properties_ of an object... it only stops you from reassigning the variable (`tree` in this case) to a _new_ object.

Initializing Props

If a property (key) doesn’t exist when we try to access it with the . operator, we’ll just get undefined. One way to check for this is by using the ! (not) operator because undefined is “falsy” (meaning it evaluates to false in a boolean context). The syntax is simple:

const balances = {
  lane: 100,
  breanna: 150,
  john: 200,
};

// if bob doesn't have a balance yet
// create a new prop for him
// set to 0
if (!balances.bob) {
  balances.bob = 0;
}

Strings as Keys

Accessing a property like desk.height is great when the name of the prop is static, meaning you know what it is before runtime. But what if the key is dynamic? Like, what if the user enters a string and you need to use that as the lookup key?

Bracket notation solves this.

const desk = {
  wood: "maple",
  width: 100,
};

console.log(desk.wood);
// prints "maple"

console.log(desk["wood"]);
// also prints "maple"

const key = "wood";
console.log(desk[key]);
// also prints "maple"

This

this refers to the context where a piece of code is executed.

Global Context

// in a browser
console.log(this);
// Window { ... }

// in Node.js
console.log(this);
// {}

Strict Mode

In strict mode, this is undefined in the global scope in both the browser and Node.js.

"use strict";
console.log(typeof this);
// undefined

Method Context

Inside a standard method or a constructorthis refers to the object the method is called on.

const myObject = {
  message: "Hello, World!",
  myMethod() {
    console.log(this);
    console.log(this.message);
  },
};
myObject.myMethod();
// {"message":"Hello, World!"}
// Hello, World!

Arrow Functions

Fat arrow functions, or “arrow functions” are another way to define functions in JavaScript. Arrow functions are newer than the function keyword, however, unlike the let/const syntax, arrow functions are sometimes better, not always better.

// declaring a function without a variable
function add(x, y) {
  return x + y;
}

// declaring a function with a variable
const add = function (x, y) {
  return x + y;
};

// using the fat arrow syntax
const add = (x, y) => {
  return x + y;
};

One reason to choose an arrow function over a regular function or method is to preserve the this context. It’s particularly useful when working with objects. To be fair, in a simple object like this, the non-arrow method makes perfect sense:

const author = {
  firstName: "Jay",
  lastName: "Nejati",
  getName() {
    return `${this.firstName} ${this.lastName}`;
  },
};
console.log(author.getName());
// Prints: Jay Nejati

With a fat-arrow function, the this keyword refers to the same context as its parent. In essence, fat arrow functions “preserve” the this context. That’s why this this.firstName and this.lastName are undefined in this example:

const author = {
  firstName: "Lane",
  lastName: "Wagner",
  getName: () => {
    return `${this.firstName} ${this.lastName}`;
  },
};
console.log(author.getName());
// Prints: undefined undefined
// because `this` still refers to the global object
// and `firstName` and `lastName` are not defined globally

Developers working in some front-end JavaScript frameworks (like React or Vue) tend to use fat arrow functions often. The this context can contain a ton of component-wide state, and it needs to be preserved throughout nested function calls, so fat arrows make the code easier to read and write.

Spread Syntax

JavaScript has a really nifty spread syntax for moving groups of object properties around. It’s a great way to copy objects and merge object properties.

const engineering_dept = {
  lane: "grand magus",
  hunter: "software engineer",
  allan: "software engineer",
  matt: "software engineer",
  dan: "software engineer",
  waseem: "software engineer",
};

const video_dept = {
  stass: "video producer",
  alex: "video producer",
};

const all_employees = { ...engineering_dept, ...video_dept };
/*
{
  lane: 'grand magus',
  hunter: 'software engineer',
  allan: 'software engineer',
  matt: 'software engineer',
  dan: 'software engineer',
  waseem: 'software engineer',
  stass: 'video producer',
  alex: 'video producer'
}
*/

The spread syntax shallow-copies the properties of the objects you’re spreading. If properties have the same name, the last (right-most) object’s property will overwrite the previous ones.

const engineering_dept = {
  lane: "software engineer",
  hunter: "software engineer",
};

const video_dept = {
  lane: "cringe youtuber",
  alex: "video producer",
};

const all_employees = { ...engineering_dept, ...video_dept };
/*
{
  lane: 'cringe youtuber',
  hunter: 'software engineer',
  alex: 'video producer'
}
*/

Return Objects

In JavaScript, we can only return a single value from a function. So, when you want to return multiple values, you just return an object that contains those values.

function doAllTheMath(x, y) {
  const sum = x + y;
  const difference = x - y;
  const product = x * y;
  const quotient = x / y;
  return {
    sum,
    difference,
    product,
    quotient,
  };
}

const results = doAllTheMath(10, 5);
console.log(results.sum);
// 15
console.log(results.difference);
// 5
console.log(results.product);
// 50
console.log(results.quotient);
// 2

Destructuring

It’s admittedly annoying to have to get the return values from an object by using the . operator. The destructuring assignment lets us unpack object properties easily.

Instead of this:

const apple = {
  radius: 2,
  color: "red",
};

const radius = apple.radius;
const color = apple.color;

We can do this:

const apple = {
  radius: 2,
  color: "red",
};

const { radius, color } = apple;

It can be used to unpack function return values:

function getApple() {
  const apple = {
    radius: 2,
    color: "red",
  };
  return apple;
}

const { radius, color } = getApple();
console.log(radius); // 2
console.log(color); // red

Destructuring also works in function parameters, which means that if you write a function that takes an object as an argument, you can unpack the object’s properties in function definition.

Instead of this:

function eatApple(apple) {
  console.log(`ate a ${apple.color} apple with a radius of ${apple.radius}`);
}

We can do this:

function eatApple({ radius, color }) {
  console.log(`ate a ${color} apple with a radius of ${radius}`);
}

Not Bound

Methods in JavaScript are not bound to their object by default (as they are in languages like Python and Go). So if you use a method as a “callback” function, you may run into issues with the this keyword:

const user = {
  name: "Jay",
  sayHi() {
    console.log(`Hi, my name is ${this.name}`);
  },
};

user.sayHi();
// Hi, my name is Jay

const sayHi = user.sayHi;
sayHi();
// TypeError: Cannot read properties of undefined (reading 'name')

This happens a lot when passing a method as a callback function to another function:

const user = {
  firstName: "Jay",
  lastName: "Nejati",
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  },
};

function getGreeting(introduction, nameCallback) {
  return `${introduction}, ${nameCallback()}`;
}

console.log(user.getFullName());
// Jay Nejati
console.log(getGreeting("Hello", user.getFullName));
// TypeError: Cannot read properties of undefined (reading 'firstName')

Still Curious About