JavaScript object methods explained: this, ownership, and prototypes

Diagram showing JavaScript object methods, this binding, and prototype sharing

JavaScript object methods are functions stored as object properties. The call site determines the value of this, while the method’s location determines whether it is an own property or inherited through a prototype.

The same ideas apply to object literals, instances created by classes, and built-in APIs such as Object.keys. A small set of concrete examples makes the differences clear.

JavaScript object methods: function properties, shorthand syntax, and calls

A function becomes an object method when it is assigned to a property and invoked through that object. The traditional function-property syntax looks like this:

const user = { name: “Mina”, greet: function () { return “Hi, ” + this.name; } };

Calling user.greet() executes the function with user as its receiver. The method shorthand provides the same behavior with less syntax:

const user = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

Here, user.greet() returns “Hi, Mina”. Shorthand is not a special kind of function. It is concise syntax for defining a regular method in an object literal. The important detail is the property access followed by parentheses: user.greet().

How this gets its value from the call-site receiver

For regular object methods, this usually comes from the object before the dot. In user.greet(), the receiver is user. Calling the same function through another object changes the receiver:

const colleague = { name: “Ravi” }; user.greet.call(colleague);

This call returns “Hi, Ravi”. The function has not changed; only its receiver has.

Detaching the function removes that receiver:

const speak = user.greet; speak();

In strict-mode code, including JavaScript modules and class methods, this is undefined in the detached call. The method therefore cannot read this.name. Use user.greet() when the receiver should remain attached, or create a bound function with const speak = user.greet.bind(user).

An arrow function does not acquire its own receiver from a method call. Its this is lexical, inherited from the surrounding scope. Therefore, greet: () => this.name is not a replacement for a regular receiver-based method when the function needs the object that appears before the dot.

Object methods: own properties versus instance and class prototype methods

Methods defined directly in an object literal are own properties. The function belongs to that particular object:

const literalUser = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

A class uses a different ownership model:

class User { constructor(name) { this.name = name; } greet() { return “Hi, ” + this.name; } } const mina = new User(“Mina”);

mina.name is an own property because the constructor creates it on the instance. mina.greet works, but the function is stored on User.prototype, not copied onto mina. JavaScript looks along the prototype chain when it does not find a property directly on the instance.

Object.hasOwn(mina, “name”) returns true, while Object.hasOwn(mina, “greet”) returns false. Prototype sharing lets every User instance use one method function. Assigning a function inside the constructor would instead create a separate own method for every instance.

JS object methods and useful static Object methods

Some object methods are static methods on the built-in Object constructor. They are called as Object.method(), not as methods of the data object. For example:

Object.keys(user) returns an array of the object’s enumerable own property names, such as [“name”, “greet”]. Related static methods include Object.values(user) for values and Object.entries(user) for key-value pairs.

Object.hasOwn(user, “name”) checks own-property ownership without searching the prototype chain. Object.assign(target, source) copies enumerable properties into a target object, while Object.create(prototype) creates an object with a chosen prototype.

These static APIs differ from instance or prototype methods such as user.greet() and user.toString(). The first group is called on Object; the second is reached through a specific object and may use that object as the receiver.