- Published on
Functions
Table of Contents
Functions
Functions
in JavaScript are blocks of reusable code that can be executed when called upon. They are a fundamental building block of JavaScript programming and are used to encapsulate
a set of instructions.
Example:
// Function declaration
function greet(name) {
console.log(`Hello, ${name}!`)
}
// Function call
greet('Rajnish') // Output: Hello, Rajnish!
In this example, greet
is a function that takes a name
parameter and logs a greeting message to the console.
Function Declaration vs. Function Expression
Function Declaration
Function declarations
define named functions. They are hoisted to the top of their scope, meaning they can be called before they are declared in the code.
Example:
greet('Rajnish') // Output: Hello, Rajnish!
function greet(name) {
console.log(`Hello, ${name}!`)
}
Function Expression
Function expressions
, on the other hand, define functions as part of an expression. They are not hoisted and cannot be called before they are defined.
Example:
const callGreet = function greet(name) {
console.log(`Hello, ${name}!`)
}
callGreet('Rajnish') // Output: Hello, Rajnish!
Arrow Functions
Arrow functions
are a more concise way to write function expressions in JavaScript. They have a shorter syntax compared to traditional function expressions and lexically bind the this
value.
Example:
const callGreet = (name) => {
console.log(`Hello, ${name}!`)
}
callGreet('Rajnish') // Output: Hello, Rajnish!
Scope and Closures
Scope
refers to the visibility and accessibility of variables within a particular context in JavaScript. Closures
occur when a function is able to remember and access its lexical scope even when it is executed outside of that scope.
Example:
function outer() {
let outerVar = 'I am from outer function'
function inner() {
console.log(outerVar)
}
inner() // Output: I am from outer function
}
outer()
In this example, the inner
function can access the outerVar
variable because of lexical scoping. The inner
function is lexically within the scope of the outer
function, so it has access to the variables declared in outer
.
function outer() {
let outerVar = 'I am from outer function'
function inner() {
console.log(outerVar)
}
return inner
}
const innerFunction = outer()
innerFunction() // Output: I am from outer function
Closures
occur when a function is able to remember and access its lexical scope even when it is executed outside of that scope.
In this example, even though the inner
function is executed outside of the outer
function, it still has access to the outerVar
variable due to closure. The inner function closes over
the lexical environment of its outer function, allowing it to access variables from that outer function even after the outer function has finished executing.
function counter() {
let count = 0
return function () {
return ++count
}
}
const increment = counter()
console.log(increment()) // Output: 1
console.log(increment()) // Output: 2
console.log(increment()) // Output: 3
Closures
can also be used to create private variables or to maintain state between function calls.
In this example, the counter
function returns a closure
that maintains a private count
variable. Each time the returned function is called, it increments and returns the count
variable.
function greet(name) {
setTimeout(function () {
console.log(`Hello, ${name}!`)
}, 1000)
}
greet('Rajnish')
Closures
are commonly used with callback functions to maintain state or access outer variables.
In this example, the anonymous function passed to setTimeout
has access to the name
variable from the outer greet
function due to closure
. Even though the greet
function has finished executing by the time the callback
is invoked, the callback still remembers and can access the name
variable.
Privacy in Object-Oriented Programming
In object-oriented programming, scope and closures can be used to create private variables and methods within objects, providing encapsulation and data hiding.
function createPerson(name) {
let _name = name // private variable
return {
getName: function () {
return _name // closure maintains access to _name
},
setName: function (newName) {
_name = newName // can only be modified through setName method
},
}
}
const person = createPerson('Rajnish')
console.log(person.getName()) // Output: Rajnish
person.setName('Kumar')
console.log(person.getName()) // Output: Kumar
console.log(person._name) // Output: undefined (private variable)
In this example, the _name
variable is private to the createPerson
function and can only be accessed or modified through the returned object's methods (getName
and setName
). Closures maintain access to the _name
variable even after the createPerson
function has finished executing, providing encapsulation and data privacy.
Event Handlers and Asynchronous Operations
Scope and closures are commonly used in event handlers and asynchronous operations to maintain access to outer variables within callback functions.
function createCounter() {
let count = 0
setInterval(function () {
count++
console.log(`Count: ${count}`)
}, 1000)
}
createCounter()
In this example, the createCounter
function creates a closure around the count
variable, allowing the setInterval
callback function to access and increment the count
variable every second. The closure ensures that the count
variable is not accessible from outside the createCounter
function, maintaining data privacy.
Memoization in Recursive Functions
Closures can be used to implement memoization, a technique used to improve the performance of recursive functions by caching the results of previous function calls.
function fibonacci() {
let cache = {}
return function fib(n) {
if (n in cache) {
return cache[n]
} else {
if (n <= 1) {
return n
} else {
cache[n] = fib(n - 1) + fib(n - 2)
return cache[n]
}
}
}
}
const memoizedFibonacci = fibonacci()
console.log(memoizedFibonacci(10)) // Output: 55
In this example, the fibonacci
function returns a closure that implements memoization for the Fibonacci sequence calculation. The cache
object stores the results of previous function calls, allowing the function to return cached values instead of recalculating them, improving performance.
Callback Functions
Callback functions are functions that are passed as arguments to other functions and are executed at a later time or under certain conditions.
function greet(name, callback) {
console.log(`Hello, ${name}!`)
callback()
}
function sayGoodbye() {
console.log('Goodbye!')
}
greet('Rajnish', sayGoodbye)
// Output:
// Hello, Rajnish!
// Goodbye!
In this example, sayGoodbye
is a callback function passed to the greet
function and executed after the greeting message.
Higher-Order Functions
Higher-order functions are functions that take other functions as arguments or return functions as their result.
function multiplier(factor) {
return function (x) {
return x * factor
}
}
const double = multiplier(2)
console.log(double(5)) // Output: 10
In this example, multiplier
is a higher-order function that returns a new function (function(x) { return x * factor; }
). This returned function can then be stored in a variable (double
) and called later.