JavaScript Questions. Practice

TheTechnoCult
10 min readDec 21, 2023

--

1. What’s in the console?

console.log(typeof f1);  // Output: "function" --> f1 is hoisted
console.log(typeof f2); // Output: "undefined"
console.log(typeof f3); // Output: ReferenceError (TDZ)

function f1() {}; // typeof "function"
var f2 = function () {}; // typeof "function"
let f3 = () => {}; // typeof "function"

The example demonstrates the concept of hoisting and how it affects different types of function declarations and expressions, particularly when using var, let, and function declarations.

(1) Function declarations in JavaScript are hoisted. (2) When using var, the variable declaration is hoisted to the top of its scope, but not its initialization or definition. (3) Variables let (and const) are not initialized until the line of code where they are actually defined is reached. The period from the beginning of the block until the declaration is reached is known as the Temporal Dead Zone (TDZ). Accessing the variable in the TDZ results in a ReferenceError.

2. What’s in the console?

var o = {};
var i = 0;

(function () {
o.a = 'a';
i++; // 1
})();

console.log(o, i); // Output: {a: 'a'}, 1

(function (o, i) {
o.b = 'b';
i++;
})(o, i);

console.log(o, i); // Output: {a: 'a', b: 'b'}, 1

// ...
// (function (o, i) { // (o, i) here are parameters
// i - new instance
// o - link to the global object o
// o.b = 'b'; // o here is parameter, but was reassigned by reference to the global object
// i++; // 2, i here is paramets, primitive type is not passed by reference
// })(o, i); // (o, i) here are arguments

// console.log(o, i); // i = 1, in global
// IIFE Syntax
(function (v1, v2) {
console.log(v1, v2) // Output: 11, 2
})(11, 2);

Second IIFE with Parameters:

  • o.b = 'b': This adds a property b with the value 'b' to the object o. Even though o is a parameter here, it still refers to the same object in the global scope, so this change is reflected globally.
  • i++: This increments the parameter i by 1. However, this i is local to the function and does not affect the global i. Inside this function, i becomes 2, but this change is not seen outside the function.

3. What’s in the console?

var o = {}
var a = []

console.warn(o == a) // Output: false
console.warn(o === a) // Output: false
console.warn(typeof o == typeof a) // Output: true
console.warn(typeof o === typeof a) // Output: true

console.warn(o == a) Output: false. When comparing objects (including arrays) using == or ===, the comparison checks for reference equality. It means two objects are only equal if they reference the exact same instance. Since o and a are two different instances, the result is false.

console.warn(typeof o == typeof a) Output: true. For both an {} and an [], typeof returns "object".

4. What’s in the console?

function Constructor1 () {};
var Constructor2 = function () {};
var Constructor3 = () => {};

console.log(new Constructor1()); // Output: Constructor1 {}
console.log(new Constructor2()); // Output: Constructor2 {}
console.log(new Constructor3()); // Output: TypeError --> the arrow function isn't constructable and doesn't have its own this

Constructor3 is an arrow function. Arrow functions, introduced in ES6 (ECMAScript 2015), are 'callable' but not 'constructible'. This means you can execute Constructor3 like a regular function, but you cannot use the new operator with it, as arrow functions do not have their own this context and cannot act as constructors.

5. What’s in the console?

function Constructor() {};
var o = new Constructor();
console.log(o.__proto__ === Constructor.prototype); // Output: true

o.__proto__ is a reference to the prototype of the object o. The __proto__ property (which is actually a getter/setter, not a real property) of an object points to the prototype object from which the object has inherited properties (note that __proto__ is considered non-standard and is not recommended for use in production code only as answer for tricky interview questions).

The statement o.__proto__ === Constructor.prototype checks whether the prototype of the object o is the same as the Constructor function's prototype object. In prototypal inheritance, this is true for objects created using new with a constructor function.

6. Is the request sent to the server?

fetch('http://www.sometestdomain.come')
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error)
});

while (true) console.log(1) // Don't launch it in the console, calls stack overflow

(yes) but

  • The fetch request is likely sent to the server because the asynchronous request is offloaded to the browser's Web APIs, independent of the JavaScript main thread.
  • However, the response from the fetch request will not be handled (i.e., neither the then nor the catch callbacks will run) as long as the main thread is blocked by the infinite loop.

7. How to reorganize this correctly?

arr
.sort()
.filter()
.reduce()
.map()

The right way to put filter first, then sort, map and reduce.

arr
.filter() // First, filter the array.
.sort() // Then, sort the array.
.map() // After filtering, apply a function to each element.
.reduce() // Finally, reduce the array to a single value.

