Create Hello World Function
Write a function
createHelloWorld
. It should return a new function that always returns"Hello World"
.Function Syntax
In JavaScript, there are two main ways to declare a function. One of which is to use the
function
keyword.Basic Syntax
The syntax is:
function f(a, b) {
const sum = a + b;
return sum;
}
console.log(f(3, 4)); // 7
In this example, f
is the name of the function. (a, b)
are the arguments. You can write any logic in the body and finally return
a result. You are allowed to return nothing, and it will instead implicitly return undefined
Anonymous Function
You can optionally exclude the name of the function after the
function
keyword.
var f = function(a, b) {
const sum = a + b;
return sum;
}
console.log(f(3, 4)); // 7
Immediately Invoked Function Expression (IIFE)
You can create a function and immediately execute it in Javascript.
const result = (function(a, b) {
const sum = a + b;
return sum;
})(3, 4);
console.log(result); // 7
Why would you write code like this?
It gives you the opportunity to encapsulate a variable within a new scope. For example, another developer can immediately see that
sum
can't be used anywhere outside the function body.Functions Within Functions
A powerful feature of JavaScript is you can actually create functions within other functions and even return them!
function createFunction() {
function f(a, b) {
const sum = a + b;
return sum;
}
return f;
}
const f = createFunction();
console.log(f(3, 4)); // 7
In this example, createFunction()
returns a new function. Then that function can be used as normal.
Function Hoisting
JavaScript has a feature called hoisting where a function can sometimes be used before it is initialized. You can only do this if you declare functions with the
function
syntax.
function createFunction() {
return f;
function f(a, b) {
const sum = a + b;
return sum;
}
}
const f = createFunction();
console.log(f(3, 4)); // 7
In this example, the function is returned before it is initialized. Although it is valid syntax, it is sometimes considered bad practice as it can reduce readability.
Closures
When a function is created, it has access to a reference to all the variables declared around it, also known as it's lexical environment. The combination of the function and its environment is called a closure. This is a powerful and often used feature of the language.
function createAdder(a) {
function f(b) {
const sum = a + b;
return sum;
}
return f;
}
const f = createAdder(3);
console.log(f(4)); // 7
In this example, createAdder
passes the first parameter a
and the inner function has access to it. This way, createAdder
serves as a factory of new functions, with each returned function having different behavior.
Arrow Syntax
The other common way to declare functions is with arrow syntax.
Basic Syntax
const f = (a, b) => { const sum = a + b; return sum; }; console.log(f(3, 4)); // 7
Omit Return
If you can write the code in a single line, you can omit the
return
keyword. This can result in very short code.
const f = (a, b) => a + b;
console.log(f(3, 4)); // 7
Differences
There are 3 major differences between arrow syntax and function syntax.
More minimalistic syntax. This is especially true for anonymous functions and single-line functions. For this reason, this way is generally preferred when passing short anonymous functions to other functions.
No automatic hoisting. You are only allowed to use the function after it was declared. This is generally considered a good thing for readability.
Can't be bound to
this
,super
, andarguments
or be used as a constructor. These are all complex topics in themselves but the basic takeaway should be that arrow functions are simpler in their feature set.
Rest Arguments
You can use rest syntax to access all the passed arguments as an array. This isn't necessary for this problem, but it will be a critical concept for many problems.
Basic Syntax
The syntax is:
function f(...args) {
const sum = args[0] + args[1];
return sum;
}
console.log(f(3, 4)); // 7
In this example the variable args
is [3, 4]
.
Why
It may not be immediately obvious why you would use this syntax because you can always just pass an array and get the same result.
The primary use-case is for creating generic factory functions that accept any function as input and return a new version of the function with some specific modification.
By the way, a function that accepts a function and/or returns a function is called a higher-order function, and they are very common in JavaScript.
function log(inputFunction) {
return function(...args) {
console.log("Input", args);
const result = inputFunction(...args);
console.log("Output", result);
return result;
}
}
const f = log((a, b) => a + b);
f(1, 2); // Logs: Input [1, 2] Output 3
Solutions to Problem
Function Syntax
var createHelloWorld = function() {
return function() {
return "Hello World";
}
};
Arrow Syntax
var createHelloWorld = function() {
return () => "Hello World";
};
Arrow Syntax + Rest Arguments
var createHelloWorld = function() {
return (...args) => "Hello World";
};
Counter
Given an integer n
, return a counter
function. This counter
function initially returns n
and then returns 1 more than the previous value every subsequent time it is called (n
, n + 1
, n + 2
, etc).
Closure Example
In Javascript, you can declare functions within other functions and return them. The inner function has access to any variables declared above it.
function createAdder(a) {
return function add(b) {
const sum = a + b;
return sum;
}
}
const addTo2 = createAdder(2);
addTo2(5); // 7
The inner function add
has access to a
. This allows the outer function to serve as a factory of new functions, each with different behavior.
Closures Versus Classes
You may notice that in the above example createAdder
is very similar to a class constructor.
class Adder {
constructor(a) {
this.a = a;
}
add(b) {
const sum = this.a + b;
return sum;
}
}
const addTo2 = new Adder(2);
addTo2.add(5); // 7
Besides differences in syntax, both code examples essentially serve the same purpose. They both allow you to pass in some state in a "constructor" and have "methods" that access this state.
One key difference is that closures allow for true encapsulation. In the class example, there is nothing stopping you from writing addTo2.a = 3;
and breaking it's expected behavior. However, in the closure example, it is theoretically impossible to access a
. Note that as of 2022, true encapsulation is achievable in classes with # prefix syntax.
Another difference is how the functions are stored in memory. If you create many instances of a class, each instance stores a single reference to the prototype object where all the methods are stored. Whereas for closures, all the "methods" are generated and a "copy" of each is stored in memory each time the outer function is called. For this reason, classes can be more efficient, particularly in the case where there are many methods.
Unlike in languages like Java, you will tend to see code written with functions rather than with classes. But since JavaScript is a multi-paradigm language, it will depend on the particular project you are working on.
Approach 1: Increment Then Return
We declare a variable currentCount
and set it equal to n - 1
. Then inside the counter function, increment currentCount
and return the value. Note that since currentCount
is modified, it should be declared with let
rather than const
.
var createCounter = function(n) {
let currentCount = n - 1;
return function() {
currentCount += 1;
return currentCount;
};
};
Approach 2: Postfix Increment Syntax
JavaScript provides convenient syntax that returns a value and then increments it. This allows us to avoid having to initially set a variable to n - 1
.
var createCounter = function(n) {
return function() {
return n++;
};
};
Approach 3: Prefix Decrement and Increment Syntax
JavaScript also has syntax that allows you to increment a value and then return it. Because the increment happens before the value is returned, we must first decrement the value initially similar to Approach 1.
var createCounter = function(n) {
--n;
return function() {
return ++n;
};
};
Approach 4: Postfix Increment Syntax With Arrow Function
We can reduce the amount of code in Approach 2 by using an arrow function with an implicit return.
var createCounter = function(n) {
return () => { return n++; };
};
To Be Or Not To Be
Write a function expect
that helps developers test their code. It should take in any value val
and return an object with the following two functions.
toBe(val)
accepts another value and returnstrue
if the two values===
each other. If they are not equal, it should throw an error"Not Equal"
.
notToBe(val)
accepts another value and returns true
if the two values !==
each other. If they are equal, it should throw an error "Equal"
.
/**
* @param {any} val
* @return {Object}
*/
var expect = function(val) {
return {
toBe: function(otherVal) {
if (val === otherVal) {
return true;
} else {
throw new Error("Not Equal");
}
},
notToBe: function(otherVal) {
if (val !== otherVal) {
return true;
} else {
throw new Error("Equal");
}
}
};
};
Counter II
Write a function createCounter
. It should accept an initial integer init
. It should return an object with three functions.
The three functions are:
increment()
increases the current value by 1 and then returns it.decrement()
reduces the current value by 1 and then returns it.reset()
sets the current value toinit
and then returns it.
/**
* @param {integer} init
* @return { increment: Function, decrement: Function, reset: Function }
*/
var createCounter = function(init) {
let present = init;
return {
increment:()=> ++present,
decrement:()=> --present,
reset:()=> present = init,
}
};
/**
* const counter = createCounter(5)
* counter.increment(); // 6
* counter.reset(); // 5
* counter.decrement(); // 4
*/
Apply Transform Over Each Element in Array
Given an integer array arr
and a mapping function fn
, return a new array with a transformation applied to each element.
The returned array should be created such that returnedArray[i] = fn(arr[i], i)
.
Please solve it without the built-in Array.map
method.
var map = function(arr, fn) {
const transformedArr = [];
for (let i = 0; i < arr.length; i++) {
transformedArr[i] = fn(arr[i], i);
}
return transformedArr;
};