Lesson 11 of 14

Lesson 11 — Object methods, this, constructors, and new

Title: Object Methods, this, Constructor Functions, and the new Operator

Description: Objects can store both data and behavior. A method is a function stored as an object property. Inside a normal method, this refers to the object that received the call. Before modern class syntax, JavaScript used constructor functions with the new operator to create multiple similar objects (for example, test runs or API clients).

Why it matters for QA: Page Objects, API clients, and builders are objects with methods. If this points to the wrong object, helpers break silently. Understanding new and constructors helps you read framework code, fixtures, and error messages like TypeError: X is not a constructor.


1. Object with a method

An object can group related data and actions.

javascriptjavascript
const loginPage = {
  name: "Login",
  path: "/login",

  open() {
    console.log(`Open ${this.path}`);
  },
};

loginPage.open();

open is a method — a function that belongs to the object.


2. What this means

this is the object before the dot when you call a method.

javascriptjavascript
const dashboardPage = {
  title: "Dashboard",

  expectLoaded() {
    console.log(`Expect heading: ${this.title}`);
  },
};

dashboardPage.expectLoaded();

Here, this is dashboardPage, so this.title is "Dashboard".


3. this depends on how you call the function

If you pull a method out of the object, this is lost unless you bind it.

javascriptjavascript
const profilePage = {
  name: "Profile",

  logName() {
    console.log(this.name);
  },
};

profilePage.logName(); // "Profile"

const detached = profilePage.logName;
// detached(); // TypeError or undefined — `this` is not profilePage

In QA code, pass callbacks carefully. Prefer calling page.method() on the object, not storing a bare method reference unless you know binding rules.


4. Do not use arrow functions for object methods when you need this

Arrow functions do not have their own this. They use this from the surrounding scope.

javascriptjavascript
const brokenPage = {
  name: "Settings",

  open: () => {
    console.log(this.name); // usually undefined in a script/module
  },
};

brokenPage.open();

Use a normal method or function syntax when the method must read this.name, this.baseUrl, and so on.


5. Constructor function

A constructor is a regular function meant to be called with new. It prepares a fresh object.

javascriptjavascript
function TestRun(name, retryCount = 0) {
  this.name = name;
  this.retryCount = retryCount;
}

TestRun.prototype.describe = function () {
  return `${this.name} (retries=${this.retryCount})`;
};

const smokeRun = new TestRun("smoke", 1);
const regressionRun = new TestRun("regression", 2);

console.log(smokeRun.describe());
console.log(regressionRun.describe());

Naming convention: constructor function names often start with a capital letter (TestRun), like classes.


6. What new does

new creates a new object and sets it as this inside the constructor function.

javascriptjavascript
function ApiClient(baseUrl, token) {
  this.baseUrl = baseUrl;
  this.token = token;
}

ApiClient.prototype.createUser = function (email) {
  console.log(`POST ${this.baseUrl}/users as ${this.token}`);
  console.log(`Body email: ${email}`);
};

const client = new ApiClient("https://api.example.com", "secret-token");
client.createUser("qa@example.com");

Without new, this may be undefined (strict mode) or the global object, and properties will not attach to your instance.

javascriptjavascript
// const bad = ApiClient("https://api.example.com", "token");
// Avoid: call constructors with `new`.

7. Methods on the prototype

Put shared methods on Constructor.prototype so every instance reuses one function instead of copying it on each object.

javascriptjavascript
function UserBuilder() {
  this.user = {
    email: "qa@example.com",
    role: "viewer",
    isActive: true,
  };
}

UserBuilder.prototype.withRole = function (role) {
  this.user = { ...this.user, role };
  return this;
};

UserBuilder.prototype.build = function () {
  return { ...this.user };
};

const admin = new UserBuilder().withRole("admin").build();
console.log(admin);

This pattern is the foundation of class syntax in a later lesson.


8. QA-style examples

Page-like object

javascriptjavascript
function LoginPage() {
  this.emailSelector = "[data-testid='email']";
  this.passwordSelector = "[data-testid='password']";
}

LoginPage.prototype.fillEmail = function (email) {
  console.log(`Fill ${this.emailSelector} with ${email}`);
};

LoginPage.prototype.login = function (email, password) {
  this.fillEmail(email);
  console.log(`Fill ${this.passwordSelector}`);
  console.log("Click submit");
};

const loginPage = new LoginPage();
loginPage.login("qa@example.com", "Secret123!");

The test describes what happens; the object hides how (selectors and steps).

Check instanceof

javascriptjavascript
console.log(smokeRun instanceof TestRun); // true
console.log({} instanceof TestRun); // false

Useful when debugging factories or custom error types.


9. Constructor vs object literal

ApproachWhen it helps in QA
Object literal { open() {} }One-off config, small fixture, single page mock
Constructor + newMany instances with the same shape (runs, clients, builders)
class (later lesson)Same idea as constructor, clearer syntax for large frameworks

Official docs


Quick recap

TopicTakeaway for QA automation
MethodFunction on an object: page.open()
thisThe object before the dot when the method is called
Arrow methodAvoid when the method must use this
ConstructorFunction that initializes instance properties
newCreates instance and sets this inside constructor
prototypeShared methods for all instances
instanceofchecks whether an object’s prototype chain includes the prototype property of a constructor function.

Suggested exercises

  1. Create an object checkoutPage with name and a method open() that logs this.name.
  2. Call open() directly and via a variable const fn = checkoutPage.open — explain the difference.
  3. Write a constructor TestCase(title) and create two instances with new.
  4. Add a method describe() on TestCase.prototype that returns the title.
  5. Build a tiny ApiClient(baseUrl) with a method getHealth() that logs this.baseUrl.

Homework

Short tasks (about 20–30 minutes). Click a task title to reveal the prompt.

Task 1: object method and this

Create const settingsPage = { name: "Settings", open() { ... } }. The method must log Open ${this.name}. Call settingsPage.open() and explain what this is.

Task 2: lost this

Store const open = settingsPage.open and try to call open(). Explain why this.name fails or is wrong.

Task 3: constructor and new

Write function TestRun(name, retryCount = 0) that sets this.name and this.retryCount. Create two runs with new and log their properties.

Task 4: prototype method

Add TestRun.prototype.describe that returns `${this.name} (retries=${this.retryCount})`. Call describe() on both instances.

Task 5: instanceof

Log smokeRun instanceof TestRun and ({}) instanceof TestRun. Explain both results.