TL;DR: A function is a named, reusable block of instructions. You define it once and run it whenever you need it. JavaScript has three syntaxes: function declaration, function expression, and arrow function (=>). AI uses all three — arrow functions for callbacks and React, regular functions for named top-level logic. Forgetting return is the #1 function bug.
Why AI Coders Need to Know This
When you ask an AI coding tool to "add a button that calculates shipping cost," it doesn't write that calculation inline inside the HTML. It wraps the logic in a function. When you ask it to "fetch user data from an API," it creates a function for that. When you ask for form validation, dark mode toggle, price formatting, or error handling — functions.
Functions are the unit of reusable work in JavaScript. Every meaningful action in an app lives inside one. If you build with AI tools regularly, you are reading function-dense code every single day, whether you realize it or not. Understanding functions means you can:
- Predict what a block of AI-generated code will do before you run it
- Spot missing
returnstatements before they silently break your feature - Understand why AI keeps writing those
() =>arrow functions everywhere - Debug errors like "is not a function" or "cannot read property of undefined"
- Ask AI for better-structured code instead of one massive function that does everything
According to the JavaScript State of JavaScript 2025 survey, arrow functions and async/await are now the dominant patterns in modern codebases, used by over 94% of JavaScript developers. AI tools have fully adopted these patterns. Knowing how to read them is table stakes for anyone building real software with AI.
Real Scenario
You're building an e-commerce product page in Cursor. You type:
Prompt You Might Use
Add a "Calculate Shipping" button to the product page.
When clicked, it should:
1. Read the weight from an input field (in pounds)
2. Read the destination from a dropdown (domestic or international)
3. Calculate the shipping cost: $5 base + $1.50 per pound for domestic,
$15 base + $3.00 per pound for international
4. Display the result in a div with id "shipping-result"
5. Show an error if the weight field is empty or not a number
Add clear comments to every function.
This prompt is specific. It tells the AI the inputs, the logic, the output, and the error case. When you're this precise, AI generates clean, well-structured code with properly named functions. When you're vague ("make a shipping button"), you get one giant, hard-to-debug function that does everything in one place.
What AI Generated
Here's a realistic example of what Cursor might generate for the shipping calculator, with three functions working together:
/**
* Validates the weight input and returns a number,
* or null if the value is missing or not a valid number.
*/
function parseWeight(inputValue) {
const weight = parseFloat(inputValue);
if (isNaN(weight) || weight <= 0) {
return null; // signal that validation failed
}
return weight;
}
/**
* Calculates shipping cost based on weight and destination.
* Returns the total cost as a number (in dollars).
*/
function calculateShipping(weightLbs, destination) {
if (destination === "international") {
return 15 + weightLbs * 3.0;
}
return 5 + weightLbs * 1.5; // default: domestic
}
/**
* Handles the button click: reads inputs, validates, calculates,
* and updates the DOM with the result or an error message.
*/
function handleShippingCalculate() {
const weightInput = document.getElementById("product-weight");
const destinationSelect = document.getElementById("destination");
const resultDiv = document.getElementById("shipping-result");
const weight = parseWeight(weightInput.value);
if (weight === null) {
resultDiv.textContent = "Please enter a valid weight in pounds.";
resultDiv.style.color = "red";
return; // stop here — don't try to calculate with bad data
}
const destination = destinationSelect.value;
const cost = calculateShipping(weight, destination);
resultDiv.textContent = `Shipping cost: $${cost.toFixed(2)}`;
resultDiv.style.color = "green";
}
// Attach the handler to the button
document.getElementById("calculate-shipping-btn")
.addEventListener("click", handleShippingCalculate);
Notice what AI did: it split the work into three functions. One validates, one calculates, one handles the click. This is good function design — each piece does one job.
Understanding Each Part
The Three Ways AI Writes Functions
JavaScript has three syntaxes for creating functions. AI uses all of them. Here's what each looks like and when you'll see it:
1. Function Declaration
function calculateShipping(weightLbs, destination) {
return 5 + weightLbs * 1.5;
}
The classic form. Starts with the keyword function, has a name, takes parameters in parentheses, does work inside curly braces, and uses return to send a value back. AI uses this for top-level named functions — the "main workers" of a script. These are hoisted, meaning you can call them before they appear in the file.
2. Function Expression
const calculateShipping = function(weightLbs, destination) {
return 5 + weightLbs * 1.5;
};
Same logic, but stored in a variable. Not hoisted — you must define it before you call it. AI uses this less often in modern code, but you'll see it in older codebases and in situations where a function needs to be conditionally assigned.
3. Arrow Function (=>)
const calculateShipping = (weightLbs, destination) => {
return 5 + weightLbs * 1.5;
};
// Short form — single expression, implicit return:
const double = num => num * 2;
The modern, concise syntax. Uses => instead of the function keyword. When the function body is a single expression, you can drop the curly braces and the return keyword — the result is returned automatically. AI uses arrow functions constantly for callbacks, React components, and array methods.
Parameters and Return Values
A function can receive input through parameters — variables listed in the parentheses when you define the function. When you call the function, you pass in arguments — the actual values.
// 'weightLbs' and 'destination' are parameters (placeholders)
function calculateShipping(weightLbs, destination) {
return 5 + weightLbs * 1.5;
}
// 2.5 and "domestic" are arguments (real values)
const cost = calculateShipping(2.5, "domestic");
// cost is now 8.75
The return statement sends a value back to wherever the function was called. If there's no return, the function automatically returns undefined. This is the single most common function bug in AI-generated code.
⚠️ The Missing Return Bug If your function is supposed to produce a value but you're getting undefined, the function is probably missing a return statement. The code ran, the calculation happened — but the result went nowhere. Always check that functions which compute something end with return.
// ❌ Bug: calculates correctly but returns undefined
function calculateShipping(weight, destination) {
const cost = 5 + weight * 1.5;
// forgot to return!
}
const price = calculateShipping(3, "domestic");
console.log(price); // undefined — not 9.5
// ✅ Fixed:
function calculateShipping(weight, destination) {
const cost = 5 + weight * 1.5;
return cost; // now the caller gets the value
}
Callback Functions — Why AI Puts Functions Inside Functions
A callback is a function you pass to another function as an argument. The receiving function will call your callback at the right moment. This sounds abstract, but you see it every time AI writes array methods or event listeners.
// Array of product weights
const weights = [1.5, 3.2, 0.8, 4.1];
// .map() takes a callback function — AI uses arrow functions here constantly
const shippingCosts = weights.map(weight => 5 + weight * 1.5);
// shippingCosts: [7.25, 9.8, 6.2, 11.15]
// .filter() — keep only packages under 2 lbs
const lightPackages = weights.filter(weight => weight < 2);
// lightPackages: [1.5, 0.8]
// Event listener — the arrow function is a callback
document.getElementById("calc-btn")
.addEventListener("click", () => {
handleShippingCalculate();
});
In each case, you're not calling the arrow function yourself — you're handing it to .map(), .filter(), or addEventListener() to call when the time is right. That's the callback pattern. AI uses it constantly because JavaScript is event-driven and array-processing-heavy, and callbacks are the cleanest way to express both.
When AI Uses Arrow vs Regular Functions
This trips up a lot of vibe coders. Here's the practical guide:
Arrow Functions (=>)
- Callbacks passed to
.map(),.filter(),.reduce() - Event listeners on elements
- React functional components
- Promise chains (
.then(),.catch()) - Short utility functions stored in variables
Regular Functions
- Named top-level functions in a script
- Object methods that use
this - Constructor functions (class methods)
- Generator functions
- Any function that needs to be called before it's defined (hoisting)
The key technical difference: arrow functions don't have their own this. They inherit this from the surrounding scope. Regular functions get their own this based on how they're called. For most beginner code, this only matters when working with object methods or class instances.
What AI Gets Wrong
1. Functions That Do Too Many Things
Under time pressure (or vague prompts), AI generates "god functions" — single functions that fetch data, parse it, validate it, update the DOM, handle errors, and maybe also log analytics. These are nearly impossible to debug because when something breaks, you don't know which step failed.
💡 Fix It Add this to your prompt: "Break this into small, single-purpose functions. Each function should do exactly one job and have a name that describes that job." Then ask AI to show you how the functions connect.
2. The Missing return Statement
Already covered, but worth repeating: AI forgets return more often than any other single error. It's especially common in refactoring — when AI rewrites a function, it sometimes drops the return. The symptom is always undefined where you expected a value.
3. Using Arrow Functions as Object Methods
Arrow functions don't have their own this. If AI writes an object method as an arrow function and that method needs to reference the object with this, it will fail silently or produce wrong results.
// ❌ Arrow function as object method — 'this' is wrong
const calculator = {
baseRate: 5,
calculate: (weight) => {
return this.baseRate + weight * 1.5;
// 'this' here is the outer scope, not 'calculator'
// this.baseRate is undefined — result: NaN
}
};
// ✅ Regular function method — 'this' is correct
const calculator = {
baseRate: 5,
calculate: function(weight) {
return this.baseRate + weight * 1.5;
// 'this' refers to the calculator object — works!
}
};
4. Not Handling the Case Where a Function Returns undefined
When you chain function calls — processData(fetchData()) — and fetchData returns undefined, processData receives undefined as its argument. AI often doesn't add guards for this, resulting in crashes deep in the call chain that are hard to trace back to the source.
5. Async Functions Without await
AI-generated code that calls an API uses async functions and await. A common mistake is writing an async function but forgetting await inside it. The function runs but returns a Promise object instead of the resolved value — and then code that expects a number or string gets an object instead.
// ❌ Forgot await — returns a Promise, not the data
async function getTemperature(city) {
const response = fetch(`/api/weather?city=${city}`); // missing await!
const data = response.json(); // also missing await — crashes here
return data.temperature;
}
// ✅ Correct async/await usage
async function getTemperature(city) {
const response = await fetch(`/api/weather?city=${city}`);
const data = await response.json();
return data.temperature;
}
How to Debug with AI
Reading "is not a function" Errors
The most jarring function error reads like:
TypeError: handleShippingCalculate is not a function
at HTMLButtonElement.onclick (index.html:45:5)
This almost always means one of three things:
- The function was never defined (AI forgot to include it, or it's in a different file that wasn't loaded)
- The function name is misspelled in the call (
handleShippingCalcinstead ofhandleShippingCalculate) - The variable that was supposed to hold the function holds something else instead (maybe it was overwritten)
Open the browser console, type the function name exactly as written, and press Enter. If the console shows undefined, the function doesn't exist in scope. If it shows a function definition, the name is right — look for a call-site typo.
Tracing undefined Back to a Missing return
When a calculation gives you undefined, add console.log calls at each step:
function handleShippingCalculate() {
const weight = parseWeight(weightInput.value);
console.log("weight:", weight); // check: is this a number or null?
const cost = calculateShipping(weight, destination);
console.log("cost:", cost); // check: is this a number or undefined?
// if cost is undefined, calculateShipping is missing a return
}
This technique — adding console.log at each step — is the fastest way to find which function is returning the wrong thing.
Cursor-Specific Tips
In Cursor, select a function that's producing wrong output and press Cmd+K. Type: "This function returns undefined instead of a number. Find the missing return statement and add it." Cursor will scan the function body and insert the correct return in the right place.
For this binding issues, select the problematic method and type in the chat: "This arrow function is being used as an object method and needs access to 'this'. Convert it to a regular function." Cursor handles this conversion correctly in almost all cases.
Windsurf Tips
In Windsurf, open the Cascade AI panel and describe the behavior: "My calculateShipping function returns undefined even though I can see the calculation in the function body. What's wrong?" Windsurf's context-aware analysis will spot missing return statements and async/await issues without you needing to identify them yourself.
Claude Code Tips
Paste the function and the error into Claude Code with context: "This function is supposed to return a shipping cost but returns undefined. The console shows the calculation runs correctly but the result never reaches the caller. What's the issue?" Claude Code is particularly good at explaining the function call stack when you give it the full error trace.
Functions in Real Projects
As your AI-built projects grow, functions appear in more specialized forms. Here's a quick map of what you'll encounter:
- Async functions — Functions that use
awaitto pause for an API response or database query. Always paired withasyncin the function definition and wrapped intry/catchfor error handling. - React components — In React, every component is a function. It takes a
propsobject as its parameter and returns JSX (which compiles to HTML). AI writes these as arrow functions almost universally:const ProductCard = ({ name, price }) => { ... } - Event handler functions — Functions attached to UI events like clicks, form submissions, and keyboard input. AI names these with the pattern
handle+ event:handleSubmit,handleClick,handleKeyDown. - Utility functions — Small, pure functions that transform data:
formatCurrency(),truncateText(),calculateTax(). AI often puts these in a separateutils.jsfile. - Higher-order functions — Functions that take or return other functions.
.map(),.filter(), and.reduce()are all higher-order functions. You'll see AI use these everywhere for data transformation.
What to Learn Next
Functions make sense of variables — they're the instructions that act on stored data. Once you're comfortable with both, the next step is understanding how your JavaScript communicates with the outside world through APIs.
FAQ
A function declaration uses the function keyword and is hoisted — you can call it before it appears in the file. An arrow function (=>) is a more concise syntax that does not have its own this binding. AI uses arrow functions for array methods like .map() and .filter(), for React components, and for callbacks. It uses function declarations for top-level named functions that need to be called anywhere in the file.
If a function has no return statement, it automatically returns undefined. This is a common source of bugs in AI-generated code: the AI writes a function that calculates a value but forgets to return it. The calling code then receives undefined instead of the expected result. Always check that functions which are supposed to produce a value end with a return statement.
A callback function is a function passed as an argument to another function, to be called later. For example, when you write [1, 2, 3].map(num => num * 2), the arrow function (num => num * 2) is a callback — you hand it to .map() and .map() calls it for each item in the array. AI uses callbacks constantly for array methods, event listeners, and asynchronous operations like fetch().
Regular functions create their own this context based on how they are called. Arrow functions inherit this from the surrounding scope where they were defined. This means arrow functions are unreliable as object methods when you need to reference the object itself with this, but perfect for callbacks inside class methods, since they inherit the class instance's this. AI sometimes gets this wrong, creating arrow functions as object methods when a regular function is needed.
Ideally, one. The Single Responsibility Principle says a function should do one thing and do it well. AI often violates this by generating functions that fetch data, process it, update the DOM, and handle errors all in the same block. When AI generates a large function, ask it to break it into smaller, named functions: one to fetch, one to process, one to display. This makes debugging dramatically easier.