Working with Function Flashcards
What is a function?
In JavaScript, a function is a reusable block of code designed to perform a specific task or calculate a value. Functions are defined using the function
keyword, a function expression, or as arrow functions (ES6), and they can take parameters, execute logic, and return a result.
Key Characteristics:
- Definition: Functions are defined with a name (optional for anonymous functions), parameters (optional), and a body containing the code.
- Invocation: Functions are executed by calling them with parentheses ()
, optionally passing arguments.
- Return: Functions can return a value using the return
statement; if no return
is specified, they return undefined
by default.
- Scope: Functions create their own scope, meaning variables declared inside are local unless explicitly made global.
- First-class citizens: Functions can be assigned to variables, passed as arguments, or returned from other functions.
Syntax Examples:
-
Function Declaration:
javascript function greet(name) { return `Hello, ${name}!`; } console.log(greet("Alice")); // Output: Hello, Alice!
-
Function Expression (named or anonymous):
javascript const add = function (a, b) { return a + b; }; console.log(add(2, 3)); // Output: 5
-
Arrow Function (concise, ES6):
javascript const multiply = (x, y) => x * y; console.log(multiply(4, 5)); // Output: 20
Key Features:
- Parameters: Functions can accept parameters (default values are supported in ES6, e.g., function log(message = 'Hi') {}
).
- Hoisting: Function declarations are hoisted, meaning they can be called before their definition. Function expressions are not.
- Closures: Functions can retain access to variables in their outer scope even after the outer function has finished executing.
- IIFE: Immediately Invoked Function Expressions run as soon as they’re defined, e.g., (function() { console.log('Run!'); })();
.
Use Cases:
- Modularizing code for reusability.
- Handling events (e.g., button.onclick = function() {}
).
- Creating callbacks or higher-order functions (e.g., array.map(item => item * 2)
).
- Encapsulating logic to avoid polluting the global scope.
Functions are a core building block of JavaScript, enabling structured, dynamic, and functional programming patterns.
Why are functions first class objects in JavaScript?
A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
In JavaScript, functions are first-class objects (or first-class citizens) because they are treated as regular objects with the same privileges as any other data type, like strings or numbers. This means functions can be manipulated and used in flexible ways, enabling powerful programming patterns. Here’s why:
Reasons Functions Are First-Class Objects:
1. Can Be Assigned to Variables:
Functions can be stored in variables, just like any other value.
javascript const greet = function() { console.log("Hello!"); }; greet(); // Output: Hello!
-
Can Be Stored in Data Structures:
Functions can be elements in arrays, objects, or other structures.javascript const actions = { sayHi: function() { console.log("Hi!"); } }; actions.sayHi(); // Output: Hi!
-
Can Be Passed as Arguments:
Functions can be passed to other functions as parameters, enabling callbacks and functional programming.javascript function execute(fn) { fn(); } execute(function() { console.log("Running!"); }); // Output: Running!
-
Can Be Returned from Functions:
Functions can return other functions, supporting closures and factory functions.javascript function createAdder(x) { return function(y) { return x + y; }; } const add5 = createAdder(5); console.log(add5(3)); // Output: 8
-
Have Properties and Methods:
Since functions are objects, they can have properties and methods, like any object.javascript function myFunc() {} myFunc.customProp = "Test"; console.log(myFunc.customProp); // Output: Test
-
Can Be Created Dynamically:
Functions can be defined at runtime, anonymously or otherwise, using expressions or constructors (thoughFunction
constructor is rarely used).javascript const subtract = new Function("a", "b", "return a - b"); console.log(subtract(5, 2)); // Output: 3
Why This Matters:
- Flexibility: Treating functions as first-class objects allows JavaScript to support functional programming paradigms, like higher-order functions, currying, and composition.
- Callbacks and Events: Passing functions as arguments is key for asynchronous operations (e.g., setTimeout
, event listeners).
- Closures: Returning functions enables encapsulation and persistent state (e.g., in module patterns).
- Dynamic Behavior: Assigning functions to variables or properties supports dynamic and modular code.
Technical Context:
JavaScript functions inherit from the Function
object, making them objects under the hood. They have built-in properties like length
(number of parameters) and prototype
, and methods like call()
, apply()
, and bind()
, reinforcing their object-like nature.
This first-class status is a cornerstone of JavaScript’s versatility, enabling patterns like functional programming, event-driven programming, and modular design that power modern web development.
What is a function declaration?
A function definition (also called a function declaration, or function statement)
consists of the function keyword, followed by:
The name of the function.
A list of parameters to the function, enclosed in parentheses and separated by commas.
The JavaScript statements that define the function, enclosed in curly brackets, { /* … */ }.
Example:
function square(number) { return number * number; }
What is a function expression?
A function expression
is a function defined within an expression.
Example:
~~~
const getRectArea = function (width, height) {
return width * height;
};
console.log(getRectArea(3, 4));
// Expected output: 12```
How is a function called in JavaScript?
A function is called by its name, followed by parentheses with possible parameters.
Example:
functionName(parameters);.
What is a recursive function in JavaScript?
A recursive function
in JavaScript is a function that calls itself to solve a problem. An example of this is a function that calculates the factorial of a number.
function factorial(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
You could then compute the factorials of 1 through 5 as follows:
console.log(factorial(1)); // 1
console.log(factorial(2)); // 2
console.log(factorial(3)); // 6
console.log(factorial(4)); // 24
console.log(factorial(5)); // 120
What is function hoisting in JavaScript?
Hoisting
is JavaScript’s behavior of moving declarations to the top. In the context of functions, even if a function is declared after its call in the code, JavaScript will move the function declaration to the top, so it can be called before its declaration.
~~~
Consider the example below:
console.log(square(5)); // 25
function square(n) {
return n * n;
}```
This code runs without any error, despite the square()
function being called before it’s declared. This is because the JavaScript interpreter hoists the entire function declaration to the top of the current scope, so the code above is equivalent to:
```// All function declarations are effectively at the top of the scope
function square(n) {
return n * n;
}
console.log(square(5)); // 25```
Does function hoisting work with function expressions in JavaScript?
No, function hoisting
only works with function declarations — not with function expressions. The following code will not work:
~~~
console.log(square(5)); // ReferenceError: Cannot access ‘square’ before initialization
const square = function (n) {
return n * n;
};```
Explain function scope
Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined.
For example, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function, and any other variables to which the parent function has access.
What is a recursive function?
A function that calls itself is called a recursive function.
How can a function call itself?
A function can refer to and call itself using one of the following:
The function’s namearguments.callee()
An in-scope variable that refers to the function
For example, consider the following function definition:
~~~
const foo = function bar() {
// statements go here
};```
Within the function body, the following are all equivalent:
`
1. bar()
2. arguments.callee()
3. foo()`
What is the call stack?
A call stack is a mechanism for an interpreter to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function, etc.
When a script calls a function, the interpreter adds it to the call stack and then starts carrying out the function.
Any functions that are called by that function are added to the call stack further up, and run where their calls are reached.
When the current function is finished, the interpreter takes it off the stack and resumes execution where it left off in the last code listing.
If the stack takes up more space than it was assigned, a “stack overflow” error is thrown.
Note: The call stack is also called function stack in some places.
Properties of nested functions and closures
The inner function can be accessed only from statements in the outer function.
The inner function forms a closure: the inner function can use the arguments and variables of the outer function, while the outer function cannot use the arguments and variables of the inner function.
Since the inner function forms a closure, you can call the outer function and specify arguments for both the outer and inner function:
~~~
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
const fnInside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give it
console.log(fnInside(5)); // 8
console.log(outside(3)(5)); // 8```
Explain Closures
In JavaScript, a closure is a fundamental concept that has to do with how functions and their lexical environments work. Let’s break down the components and principles of a JavaScript closure:
Function Scope:
In JavaScript, each function has its own scope. Variables declared within a function are only accessible within that function, and they are not visible to the outside world.Lexical Scope:
JavaScript uses lexical scoping, which means that a function’s scope is determined by its location in the source code when it’s defined. This is also referred to as “static scope.”Nested Functions:
You can define functions inside other functions. When you do this, the inner function has access to the variables and parameters of the outer (enclosing) function. This forms a closure.
What is the arguments object of a function?
The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:
arguments[i];
where i
is the ordinal number of the argument, starting at 0
. So, the first argument passed to a function would be arguments[0]
. The total number of arguments is indicated by arguments.length.
Using the arguments object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don’t know in advance how many arguments will be passed to the function. You can use arguments.length
to determine the number of arguments actually passed to the function, and then access each argument using the arguments object.
Note: The arguments variable is “array-like”, but not an array. It is array-like in that it has a numbered index and a length property. However, it does not possess all of the array-manipulation methods.
How do you set default parameters in JavaScript?
In JavaScript, you can set default parameters for functions to provide fallback values when arguments are not passed or are undefined
. Default parameters were introduced in ES6 (2015) and are defined directly in the function’s parameter list.
Syntax:
```javascript
function functionName(param1 = defaultValue1, param2 = defaultValue2) {
// Function body
}
~~~
How It Works:
- If an argument is provided, the default value is ignored.
- If an argument is missing or undefined
, the default value is used.
- Default values can be any expression, including literals, variables, or even function calls.
Examples:
-
Basic Default Parameters:
javascript function greet(name = "Guest") { return `Hello, ${name}!`; } console.log(greet("Alice")); // Output: Hello, Alice! console.log(greet()); // Output: Hello, Guest! console.log(greet(undefined)); // Output: Hello, Guest!
-
Multiple Default Parameters:
javascript function calculateTotal(price = 0, tax = 0.1) { return price + price * tax; } console.log(calculateTotal(100)); // Output: 110 (tax defaults to 0.1) console.log(calculateTotal(100, 0.2)); // Output: 120 (tax is 0.2) console.log(calculateTotal()); // Output: 0 (price defaults to 0, tax to 0.1)
-
Using Expressions or Functions as Defaults:
javascript function getDefaultMessage() { return "Welcome!"; } function display(message = getDefaultMessage()) { console.log(message); } display(); // Output: Welcome! display("Hi!"); // Output: Hi!
-
Default Parameters with Objects or Arrays:
javascript function drawShape({ x = 0, y = 0 } = {}) { console.log(`Drawing at x: ${x}, y: ${y}`); } drawShape({ x: 10, y: 20 }); // Output: Drawing at x: 10, y: 20 drawShape(); // Output: Drawing at x: 0, y: 0
-
Using Previous Parameters in Defaults:
Default parameters can reference earlier parameters.javascript function adjustValue(base = 10, increment = base * 2) { return base + increment; } console.log(adjustValue(5)); // Output: 15 (increment = 5 * 2 = 10) console.log(adjustValue()); // Output: 30 (base = 10, increment = 10 * 2 = 20)
Key Notes:
- Undefined vs. Null: Passing undefined
triggers the default value, but passing null
does not ( null
is treated as a valid value).
javascript function test(param = "Default") { console.log(param); } test(null); // Output: null test(); // Output: Default
- Order Matters: Parameters with defaults should typically come after non-default parameters to avoid confusion, though JavaScript doesn’t enforce this.
javascript function example(a, b = 2) {} // Good practice function example(a = 1, b) {} // Potentially confusing, as b is required
- Destructuring with Defaults: You can use default values in destructured parameters (as shown in the object example above).
- Pre-ES6 Workaround: Before ES6, default values were often handled with logical OR (
||
) or conditional checks, but these could fail with falsy values like 0
or ""
.javascript function oldGreet(name) { name = name || "Guest"; // Problem: falsy values like "" or 0 trigger default return `Hello, ${name}!`; }
Benefits:
- Simplifies function logic by reducing manual checks for undefined
.
- Makes APIs more intuitive with sensible defaults.
- Enhances code readability and maintainability.
Default parameters are a powerful feature in JavaScript, widely used in modern codebases for cleaner and more robust functions.
What are rest parameters?
In JavaScript, rest parameters allow a function to accept an indefinite number of arguments as an array. Introduced in ES6, they’re defined using the spread operator (...
) followed by a parameter name in the function’s parameter list. Rest parameters simplify handling variable-length argument lists compared to the older arguments
object.
Syntax:
```javascript
function functionName(…restParam) {
// restParam is an array containing all passed arguments
}
~~~
Key Points:
- Collection: Rest parameters collect all arguments (or remaining arguments) into a single array.
- Position: Must be the last parameter in the function definition.
- Array Methods: Since restParam
is a true array, you can use array methods like map()
, filter()
, or reduce()
.
- Not arguments
: Unlike the arguments
object (an array-like structure), rest parameters are cleaner and more flexible.
Examples:
1. Basic Rest Parameter:
javascript function sum(...numbers) { return numbers.reduce((total, num) => total + num, 0); } console.log(sum(1, 2, 3)); // 6 console.log(sum(10)); // 10 console.log(sum()); // 0
-
With Other Parameters:
javascript function greet(greeting, ...names) { return names.map(name => `${greeting}, ${name}!`); } console.log(greet("Hi", "Alice", "Bob")); // ["Hi, Alice!", "Hi, Bob!"]
-
Rest vs. Fixed Parameters:
javascript function logDetails(first, second, ...rest) { console.log(`First: ${first}`); console.log(`Second: ${second}`); console.log(`Rest: ${rest}`); } logDetails(1, 2, 3, 4, 5); // Output: // First: 1 // Second: 2 // Rest: [3, 4, 5]
Restrictions:
- Only one rest parameter per function, and it must be last:
javascript function invalid(...rest, x) {} // SyntaxError
- Works in function declarations, expressions, and arrow functions:
javascript const fn = (...args) => args.join(", "); console.log(fn("a", "b", "c")); // "a, b, c"
Use Cases:
- Handling variable numbers of inputs (e.g., summing numbers, logging arguments).
- Simplifying function APIs that need flexibility (e.g., event handlers, formatters).
- Replacing arguments
for cleaner code in modern JavaScript.
Comparison to arguments
:
- Old Way (arguments
):
javascript function oldSum() { return Array.from(arguments).reduce((total, num) => total + num, 0); }
- Rest Way:
javascript function sum(...nums) { return nums.reduce((total, num) => total + num, 0); }
- Rest parameters are preferred because they’re explicit, true arrays, and avoid
arguments
’ quirks (e.g., not working well in arrow functions).Rest parameters make functions more flexible and readable, streamlining variable argument handling in JavaScript.
How do you set default parameters in JavaScript?
In JavaScript, default parameters let you set fallback values for function arguments if they’re missing or undefined
. Defined in the parameter list, they were introduced in ES6.
Syntax:
```javascript
function name(param1 = default1, param2 = default2) {
// Code
}
~~~
Examples:
1. Basic:
javascript function greet(name = "Guest") { return `Hello, ${name}!`; } console.log(greet()); // Hello, Guest! console.log(greet("Alice")); // Hello, Alice!
-
Multiple Defaults:
javascript function total(price = 0, tax = 0.1) { return price + price * tax; } console.log(total(100)); // 110
-
Expressions as Defaults:
javascript function display(msg = "Welcome!") { console.log(msg); } display(); // Welcome!
Notes:
- undefined
triggers defaults; null
doesn’t.
- Defaults can use earlier parameters or complex expressions.
- Best to place defaults after required parameters.
- Pre-ES6 used ||
or conditionals, but these mishandled falsy values.
Default parameters make functions cleaner and more intuitive.
What is The difference between rest parameters and the arguments object?
Rest Parameters vs. Arguments Object in JavaScript:
-
Rest Parameters (
...param
):- ES6 feature, defined with
...
in function parameters. - Collects arguments into a true array.
- Must be the last parameter.
- Explicit, works in all function types (including arrow functions).
- Supports array methods directly (e.g.,
map
,reduce
).javascript function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
- ES6 feature, defined with
-
Arguments Object:
- Pre-ES6, available in all non-arrow functions.
- Array-like object (not a true array) containing all arguments.
- No array methods; requires conversion (e.g.,
Array.from(arguments)
). - Implicit, less readable, unavailable in arrow functions.
javascript function sum() { return Array.from(arguments).reduce((a, b) => a + b, 0); }
Key Difference: Rest parameters are explicit, true arrays, and modern; arguments
is implicit, array-like, and outdated. Use rest parameters for cleaner, more flexible code.
What is the problem with the arguments object on non-strict functions?
In non-strict mode, the arguments
object in JavaScript has quirks:
-
Alias Issue:
arguments
entries are linked to named parameters, so changing one updates the other, causing unexpected behavior.javascript function example(a) { arguments[0] = 10; console.log(a); // 10 (a changes too) }
- Performance: Less optimized than rest parameters.
-
Clunky: Array-like, not a true array, so array methods (e.g.,
map
) require conversion.
These issues make arguments
unreliable and cumbersome compared to rest parameters (...args
), which are cleaner and avoid such problems.
What happens to the arguments object on non-strict function if it has non simple parameters like rest parameter, default or destructured parameters?
In non-strict mode, when a JavaScript function uses non-simple parameters (like rest parameters, default parameters, or destructured parameters), the arguments
object behaves differently compared to functions with simple parameters (e.g., just named parameters like a, b, c
).
Key Behavior:
- No Aliasing: Unlike simple parameters, where arguments
entries are linked to named parameters (changing one affects the other), non-simple parameters break this aliasing. The arguments
object still exists and holds all passed arguments, but it’s independent of rest, default, or destructured parameters.
- Independent Updates: Modifying arguments[i]
doesn’t affect non-simple parameters, and modifying non-simple parameters doesn’t update arguments
.
Examples:
-
Rest Parameters:
javascript function example(a, ...rest) { console.log(arguments[0]); // 1 console.log(rest); // [2, 3] arguments[0] = 10; console.log(a); // 1 (a unchanged) a = 20; console.log(arguments[0]); // 10 (arguments unchanged) } example(1, 2, 3);
-
arguments
contains[1, 2, 3]
, butrest
is a separate array[2, 3]
. - No aliasing:
a
andarguments[0]
are independent.
-
-
Default Parameters:
javascript function example(a = 5) { console.log(arguments[0]); // 1 or undefined (if no arg) console.log(a); // 1 or 5 (default used if undefined) arguments[0] = 10; console.log(a); // 1 (a unchanged) a = 20; console.log(arguments[0]); // 10 (arguments unchanged) } example(1);
- If no argument is passed,
arguments[0]
isundefined
, buta
gets the default5
. - No aliasing between
a
andarguments[0]
.
- If no argument is passed,
-
Destructured Parameters:
javascript function example({ x } = { x: 0 }) { console.log(arguments[0]); // { x: 1 } console.log(x); // 1 arguments[0].x = 10; console.log(x); // 1 (x unchanged, not aliased) x = 20; console.log(arguments[0].x); // 10 (arguments unchanged) } example({ x: 1 });
-
arguments[0]
holds the passed object{ x: 1 }
, butx
is a separate variable. - No direct aliasing, though
arguments[0]
references the same object (changes to its properties persist, butx
itself is independent).
-
Why This Happens:
- Non-simple parameters (rest, default, destructured) require more complex handling during function execution, so JavaScript engines avoid the legacy aliasing behavior of arguments
to simplify semantics and improve performance.
- In non-strict mode, arguments
is still created but acts as a standalone snapshot of the arguments passed, not a live mirror of named parameters.
Implications:
- Predictability: Breaking aliasing makes behavior more consistent and less error-prone compared to simple parameters in non-strict mode.
- Still Quirky: arguments
remains array-like (not a true array) and is less reliable than rest parameters, especially with rest or default parameters in play.
- Recommendation: Use rest parameters (...args
) instead of arguments
for modern code, as they’re explicit, true arrays, and avoid these complexities.
In summary, non-simple parameters in non-strict mode make the arguments
object independent of rest, default, or destructured parameters, eliminating aliasing but still retaining arguments
as a legacy, less flexible structure.
What does it mean that the arguments object is an array-like object and not an Array?
The arguments
object is array-like because it has indexed elements (e.g., arguments[0]
) and a length
property, like an array. However, it’s not a true Array
because it lacks array methods (e.g., map
, filter
) and isn’t an instance of the Array
constructor. You’d need to convert it (e.g., Array.from(arguments)
) to use array features.
```javascript
function example() {
console.log(arguments[0]); // 1
console.log(arguments.length); // 3
// arguments.map(x => x * 2); // Error: arguments.map is not a function
const arr = Array.from(arguments); // Convert to array
console.log(arr.map(x => x * 2)); // [2, 4, 6]
}
example(1, 2, 3);
~~~
arguments
acts array-like (access arguments[0]
, has length
), but isn’t an Array
, so it lacks methods like map
until converted.
What are the properties of the arguments object?
The arguments
object in JavaScript is an array-like object with:
- Indexed elements: Access arguments like arguments[0]
.
- length: Number of arguments passed.
- callee (non-strict mode): Reference to the currently executing function.
It lacks array methods (e.g., map
).
Example:
```javascript
function example() {
console.log(arguments[0]); // 1
console.log(arguments.length); // 2
console.log(arguments.callee); // [Function: example]
}
example(1, 2);
~~~
What is a method?
A method in JavaScript is a function that is a property of an object, used to perform actions or computations related to that object.
Example:
```javascript
const obj = {
greet: function() {
console.log(“Hello!”);
}
};
obj.greet(); // Output: Hello!
~~~