8. What is the biggest possible number for var?

let n = 0

Answer:

let n = Number.MAX_SAFE_INTEGER // 2^53 - 1 = 9,007,199,254,740,991

The value of Number.MAX_SAFE_INTEGER is 2^53 - 1, which is equal to 9,007,199,254,740,991.

In scenarios where you need to handle very large integers beyond the safe integer limit, JavaScript (as of ES2020) provides the BigInt type, which can represent integers of arbitrary size. For instance:

let bigInteger = BigInt("9007199254740992"); // a number larger than MAX_SAFE_INTEGER

The maximum size of a BigInt is constrained by the system's resources, specifically the available memory.

9. Get an array of duplicates from the array.

const arr = [1, 2, 3, 4, 2, 3, 2];

const duplicates = arr.reduce((acc, el) => {
// Initialize the count for this element if it doesn't exist
acc.counts[el] = (acc.counts[el] || 0) + 1;

// If the count is exactly 2, it means it's a duplicate and we add it to the duplicates array
if (acc.counts[el] === 2) {
acc.duplicates.push(el);
}

return acc;
}, { counts: {}, duplicates: [] }).duplicates;

console.log(duplicates); // Output: [2, 3]

10. To flatten an array using the reduce method.

function flattenArray(arr) {
return arr.reduce((acc, val) => {
return acc.concat(Array.isArray(val) ? flattenArray(val) : val);
}, []);
}

// Example usage
const nestedArray = [1, [2, 3], [4, [5, 6, [7]], 8]];
const flatArray = flattenArray(nestedArray);
console.log(flatArray); // Output: [1, 2, 3, 4, 5, 6, 7, 8]

11. The function composition of [f(x), g(x), h(x)] is fn(x) = f(g(h(x))).

const compose = (funs) => (value) => 
funs.reduceRight((acc, fn) => fn(acc), value)

12. Given a function fn, return a memoized version of that function.

function memoize(fn) {
const cache = {}
return function(...args) {
const key = args
if (!(key in cache)) {
cache[key] = fn(...args)
}
return cache[key]
}
}

// or (faster)

function memoize(fn) {
const cache = {}
return function(...args) {
const key = (args).toString()
if (cache[key] === undefined) {
cache[key] = fn(...args)
}
return cache[key]
}
}

13. Write a class in ES5 and then write the equivalent in ES6.

  • ES5 Syntax
// ES5: Function Constructor
function Person(name, age) {
this.name = name;
this.age = age;
}

// Attaching a method to the prototype
Person.prototype.greet = function() {
return "Hello, my name is " + this.name + " and I am " + this.age + " years old.";
};

// Example usage
var person1 = new Person("Alice", 30);
console.log(person1.greet()); // "Hello, my name is Alice and I am 30 years old."
  • ES6 Syntax
// ES6: Class Syntax
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

greet() {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
}

// Example usage
const person2 = new Person("Bob", 25);
console.log(person2.greet()); // "Hello, my name is Bob and I am 25 years old."

14. Where to use promises?

When dealing with asynchronous operations such as network requests, file system operations, or any operations that take time to complete.

15. Explain async and await in the context of promises.

Promises are a the base for the async/await syntax, which is a more recent addition to JavaScript for handling asynchronous operations. This syntax provides a way to write asynchronous code in a manner that resembles synchronous code, improving readability and maintainability.

async function fetchData() {
// Asynchronous operation (e.g., fetching data from an API)
const response = await fetch('https://api.example.com/data');

// The code below will not execute until the Promise (fetch) is resolved
const data = await response.json();

return data;
}

// Using the async function
fetchData().then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});

16. What is Symbol, how can a well known Symbol be used for iteration and combination with the yield keyword.

Symbol is a primitive data type introduced in ES6 that are used to create unique identifiers for object properties.

let symbol1 = Symbol("description");
let symbol2 = Symbol("description");

console.log(symbol1 === symbol2); // false

The Symbol.iterator symbol lets you define an iterator for any object. When an object implements the Symbol.iterator method, it becomes iterable, which means it can be used with loops like for...of.

const collection = {
items: [1, 2, 3],
[Symbol.iterator]: function* () {
for (let item of this.items) {
yield item;
}
}
};

for (let item of collection) {
console.log(item); // Logs 1, 2, 3
}

17. What is in the console?

setTimeout(function timeout() {
console.log(1);
}, 0);

let p = new Promise(function (res, rej) {
console.log(2);
res();
});

p.then(function () {
console.log(3);
});

console.log(4);

// Output: 2,4,3,1

