Flat vector cover illustration showing JavaScript Objects and Methods as a modular car

JavaScript Objects and Methods: A Beginner's Guide That Actually Sticks

Ilyas elaissi
Ilyas Elaissi
9 min readJune 2, 2026

An object in JavaScript is just a bag of named values. That is the entire idea. Once that clicks, JavaScript Objects & Methods stop feeling like a topic and start feeling like the default way you model anything real, whether that's a coffee machine, a user session, or a delivery van with a driver inside it.

I'll walk through the whole thing using a car. Same example I learned on, slightly different names so we are not all writing the same Ford Model T for the hundredth time. By the end you'll know how to spin up objects, attach behavior to them, point one object at another, and use the built-ins like Object.keys() and Object.entries() without looking them up every time.

A car is a good first object

Here is a car as a plain object literal. This is the object initializer syntax, the curly-brace form most people reach for first.

const car = {
  brand: "Tesla",
  model: "Model 3",
  weight: 1611,
  isElectric: true
};

Each line inside the braces is a property paired with a value. brand is a key, "Tesla" is its value, and that pairing is the whole contract: a name on the left, anything you want on the right. Numbers, strings, booleans, arrays, other objects, functions. JavaScript does not care.

Every car you ever build will share the same set of property names. The values change. That's the difference between a class of thing and an instance of it. You and I are both "person" shaped. Our values for firstName differ.

This is what people mean by an object-based paradigm in JavaScript. You describe the world as a bunch of named bundles, and you give those bundles behavior.

Reading and writing properties

Two ways to read a property. The first is dot notation:

console.log(car.model); // "Model 3"

The second is bracket notation, where the property name is a string:

console.log(car["model"]); // "Model 3"

The practical difference between dot notation and bracket notation in JavaScript comes down to two cases. Use brackets when the key has spaces or special characters (car["max speed"]), or when the key is in a variable you do not know until runtime:

const field = "weight";
console.log(car[field]); // 1611

Outside those cases, dot access is shorter and easier to read. I use brackets maybe one time in twenty.

Writing is the same shape:

car.color = "midnight blue";
car["year"] = 2023;

If the key did not exist before, JavaScript creates it. No declaration, no schema, no ceremony. This is freeing and occasionally terrible, which we'll come back to.

Giving the car something to do

A property whose value is a function is a method. That is the whole distinction. A function sitting on the wall is a function; a function sitting on an object is a method.

Here is how to add methods to a JavaScript object directly on the literal:

const car = {
  brand: "Tesla",
  model: "Model 3",
  isDriving: false,
  drive() {
    this.isDriving = true;
    console.log(`${this.brand} ${this.model} is driving`);
  },
  stop() {
    this.isDriving = false;
    console.log(`${this.brand} ${this.model} stopped`);
  }
};

car.drive(); // Tesla Model 3 is driving
car.stop();  // Tesla Model 3 stopped

The drive() and stop() shorthand is just a cleaner way to write drive: function() {...}. They behave identically.

Now the part that trips up almost everyone the first time.

Flat vector illustration of the this keyword in JavaScript pointing back to its object

How does the this keyword actually work?

Inside drive, this refers to whatever object the method was called on. car.drive() means "run drive, and while you do, this is car." So this.isDriving = true is the same as car.isDriving = true in that moment.

If you ripped the method out and called it standalone, this would no longer point at car:

const looseDrive = car.drive;
looseDrive(); // this is undefined (in strict mode) or the global object

That is the most common gotcha in the language. The this keyword in JavaScript is not bound to where the function was defined. It is bound to how the function was called. Method call uses dot, this is the thing on the left of the dot. That rule will get you through 90% of cases.

Arrow functions are the exception worth knowing. An arrow function does not get its own this; it inherits from the surrounding scope. So this breaks:

const car = {
  brand: "Tesla",
  drive: () => {
    console.log(this.brand); // undefined
  }
};

Use regular function syntax for methods. Use arrows for callbacks inside methods. That is the safe default.

Building many cars with a constructor function

The literal approach is fine for one-off objects. The moment you want fifty cars, copy-pasting becomes painful. This is where the constructor function pattern earns its keep.

A constructor is a normal function you call with new. Convention: capitalize the first letter so you can spot it.

function Car(brand, model, weight, isElectric) {
  this.brand = brand;
  this.model = model;
  this.weight = weight;
  this.isElectric = isElectric;
  this.isDriving = false;

  this.drive = function() {
    this.isDriving = true;
    console.log(`${this.brand} ${this.model} is driving`);
  };

  this.stop = function() {
    this.isDriving = false;
    console.log(`${this.brand} ${this.model} stopped`);
  };
}

const car1 = new Car("Tesla", "Model 3", 1611, true);
const car2 = new Car("Toyota", "Corolla", 1340, false);

car1.drive(); // Tesla Model 3 is driving
console.log(car2.isElectric); // false

The new keyword does four things, and it is worth knowing all of them. It creates an empty object, sets this inside the function to that new object, runs the function body so your assignments populate it, and finally returns the object. Skip new and this will be wrong, the function will return undefined, and your variable will be empty. I have lost an hour to that mistake more than once.

That is the JavaScript object constructor function explained in one paragraph. Modern code often uses class syntax instead, but class is mostly cosmetic sugar over this exact pattern. If you understand constructors, classes are a five-minute upgrade later.

One small efficiency note. Defining drive and stop inside the constructor means every car gets its own copy of those functions. For two cars, who cares. For ten thousand, you would want the methods on a shared prototype using Car.prototype.drive = function() {...}. That is object inheritance through the prototype chain, and it's where the real difference between static vs instance methods shows up in practice. Worth reading about on the MDN Web Docs once you are comfortable with the basics.

