Part 1 · Lesson 7 · 18 min read
Functions
Package a piece of logic, give it inputs and get a result back. The difference between return and console.log, and why every interview answer is a function.
On this page
- A function is a machine
- Defining and calling
- Parameters and arguments
- return vs console.log
- Traced: jumping into a function and back
- Variables inside stay inside
- return ends the function early
- Arrow functions
- Default parameter values
- Passing values vs passing references
- Why every interview answer is a function
- Practice questions (10)
- Make it stick
A function is a machine
Think of a juicer. Oranges go in and juice comes out. You do not build a new juicer every morning. You build it once and use it whenever you want.
A function is a named block of code that does one job. You give it inputs. It does its work. It gives back a result.
So far you have written the same logic again and again (is it even? sum of digits? is it prime?). A function lets you write the logic once and give it a name. Then you can use it from anywhere, even from inside other functions.
Defining and calling
function greet(name) { // define: "here is how to greet someone" return "Hello, " + name + "!";}console.log(greet("Asha")); // Hello, Asha!console.log(greet("Rahul")); // Hello, Rahul!- Defining a function (
function greet(name) { … }) means writing it. This builds the machine. Nothing runs yet. - Calling a function (
greet("Asha")) means running it. The round brackets make it run. - You can call it as many times as you like, with different inputs.
Parameters and arguments
function add(a, b) { // a and b are PARAMETERS: names for the inputs return a + b;}console.log(add(2, 3)); // 5 2 and 3 are ARGUMENTS: the actual valuesconsole.log(add(10, -4)); // 6console.log(add(7)); // NaN b was not given, so it is undefined; 7 + undefined is NaNA parameter is a name for an input. You write it in the definition, inside the round brackets. An argument is the real value you pass when you call the function. The one-line difference: a parameter is the empty box, and an argument is what you put in the box.
JavaScript matches them by position. The first argument goes to the first parameter, and so on. If you pass too few arguments, the missing parameter becomes undefined. undefined is a special value that means “there is no value here”. If you do maths with undefined, you get NaN. NaN means “Not a Number”. It is the result of a maths operation that has no valid number answer.
return vs console.log
This is the most important difference in this lesson. Mixing them up is one of the most common mistakes in interviews.
console.log(v)is a built-in function that prints a value on the screen so a person can read it.return vis a statement that sends the value back to the code that called the function. It also ends the function.
An everyday comparison: console.log is like reading the answer out loud in a room. return is like handing the answer on paper to the person who asked. Only the paper can be used for the next step.
function doubleLog(n) { console.log(n * 2); // shows the value on screen... and that's all}function doubleReturn(n) { return n * 2; // hands the value BACK to whoever called it}const x = doubleLog(5); // prints 10 — but x gets nothingconst y = doubleReturn(5); // prints nothing — y gets 10console.log(x); // undefinedconsole.log(y); // 10console.log(doubleReturn(5) + 1); // 11 — a returned value can be used in more maths/* Output:10undefined1011*/console.log(v) | return v | |
|---|---|---|
| Who sees the value | A human, on screen | The code that called the function |
| Can it be used afterwards? | No — it is gone | Yes — store it, compare it, add to it |
| What the function gives back | undefined | v |
| In an interview / LeetCode | Wrong answer (the judge cannot see printed text) | Correct |
Traced: jumping into a function and back
Defining a function does NOT run it. JavaScript just remembers the recipe and skips to line 6.
▶function square(x) {2 const result = x * x;3 return result;4}5 6const a = square(3);7const b = square(4);8console.log(a + b);JavaScript keeps track of this “jump in, jump back” with the call stack. The call stack is a list of the function calls that are running right now. The newest call is on top. When a call finishes, it is removed and JavaScript goes back to the one below. The JavaScript series explains it in depth.
For DSA you need to know one fact now: every call gets its own fresh copy of the parameters and inner variables.
Variables inside stay inside
function makeTotal() { let total = 100; // exists only while makeTotal runs return total;}console.log(makeTotal()); // 100console.log(total); // ReferenceError: total is not definedScope means the part of the code where a variable can be used. A variable made inside a function exists only while that call runs. Code outside the function cannot see it.
This is useful. Your i or sum inside one function can never clash with an i or sum in another place. To get a value out, return it.
return ends the function early
return does two things. It gives a value back, and it stops the function right there, even in the middle of a loop. Any code after it in that call does not run. This makes “search until found” very clean:
function firstDivisor(n) { for (let d = 2; d < n; d++) { if (n % d === 0) { return d; // found one: leave the function (and the loop) immediately } } return -1; // reached only if the loop never returned}console.log(firstDivisor(15)); // 3console.log(firstDivisor(13)); // -1“Return as soon as you know the answer. Return a default value at the very end.” Here -1 means “not found”. A convention is a habit that most programmers follow. You will see this one in many search problems (Lesson 8 and Lesson 28).
Arrow functions
An arrow function is a shorter way to write a function. It uses => (an arrow). If the body is one expression, the value is returned automatically, so you do not write return. You will see this style a lot in modern code. For everything in this series, it works the same as a normal function:
// these three are the same functionfunction square1(n) { return n * n; }const square2 = function (n) { return n * n; };const square3 = (n) => n * n; // arrow function: short, returns automaticallyconsole.log(square1(5), square2(5), square3(5)); // 25 25 25Default parameter values
A default parameter value is a backup value written with = in the parameter list. JavaScript uses it when the caller does not pass that argument. This avoids the undefined and NaN problems shown above.
function greet(name = "friend") { // "friend" is used when no name is given return "Hello, " + name;}console.log(greet("Asha")); // Hello, Ashaconsole.log(greet()); // Hello, friendPassing values vs passing references
What happens when a function changes a parameter? It depends on the type of value. A reference is the address of where a value is stored, like a house address written on paper. If you give a friend your address (not a copy of your house), they visit the same house.
function addOne(n) { n = n + 1; // changes the function's own copy only}let x = 5;addOne(x);console.log(x); // 5 numbers are passed as a copyfunction addItem(list) { list.push(99); // changes the array the caller passed in}const nums = [1, 2];addItem(nums);console.log(nums); // [ 1, 2, 99 ] arrays and objects are shared- Numbers, strings and booleans (true or false) are passed as a copy. Changing the parameter inside the function never changes the caller's variable.
- Arrays and objects are passed as a reference. The function gets the address of the same array, not a copy. If it changes the contents (for example with
push), the caller sees the change. - One limit: if the function gives the parameter a brand-new array (
list = []), that only changes the function's own variable. The caller's array stays the same.
Many problems say “modify the array in place”. In place means you change the original array and do not make a new one (for example LeetCode 283, Move Zeroes). This works because arrays are passed by reference. When a problem says “do not modify the input”, copy the array first (Lesson 8 shows how).
Why every interview answer is a function
LeetCode is a website with coding practice problems. On LeetCode and in most interviews you are given an empty function to fill in, like this:
/** * @param {number[]} nums * @return {number} */var maxValue = function (nums) { // your code here — RETURN the answer, don't print it};The judge is the program that checks your answer. It calls your function with test inputs. Then it compares what you return with the expected answer. So from now on, every practice answer in this series is a function that returns its result. After it come a few console.log calls to test it. This is how you would check your own work before you submit.
Practice questions
Write square(n) that returns n × n.
Hint 1
Hint 2
return n * n; — do not print inside the function.Show all 3 solutions with explanations
Define square(n) and return n * n.
function square(n) { return n * n;}console.log(square(5)); // 25console.log(square(-3)); // 9The smallest useful function. It returns the value so the caller can use it — for example square(3) + square(4).
An arrow function is a short way to write a function. The value after => is returned automatically.
const square = (n) => n * n;console.log(square(5)); // 25Same behaviour, shorter to write. It is common in modern JavaScript.
The ** operator raises a number to a power. n ** 2 means n times n.
const square = (n) => n ** 2;console.log(square(5)); // 25console.log(Math.pow(5, 2)); // 25** and Math.pow (a built-in function that does the same job) work for any power, not only 2.
All three are correct. Use the style your team uses. In interviews, Approach 1 is the clearest.
Write isEven(n) that returns true or false.
Hint 1
true or false, not print.Hint 2
n % 2 === 0 is already true or false. You can return it directly.Show all 2 solutions with explanations
A comparison such as n % 2 === 0 already gives true or false (a boolean). So return it directly. The % operator gives the remainder after division.
function isEven(n) { return n % 2 === 0;}console.log(isEven(4)); // trueconsole.log(isEven(7)); // falseInterviewers like this form. You do not need an if when the answer is already true or false.
Check the condition, then return true or false explicitly.
function isEven(n) { if (n % 2 === 0) { return true; } else { return false; }}console.log(isEven(4)); // trueThis is correct, but longer. If you ever write if (condition) return true; else return false;, you can replace it with return condition;.
Use Approach 1.
Write max2(a, b), then use it to write max3(a, b, c).
Hint 1
max2 first.Hint 2
max2(max2(a, b), c).Show all 2 solutions with explanations
Solve the small problem once. Then reuse that function (a function used by another function is called a helper function).
function max2(a, b) { return a > b ? a : b; // "? :" means: if a > b then a, else b}function max3(a, b, c) { return max2(max2(a, b), c);}console.log(max2(4, 9)); // 9console.log(max3(4, 9, 2)); // 9console.log(max3(7, 7, 3)); // 7Functions can call other functions. max3 has no new logic, so there is little that can go wrong.
The built-in function Math.max accepts any number of arguments and returns the largest.
const max3 = (a, b, c) => Math.max(a, b, c);console.log(max3(4, 9, 2)); // 9This is the shortest way. Mention it, but expect the interviewer to ask you to write it yourself.
Approach 1 shows the most important skill in this lesson: breaking a problem into small functions that you can reuse.
Convert Celsius to Fahrenheit: F = C × 9 / 5 + 32.
Hint 1
Show all 2 solutions with explanations
Return the formula.
function toFahrenheit(c) { return (c * 9) / 5 + 32;}console.log(toFahrenheit(100)); // 212console.log(toFahrenheit(0)); // 32console.log(toFahrenheit(-40)); // -40Test with values you already know (boiling point, freezing point, −40). This is a quick way to catch a mistake in a formula.
A one-line formula fits an arrow function well. The reverse formula is C = (F − 32) × 5 / 9.
const toFahrenheit = (c) => (c * 9) / 5 + 32;const toCelsius = (f) => ((f - 32) * 5) / 9;console.log(toFahrenheit(100)); // 212console.log(toCelsius(212)); // 100console.log(toCelsius(toFahrenheit(37))); // 37If you convert there and back, you should get the original value. This is another useful self-test.
How many prime numbers are there from 1 to N?
Hint 1
isPrime(n) first (Lesson 4, question 10).Hint 2
isPrime returns true.Show all 2 solutions with explanations
Split the problem in two. isPrime checks one number. countPrimes does the counting.
function isPrime(n) { if (n < 2) return false; for (let d = 2; d * d <= n; d++) { if (n % d === 0) return false; // early return } return true;}function countPrimes(N) { let count = 0; for (let i = 2; i <= N; i++) { if (isPrime(i)) count++; } return count;}console.log(countPrimes(10)); // 4console.log(countPrimes(20)); // 8An early return false replaces the flag variable and the break from Lesson 4. Splitting a problem into helper functions shows an interviewer that you solve problems well.
The sieve is an old method that finds all primes up to N by crossing out numbers. A multiple of p is a number you get by multiplying p by 1, 2, 3 and so on.
- Mark every number from 2 to N as “possibly prime”.
- For each number still marked, cross out all its multiples.
- Whatever is still marked at the end is prime.
function countPrimes(N) { const isPrime = new Array(N + 1).fill(true); isPrime[0] = isPrime[1] = false; for (let p = 2; p * p <= N; p++) { if (!isPrime[p]) continue; for (let m = p * p; m <= N; m += p) { isPrime[m] = false; // m is a multiple of p } } let count = 0; for (let i = 2; i <= N; i++) if (isPrime[i]) count++; return count;}console.log(countPrimes(10)); // 4console.log(countPrimes(20)); // 8This uses an array (Lesson 8) of true/false values. It does not test each number alone. It crosses out multiples in bulk, which is much faster for large N. Lesson 13 explains it in detail. (LeetCode 204 counts primes less than N. For that problem, change <= N to < N.)
Approach 1 is fine for small N and is easy to write. For large N (LeetCode 204 allows N up to 5 million), the sieve is the expected answer.
Compute baseexp for a non-negative whole exponent, without using ** or Math.pow.
Hint 1
exp) says how many times. Start a product at 1 and multiply it by the base, exp times.Show all 2 solutions with explanations
Keep a running product in a variable (an accumulator is a variable that collects a result step by step). Multiply it by base once per loop round.
function power(base, exp) { let result = 1; for (let i = 1; i <= exp; i++) { result *= base; } return result;}console.log(power(2, 10)); // 1024console.log(power(5, 0)); // 1When exp = 0, the loop never runs and the answer stays 1. That is correct.
Fast power uses this fact: 2¹⁰ = (2⁵)². Each step squares the base and halves the exponent. When the exponent is odd, one extra factor is multiplied into the result first.
function power(base, exp) { let result = 1; while (exp > 0) { if (exp % 2 === 1) result *= base; // odd: use one factor now base *= base; // square the base exp = Math.floor(exp / 2); // halve the exponent } return result;}console.log(power(2, 10)); // 1024console.log(power(3, 5)); // 243For exp = 1,000,000, the simple loop runs a million times. This version runs about 20 times. This is the answer to LeetCode 50 (Pow(x, n)), covered in Lesson 13.
Write Approach 1 first. If the exponent can be large, mention Approach 2 or write it.
Write lcm(a, b): the smallest number divisible by both a and b.
Hint 1
Hint 2
gcd in Lesson 5. So lcm = a × b / gcd(a, b).Show all 2 solutions with explanations
Write gcd as a helper function. Then lcm = a / gcd × b.
function gcd(a, b) { while (b !== 0) { const r = a % b; a = b; b = r; } return a;}function lcm(a, b) { return (a / gcd(a, b)) * b; // divide first to keep numbers small}console.log(lcm(4, 6)); // 12console.log(lcm(5, 7)); // 35For 4 and 6, the gcd is 2, so lcm = 4 / 2 × 6 = 12. Dividing first keeps the middle value small, so it does not become too big.
List the multiples of the larger number one by one. Stop at the first one that the smaller number also divides evenly.
function lcm(a, b) { const big = Math.max(a, b), small = Math.min(a, b); let m = big; while (m % small !== 0) { m += big; } return m;}console.log(lcm(4, 6)); // 12console.log(lcm(5, 7)); // 35This is easy to understand. But for large numbers with no common factors, it can take many loop rounds.
Use Approach 1. Reusing gcd is faster, and it is a good example of helper functions.
Predict both lines of output:
function addAndShow(a, b) { console.log(a + b);}const result = addAndShow(3, 5);console.log(result);8 undefined
Hint 1
return?Show all 2 solutions with explanations
Follow the program line by line. Remember what the function returns.
function addAndShow(a, b) { console.log(a + b);}const result = addAndShow(3, 5);console.log(result);/* Output:8undefined*/Sometimes your answer “prints the right thing” but the judge says “wrong answer”. This is almost always the reason: the function printed instead of returning.
Return the value instead of printing it. Let the caller print.
function add(a, b) { return a + b;}const result = add(3, 5);console.log(result); // 8Now the value goes back to the caller. The caller can print it, store it or use it in more calculations.
The digital root of n: add up the digits of n, and repeat on the result until only one digit remains.
Hint 1
sumDigits in Lesson 5. It adds up the digits of a number. Use it as a helper.Hint 2
sumDigits(n) while n has more than one digit (n ≥ 10).Show all 2 solutions with explanations
Use a loop that calls a helper function. The helper has its own loop inside. (Math.floor rounds a number down to a whole number, so Math.floor(n / 10) removes the last digit.)
function sumDigits(n) { let sum = 0; while (n > 0) { sum += n % 10; n = Math.floor(n / 10); } return sum;}function digitalRoot(n) { while (n >= 10) { n = sumDigits(n); } return n;}console.log(digitalRoot(9875)); // 2console.log(digitalRoot(7)); // 7| n | n >= 10? | sumDigits(n) |
|---|---|---|
| 9875 | true | 29 |
| 29 | true | 11 |
| 11 | true | 2 |
| 2 | false | (return 2) |
A number and the sum of its digits leave the same remainder when divided by 9. Because of this, the digital root of a positive number n is 1 + (n − 1) % 9.
function digitalRoot(n) { if (n === 0) return 0; return 1 + ((n - 1) % 9);}console.log(digitalRoot(9875)); // 2console.log(digitalRoot(9)); // 9There is no loop at all. This is the follow-up answer for LeetCode 258 (Add Digits), which asks for a solution without loops.
You should be able to write Approach 1. Approach 2 is the clever follow-up. Mention it only if you can explain why it works.
Return true if n is a power of two. (LeetCode 231.)
Hint 1
Hint 2
Show all 3 solutions with explanations
Keep halving while the number is even. If it was a power of two, you end at exactly 1.
function isPowerOfTwo(n) { if (n <= 0) return false; while (n % 2 === 0) { n = n / 2; } return n === 1;}console.log(isPowerOfTwo(16)); // trueconsole.log(isPowerOfTwo(12)); // falseconsole.log(isPowerOfTwo(1)); // true12 becomes 6, then 3. The number 3 is odd and is not 1, so the answer is false.
Make the numbers 1, 2, 4, 8, … until the value reaches or passes n. Then compare it with n.
function isPowerOfTwo(n) { let p = 1; while (p < n) { p *= 2; } return p === n;}console.log(isPowerOfTwo(16)); // trueconsole.log(isPowerOfTwo(12)); // falseThis reuses the idea “smallest power of two that is at least N” from Lesson 5.
Binary is the number system that uses only 0 and 1 (bits). In binary, a power of two has exactly one 1 (8 is 1000). Subtracting 1 changes it to 0111. The two numbers share no 1 bit, so n & (n - 1) is 0.
function isPowerOfTwo(n) { return n > 0 && (n & (n - 1)) === 0;}console.log(isPowerOfTwo(16)); // trueconsole.log(isPowerOfTwo(12)); // falseThe & operator (bitwise AND) compares two numbers bit by bit. It keeps a 1 only where both numbers have a 1. This is the well-known one-line answer for LeetCode 231. Lesson 58 explains bit operations fully.
Approach 1 is easy to explain and correct. Mention Approach 3 as the follow-up that takes constant time (the same short time for any n).
Make it stick
- Explain the difference between
returnandconsole.login one sentence each. - Write
isPrimewith an early return, from memory. - What does a function return if it has no
returnstatement?
Revisit schedule: solve two of the questions again tomorrow, again in 3 days, and once more in a week — without looking at the answers. Each time it will feel easier. That is the memory forming.
Next lesson: arrays (one variable that stores many values) and the first real interview questions.
Practise on LeetCode
Real interview problems that use exactly what this lesson teaches. Solve them in JavaScript after the practice questions above.
Published by Jainik · Last updated · How these lessons are made
Keep learning
Related series