What is a Function in JavaScript?

A function is a block of reusable code that performs a specific task in JavaScript.

Functions let you write code once and reuse it multiple times, making scripts modular and maintainable.

Real-life Example

In my automation scripts, I create functions for login, form submission, or navigation to reuse them across multiple tests.

Real-time Testing Example

In Playwright, a login() function is created once and called in all tests that require authentication.


Advertisement

Types of Functions

JavaScript has three main types of functions, each with slightly different syntax and behavior.

Function Declaration

  • Named functions

  • Hoisted

  • Can be called before declaration

Function Expression

  • Assigned to a variable

  • Not hoisted

Arrow Functions

  • Modern, shorter syntax

  • this behaves differently

Real-life Example

In my automation scripts:

  • I use function declarations for reusable actions like login.

  • I use function expressions for optional callbacks.

  • I use arrow functions for Playwright async actions.


Function Declaration vs Function Expression

A function declaration is hoisted and can be called before its definition, while a function expression is not hoisted and must be defined before calling.

Feature Function Declaration Function Expression
Hoisting Hoisted → can be called before definition Not hoisted → must be defined before call
Syntax function funcName() {} const funcName = function() {}
Usage Standard reusable functions Assignable to variables, used as callbacks
Name Must have a name Can be anonymous or named

Real-time Testing Example

In Playwright, a function expression is often used to assign a helper function to a variable that might vary between tests.


What are Arrow Functions?

Arrow functions are a concise syntax for writing functions in JavaScript, introduced in ES6, and they inherit this from the parent scope.

Key Features

  • They do not have their own this (they inherit it from the parent context).

  • They cannot be used as constructors.

  • They are ideal for inline functions or callbacks.

Real-time Testing Example

An arrow function is used for async Playwright operations, such as filling forms or clicking buttons, across multiple tests.


Normal Functions vs Arrow Functions

Normal functions have their own context and can be used as constructors, while arrow functions inherit this from the parent scope and cannot be used as constructors.

Feature Normal Function Arrow Function
this binding Has its own this Inherits this from parent
Constructor Can be used as a constructor Cannot be used as a constructor
Syntax function name() {} const name = () => {}
Hoisting Hoisted (if a function declaration) Not hoisted
Use case General reusable functions Inline functions, callbacks, async tasks

Real-time Testing Example

In Playwright:

  • Arrow functions are used for test steps inside test() blocks.

  • Normal functions are used to create reusable helpers or page object methods.


Why Arrow Functions Are Used in Automation

Arrow functions are commonly used in automation because they provide concise syntax and inherit this from the parent scope, making async test steps cleaner and less error-prone.

Real-life Example

In my Playwright scripts, arrow functions are used for reusable async actions such as login and form submission, keeping the test steps compact and predictable.


The this Keyword

The value of this depends on the context in which a function runs.

  • Global context — in the browser, this points to window.

  • Function context (normal function) — this refers to the object that called the function (or the global object when called plainly).

  • Arrow functions — do not have their own this; they inherit it from the parent scope, which prevents context issues when using callbacks inside async operations.

  • Class/objects — inside methods, this refers to the instance of the class.


Default Parameters

Default parameters allow a function to assign default values to arguments if no value or undefined is passed.

JavaScript ES6 introduced default parameters to avoid undefined values when arguments are missing.

Real-life Example

In my automation scripts, default parameters are used so that if no arguments are provided, default credentials are used.


Callback Functions

A callback function is a function passed as an argument to another function to be executed later.

A callback is a function provided to another function to be called at the appropriate time.

Real-life Example

In my automation scripts, I use callbacks to handle actions that should run after a step completes.

Real-time Testing Example

In Playwright, a callback can be used within async operations to run logic after an action completes.


Why Arrow Functions Cannot Be Constructors

Arrow functions cannot be used as constructors because:

  • They do not have their own this, so new cannot bind a new object to them.

  • They do not have the internal [[Construct]] method, which is required for a function to be used with the new keyword.


Parameterized Functions

Functions can take arguments (parameters), such as username and password, enabling dynamic test data usage.