Putting an object inside an object

Real things are not flat. A car has a driver. The driver has a name. The name has a first and last part. You model this by storing objects as values of other objects.

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

function Car(brand, model) {
  this.brand = brand;
  this.model = model;
  this.driver = null;

  this.getIn = function(person) {
    this.driver = person;
    console.log(`${person.firstName} ${person.lastName} entered the car`);
  };
}

const car1 = new Car("Tesla", "Model 3");
const person1 = new Person("Bob", "Miller");

car1.getIn(person1);
// Bob Miller entered the car

console.log(car1.driver.firstName); // Bob

Reaching into nested objects in JavaScript is just dot-chaining: car1.driver.firstName. The catch is that if any link in the chain is undefined or null, you get a runtime error. The optional chaining operator handles that:

console.log(car1.driver?.firstName); // Bob
console.log(car2.driver?.firstName); // undefined, no crash

That ?. has saved me a depressing number of null-check if statements. Use it freely when you are not sure a property exists.

If you want to know how to access nested objects in JavaScript when the keys are dynamic, fall back to bracket notation at each level: car1["driver"]["firstName"]. It is the same idea, just spelled out.

👉 Also read: How to detect word wrap in a textarea with JavaScript

The built-in Object methods you will actually use

The global Object has a bunch of static helpers. These are the ones I reach for weekly.

Object.keys(obj) returns an array of the property names. Object.values(obj) returns an array of the values. Object.entries(obj) returns an array of [key, value] pairs. Once you know how to use Object.keys and Object.values in JavaScript, iterating an object stops being a chore:

const car = { brand: "Tesla", model: "Model 3", weight: 1611 };

Object.keys(car);    // ["brand", "model", "weight"]
Object.values(car);  // ["Tesla", "Model 3", 1611]
Object.entries(car); // [["brand", "Tesla"], ["model", "Model 3"], ["weight", 1611]]

for (const [key, value] of Object.entries(car)) {
  console.log(`${key}: ${value}`);
}

Object.assign(target, source) copies properties from source to target. Mostly replaced by the spread operator merging trick below, but still useful when you want to mutate an existing object on purpose.

Object.freeze(obj) locks an object so its properties cannot be changed, added, or removed. Handy for constants you actually want to be constant. Note it is shallow: nested objects can still be mutated.

Object.create(proto) builds a new object with a specific prototype. This is also how you make null-prototype objects with Object.create(null), which are useful as pure dictionaries because they do not inherit toString, hasOwnProperty, and friends. If you have ever had a user submit __proto__ as a form key, you understand why this matters.

The spread operator is the everyday workhorse for combining objects without mutating them:

const base = { brand: "Tesla", model: "Model 3" };
const upgraded = { ...base, color: "red", year: 2023 };
// { brand: "Tesla", model: "Model 3", color: "red", year: 2023 }

Later keys overwrite earlier ones, which is the whole pattern behind config defaults and React state updates.

Flat vector illustration showing nested objects in JavaScript with layered geometric boxes

Getters, setters, and computed properties

Sometimes a property should look static from the outside but actually run code when you read or write it. That is what getters and setters are for:

const car = {
  brand: "Tesla",
  model: "Model 3",
  get fullName() {
    return `${this.brand} ${this.model}`;
  },
  set fullName(value) {
    [this.brand, this.model] = value.split(" ");
  }
};

console.log(car.fullName); // "Tesla Model 3"
car.fullName = "Toyota Corolla";
console.log(car.brand); // "Toyota"

You access car.fullName like a property, no parentheses, but a function runs underneath. I use this sparingly because it hides behavior behind what looks like a field, and that surprise is worth the cost only when the abstraction is genuinely cleaner.

Where objects get weird

A few things will bite you. Worth knowing up front.

Comparing two objects with === checks reference identity, not contents. {a: 1} === {a: 1} is false. Even when every key and value matches, they are different objects in memory. To compare contents you either write your own check or use a library.

Object coercion happens when JavaScript needs a primitive from an object, like in alert(obj) or "" + obj. By default you get "[object Object]", which is the kind of bug that ships to production. Define a toString() method if you care.

typeof lies about objects. typeof null is "object". Arrays are objects too: typeof [] is "object". Use Array.isArray() for arrays and an explicit === null check for null.

Mutating an object passed into a function changes it for the caller. Objects are passed by reference. If you want a safe copy, { ...original } gets you a shallow clone. For deep structures, structuredClone(obj) is now built into modern browsers and Node, and it actually works on nested data, dates, and Maps.

A small beginner guide to JavaScript objects and methods, condensed

If you want the minimum viable mental model, it is this:

  • An object is named values inside braces.
  • A method is a function that lives on an object.
  • this inside a method is the object you called the method on.
  • new ConstructorFn(...) builds a fresh object using a function as a template.
  • Nested objects are just values that happen to be objects. Walk them with dots or ?..
  • Object.keys(), Object.values(), and Object.entries() turn objects into arrays you can loop over.

Sites like W3Schools and GeeksforGeeks have decent quick-reference tables if you want a second angle on the syntax. For deeper specifics, MDN is the source I trust.

The last thing I'd push you toward is just building something with this. Model a playlist, a shopping cart, a chess board, anything where there are clear nouns and verbs. Modeling real-world things as objects is where the abstractions start to feel obvious instead of academic. The first time you reach for an object without thinking about it, you are done learning the basics.

Then go break a few. Pass methods around without their objects. Watch this disappear. Try to mutate a frozen object. The error messages teach faster than any tutorial, including this one.

Get CodeTips in your inbox

Free subscription for coding tutorials, best practices, and updates.

More from CodeTips