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.
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.
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.
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.
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.
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.
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.
// 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.
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
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
console.log(smokeRun instanceof TestRun); // true
console.log({} instanceof TestRun); // false
Useful when debugging factories or custom error types.
9. Constructor vs object literal
| Approach | When it helps in QA |
|---|---|
Object literal { open() {} } | One-off config, small fixture, single page mock |
Constructor + new | Many instances with the same shape (runs, clients, builders) |
class (later lesson) | Same idea as constructor, clearer syntax for large frameworks |
Official docs
Quick recap
| Topic | Takeaway for QA automation |
|---|---|
| Method | Function on an object: page.open() |
this | The object before the dot when the method is called |
| Arrow method | Avoid when the method must use this |
| Constructor | Function that initializes instance properties |
new | Creates instance and sets this inside constructor |
prototype | Shared methods for all instances |
instanceof | checks whether an object’s prototype chain includes the prototype property of a constructor function. |
Suggested exercises
- Create an object
checkoutPagewithnameand a methodopen()that logsthis.name. - Call
open()directly and via a variableconst fn = checkoutPage.open— explain the difference. - Write a constructor
TestCase(title)and create two instances withnew. - Add a method
describe()onTestCase.prototypethat returns the title. - Build a tiny
ApiClient(baseUrl)with a methodgetHealth()that logsthis.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.