setTimeout is called with a delay of 0 milliseconds. However, even with a delay of 0, the callback to setTimeout (the function printing "1") is placed in the Web API environment, and it will be moved to the callback queue only after the current call stack is clear. This is an example of a task that is executed in the macro task queue.

When the Promise is created, its executor function is executed immediately. Thus, "2" is printed directly to the console.

The promise is resolved with res(). The .then method attached to the promise is scheduled to run after the script has finished executing and when the microtask queue is processed. This .then function, which prints "3", is a microtask.

The console.log(4) is a part of the main script and executes immediately after the Promise creation and resolution, printing "4".

(!) Microtasks (like promise callbacks) have priority over macrotasks (like setTimeout callbacks), which is why "3" is printed before "1" even though the promise's .then is resolved after the setTimeout is scheduled.

18. What’s in the console?

console.log(undefined && 4 && 7)

// Output: undefined

The expression console.log(undefined && 4 && 7) evaluates to undefined due to the way the logical AND (&&) operator works in JavaScript. The AND operator evaluates expressions from left to right and stops at the first falsy value it encounters, returning that value. If all values are truthy, it returns the last value.

console.log( 1 && 2 || 3)

// Output: 2

1 && 2: The logical AND operator (&&) evaluates from left to right. If the left operand is truthy, it returns the right operand. In this case, both 1 and 2 are truthy, so it returns the right operand, which is 2.

2 || 3: The logical OR operator (||) also evaluates from left to right. If the left operand is truthy, it returns the left operand. Since the left operand (2) is truthy, it returns 2.

console.log( 1 && null || 3)

// Output: 3

1 && null returns null, since 3 is truthy, the result of null || 3 is 3.

19. What’s in the console?

(function () {
console.log(1)
setTimeout(function () {
console.log(2)
}, 1000)
setTimeout(function () {
console.log(3)
}, 0)
console.log(4)
})()

// Output:
/*
1
4
undefined (result of the IIFE)
3
2
*/

undefined as the result of the entire Immediately Invoked Function Expression (IIFE).

20. What’s in the console?

var obj = {
name: 'Name',
method: function () {
return this.name
}
}

var method = obj.method

console.log(method()) // Output: (loses context)
console.log(obj.method()) // Output: Name

21. Create a function that can be canceled.

const cancellable = (fn, args, t) => {
const id = setTimeout(fn, t, ...args)
return () => clearTimeout(id)
}

22. Create a function using promise that can be canceled.

  • If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result.
  • If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded".
var timeLimit = function(fn, t) {
return function(...args) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject("Time Limit Exceeded"), t)
})

// Race the function against the timeout
return Promise.race([fn(...args), timeoutPromise])
}
};

23. Create a debounce function.

const debounce = function(fn, t) {
let id
return function(...args) {
clearTimeout(id)
id = setTimeout(()=> {
fn(...args)
}, t)
}
}

24. Create a chunk array.

const chunk = function(arr, size) {
return arr.reduce((chunks, item, index) => {
const chunkIndex = Math.floor(index / size)
if (!chunks[chunkIndex]) {
chunks[chunkIndex] = []
}
chunks[chunkIndex].push(item)
return chunks
}, [])
}

25. Create a function for parallel call.

function parallel(functions, doneAll) {
// Map each function to a new Promise
const promises = functions.map(func => new Promise(resolve => {
func(resolve)
}))

// Use Promise.all to execute all promises in parallel
Promise.all(promises).then(results => {
doneAll(results)
})
}

const a = function (done) {
setTimeout(function () {
done('result a')
}, 300)
}

const b = function (done) {
setTimeout(function () {
done('result b')
}, 200)
}

parallel([a, b], function (results) {
console.log(results) // ['result a', 'result b']
})

26. Flatten an array using depth.

Input
arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]
n = 1
Output
[1, 2, 3, 4, 5, 6, 7, 8, [9, 10, 11], 12, 13, 14, 15]
const flat = function (arr, n, resArray = []) {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) && n > 0) {
flat(arr[i], n - 1, resArray)
} else {
resArray.push(arr[i])
}
}

return resArray
}

27. Deep clone of the Object.

function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj
}

let clone

if (Array.isArray(obj)) {
clone = []
for (let i = 0; i < obj.length; i++) {
clone[i] = deepClone(obj[i])
}
} else {
clone = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key])
}
}
}

return clone
}

28. What’s in the console?

setTimeout(() => console.log(2), 0);

Promise.resolve().then(() => setTimeout(() => console.log(1)));

setTimeout(() => console.log(3));

Promise.resolve().then(() => console.log(4));

console.log(5);

// Output: 5 4 2 3 1

--

--

No responses yet