The Benefits

  • Reusability — write code once and reuse it in multiple tests.

  • Maintainability — changes in the function update all tests that use it.

  • Readability — keeps test scripts clean and easy to understand.

  • Parameterization — functions can take arguments like username and password, allowing dynamic test data.

Quick Summary

Parameterized functions enable dynamic test data, improving reusability, maintainability, and readability across an automation suite.


Try It: Hoisting, this and a Missing Return

Save as funcs.mjs and run node funcs.mjs. We ran it on Node 22:

// 1. Hoisting: declarations work before their line, expressions don't
console.log('declaration before its line:', add(2, 3));
try { multiply(2, 3); } catch (e) { console.log('expression before its line:', e.constructor.name, '-', e.message); }
function add(a, b) { return a + b; }
const multiply = function (a, b) { return a * b; };

// 2. `this`: a regular method sees the object; an arrow function doesn't
const loginPage = {
  url: '/login',
  regular() { return this.url; },
  arrow: () => (typeof this === 'undefined' ? 'undefined (no own this)' : this.url),
  delayedArrow() { return new Promise(r => setTimeout(() => r(this.url), 0)); },   // arrow inside a method: inherits it
};
console.log('regular method this.url :', loginPage.regular());
console.log('arrow method this.url   :', loginPage.arrow());
console.log('arrow inside a method   :', await loginPage.delayedArrow());

// 3. Default parameters
const login = (user = 'standard_user', timeout = 5000) => `${user} / ${timeout}ms`;
console.log('defaults:', login(), '| override:', login('admin'));

// 4. A classic async helper bug: forgetting to return the promise
const slowStep = () => new Promise(r => setTimeout(() => r('done'), 20));
const forgotReturn = () => { slowStep(); };            // braces, no return
const withReturn = () => slowStep();                   // concise body returns it
console.log('forgot return →', await forgotReturn(), '| with return →', await withReturn());

Output:

declaration before its line: 5
expression before its line: ReferenceError - Cannot access 'multiply' before initialization
regular method this.url : /login
arrow method this.url   : undefined (no own this)
arrow inside a method   : /login
defaults: standard_user / 5000ms | override: admin / 5000ms
forgot return → undefined | with return → done
  • Hoisting: the declaration worked before its line; the const expression threw.
  • this: an arrow function used as a method has no this of its own, so a page object method written as an arrow can't read this.url. Use regular (shorthand) methods for page objects — or classes — and arrows inside them for callbacks, where inheriting this is exactly what you want.
  • The missing return: an arrow function with braces { … } doesn't return anything unless you write return. await forgotReturn() resolved immediately to undefined — in a test helper, that means the next step runs before the previous one finished. Write async helpers with await inside, or return the promise.

See how the same helper functions look in Selenium, Cypress and Playwright in the Selenium ⇄ Playwright ⇄ Cypress Translator. Next: Promises & async/await.

From Real Projects

My automation background is Java with Selenium and TestNG. JavaScript comes up more and more for testers because tools like Playwright and Cypress are built on it — and most concepts on this page have a direct Java equivalent. Learning it alongside Java makes switching between frameworks much easier. Arrow functions don't have their own this — keep that in mind for page objects.


📚 Official documentation: MDN: JavaScript Guide · dev.java: Learn Java (Oracle)

FAQs

What is a function in JavaScript?

A block of reusable code that performs a specific task, letting you write logic once and reuse it to keep scripts modular and maintainable.

What are the main types of functions?

  • Function declarations (named, hoisted)

  • Function expressions (assigned to variables, not hoisted)

  • Arrow functions (concise ES6 syntax with inherited this)

What is the difference between a function declaration and a function expression?

A declaration is hoisted and callable before definition; an expression is not hoisted and must be defined before it's called.

What is the difference between a normal function and an arrow function?

A normal function has its own this and can be a constructor; an arrow function inherits this from the parent scope and cannot be a constructor.

Why are arrow functions used in automation?

Their concise syntax and inheritance make async test steps cleaner and less error-prone.

What is a callback function?

A function passed as an argument to another function, to be executed later—useful for running logic after an action completes.

Why can't arrow functions be used as constructors?

Because they don't have their own this and lack the internal [[Construct]] method that new requires.