- Published on
BigFrontEnd JavaScript Quizzes
- Authors

- Name
- Yinhuan Yuan
Introduction
This blog post summarizes JavaScript Quizzes on BigFrontEnd.Dev.
- 1. Hoisting
- 2.Scope
- 3. Implicit Coercion
- 4.Closure
- 5.Prototype
- 6.this
- 7.Symbol
- 8.Promise
- 1.Promise order
- 2. Promise executor
- 3. Promise then callbacks
- 4. Promise then callbacks II
- 18. Promise executor II
- 23. Promise.all()
- 36. Promise.prototype.finally()
- 47. Promise Order II
- 98. Promise executor IV
- 103. Promise Order III
- 106. Promise.all() II
- 50. async await
- 130. async
- 131. return await
- 60. postMessage
- 61. onClick
- 62. MessageChannel
- 9.Iterator and Generator
- 10.Operator
- 11.Function
- 12. Array
- 13.time
- 14.Object
- 15.String
- 16.Proxy
- 17.Other
1. Hoisting
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their respective scopes during the compilation phase, before the code is executed
- Variable declaration
varx is hoisted to the top of its scope, but the initialization is not. Trying to access it befor initlialization will return aundefined - Function declarations and implementation are also hoisted.
constandlet: While they are hoisted, they are not initialized, and trying to access them before their declaration will result in aReferenceError.- Function declarations inside blocks: In non-strict mode, function declarations inside blocks are hoisted to the top of the enclosing function or global scope. This can lead to unexpected behavior. In strict mode (introduced in ES5), function declarations inside blocks are block-scoped, meaning they are not hoisted outside of the block.
window.fooalways refers to the globalfoo(in a browser environment).- Variable declaration
varis not limited in block. - In IIFE, the function definition outside can not be used inside.
27. Hoisting I
27.https://bigfrontend.dev/quiz/Hoisting-I
const a = 1
console.log(a)
var b
console.log(b)
b = 2
console.log(c)
var c = 3
console.log(d)
let d = 2
Solution: To explain what's happening in this code snippet, let's go through it line by line:
const a = 1
console.log(a) // Output: 1
We declare a constant a and assign it the value 1. When we log it, it prints 1.
var b
console.log(b) // Output: undefined
We declare a variable b using var, but don't assign it a value. When we log it, it prints undefined.
b = 2
console.log(c) // Output: undefined
We assign the value 2 to b. Then we try to log c, which hasn't been declared yet. However, due to hoisting (explained below), this doesn't throw an error, but logs undefined.
var c = 3
console.log(d) // Throws a ReferenceError
We declare c and assign it the value 3. Then we try to log d, which hasn't been declared yet. This throws a ReferenceError.
let d = 2
We declare d using let and assign it the value 2. This line is never reached due to the error on the previous line.
Key points:
- Variables declared with
varare hoisted, meaning their declarations (but not assignments) are moved to the top of their scope. That's why we can usecbefore its declaration without an error. - Variables declared with
letorconstare not hoisted in the same way. Trying to use them before declaration results in a ReferenceError. constvariables must be assigned a value when declared and cannot be reassigned.- If a variable is declared but not assigned a value, it has the value
undefined.
1
undefined
undefined
Error
28. Hoisting II
28.https://bigfrontend.dev/quiz/Hoisting-II
const func1 = () => console.log(1)
func1()
func2()
function func2() {
console.log(2)
}
func3()
var func3 = function func4() {
console.log(3)
}
Solution:
First, we define
func1as an arrow function that logs the number 1.We call
func1(), which will output1to the console.We then call
func2(). Even thoughfunc2is defined after this call in the code, it works because of function hoisting. Function declarations are hoisted to the top of their scope, sofunc2is available throughout the entire script.The
func2function is defined, which logs the number 2.We try to call
func3(). This will throw a TypeError because at this point,func3is undefined. Even thoughvar func3is hoisted, only the declaration is hoisted, not the assignment.Finally, we assign a function expression to
func3. This function is namedfunc4, but this name is only available inside the function itself (for recursion, for example). Outside the function, it's only known asfunc3.
The key points to understand here are:
- Function declarations (like
func2) are hoisted entirely and can be used before their actual position in the code. - Variables declared with
var(likefunc3) have their declarations hoisted, but not their assignments. Sofunc3exists but isundefinedwhen we try to call it. - Function expressions (like the one assigned to
func3) are not hoisted. They're only available after the line where they're defined.
If this code were to run, it would output:
1
2
And then throw a TypeError when trying to call func3().
const func1 = () => console.log(1)
func1() // 1
func2() // 2
// Function declarations are hoisted to the top of their scope
function func2() {
console.log(2)
}
func3() // Error
var func3 = function func4() {
console.log(3)
}
29. Hoisting III
29.https://bigfrontend.dev/quiz/Hoisting-III
var a = 1
function func() {
a = 2
console.log(a)
var a
}
func()
console.log(a)
if (!('b' in window)) {
var b = 1
}
console.log(b)
Solution:
We start by declaring a global variable
aand assigning it the value 1.We define a function
func().Inside
func():- We assign 2 to
a - We log the value of
a - We declare
var a(but this declaration is hoisted to the top of the function)
- We assign 2 to
We call
func()We log the value of the global
aWe check if
'b'is not a property of thewindowobjectIf
'b'is not inwindow, we declarevar band assign it 1We log the value of
b
Now, let's explain the output and behavior:
When
func()is called:- Due to variable hoisting, the
var ainside the function is moved to the top of the function scope. This creates a new localathat shadows the globala. - The assignment
a = 2andconsole.log(a)work with this locala. - So, the first
console.log(a)insidefunc()outputs2.
- Due to variable hoisting, the
After
func()is called, weconsole.log(a)again. This refers to the globala, which was never changed. So it outputs1.The
ifstatement checks if'b'is not a property ofwindow. In a browser environment,vardeclarations at the global scope create properties on thewindowobject. However, due to hoisting,var bis already declared before this check, so'b' in windowwould be true, and theifblock would not execute.The final
console.log(b)would outputundefined. This is becausevar bwas hoisted to the top of its scope (global in this case), but since theifblock didn't execute, it never got assigned a value.
So, the final output of this code would be:
2
1
undefined
Key points to understand:
- Variable hoisting with
var - Function scope vs global scope
- Shadowing of global variables by local variables
- How
varat the global scope interacts with thewindowobject in a browser environment
var a = 1
function func() {
a = 2
console.log(a)
var a // Move to above a = 2 and shadow the a outside
}
func() // 2
console.log(a) // 1
if (!('b' in window)) {
var b = 1 // var b is moved to the top as var a = 1
// the if condition is false. b is not assigned.
}
console.log(b) // undefined
32. Hoisting IIII
32.https://bigfrontend.dev/quiz/Hoisting-IIII
var a = 1
function a() {}
console.log(typeof a)
var b
function b() {}
b = 1
console.log(typeof b)
function c() {}
var c = 1
console.log(typeof c)
var d = 1
;(function () {
d = '2'
console.log(typeof d)
function d() {}
})()
console.log(typeof d)
var e = 1
const f = function e() {}
console.log(typeof e)
Solution:
This code explores variable and function declarations, hoisting, and scope in JavaScript. Let's break it down section by section:
var a = 1
function a() {}
console.log(typeof a) // 'number'
Here, both var a and function a() are hoisted. The function declaration initially takes precedence, but then a is reassigned to 1. So typeof a is 'number'.
var b
function b() {}
b = 1
console.log(typeof b) // 'number'
Similar to a, but the reassignment happens after both declarations. The final value of b is 1, so typeof b is 'number'.
function c() {}
var c = 1
console.log(typeof c) // 'number'
The function declaration is hoisted and then c is reassigned to 1. So typeof c is 'number'.
var d = 1
;(function () {
d = '2'
console.log(typeof d) // 'string'
function d() {}
})()
console.log(typeof d) // 'number'
This is trickier. Inside the IIFE (Immediately Invoked Function Expression):
function d()is hoisted within the function scope.d = '2'reassigns the localdto a string.- So inside the IIFE,
typeof dis 'string'. Outside the IIFE, the globaldremains unchanged, sotypeof dis still 'number'.
var e = 1
const f = function e() {}
console.log(typeof e) // 'number'
Here, the e in function e() {} is only scoped inside the function itself. It doesn't affect the outer e. So typeof e remains 'number'.
Key points to understand:
- Function declarations are hoisted and initially take precedence over variable declarations.
- Reassignments happen after hoisting, potentially changing the type of a variable.
- Function expressions (like in the last example) don't create a variable in the outer scope.
- In function scopes, local variables take precedence over global ones.
- The
typeofoperator returns a string indicating the type of the unevaluated operand.
This code demonstrates some of the more complex behaviors of JavaScript regarding variable declarations, function declarations, and scope. It's a good reminder of why it's generally recommended to use let and const for clearer scoping rules, and to avoid reusing names for different purposes. Would you like me to elaborate on any specific part of this explanation?
38. Hoisting IV
38.https://bigfrontend.dev/quiz/Hoisting-IV
let foo = 10
function func1() {
console.log(foo)
var foo = 1
}
func1()
function func2() {
console.log(foo)
let foo = 1
}
func2()
Solution: This code demonstrates the difference between variable hoisting with var and the temporal dead zone (TDZ) with let in JavaScript. Let's break it down:
let foo = 10- This declares a variable
fooin the global scope with a value of 10.
- This declares a variable
function func1() { ... }- This defines a function
func1.
- This defines a function
Inside
func1:console.log(foo) var foo = 1- Due to hoisting, this is equivalent to:
var foo console.log(foo) foo = 1 - The
vardeclaration is hoisted to the top of the function scope, but the initialization isn't. - So when
console.log(foo)is executed,fooexists in the function scope but isundefined.
- Due to hoisting, this is equivalent to:
func1()- This calls
func1, which will logundefined.
- This calls
function func2() { ... }- This defines a function
func2.
- This defines a function
Inside
func2:console.log(foo) let foo = 1- Unlike
var,letdeclarations are not hoisted. - The
foovariable is in the temporal dead zone from the start of the block until the point of declaration. - Accessing
foobefore its declaration results in aReferenceError.
- Unlike
func2()- This calls
func2, which will throw aReferenceError.
- This calls
Output:
undefined
Uncaught ReferenceError: Cannot access 'foo' before initialization
Key points:
- Variables declared with
varare hoisted to the top of their scope. The declaration is hoisted, but not the initialization. - Variables declared with
let(andconst) are not hoisted. They exist in a "temporal dead zone" from the start of their block until the point where they are declared. - Accessing a
letvariable before its declaration results in aReferenceError, while accessing avarvariable before its declaration results inundefined. - This code demonstrates why
letcan help catch potential errors by making it impossible to accidentally use a variable before it's declared.
42. Hoisting V
42.https://bigfrontend.dev/quiz/hoisting-v
;(() => {
if (!fn) {
function fn() {
console.log('2')
}
}
fn()
})()
function fn() {
console.log('1')
}
// another one
function fn1() {
console.log('3')
}
;(() => {
if (!fn1) {
function fn1() {
console.log('4')
}
}
fn1()
})()(
// another one !
() => {
if (false) {
function fn3() {
console.log('5')
}
}
fn3()
}
)()
Solution:
In IIFE (Immediately Invoked Function Expression), the function definition outside can not be used inside. (!fn) and (!fn1) return true .
'2'
'4'
Error
45. Hoisting VI
45.https://bigfrontend.dev/quiz/Hoisting-VI
var foo = 1
;(function () {
console.log(foo)
foo = 2
console.log(window.foo)
console.log(foo)
var foo = 3
console.log(foo)
console.log(window.foo)
})()
Solution: This code demonstrates variable hoisting, scope, and the difference between local and global variables in JavaScript. Let's break it down line by line:
var foo = 1;- This declares a global variable
fooand assigns it the value 1.
- This declares a global variable
(function () { ... })()- This is an Immediately Invoked Function Expression (IIFE). It creates a new scope.
Inside the IIFE:
a.
console.log(foo);- Due to hoisting, the local
var foodeclaration (line 7) is moved to the top of the function scope, but not its initialization. - At this point,
fooisundefinedin the local scope. - Output:
undefined
b.
foo = 2;- This assigns 2 to the local
foo, not the global one.
c.
console.log(window.foo);- This explicitly references the global
foo(assuming this is run in a browser). - The global
foois still 1. - Output:
1
d.
console.log(foo);- This refers to the local
foo, which is now 2. - Output:
2
e.
var foo = 3;- This is the actual declaration and initialization of the local
foo. - Remember, the declaration part was hoisted, but the initialization happens here.
f.
console.log(foo);- The local
foois now 3. - Output:
3
g.
console.log(window.foo)- Again, this explicitly references the global
foo, which is still 1. - Output:
1
- Due to hoisting, the local
So, the complete output will be:
undefined
1
2
3
1
Key points:
- Variable declarations with
varare hoisted to the top of their scope. - Function scopes create their own context for variables.
- When a variable is declared inside a function with
var, it's local to that function. - Assigning a value to a variable that hasn't been declared with
varin the current scope will create or modify a global variable (if not in strict mode). window.fooalways refers to the globalfoo(in a browser environment).
This code illustrates the complexity that can arise from variable hoisting and the importance of understanding scope in JavaScript.
39. var
39.https://bigfrontend.dev/quiz/var
function foo() {
console.log(i)
for (var i = 0; i < 3; i++) {
console.log(i)
}
}
foo()
Solution: Let's break down this code and explain its behavior:
function foo() { ... }- This defines a function named
foo.
- This defines a function named
Inside
foo():console.log(i)- This line tries to log the value of
ibefore the for loop. - Due to hoisting of
vardeclarations, this is equivalent to:var i console.log(i) - At this point,
iexists but isundefined.
- This line tries to log the value of
The
forloop:for (var i = 0; i < 3; i++) { console.log(i) }- This is a standard for loop that initializes
ito 0, runs whileiis less than 3, and incrementsiafter each iteration. - Inside the loop, it logs the current value of
i.
- This is a standard for loop that initializes
foo()- This calls the
foofunction.
- This calls the
When we run this code, the output will be:
undefined
0
1
2
Explanation of the output:
- The first
undefinedcomes from loggingibefore it's initialized in the for loop. - The subsequent 0, 1, and 2 come from logging
iinside the for loop for each iteration.
Key points:
- Variables declared with
varare hoisted to the top of their function scope. - Hoisting moves the declaration, but not the initialization.
- This is why we can access
ibefore the for loop without getting a ReferenceError, but its value isundefined. - Inside the for loop,
ibehaves as expected, taking on the values 0, 1, and 2.
This quiz demonstrates the concept of variable hoisting in JavaScript and how it can lead to potentially unexpected behavior when variables are accessed before their apparent declaration in the code.
67. if
67.https://bigfrontend.dev/quiz/if
if (true) {
function foo() {
console.log('BFE')
}
}
if (false) {
function bar() {
console.log('dev')
}
}
foo()
bar()
Solution:
This code demonstrates the behavior of function declarations within block statements in JavaScript. Let's break it down:
First
ifblock:if (true) { function foo() { console.log('BFE') } }- This block will always execute because the condition is
true. - It declares a function
foo.
- This block will always execute because the condition is
Second
ifblock:if (false) { function bar() { console.log('dev') } }- This block will never execute because the condition is
false. - It declares a function
bar, but this declaration is inside a block that doesn't run.
- This block will never execute because the condition is
foo()- This attempts to call the function
foo.
- This attempts to call the function
bar()- This attempts to call the function
bar.
- This attempts to call the function
The behavior of this code can vary depending on the JavaScript environment and whether it's running in strict mode or not. Let's consider two scenarios:
Scenario 1: Non-strict mode in most browsers:
foo()will execute and log 'BFE'.bar()will throw a ReferenceError becausebaris not defined.
Scenario 2: Strict mode or in some modern JavaScript environments:
foo()will execute and log 'BFE'.bar()will throw a ReferenceError becausebaris not defined.
The key points are:
Function declarations in blocks (block-scoped function declarations) are not part of the ECMAScript specification before ES6 and their behavior was historically inconsistent across browsers.
In modern JavaScript (ES6+), function declarations in blocks are supposed to be block-scoped, meaning they should only be accessible within the block they're declared in.
However, for compatibility reasons, many browsers still hoist function declarations in blocks to the containing function or global scope in non-strict mode.
In strict mode, block-scoped function declarations behave as they're supposed to in ES6+: they're only accessible within their block.
The function
baris never actually created because it's in a block that doesn't execute.
This quiz tests understanding of:
- Function declarations in block statements
- The effect of conditions on whether code in blocks is executed
- Potential differences between strict and non-strict mode
- The concept of hoisting and block scope
It's a good example of why it's generally safer to use function expressions with const or let instead of function declarations in blocks, to avoid these ambiguities and ensure consistent behavior across different JavaScript environments.
68. if II
68.https://bigfrontend.dev/quiz/if-II
if (
function foo() {
console.log('BFE')
}
) {
console.log('dev')
}
foo()
Solution:
This code demonstrates some interesting behavior in JavaScript related to function declarations, expressions, and hoisting. Let's break it down:
The
ifstatement:if ( function foo() { console.log('BFE') } ) { console.log('dev') }- The condition of the
ifstatement is a function expression. - Function expressions are truthy values in JavaScript.
- Therefore, the condition always evaluates to
true. - The body of the
ifstatement will always execute, logging 'dev'.
- The condition of the
Inside the
ifcondition:- The function expression
function foo(){ console.log('BFE') }is created but not executed. - This function is not assigned to any variable or property.
- The function expression
foo()- This attempts to call a function named
foo.
- This attempts to call a function named
Now, let's consider the behavior:
The
ifstatement condition is true, so 'dev' will be logged to the console.The function expression in the
ifcondition does not create a variable namedfooin the outer scope. It only exists within the scope of theifstatement.When
foo()is called outside theifblock, it will result in a ReferenceError becausefoois not defined in this scope.
The output and behavior will be:
dev
Uncaught ReferenceError: foo is not defined
Key points:
- Function expressions in conditions are truthy.
- Function expressions do not create variables in the surrounding scope, unlike function declarations.
- The function
foois not hoisted or made available outside theifstatement.
This quiz tests understanding of:
- The difference between function declarations and function expressions
- Scoping rules in JavaScript
- How JavaScript evaluates truthy/falsy values in conditions
- Common misconceptions about function availability
It's a tricky example that highlights the importance of understanding how function expressions work in JavaScript, especially when used in unusual contexts like if statement conditions.
70. function
70.https://bigfrontend.dev/quiz/function
function foo() {
console.log(1)
}
var foo = 2
function foo() {
console.log(3)
}
foo()
Solution: This code demonstrates function hoisting, variable hoisting, and function overwriting in JavaScript. Let's break it down step by step:
First function declaration:
function foo() { console.log(1) }Variable declaration and assignment:
var foo = 2Second function declaration:
function foo() { console.log(3) }Function call:
foo()
Now, let's consider how JavaScript interprets this code:
Function declarations are hoisted to the top of their scope. Both function declarations for
fooare hoisted.Variable declarations (but not assignments) are also hoisted. The
var foodeclaration is hoisted, but not its assignment.The second function declaration overwrites the first one.
After hoisting and before execution, the code is effectively interpreted like this:
function foo() { console.log(1) } function foo() { console.log(3) } var foo foo = 2 foo()When the assignment
foo = 2is executed, it overwrites the functionfoowith the number 2.When
foo()is called,foois no longer a function, it's the number 2.
Therefore, when this code is executed:
- It will throw a TypeError: foo is not a function
The error occurs because we're trying to call foo as a function, but at that point, foo is the number 2.
Key points:
- Function declarations are hoisted and can overwrite each other.
- Variable declarations are hoisted, but not their assignments.
- Assignments can overwrite functions.
- Trying to call a non-function value as a function results in a TypeError.
This quiz tests understanding of:
- Function and variable hoisting
- The order of precedence in declarations and assignments
- How variables can overwrite functions
- The importance of the final state of a variable/function before it's used
It's a tricky example that highlights some of the potential pitfalls in JavaScript when mixing function declarations, variable declarations, and assignments with the same name.
89. let
89.https://bigfrontend.dev/quiz/let
let a = 1
;(function () {
let foo = () => a
let a = 2
console.log(foo())
})()
Solution: This code demonstrates lexical scoping and the temporal dead zone in JavaScript. Let's break it down step by step:
let a = 1;- Declares a variable
ain the global scope and initializes it to 1.
- Declares a variable
(function() { ... }())- This is an Immediately Invoked Function Expression (IIFE). It creates a new scope.
Inside the IIFE:
let foo = () => a- Declares a function
foothat returns the value ofa. - The
ahere refers to theain its lexical scope, which is the IIFE, not the globala.
- Declares a function
let a = 2;- Declares a new variable
ainside the IIFE, shadowing the globala. - This
ais initialized to 2.
- Declares a new variable
console.log(foo())- Calls
foo()and logs its result.
- Calls
Now, here's the tricky part:
- When
foois defined, it captures theafrom its lexical environment (the IIFE). - However, at the point where
foois defined, the localaexists but hasn't been initialized yet. This is known as the Temporal Dead Zone (TDZ). - When
foo()is called, it tries to access this locala, which is now initialized to 2.
Therefore, the output will be:
2
Key points:
- Lexical scoping: The function
foocaptures theafrom its immediate scope (the IIFE), not the global scope. - Temporal Dead Zone: Variables declared with
letexist from the start of their block, but can't be accessed until after their declaration. - Variable shadowing: The
ainside the IIFE shadows the globala. - Closure:
fooforms a closure over its lexical environment, which includes the locala.
This code demonstrates why it's important to be careful with variable declarations and their placement, especially when dealing with closures and nested scopes. It's a classic example used to test understanding of JavaScript's scoping rules and the behavior of let declarations.
116. strict
116.https://bigfrontend.dev/quiz/strict
function a() {
'use strict'
dev = 'BFE'
console.log(dev)
}
a()
Solution:
I'd be happy to explain that code snippet. Let's break it down line by line:
function a() {
'use strict'
dev = 'BFE'
console.log(dev)
}
a()
function a() {: This declares a function namedawith no parameters.'use strict': This enables strict mode for the function. Strict mode helps catch common coding errors and prevents the use of certain error-prone features.dev = 'BFE': This line attempts to assign the string 'BFE' to a variable nameddev. However, there's novar,let, orconstdeclaration fordev.console.log(dev): This would attempt to print the value ofdevto the console.}: This closes the function definition.a(): This calls the functiona.
The key issue in this code is on line 3. In strict mode, assigning to an undeclared variable throws an error. So when this code runs, it will throw a ReferenceError before it reaches the console.log statement.
If you want this code to work as intended, you should declare the variable dev using let, const, or var (though var is generally discouraged in modern JavaScript). For example:
function a() {
'use strict'
let dev = 'BFE'
console.log(dev)
}
a()
This version would successfully log 'BFE' to the console when the function is called.
2.Scope
Scope defines the visibility and accessibility of variables within your code. Variables can have either global scope (accessible from anywhere in the code) or local scope (accessible only within a specific function or block).
The scope chain is a mechanism in JavaScript that determines the order in which variable lookups are performed. When a variable is referenced, JavaScript searches for it starting from the innermost scope and moving outward until it finds the variable.
The lexical environment consists of all the variables and functions that are in scope at a particular point in code. It contains two components: 1. Environment Record: 2. Reference to the Outer Lexical Environment:
- var is hoisted out of the block and the let is hoisted within the block.
const a = b = c = 1isc=1;b=c;const a = b.
5. scope
5.https://bigfrontend.dev/quiz/block-scope-1
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
Solution: This code contains two for loops, each using a different variable declaration (var and let) and both setting up setTimeout callbacks. These callbacks log the value of i after a delay of 0 milliseconds. The key difference lies in how var and let handle scope, particularly within asynchronous callbacks like those in setTimeout.
- First Loop: Using
var
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
Explanation:
varis function-scoped (or globally scoped if not in a function). It means that the variableiis the same for every iteration of the loop, and its value is shared across all thesetTimeoutcallbacks.- The
setTimeoutfunction is asynchronous and runs after the loop has completed. By the time thesetTimeoutcallbacks are executed, the loop has already incrementedito5. - As a result, each
setTimeoutlogs the value ofiafter the loop has finished, so all of them log5.
Output:
5
5
5
5
5
- Second Loop: Using
let
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
Explanation:
letis block-scoped. Each iteration of the loop creates a new block-scopedivariable. Therefore, eachsetTimeoutcallback captures its own distinctivalue.- When the
setTimeoutcallbacks are executed, each callback logs the value ofithat was current at the time of that particular iteration. - As a result, the values logged are
0,1,2,3, and4, corresponding to each iteration of the loop.
Output:
0
1
2
3
4
3.Summary:
- With
var: The loop variableiis shared across all iterations, so allsetTimeoutcallbacks log the final value ofi, which is5. - With
let: Each iteration creates a newivariable that is unique to that iteration, so eachsetTimeoutcallback logs the correct value ofifrom that particular iteration.
76. const
76.https://bigfrontend.dev/quiz/const
function func() {
const a = (b = c = 1)
}
func()
console.log(typeof a, typeof b, typeof c)
Solution: Certainly! This code demonstrates some interesting behavior related to variable declaration and scope in JavaScript. Let's break it down:
Function Definition:
function func() { const a = (b = c = 1) }This function contains a single line that looks like it's declaring three variables, but it's not doing exactly what it might seem.
Function Call:
func()This executes the function.
Console Log:
console.log(typeof a, typeof b, typeof c)This attempts to log the types of
a,b, andc.
Now, let's analyze what's happening:
Inside the function:
const a = b = c = 1is actually equivalent to:c = 1 b = c const a = bcandbare created as global variables (if not in strict mode) because they don't have a declaration keyword.ais declared as a constant, but only within the function's scope.
After the function call:
ais not accessible outside the function because it's block-scoped (due toconst).bandcare global variables (in non-strict mode) and retain their values.
The
console.logstatement:typeof ais "undefined" becauseais not defined in the global scope.typeof bis "number" becausebis a global variable with the value 1.typeof cis "number" becausecis also a global variable with the value 1.
Therefore, the output will be:
undefined number number
Key points:
- Only
ais truly declared as a local variable (withconst). bandcbecome global variables due to the lack of declaration keywords.- This behavior can be a source of bugs and is one reason why "use strict" is recommended.
- In strict mode, this would actually throw an error because
bandcwould be considered undeclared.
This code demonstrates the importance of properly declaring variables and understanding scope in JavaScript. It's generally a good practice to always use let, const, or var when declaring variables to avoid unintentional global variable creation.
3. Implicit Coercion
<, >, <=, >=convert thenullandundefinedto Number.==does not conver them and only return true for comparingnullandundefined.
9. null and undefined
9.https://bigfrontend.dev/quiz/null-and-undefined
console.log(JSON.stringify([1, 2, null, 3]))
console.log(JSON.stringify([1, 2, undefined, 3]))
console.log(null === undefined)
console.log(null == undefined)
console.log(null == 0)
console.log(null < 0)
console.log(null > 0)
console.log(null <= 0)
console.log(null >= 0)
console.log(undefined == 0)
console.log(undefined < 0)
console.log(undefined > 0)
console.log(undefined <= 0)
console.log(undefined >= 0)
Solution: Let's go through this code line by line:
console.log(JSON.stringify([1,2,null,3]))- JSON.stringify converts null to null in the JSON string.
- Output:
'[1,2,null,3]'
console.log(JSON.stringify([1,2,undefined,3]))- JSON.stringify omits undefined values in arrays.
- Output:
'[1,2,null,3]'
console.log(null === undefined)- Strict equality check. null and undefined are different types.
- Output:
false
console.log(null == undefined)- Loose equality. null and undefined are considered equal.
- Output:
true
console.log(null == 0)- Loose equality. null is not considered equal to 0.
- Output:
false
console.log(null < 0)- Comparison converts null to 0. 0 is not less than 0.
- Output:
false
console.log(null > 0)- Comparison converts null to 0. 0 is not greater than 0.
- Output:
false
console.log(null <= 0)- Comparison converts null to 0. 0 is less than or equal to 0.
- Output:
true
console.log(null >= 0)- Comparison converts null to 0. 0 is greater than or equal to 0.
- Output:
true
console.log(undefined == 0)- Loose equality. undefined is not considered equal to 0.
- Output:
false
console.log(undefined < 0)- Comparison with undefined returns false.
- Output:
false
console.log(undefined > 0)- Comparison with undefined returns false.
- Output:
false
console.log(undefined <= 0)- Comparison with undefined returns false.
- Output:
false
console.log(undefined >= 0)- Comparison with undefined returns false.
- Output:
false
Key points:
- JSON.stringify treats null and undefined differently in arrays.
- null and undefined are loosely equal (==) but not strictly equal (===).
- In comparisons (
<, >, <=, >=), null is converted to 0. - undefined is not equal to 0 and all comparisons with undefined return false.
- These examples demonstrate the quirks of JavaScript's type coercion and comparison rules, which can sometimes lead to unexpected results if not well understood.
10. Equal
10.https://bigfrontend.dev/quiz/Equal-1
console.log(0 == false)
console.log('' == false)
console.log([] == false)
console.log(undefined == false)
console.log(null == false)
console.log('1' == true)
console.log(1n == true)
console.log(' 1 ' == true)
Solution:
This code demonstrates JavaScript's type coercion rules when using the loose equality operator (==). Let's go through each line:
console.log(0 == false)- false is coerced to 0
- 0 == 0 is true
- Output:
true
console.log('' == false)- false is coerced to 0
- '' is coerced to 0
- 0 == 0 is true
- Output:
true
console.log([] == false)- false is coerced to 0
- [] is first converted to a primitive, which results in ''
- '' is then coerced to 0
- 0 == 0 is true
- Output:
true
console.log(undefined == false)- undefined is not coerced to a number in this case
- undefined == false is always false
- Output:
false
console.log(null == false)- null is not coerced to a number in this case
- null == false is always false
- Output:
false
console.log('1' == true)- true is coerced to 1
- '1' is coerced to 1
- 1 == 1 is true
- Output:
true
console.log(1n == true)- true is coerced to 1
- 1n (BigInt) is compared to 1
- 1n == 1 is true
- Output:
true
console.log(' 1 ' == true)- true is coerced to 1
- ' 1 ' is trimmed and coerced to 1
- 1 == 1 is true
- Output:
true
Key points:
- The == operator performs type coercion before comparison.
- false is typically coerced to 0, and true to 1.
- Empty strings and arrays are coerced to 0 when compared with booleans.
- undefined and null are not coerced to numbers when compared with booleans.
- Strings containing numbers (even with whitespace) are coerced to their numeric values.
These examples highlight why it's often recommended to use the strict equality operator (===) instead of ==, as it avoids these sometimes unintuitive type coercions.
30. Equal II
30.https://bigfrontend.dev/quiz/Equal-II
What does the code snippet to the right output by console.log?
ref to the The Abstract Equality Comparison Algorithm
console.log([1] == 1)
console.log([1] == '1')
console.log(['1'] == '1')
console.log(['1'] == 1)
console.log([1] == ['1'])
console.log(new Boolean(true) == 1)
console.log(new Boolean(true) == new Boolean(true))
console.log(Boolean(true) == '1')
console.log(Boolean(false) == [0])
console.log(new Boolean(true) == '1')
console.log(new Boolean(false) == [0])
console.log(null == undefined)
Solution:
Let's break down each comparison:
[1] == 1: true. The array[1]is converted to a primitive, which becomes'1', then to a number 1.[1] == '1': true.[1]is converted to'1', so it's'1' == '1'.['1'] == '1': true. Similar to #2,['1']becomes'1'.['1'] == 1: true.['1']becomes'1', then'1'is converted to number 1.[1] == ['1']: false. When comparing two objects (arrays are objects), it checks if they're the same object in memory, which they're not.new Boolean(true) == 1: true. The Boolean object is converted to its primitive valuetrue, which is then converted to number 1.new Boolean(true) == new Boolean(true): false. These are two different objects in memory.Boolean(true) == '1': true.Boolean(true)is justtrue, which is converted to number 1, then'1'is also converted to 1.Boolean(false) == [0]: true.[0]is converted to'0', then to number 0, which equalsfalse.new Boolean(true) == '1': true. The Boolean object is converted to its primitive valuetrue, which is then converted to number 1.new Boolean(false) == [0]: false. The Boolean object is converted to its primitive valuetrue, which is then converted to number 1.null == undefined: true. This is a special case in JavaScript wherenullandundefinedare considered loosely equal.
Key points:
- The
==operator performs type coercion. - Arrays are typically converted to strings, then to numbers if necessary.
Booleanobjects (new Boolean()) behave differently from boolean primitives.- When comparing objects, it checks if they're the same object in memory.
nullandundefinedare loosely equal to each other, but not to any other values.
console.log([1] == 1) // true
console.log([1] == '1') // true
console.log(['1'] == '1') // true
console.log(['1'] == 1) // true
console.log([1] == ['1']) // false
console.log(new Boolean(true) == 1) // true
console.log(new Boolean(true) == new Boolean(true)) // false
console.log(Boolean(true) == '1') // true
console.log(Boolean(false) == [0]) // true
console.log(new Boolean(true) == '1') // true
console.log(new Boolean(false) == [0]) // false
console.log(null == undefined) // true
79. Equal III
79.https://bigfrontend.dev/quiz/equal-iii
console.log(((2.0 == '2') == new Boolean(true)) == '1')
Solution: This code demonstrates JavaScript's type coercion and the behavior of the loose equality operator (==). Let's break it down step by step:
2.0 == "2" == new Boolean(true) == "1"This is evaluated from left to right:
First comparison:
2.0 == "2"- The string "2" is coerced to a number
- 2.0 == 2 is true
Second comparison:
true == new Boolean(true)- The result of the previous comparison (true) is compared with the Boolean object
- When comparing a primitive to an object with ==, the object is converted to a primitive
- new Boolean(true) is converted to the primitive true
- true == true is true
Third comparison:
true == "1"- The boolean true is coerced to a number (1)
- The string "1" is coerced to a number (1)
- 1 == 1 is true
Therefore, the entire expression evaluates to true.
The output will be:
true
Key points:
- The loose equality operator (==) performs type coercion.
- Numbers and strings are compared numerically after coercion.
- Boolean objects are converted to primitive booleans when compared with ==.
- true is coerced to 1 when compared with a string or number.
- The expression is evaluated left to right, with each comparison resulting in a boolean that's used in the next comparison.
This code is a tricky example that highlights the complexity of JavaScript's type coercion rules. It's generally recommended to use the strict equality operator (===) to avoid unexpected behavior due to type coercion, unless you specifically need the coercion behavior of ==.
101. Equal IV
101.https://bigfrontend.dev/quiz/Equal-IV
console.log([0] == '')
console.log([0] == 0)
Solution: Let's break down this code and explain what's happening:
console.log([0] == '')
console.log([0] == 0)
These lines are using the loose equality operator (==) to compare an array containing a single element 0 with an empty string and the number 0. The key to understanding this is knowing how JavaScript performs type coercion when using ==.
Let's examine each comparison:
[0] == ''When comparing an array to a primitive value using
==, JavaScript first tries to convert the array to a primitive. It does this by calling the array'stoString()method.[0].toString()returns"0"(a string containing "0")- Now we're comparing
"0" == '' - A non-empty string is not equal to an empty string
- Therefore, this comparison returns
false
[0] == 0Again, the array
[0]is converted to a primitive:[0].toString()returns"0"(a string containing "0")- Now we're comparing
"0" == 0 - When comparing a string to a number, JavaScript attempts to convert the string to a number
Number("0")is00 == 0is true- Therefore, this comparison returns
true
So, the output of this code will be:
false
true
This example demonstrates several important concepts in JavaScript:
- Type coercion: How JavaScript converts between types when using the
==operator. - Array to primitive conversion: How arrays are converted to primitives (using
toString()). - String to number conversion: How strings are converted to numbers in certain comparisons.
It's worth noting that these kinds of comparisons can lead to unexpected results, which is why it's often recommended to use the strict equality operator (===) instead, which doesn't perform type coercion.
8. Implicit Coercion I
8.https://bigfrontend.dev/quiz/Implicit-Conversion-1
console.log(Boolean('false'))
console.log(Boolean(false))
console.log('3' + 1)
console.log('3' - 1)
console.log('3' - ' 02 ')
console.log('3' * ' 02 ')
console.log(Number('1'))
console.log(Number('number'))
console.log(Number(null))
console.log(Number(false))
console.log(Number(undefined))
Solution: Let's go through this code line by line:
console.log(Boolean('false'))- This converts the string 'false' to a boolean.
- Any non-empty string is truthy in JavaScript.
- Output:
true
console.log(Boolean(false))- This is already a boolean false value.
- Output:
false
console.log('3' + 1)- When using the + operator with a string, JavaScript performs string concatenation.
- The number 1 is converted to a string.
- Output:
'31'
console.log('3' - 1)- For the - operator, JavaScript tries to convert the string to a number.
- '3' is converted to the number 3, then 1 is subtracted.
- Output:
2
console.log('3' - ' 02 ')- Both strings are converted to numbers.
- '3' becomes 3, ' 02 ' becomes 2 (leading/trailing spaces are ignored).
- Output:
1
console.log('3' * ' 02 ')- Similar to subtraction, both are converted to numbers for multiplication.
- Output:
6
console.log(Number('1'))- Converts the string '1' to a number.
- Output:
1
console.log(Number('number'))- Tries to convert 'number' to a number, which isn't possible.
- Output:
NaN(Not a Number)
console.log(Number(null))- null is converted to 0 when using the Number() function.
- Output:
0
console.log(Number(false))- false is converted to 0 when using the Number() function.
- Output:
0
console.log(Number(undefined))undefinedis converted toNaNwhen using the Number() function.- Output:
NaN
This code demonstrates various type coercions in JavaScript, particularly:
- How Boolean() handles different values
- How JavaScript handles operations between strings and numbers
- How the Number() function converts different types to numbers
These examples highlight the importance of understanding type coercion in JavaScript to avoid unexpected results in operations.
11. Implicit Coercion II
11.https://bigfrontend.dev/quiz/Implicit-Conversion-II
console.log([] + [])
console.log([] + 1)
console.log([[]] + 1)
console.log([[1]] + 1)
console.log([[[[2]]]] + 1)
console.log([] - 1)
console.log([[]] - 1)
console.log([[1]] - 1)
console.log([[[[2]]]] - 1)
console.log([] + {})
console.log({} + {})
console.log({} - {})
Solution:
Certainly. This code demonstrates some of JavaScript's more unusual type coercion rules, especially when dealing with arrays, objects, and the + and - operators. Let's break it down:
console.log([] + [])- Both arrays are converted to empty strings
- "" + "" results in an empty string
- Output:
""
console.log([] + 1)- [] is converted to an empty string
- "" + 1 coerces 1 to a string
- Output:
"1"
console.log([[]] + 1)- [[]] is converted to ""
- "" + 1 coerces 1 to a string
- Output:
"1"
console.log([[1]] + 1)- [[1]] is converted to "1"
- "1" + 1 coerces 1 to a string
- Output:
"11"
console.log([[[[2]]]] + 1)- [[[[2]]]] is converted to "2"
- "2" + 1 coerces 1 to a string
- Output:
"21"
console.log([] - 1)- [] is converted to 0 for subtraction
- 0 - 1 is performed
- Output:
-1
console.log([[]] - 1)- [[]] is converted to 0 for subtraction
- 0 - 1 is performed
- Output:
-1
console.log([[1]] - 1)- [[1]] is converted to 1 for subtraction
- 1 - 1 is performed
- Output:
0
console.log([[[[2]]]] - 1)- [[[[2]]]] is converted to 2 for subtraction
- 2 - 1 is performed
- Output:
1
console.log([] + {})- [] becomes ""
- becomes "[object Object]"
- Output:
"[object Object]"
console.log({} + {})- Both become "[object Object]"
- Output:
"[object Object][object Object]"
console.log({} - {})- Both are converted to NaN for subtraction
- NaN - NaN is NaN
- Output:
NaN
Key points:
- The + operator with arrays tends to convert them to strings.
- The - operator with arrays tends to convert them to numbers.
- Empty arrays are treated as 0 in numeric contexts and "" in string contexts.
- Nested arrays are flattened to their innermost value when converted to primitives.
- Objects, when converted to primitives, typically become "[object Object]".
- These examples show why it's important to be cautious when performing operations on mixed types in JavaScript, as the results can be unintuitive.
Reference: https://bigfrontend.dev/quiz/Implicit-Conversion-II/discuss
35. Implicit Coercion III
35.https://bigfrontend.dev/quiz/Implicit-Conversion-III
console.log([] + {})
console.log(+{})
console.log(+[])
console.log({} + [])
console.log({} + [])
console.log({} + [])
console.log({} + [])
console.log({} + +[])
console.log({} + +[] + {})
console.log({} + +[] + {} + [])
Solution: This code demonstrates some of JavaScript's type coercion rules and the behavior of the + operator with different types. Let's go through each line:
console.log( [] + {} )- Output:
"[object Object]" []is coerced to an empty string ""{}is coerced to "[object Object]"- The result is string concatenation
- Output:
console.log( + {} )- Output:
NaN - The unary
+attempts to convert{}to a number, which results inNaN
- Output:
console.log( + [] )- Output:
0 - The unary
+converts an empty array to 0
- Output:
console.log( {} + [])- Output:
"[object Object]" - In most browsers, this is interpreted the same as
({} + []) {}is coerced to "[object Object]" and[]to an empty string
- Output:
console.log( ({}) + [])- Output:
"[object Object]" - Same as #4, but the parentheses ensure
{}is treated as an object literal
- Output:
console.log( ({}) + [])- Output:
"[object Object]" - Identical to #5
- Output:
console.log( ({}) + [])- Output:
"[object Object]" - Identical to #5 and #6
- Output:
console.log( {} + + [])- Output:
"[object Object]0" +[]evaluates to 0- In most browsers, this is interpreted as
({}+0), which coerces{}to "[object Object]" - However, some browsers might interpret this as
{}; +[], which would output 0
- Output:
console.log( {} + + [] + {} )- Output:
"[object Object]0[object Object]" +[]evaluates to 0{}is coerced to "[object Object]"- So it's effectively
"[object Object]" + 0 + "[object Object]"
- Output:
console.log( {} + + [] + {} + [])- Output:
"[object Object]0[object Object]" - Similar to #9, but the final
+ []adds an empty string - It's effectively
"[object Object]" + 0 + "[object Object]" + ""
- Output:
Key points:
- Empty arrays
[]are often coerced to empty strings or 0, depending on the context - Objects
{}are typically coerced to "[object Object]" when used with the+operator - The unary
+operator attempts to convert its operand to a number - The behavior of
{} + ...at the start of a line can be ambiguous and may be interpreted differently in some JavaScript environments - These examples demonstrate why it's important to be explicit about type conversions and to avoid relying on implicit coercion in JavaScript
46. Implicit Coercion IV
46.https://bigfrontend.dev/quiz/implicit-coersion-2
const foo = [0]
if (foo) {
console.log(foo == true)
} else {
console.log(foo == false)
}
Solution: This code demonstrates some interesting aspects of JavaScript's type coercion and truthiness. Let's break it down:
const foo = [0]- This creates a constant
foothat is an array containing a single element, the number 0.
- This creates a constant
if (foo) { ... } else { ... }- In JavaScript, when a non-boolean value is used in a boolean context (like an if statement), it is coerced to a boolean.
- Arrays, even empty ones, are always truthy in JavaScript.
- So,
foo(an array) is coerced totruefor the purpose of the if condition.
Inside the if block:
console.log(foo == true)- This uses the loose equality operator
==, which allows type coercion. - When comparing an array to a boolean using
==, JavaScript follows these steps: a. It tries to convert the array to a primitive value. For arrays, this means calling itstoString()method. b.[0].toString()results in the string "0". c. The string "0" is then compared totrue. d. When comparing a string to a boolean, both are converted to numbers. e. "0" becomes the number 0, and true becomes the number 1. f. 0 is not equal to 1. - Therefore,
foo == trueevaluates tofalse.
- This uses the loose equality operator
The output of this code will be:
false
Key points:
- Arrays are truthy in boolean contexts (like if statements), regardless of their contents.
- The loose equality operator
==performs type coercion, which can lead to unexpected results. - When comparing different types with
==, JavaScript follows a complex set of rules to coerce the values. - In this case, even though
foois truthy in the if statement, it's not loosely equal totrue.
This code highlights the difference between:
- The truthiness of a value (used in boolean contexts like if statements)
- The result of loosely comparing a value to
trueorfalse
It's a good example of why it's often recommended to use strict equality === in JavaScript to avoid unexpected type coercion issues.
24. Equality & Sameness
24.https://bigfrontend.dev/quiz/Equality-Sameness
console.log(0 == '0')
console.log(0 === '0')
console.log(Object.is(0, '0'))
console.log(0 == 0)
console.log(0 === 0)
console.log(Object.is(0, 0))
console.log(0 == -0)
console.log(0 === -0)
console.log(Object.is(0, -0))
console.log(NaN == NaN)
console.log(NaN === NaN)
console.log(Object.is(NaN, NaN))
console.log(0 == false)
console.log(0 === false)
console.log(Object.is(0, false))
Solution: This code demonstrates different comparison methods in JavaScript: loose equality (==), strict equality (===), and Object.is(). Each of these methods has its own rules for determining equality. Let's break down the output and explain each comparison.
Let's go through each comparison:
console.log(0 == '0')Output:true- Loose equality performs type coercion. The string '0' is coerced to a number.
console.log(0 === '0')Output:false- Strict equality checks both value and type. 0 is a number, '0' is a string.
console.log(Object.is(0, '0'))Output:falseObject.is()behaves similarly to===in this case.
console.log(0 == 0)Output:true- Same value and type.
console.log(0 === 0)Output:true- Same value and type.
console.log(Object.is(0, 0))Output:true- Same value and type.
console.log(0 == -0)Output:true- In JavaScript, 0 and -0 are considered equal under
==.
- In JavaScript, 0 and -0 are considered equal under
console.log(0 === -0)Output:true- Even strict equality considers 0 and -0 to be the same.
console.log(Object.is(0, -0))Output:false- This is where
Object.is()differs. It distinguishes between 0 and -0.
- This is where
console.log(NaN == NaN)Output:falseNaNis not equal to anything, including itself, under==.
console.log(NaN === NaN)Output:falseNaNis not equal to anything, including itself, even under===.
console.log(Object.is(NaN, NaN))Output:trueObject.is()considersNaNequal to itself.
console.log(0 == false)Output:true- Loose equality coerces
falseto 0.
- Loose equality coerces
console.log(0 === false)Output:false- Strict equality checks type. 0 is a number,
falseis a boolean.
- Strict equality checks type. 0 is a number,
console.log(Object.is(0, false))Output:falseObject.is()behaves like===in this case.
Key takeaways:
- Loose equality (
==) performs type coercion, which can lead to unexpected results. - Strict equality (
===) compares both value and type, but still considers 0 and -0 to be equal. Object.is()is the strictest form of equality:- It distinguishes between 0 and -0
- It considers NaN equal to itself
- For other comparisons, it generally behaves like
===
- NaN is not equal to itself under
==or===, but is underObject.is(). - When comparing with booleans,
==performs coercion while===andObject.is()do not.
These examples highlight the importance of understanding different equality comparisons in JavaScript, especially when dealing with edge cases like -0, NaN, or type coercion scenarios.
console.log(0 == '0') // true
console.log(0 === '0') // false
console.log(Object.is(0, '0')) // false
console.log(0 == 0) // true
console.log(0 === 0) // true
console.log(Object.is(0, 0)) // true
console.log(0 == -0) // true
console.log(0 === -0) // true
console.log(Object.is(0, -0)) // false
console.log(NaN == NaN) // false
console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
console.log(0 == false) // true
console.log(0 === false) // false
console.log(Object.is(0, false)) // false
26. true or false
26.https://bigfrontend.dev/quiz/true-or-false
console.log([] == 0)
console.log([] == false)
console.log(!![])
console.log([1] == 1)
console.log(!![1])
console.log(Boolean([]))
console.log(Boolean(new Boolean([])))
console.log(Boolean(new Boolean(false)))
Solution:
This code demonstrates some of JavaScript's type coercion rules and the behavior of the Boolean constructor. Let's break it down line by line:
console.log([] == 0)Output:trueExplanation: When comparing an array to a number using==, JavaScript first converts the array to a primitive. An empty array converts to an empty string, which then converts to 0.console.log([] == false)Output:trueExplanation: Both[]andfalseare coerced to the number 0 when using==.console.log(!![])Output:trueExplanation: The!operator first converts[]to a boolean (which istruefor any non-empty object), then negates it. The second!negates it again, resulting intrue.console.log([1] == 1)Output:trueExplanation: The array[1]is first converted to a primitive, which results in the string "1", which is then converted to the number 1.console.log(!![1])Output:trueExplanation: Similar to!![], this converts[1]to a boolean (which istrue), negates it, then negates it again.console.log(Boolean([]))Output:trueExplanation: When converted to a boolean, any non-empty object (including empty arrays) becomestrue.console.log(Boolean(new Boolean([])))Output:trueExplanation:new Boolean([])creates a Boolean object. Any object, when passed toBoolean(), returnstrue.console.log(Boolean(new Boolean(false)))Output:trueExplanation: Although we're passingfalseto the Boolean constructor,new Boolean(false)creates a Boolean object. As before, any object passed toBoolean()returnstrue.
Key takeaways:
Empty arrays (
[]) are truthy when converted to boolean, but can be equal to0orfalsewhen using loose equality (==) due to type coercion.The
!!operator is a common way to convert a value to its boolean equivalent.When comparing arrays to primitives using
==, the array is first converted to a primitive value (often a string), which can lead to unexpected results.Boolean()function converts its argument to a boolean primitive:- Objects (including arrays and Boolean objects) always convert to
true. - Empty strings,
0,null,undefined, andNaNconvert tofalse. - All other values convert to
true.
- Objects (including arrays and Boolean objects) always convert to
new Boolean()creates a Boolean object, which is always truthy, regardless of its content.
These examples highlight the importance of understanding JavaScript's type coercion rules and the difference between primitive boolean values and Boolean objects. It's generally recommended to use strict equality (===) and explicit type conversion to avoid unexpected behavior from implicit type coercion.
console.log([] == 0) // true
console.log([] == false) // true
console.log(!![]) // true
console.log([1] == 1) // true
console.log(!![1]) // true
console.log(Boolean([])) // true
console.log(Boolean(new Boolean([]))) // true
console.log(Boolean(new Boolean(false))) // true
40. RegExp.prototype.test
40.https://bigfrontend.dev/quiz/RegExp-prototype-test
console.log(/^4\d\d$/.test('404'))
console.log(/^4\d\d$/.test(404))
console.log(/^4\d\d$/.test(['404']))
console.log(/^4\d\d$/.test([404]))
Solution:
This code demonstrates how JavaScript's test() method of regular expressions behaves with different types of input. Let's break it down line by line:
console.log(/^4\d\d$/.test('404'))- Output:
true - The regular expression
^4\d\d$matches:^start of the string4the digit 4\d\dtwo digits$end of the string
- '404' is a string that perfectly matches this pattern.
- Output:
console.log(/^4\d\d$/.test(404))- Output:
true - Even though 404 is a number, JavaScript automatically converts it to a string '404' before applying the regex test.
- The result is the same as the first test.
- Output:
console.log(/^4\d\d$/.test(['404']))- Output:
true - When
test()is called with an array, JavaScript converts the array to a string. - ['404'].toString() results in '404'.
- This string matches the regex pattern.
- Output:
console.log(/^4\d\d$/.test([404]))- Output:
true - Similar to the previous case, [404] is converted to a string.
- [404].toString() results in '404'.
- This string matches the regex pattern.
- Output:
Key points:
- The
test()method returnstrueif the regex matches any part of the string, andfalseotherwise. - When
test()is called with a non-string argument, JavaScript automatically converts the argument to a string before testing. - For arrays, the
toString()method is called, which joins array elements with commas. However, in these cases, there's only one element, so no commas are involved. - Numbers are converted to their string representation.
- This behavior can sometimes lead to unexpected results if you're not aware of these automatic type conversions.
This code demonstrates the importance of understanding both regular expressions and JavaScript's type coercion rules when working with the test() method.
56. to primitive
56.https://bigfrontend.dev/quiz/primitive
// case 1
const obj1 = {
valueOf() {
return 1
},
toString() {
return '100'
},
}
console.log(obj1 + 1)
console.log(parseInt(obj1))
// case 2
const obj2 = {
[Symbol.toPrimitive]() {
return 200
},
valueOf() {
return 1
},
toString() {
return '100'
},
}
console.log(obj2 + 1)
console.log(parseInt(obj2))
// case 3
const obj3 = {
toString() {
return '100'
},
}
console.log(+obj3)
console.log(obj3 + 1)
console.log(parseInt(obj3))
// case 4
const obj4 = {
valueOf() {
return 1
},
}
console.log(obj4 + 1)
console.log(parseInt(obj4))
// case 5
const obj5 = {
[Symbol.toPrimitive](hint) {
return hint === 'string' ? '100' : 1
},
}
console.log(obj5 + 1)
console.log(parseInt(obj5))
Solution: This code demonstrates various aspects of type coercion and object-to-primitive conversion in JavaScript. Let's break it down case by case:
Case 1:
const obj1 = {
valueOf() {
return 1
},
toString() {
return '100'
},
}
console.log(obj1 + 1) // Output: 2
console.log(parseInt(obj1)) // Output: 100
- For addition,
valueOfis called first, returning 1. - For
parseInt,toStringis called, returning '100'.
Case 2:
const obj2 = {
[Symbol.toPrimitive]() {
return 200
},
valueOf() {
return 1
},
toString() {
return '100'
},
}
console.log(obj2 + 1) // Output: 201
console.log(parseInt(obj2)) // Output: 200
Symbol.toPrimitivetakes precedence overvalueOfandtoString.
Case 3:
const obj3 = {
toString() {
return '100'
},
}
console.log(+obj3) // Output: 100
console.log(obj3 + 1) // Output: 1001
console.log(parseInt(obj3)) // Output: 100
- Without
valueOf,toStringis used for all conversions. - Unary plus (+) converts '100' to number 100.
- Addition concatenates '100' and '1'.
Case 4:
const obj4 = {
valueOf() {
return 1
},
}
console.log(obj4 + 1) // Output: 2
console.log(parseInt(obj4)) // Output: NaN
valueOfis used for addition.parseInttries to usetoString, which defaults to "[object Object]", resulting in NaN.
Case 5:
const obj5 = {
[Symbol.toPrimitive](hint) {
return hint === 'string' ? '100' : 1
},
}
console.log(obj5 + 1) // Output: 2
console.log(parseInt(obj5)) // Output: 100
Symbol.toPrimitiveallows different results based on the conversion hint.- For addition, the numeric result (1) is used.
- For
parseInt, the string result ('100') is used.
Key points:
- The order of precedence for object-to-primitive conversion is:
Symbol.toPrimitive>valueOf>toString - Different operations may prefer numeric or string conversion.
parseIntalways converts its argument to a string first.- The unary plus operator (+) forces numeric conversion.
- Addition (+) can result in either numeric addition or string concatenation, depending on the operands.
This quiz tests deep understanding of JavaScript's type coercion rules and object-to-primitive conversion mechanisms.
4.Closure
A closure in JavaScript is created when a function is defined within another function. It allows the inner function to access the variables and parameters of the outer function, even after the outer function has finished executing.
- Encapsulation with Closures:
// Outer function definition
function createCounter() {
// Variable declaration in the outer function's scope
let count = 0
// Returning an object with methods
return {
// Increment method
increment: function () {
count++
},
// Decrement method
decrement: function () {
count--
},
// Get count method
getCount: function () {
return count
},
}
}
// Create a counter instance
const counter = createCounter()
// Increment the counter twice
counter.increment()
counter.increment()
// Log the count to the console
console.log(counter.getCount()) // Output: 2
// Decrement the counter
counter.decrement()
// Log the count to the console
console.log(counter.getCount()) // Output: 1
- Function Factories:
// Define createMultiplier
function createMultiplier(multiplier) {
// Return a new function
return function (number) {
return number * multiplier // Multiply input number by multiplier
}
}
const double = createMultiplier(2) // Function to double a number
const triple = createMultiplier(3) // Function to triple a number
console.log(double(5)) // 10
console.log(triple(5)) // 15
- Unintended Closures:
// Define fetchData
function fetchData(apiUrl, callback) {
// Simulate an asynchronous operation
setTimeout(() => {
const data = { name: 'John Doe', age: 30 } // Simulated API response
callback(data) // Call the provided callback with the data
}, 1000) // Simulate network delay
}
// Define processData
function processData(data) {
console.log('Received data:', data) // Process and log the data
}
fetchData('https://api.example.com/user', processData) // Fetch data and process it
- Callback Functions:
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i) // Log the value of i
}, i * 1000) // Delay based on i
}
// Output: 6, 6, 6, 6, 6
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i) // Log the value of i
}, i * 1000) // Delay based on i
}
// Output: 1, 2, 3, 4, 5
- Memory Leaks:
// Define createClosure:
function createClosure() {
// Declare a large array largeArray
let largeArray = new Array(1000000).fill('x') // Large array
// Return a closure
return function () {
console.log(largeArray.length) // Log the length of the array
largeArray = null // Free up memory
}
}
// Create a closure instance
const closure = createClosure()
// Invoke the closure
closure() // Output: 1000000
- Memoization:
// Define memoize Function
function memoize(fn) {
const cache = {} // Private cache object
return function (...args) {
const key = JSON.stringify(args) // Create a key from arguments
if (cache[key]) {
return cache[key] // Return cached result
}
const result = fn(...args)
cache[key] = result // Store result in cache
return result
}
}
// Example usage:
const fibonacci = memoize(function (n) {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.log(fibonacci(10)) // 55
- Event Handling:
function createEventListener(element, eventType) {
return function (callback) {
element.addEventListener(eventType, callback)
}
}
// Example usage:
const button = document.getElementById('myButton')
const onClick = createEventListener(button, 'click')
onClick(function () {
console.log('Button clicked!')
})
- Private Members in Constructors:
function Person(name, age) {
const privateData = { secret: 'I have a secret!' } // Private data
this.name = name
this.age = age
this.getSecret = function () {
return privateData.secret
}
}
// Example usage:
const john = new Person('John', 30)
console.log(john.name) // 'John'
console.log(john.getSecret()) // 'I have a secret!'
console.log(john.privateData) // undefined (private)
- Managing Dependencies:
function createModule(dependency) {
// Private dependency
const privateDependency = dependency
// Public methods
return {
useDependency: function () {
console.log(privateDependency)
},
}
}
// Example usage:
const module = createModule('Dependency')
module.useDependency() // Output: 'Dependency'
- Currying
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args)
} else {
return function (...moreArgs) {
return curried(...args, ...moreArgs)
}
}
}
}
// Example usage:
function add(a, b, c) {
return a + b + c
}
const curriedAdd = curry(add)
console.log(curriedAdd(1)(2)(3)) // Output: 6
- Promises and Asynchronous Operations:
function fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => response.json())
.then((data) => {
resolve(data)
})
.catch((error) => {
reject(error)
})
})
}
// Example usage:
const url = 'https://api.example.com/data'
fetchData(url)
.then((data) => {
console.log('Data fetched:', data)
})
.catch((error) => {
console.error('Error fetching data:', error)
})
- Iterators and Generators
function createIterator(arr) {
let index = 0 // Private variable
return {
next: function () {
return index < arr.length ? { value: arr[index++], done: false } : { done: true }
},
}
}
// Example usage:
const iterator = createIterator(['a', 'b', 'c'])
console.log(iterator.next()) // Output: { value: 'a', done: false }
console.log(iterator.next()) // Output: { value: 'b', done: false }
console.log(iterator.next()) // Output: { value: 'c', done: false }
console.log(iterator.next()) // Output: { done: true }
- Functional Programming
function compose(...fns) {
return function (result) {
return fns.reduceRight((acc, fn) => fn(acc), result)
}
}
// Example usage:
const add1 = (x) => x + 1
const multiply2 = (x) => x * 2
const add1ThenMultiply2 = compose(multiply2, add1)
console.log(add1ThenMultiply2(5)) // Output: 12 (5 + 1 = 6, 6 * 2 = 12)
- Timer Functions
function debounce(fn, delay) {
let timeoutID // Private variable
return function (...args) {
clearTimeout(timeoutID) // Clear previous timeout
timeoutID = setTimeout(() => {
fn(...args)
}, delay)
}
}
// Example usage:
const handleResize = debounce(() => {
console.log('Window resized')
}, 300)
window.addEventListener('resize', handleResize)
Reference: https://dev.to/jahid6597/mastering-javascript-closures-a-comprehensive-guide-1gd7
99. closure
99.https://bigfrontend.dev/quiz/closure-1
let dev = 'bfe'
function a() {
let dev = 'BFE'
return function () {
console.log(dev)
}
}
dev = 'bigfrontend'
a()()
Solution: Let's break down this code step by step:
let dev = 'bfe'
function a() {
let dev = 'BFE'
return function () {
console.log(dev)
}
}
dev = 'bigfrontend'
a()()
We start by declaring a global variable
devand assigning it the value 'bfe'.We define a function
a(). Inside this function:- We declare a new local variable
devand assign it the value 'BFE'. - We return an anonymous function that, when called, will log the value of
dev.
- We declare a new local variable
We reassign the global
devvariable to 'bigfrontend'.Finally, we call
a()(). This is actually two function calls:- First, we call
a(), which returns the inner anonymous function. - Then we immediately call that returned function.
- First, we call
Now, the key to understanding this code is the concept of closures in JavaScript. When a function is created, it retains access to the variables in its lexical scope, even if it's executed in a different scope.
So, when the inner anonymous function is created inside a(), it "closes over" the local dev variable, which has the value 'BFE'.
Even though we later change the global dev to 'bigfrontend', this doesn't affect the dev that the inner function has access to.
Therefore, when we execute a()(), it logs the value of the dev variable that was captured in its closure, which is 'BFE'.
The output of this code will be:
BFE
This example demonstrates:
- Variable scoping: The
devinsidea()is a different variable from the globaldev. - Closures: The inner function maintains access to the
devvariable from its containing scope. - The difference between variable declaration and assignment.
It's a common interview question that tests understanding of these fundamental JavaScript concepts.
5.Prototype
48. Prototype
48.https://bigfrontend.dev/quiz/prototype
function Foo() {}
Foo.prototype.bar = 1
const a = new Foo()
console.log(a.bar)
Foo.prototype.bar = 2
const b = new Foo()
console.log(a.bar)
console.log(b.bar)
Foo.prototype = { bar: 3 }
const c = new Foo()
console.log(a.bar)
console.log(b.bar)
console.log(c.bar)
Solution: This code demonstrates the concept of prototypal inheritance in JavaScript and how changes to the prototype affect instances of an object. Let's break it down step by step:
function Foo() { }- This defines a constructor function
Foo.
- This defines a constructor function
Foo.prototype.bar = 1- This adds a property
barwith value 1 toFoo's prototype.
- This adds a property
const a = new Foo()- This creates a new instance of
Foocalleda.
- This creates a new instance of
console.log(a.bar)- This logs 1, as
ainheritsbarfromFoo's prototype.
- This logs 1, as
Foo.prototype.bar = 2- This changes the value of
barinFoo's prototype to 2.
- This changes the value of
const b = new Foo()- This creates another instance of
Foocalledb.
- This creates another instance of
console.log(a.bar)- This logs 2, as
astill references the same prototype object, which now hasbarset to 2.
- This logs 2, as
console.log(b.bar)- This also logs 2, as
breferences the same prototype object asa.
- This also logs 2, as
Foo.prototype = {bar: 3}- This completely replaces
Foo's prototype with a new object that hasbarset to 3.
- This completely replaces
const c = new Foo()- This creates a third instance of
Foocalledc, which will use the new prototype.
- This creates a third instance of
console.log(a.bar)- This still logs 2, because
astill references the old prototype object.
- This still logs 2, because
console.log(b.bar)- This also logs 2, because
balso still references the old prototype object.
- This also logs 2, because
console.log(c.bar)- This logs 3, because
cwas created after the prototype was replaced and thus uses the new prototype object.
- This logs 3, because
The output will be:
1
2
2
2
2
3
Key points:
- Objects created with
newinherit properties from their constructor's prototype. - Changing a property on the prototype affects all instances that refer to that prototype.
- Replacing the entire prototype only affects instances created afterwards; existing instances still reference the old prototype.
- This demonstrates why it's generally better to modify existing prototypes rather than replacing them entirely, as replacing can break the prototype chain for existing instances.
This quiz tests understanding of JavaScript's prototypal inheritance model and how changes to prototypes affect object instances.
53. Prototype 2
53.https://bigfrontend.dev/quiz/prototype2
function F() {
this.foo = 'bar'
}
const f = new F()
console.log(f.prototype)
Solution:
This code demonstrates some fundamental concepts of JavaScript's object-oriented programming and prototypes. Let's break it down:
function F() { this.foo = 'bar' }- This defines a constructor function
F. - When called with
new, it creates an object and sets itsfooproperty to 'bar'.
- This defines a constructor function
const f = new F()- This creates a new instance of
Fand assigns it to the variablef. - The
newkeyword does several things: a. Creates a new empty object. b. Sets this new object's internal[[Prototype]](i.e.,__proto__) to the object referenced byF.prototype. c. CallsFwiththisset to the new object. d. Returns the new object (unless the constructor returns a different object).
- This creates a new instance of
console.log(f.prototype)- This attempts to log the
prototypeproperty off. - The output will be:
undefined
- This attempts to log the
The key point here is that prototype is a property of constructor functions, not of instances created from those constructors.
F.prototypeexists and is an object (by default, it's an empty object unless explicitly set to something else).fis an instance created fromF. It doesn't have its ownprototypeproperty.fdoes have an internal[[Prototype]](accessible via__proto__in most environments), which referencesF.prototype.
If we wanted to access the prototype of f, we could use:
Object.getPrototypeOf(f)- or
f.__proto__(though__proto__is deprecated for use in production code)
Both of these would give us the same object that F.prototype refers to.
This quiz tests the understanding of:
- The difference between a constructor function's
prototypeand an instance's[[Prototype]]. - How the
newkeyword works with constructor functions. - The fact that regular objects (instances) don't have a
prototypeproperty, even though they do have a prototype in their prototype chain.
51. method
51.https://bigfrontend.dev/quiz/method
// This is a trick question
// case 1
const obj1 = {
foo() {
console.log(super.foo())
},
}
Object.setPrototypeOf(obj1, {
foo() {
return 'bar'
},
})
obj1.foo()
// case 2
const obj2 = {
foo: function () {
console.log(super.foo())
},
}
Object.setPrototypeOf(obj2, {
foo() {
return 'bar'
},
})
obj2.foo()
Solution: This code demonstrates the behavior of the super keyword in JavaScript, specifically in object literals and method definitions. It's indeed a tricky question because it highlights a subtle difference in how super works in different contexts. Let's break it down:
Case 1:
const obj1 = {
foo() {
// method
console.log(super.foo())
},
}
Object.setPrototypeOf(obj1, {
foo() {
return 'bar'
},
})
obj1.foo()
In this case:
obj1is created with a methodfoousing the shorthand method syntax.- The prototype of
obj1is set to an object with its ownfoomethod. - When
obj1.foo()is called, it successfully usessuper.foo()to call thefoomethod from its prototype. - The output will be:
bar
Case 2:
const obj2 = {
foo: function () {
// property
console.log(super.foo())
},
}
Object.setPrototypeOf(obj2, {
foo() {
return 'bar'
},
})
obj2.foo()
In this case:
obj2is created with afooproperty assigned a function expression.- The prototype of
obj2is set to an object with its ownfoomethod. - When
obj2.foo()is called, it throws aSyntaxErrorbecausesuperis not allowed in this context.
The key difference:
- In Case 1, the method is defined using the shorthand syntax
foo() {...}, which allows the use ofsuper. - In Case 2, the method is defined as a property with a function expression
foo: function() {...}, which does not allow the use ofsuper.
The output will be: For Case 1: bar For Case 2: SyntaxError: 'super' keyword unexpected here
This quiz highlights that:
- The
superkeyword can be used in object literals, but only in methods defined using the shorthand syntax. - Function expressions assigned to object properties cannot use
super, even if they're used as methods. - The ability to use
superdepends on how a method is defined, not just on whether it's a method of an object.
This is a nuanced aspect of JavaScript that even experienced developers might not be aware of, making it an excellent "trick question" for testing deep language knowledge.
104. Inheritance
104.https://bigfrontend.dev/quiz/Inheritance
class A {
a = 'a'
}
A.prototype.c = 'c'
class B extends A {
b = 'b'
}
const a = new A()
const b = new B()
console.log(a.a)
console.log(a.b)
console.log(a.c)
console.log(b.a)
console.log(b.b)
console.log(b.c)
Solution:
Let's break down this code and explain what's happening:
class A {
a = 'a'
}
A.prototype.c = 'c'
class B extends A {
b = 'b'
}
const a = new A()
const b = new B()
console.log(a.a)
console.log(a.b)
console.log(a.c)
console.log(b.a)
console.log(b.b)
console.log(b.c)
This code demonstrates class inheritance and prototype chain in JavaScript. Let's go through it step by step:
We define class
Awith a propertyaset to 'a'.We add a property
ctoA's prototype, setting it to 'c'.We define class
Bthat extendsA, with its own propertybset to 'b'.We create an instance of
Acalleda.We create an instance of
Bcalledb.We then try to access various properties on both instances.
Now, let's look at each console.log statement:
console.log(a.a):ais an instance ofA, which has the propertya.- This logs
'a'.
console.log(a.b):ais an instance ofA, which doesn't have a propertyb.- This logs
undefined.
console.log(a.c):adoesn't have its own propertyc, but it can accesscfromA.prototype.- This logs
'c'.
console.log(b.a):bis an instance ofB, which extendsA. It inherits the propertyafromA.- This logs
'a'.
console.log(b.b):bis an instance ofB, which has its own propertyb.- This logs
'b'.
console.log(b.c):bdoesn't have its own propertyc, but it can accesscfromA.prototypethrough inheritance.- This logs
'c'.
Therefore, the output will be:
'a'
undefined
'c'
'a'
'b'
'c'
This code demonstrates several important concepts in JavaScript:
- Class definition and inheritance (
classandextendskeywords). - Instance properties (defined in the class body with
=). - Prototype properties (added to
A.prototype). - Property lookup through the prototype chain.
- How subclasses inherit both instance and prototype properties from their parent class.
It's a great example to understand how JavaScript handles object-oriented programming concepts like inheritance and the prototype chain.
129. Function II
129.https://bigfrontend.dev/quiz/function-ii
console.log(Function.prototype.__proto__ === Object.prototype) // true
console.log(Function.__proto__ === Object.__proto__) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
console.log(Object.constructor.prototype === Object.prototype) // false
console.log(Function.constructor === Function) // true
console.log(Object.constructor === Object) // false
console.log(Array.__proto__ === Function.__proto__) // true
console.log(Array.constructor === Function) // true
console.log(Object.__proto__ === Function) // false
console.log(Function.__proto__ === Function.prototype) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Map instanceof Map) // false
Solution: This code explores the prototype chain and constructor relationships in JavaScript. Let's break it down line by line:
console.log(Function.prototype.__proto__ === Object.prototype)- True. The prototype of Function.prototype is Object.prototype.
console.log(Function.__proto__ === Object.__proto__)- True. Both Function and Object are functions, so they share the same prototype (Function.prototype).
console.log(Function.__proto__.__proto__ === Object.prototype)- True. Function.proto is Function.prototype, and its prototype is Object.prototype.
console.log(Object.constructor.prototype === Object.prototype)- False. Object.constructor is Function, so this compares Function.prototype to Object.prototype.
console.log(Function.constructor === Function)- True. Function is its own constructor.
console.log(Object.constructor === Object)- False. Object's constructor is Function, not Object itself.
console.log(Array.__proto__ === Function.__proto__)- True. Both Array and Function are functions, so they share the same prototype (Function.prototype).
console.log(Array.constructor === Function)- True. The constructor of Array is Function.
console.log(Object.__proto__ === Function)- False. Object.proto is Function.prototype, not Function itself.
console.log(Function.__proto__ === Function.prototype)- True. Function's prototype is Function.prototype.
console.log(Object instanceof Object)- True. The prototype chain of Object eventually leads to Object.prototype.
console.log(Function instanceof Function)- True. Function is an instance of itself.
console.log(Map instanceof Map)- False. Map is not an instance of itself; it's an instance of Function.
Key points:
- In JavaScript, functions are objects, and they have a prototype chain.
- The
__proto__property (or[[Prototype]]internal slot) points to the prototype of an object. - The
prototypeproperty is used for instances created withnew. Functionis a special object that is its own constructor.- The prototype chain can be circular, which is why some of these comparisons are true.
This code demonstrates the complex relationships between objects, functions, and prototypes in JavaScript's object-oriented system.
133. toString
132.https://bigfrontend.dev/quiz/tostring
const obj = {}
const fun = () => {}
console.log(obj.toString === Object.toString)
console.log(fun.toString === Function.toString)
console.log(obj.toString === Object.prototype.toString)
console.log(fun.toString === Function.prototype.toString)
console.log(Object.toString === Object.prototype.toString)
console.log(Function.toString === Function.prototype.toString)
Solution:
Certainly! Let's break down this code and explain each comparison:
const obj = {}
const fun = () => {}
console.log(obj.toString === Object.toString)
console.log(fun.toString === Function.toString)
console.log(obj.toString === Object.prototype.toString)
console.log(fun.toString === Function.prototype.toString)
console.log(Object.toString === Object.prototype.toString)
console.log(Function.toString === Function.prototype.toString)
Let's go through each comparison:
console.log(obj.toString === Object.toString)- Result:
false obj.toStringrefers toObject.prototype.toStringObject.toStringrefers toFunction.prototype.toString(becauseObjectis a function)
- Result:
console.log(fun.toString === Function.toString)- Result:
true fun.toStringrefers toFunction.prototype.toStringFunction.toStringalso refers toFunction.prototype.toString,- Since all functions in JavaScript inherit from
Function.prototype,fun.toStringis equivalent to the same method defined onFunction.prototype. Thus, it logs true
- Result:
console.log(obj.toString === Object.prototype.toString)- Result:
true obj.toStringis inherited fromObject.prototype.toString
- Result:
console.log(fun.toString === Function.prototype.toString)- Result:
true fun.toStringis inherited fromFunction.prototype.toString
- Result:
console.log(Object.toString === Object.prototype.toString)- Result:
false Object.toStringrefers toFunction.prototype.toString(becauseObjectis a function)Object.prototype.toStringis a different method
- Result:
console.log(Function.toString === Function.prototype.toString)- Result:
true Function.toStringlooks for thetoStringmethod on theFunctionobject itself. If its not found there, it'll look up the prototype chain and find it onFunction.prototype. Therefore, both expressions essentially point to the same method. Logs true
- Result:
Key points to understand:
Objects inherit methods from their prototype chain. Regular objects inherit from
Object.prototype, while functions inherit fromFunction.prototype.Object,Function, and other built-in constructors are themselves functions, so they inherit fromFunction.prototype.The
toStringmethod on most objects comes fromObject.prototype.toString.The
toStringmethod on functions comes fromFunction.prototype.toString.When you access a method directly on a constructor (like
Object.toStringorFunction.toString), you're getting the method fromFunction.prototype, not from the constructor's own prototype.Even when two references point to the same method implementation, they might not be strictly equal (
===) if they're accessed through different paths in the prototype chain.
This code demonstrates the nuances of method inheritance and the prototype chain in JavaScript, particularly how built-in objects and functions relate to each other.
134. Object.groupBy
132.https://bigfrontend.dev/quiz/object-groupby
const items = [
{
id: 'a',
},
{
id: 'b',
},
]
const groups = Object.groupBy(items, ({ id }) => id)
console.log(items.toString())
console.log(groups.toString())
Solution:
Certainly! Let's break down this code and explain what's happening:
const items = [
{
id: 'a',
},
{
id: 'b',
},
]
const groups = Object.groupBy(items, ({ id }) => id)
console.log(items.toString())
console.log(groups.toString())
Let's go through this step by step:
We start with an array
itemscontaining two objects, each with anidproperty.Object.groupBy(items, ({id}) => id):- This is using the
Object.groupBy()method, which is a relatively new addition to JavaScript (as of my last update, it was still at the proposal stage). - It groups the elements of the
itemsarray based on the return value of the callback function. - The callback function
({id}) => idextracts theidproperty from each object and uses it as the grouping key. - The result is an object where the keys are the
idvalues, and the values are arrays of objects with thatid.
- This is using the
console.log(items.toString()):- When you call
toString()on an array, it callstoString()on each element and joins the results with commas. - For objects, the default
toString()method returns"[object Object]". - So this will output:
[object Object],[object Object]
- When you call
console.log(groups.toString()):groupsis a null-prototype object with properties for all groups, each assigned to an array containing the elements of the associated group.- null-prototype object lacks the
Object.prototype.toString()method and will trigger error.
Therefore, the output will be:
[object Object],[object Object]
TypeError: groups.toString is not a function
Key points to understand:
Object.groupBy()is a powerful method for grouping array elements. It creates a null-prototype object and lacks the theObject.prototype.toString()method.The
toString()method doesn't provide a very informative representation of objects or complex data structures. It's generally more useful to useJSON.stringify()or custom logging for these kinds of structures.When called on an array,
toString()gives a string representation of the array's elements.When called on an object,
toString()simply returns"[object Object]"unless it's overridden.
6.this
6. Arrow Function
6.https://bigfrontend.dev/quiz/6-Arrow-Function
const obj = {
dev: 'bfe',
a: function () {
return this.dev
},
b() {
return this.dev
},
c: () => {
return this.dev
},
d: function () {
return (() => {
return this.dev
})()
},
e: function () {
return this.b()
},
f: function () {
return this.b
},
g: function () {
return this.c()
},
h: function () {
return this.c
},
i: function () {
return () => {
return this.dev
}
},
}
console.log(obj.a())
console.log(obj.b())
console.log(obj.c())
console.log(obj.d())
console.log(obj.e())
console.log(obj.f()())
console.log(obj.g())
console.log(obj.h()())
console.log(obj.i()())
Solution: Let's break it down step by step:
We have an object
objwith a propertydevand several methods.obj.a()andobj.b(): These are regular function methods. When called,thisrefers to the object, so they return'bfe'.obj.c(): This is an arrow function. Arrow functions don't have their ownthiscontext, sothis.devhere refers to the global (or module) scope, which doesn't have adevproperty. It will returnundefined.obj.d(): This is a regular function that returns the result of an immediately invoked arrow function. The arrow function captures thethisfrom the outer function, so it returns'bfe'.obj.e(): This callsthis.b(), which is equivalent to callingobj.b(), so it returns'bfe'.obj.f(): This returns the functionbitself (without calling it).obj.f()()then calls this returned function, and since it's called without a context,thiswill beundefinedorglobalin non-strict mode. It will likely returnundefined.obj.g(): This callsthis.c(), which is equivalent toobj.c(). As explained earlier, this will returnundefined.obj.h(): This returns the arrow functioncitself.obj.h()()then calls this returned arrow function. Arrow functions keep their originalthisbinding, which in this case is the global context, so it will returnundefined.obj.i(): This returns a new arrow function. When this returned function is called withobj.i()(), it uses thethisfrom where it was defined (which is theobj), so it returns'bfe'.
So, the output will be:
'bfe'
'bfe'
undefined
'bfe'
'bfe'
undefined
undefined
undefined
'bfe'
This code demonstrates various behaviors of this in different function types and calling contexts in JavaScript.
102. Arrow Function II
102.https://bigfrontend.dev/quiz/Arrow-Function-II
class Site {
name = 'BFE'
getHandle() {
return {
name: 'bigfrontend',
getName1() {
return this.name
},
getName2: () => {
return this.name
},
getName3: function () {
return this.name
},
}
}
}
const site = new Site()
console.log(site.getHandle().getName1())
console.log(site.getHandle().getName2())
console.log(site.getHandle().getName3())
Solution:
Let's break down this code and explain what's happening:
class Site {
name = 'BFE'
getHandle() {
return {
name: 'bigfrontend',
getName1() {
return this.name
},
getName2: () => {
return this.name
},
getName3: function () {
return this.name
},
}
}
}
const site = new Site()
console.log(site.getHandle().getName1())
console.log(site.getHandle().getName2())
console.log(site.getHandle().getName3())
This code demonstrates different ways of defining methods and how they affect the this context. Let's go through it step by step:
We define a
Siteclass with a propertynameset to 'BFE'.The
getHandle()method returns an object with its ownnameproperty and three methods to get the name.We create an instance of
Sitecalledsite.We call each of the getName methods through
site.getHandle().
Now, let's look at each method call:
site.getHandle().getName1()getName1is defined as a method using the shorthand syntax.- When called,
thisrefers to the object it's called on (the object returned bygetHandle()). - This object has
name: 'bigfrontend', so it returns'bigfrontend'.
site.getHandle().getName2()getName2is defined as an arrow function.- Arrow functions do not have their own
thiscontext. They inheritthisfrom the enclosing scope. - In this case, the enclosing scope is the
Siteinstance, wherenameis 'BFE'. - So this returns
'BFE'.
site.getHandle().getName3()getName3is defined as a regular function.- Like
getName1, when called,thisrefers to the object it's called on (the object returned bygetHandle()). - This object has
name: 'bigfrontend', so it returns'bigfrontend'.
Therefore, the output will be:
bigfrontend
BFE
bigfrontend
This code demonstrates several important concepts in JavaScript:
- Method definitions and their effect on
thisbinding. - The behavior of arrow functions with regard to
this. - The difference between the
thiscontext in methods and in the class itself.
It's a great example to understand how this works in different contexts and how arrow functions can be used to maintain the outer this context.
19. this
19.https://bigfrontend.dev/quiz/this
const obj = {
a: 1,
b: function () {
console.log(this.a)
},
c() {
console.log(this.a)
},
d: () => {
console.log(this.a)
},
e: (function () {
return () => {
console.log(this.a)
}
})(),
f: function () {
return () => {
console.log(this.a)
}
},
}
console.log(obj.a)
obj.b()
obj.b()
const b = obj.b
b()
obj.b.apply({ a: 2 })
obj.c()
obj.d()
obj.d()
obj.d.apply({ a: 2 })
obj.e()
obj.e()
obj.e.call({ a: 2 })
obj.f()()
obj.f()()
obj.f().call({ a: 2 })
Solution:
Let's analyze each line of output:
console.log(obj.a)Output:1Explanation: This simply logs the value of theaproperty ofobj.obj.b()Output:1Explanation: When called as a method,thisinsidebrefers toobj.;(obj.b)()Output:1Explanation: The parentheses don't change the context, sothisstill refers toobj.const b = obj.b; b()Output:undefinedExplanation: Whenbis called as a standalone function,thisbecomes the global object (orundefinedin strict mode), which doesn't have anaproperty.obj.b.apply({a: 2})Output:2Explanation:applyexplicitly setsthisto the provided object{a: 2}.obj.c()Output:1Explanation: Method shorthand syntax behaves the same asb().obj.d()Output:undefinedExplanation: Arrow functions capturethisfrom their enclosing lexical scope at the time of their creation. Here,thisrefers to the global object (orundefinedin modules).;(obj.d)()Output:undefinedExplanation: Same as above, the arrow function'sthisis not affected by how it's called.obj.d.apply({a:2})Output:undefinedExplanation:applydoesn't affect arrow functions'thisbinding.obj.e()Output:undefinedExplanation:eis an immediately invoked function expression (IIFE) that returns an arrow function. The IIFE'sthisis the global object, so the returned arrow function'sthisis also the global object.;(obj.e)()Output:undefinedExplanation: Same as above, parentheses don't change the behavior.obj.e.call({a:2})Output:undefinedExplanation:calldoesn't affect the arrow function'sthisbinding.obj.f()()Output:1Explanation:fis a regular function, so itsthisisobj. It returns an arrow function that captures thisthis, so it logsobj.awhich is1.;(obj.f())()Output:1Explanation: Same as above, parentheses don't change the behavior.obj.f().call({a:2})Output:1Explanation: The arrow function returned byfhas already capturedthisasobj, socalldoesn't affect itsthisbinding.
Key takeaways:
- Regular functions and method shorthand syntax determine
thisbased on how they're called. - Arrow functions capture
thisfrom their enclosing lexical scope and are not affected by how they're called. - The
apply,call, andbindmethods can changethisfor regular functions but not for arrow functions. - Be careful when using arrow functions as object methods if you need
thisto refer to the object.
33. this II
33.https://bigfrontend.dev/quiz/this-II
const obj = {
a: 1,
b() {
return this.a
},
}
console.log(obj.b())
console.log((true ? obj.b : a)())
console.log((true, obj.b)())
console.log((3, obj['b'])())
console.log(obj.b())
console.log((obj.c = obj.b)())
Solution:
Here we define an object obj with a property a set to 1 and a method b that returns this.a.
Now let's look at each console.log statement:
console.log(obj.b())Output:1When called as a method onobj,thisinsideb()refers toobj, sothis.ais 1.console.log((true ? obj.b : a)())Output:undefinedThe ternary operator returnsobj.b, but it's then called without any context. This is equivalent tolet temp = obj.b; temp(). In this case,thisbecomesundefinedin strict mode or the global object in non-strict mode. Either way,this.ais undefined.console.log((true, obj.b)())Output:undefinedThe comma operator evaluates both operands and returns the last one. Similar to #2, this detachesbfromobjbefore calling it, sothisis lost.console.log((3, obj['b'])())Output:undefinedThis is the same as #3, just using bracket notation to accessb. The function is still called without context.console.log((obj.b)())Output:1Parentheses don't affect thethisbinding.obj.bis called as a method onobj, sothisisobj.console.log((obj.c = obj.b)())Output:undefinedThis assignsobj.bto a new propertyconobj, but then immediately calls the result of this assignment. The assignment expression returns the assigned value (the function), but detached fromobj. So it's called without context, similar to #2 and #3.
Key points to understand:
- When a function is called as a method (like
obj.b()),thisinside the function refers to the object it was called on. - When a function is called standalone (like
let f = obj.b; f()),thisis undefined (in strict mode) or the global object (in non-strict mode). - The way a function is called matters more than how it's defined when determining
this. - Parentheses around a method call don't affect
thisbinding. - Operations like the ternary operator, comma operator, or assignment can detach a method from its object, causing it to lose its
thisbinding when called.
This code demonstrates some of the trickier aspects of this in JavaScript, which can be a source of bugs if not well understood. It's one reason why arrow functions, which don't have their own this context, are often used in modern JavaScript. Would you like me to elaborate on any part of this explanation?
const obj = {
a: 1,
b() {
return this.a
},
}
console.log(obj.b()) // 1
console.log((true ? obj.b : a)()) // undefined
console.log((true, obj.b)()) // undefined
console.log((3, obj['b'])()) // undefined
console.log(obj.b()) // 1
console.log((obj.c = obj.b)()) // undefined
41. this III
41.https://bigfrontend.dev/quiz/this-III
const obj = {
a: 1,
b: this.a + 1,
c: () => this.a + 1,
d() {
return this.a + 1
},
e() {
return (() => this.a + 1)()
},
}
console.log(obj.b)
console.log(obj.c())
console.log(obj.d())
console.log(obj.e())
Solution:
This code explores the behavior of this in different contexts within an object. Let's break it down:
const obj = { ... }- This creates an object with properties a, b, c, d, and e.
a: 1- Simple property assignment.
b: this.a + 1- In this context,
thisrefers to the global object (or undefined in strict mode), not toobj. this.ais undefined, sothis.a + 1evaluates to NaN.
- In this context,
c: () => this.a + 1- This is an arrow function. Arrow functions capture
thisfrom their surrounding lexical scope. - Here,
thisis the same as in the global scope (likely window in a browser, or global in Node.js). this.ais undefined, so this will return NaN when called.
- This is an arrow function. Arrow functions capture
d() { return this.a + 1 }- This is a method. When called as
obj.d(),thiswill refer toobj. - It will return
obj.a + 1, which is 2.
- This is a method. When called as
e() { return (() => this.a + 1)() }- This method returns the result of an immediately invoked arrow function.
- The arrow function captures
thisfrom theemethod, wherethisrefers toobj. - It will return
obj.a + 1, which is 2.
Now, let's look at the console.log statements:
console.log(obj.b)- Outputs:
NaN - This is the value of
bcomputed when the object was created.
- Outputs:
console.log(obj.c())- Outputs:
NaN - The arrow function
cusesthisfrom the global scope, whereais undefined.
- Outputs:
console.log(obj.d())- Outputs:
2 - When called as a method,
thisrefers toobj, so it returns1 + 1.
- Outputs:
console.log(obj.e())- Outputs:
2 - The arrow function inside
ecapturesthisfrom the method scope, which isobj.
- Outputs:
In summary, the output will be:
NaN
NaN
2
2
This quiz demonstrates the complexity of this in JavaScript, especially:
- How
thisbehaves differently in arrow functions vs. regular functions - The importance of how a function is called (as a method or not)
- How
thisis determined at the time of object creation vs. function execution
49. this IV
49.https://bigfrontend.dev/quiz/this-4
var bar = 1
function foo() {
return this.bar++
}
const a = {
bar: 10,
foo1: foo,
foo2: function () {
return foo()
},
}
console.log(a.foo1.call())
console.log(a.foo1())
console.log(a.foo2.call())
console.log(a.foo2())
Solution:
This code demonstrates the behavior of this in JavaScript and how it changes based on how a function is called. Let's break it down step by step:
var bar = 1- This declares a global variable
barwith value 1.
- This declares a global variable
function foo() { return this.bar++ }- This defines a function
foothat increments and returnsthis.bar.
- This defines a function
const a = { ... }- This creates an object
awith propertiesbar,foo1, andfoo2.
- This creates an object
Now, let's look at each console.log statement:
console.log(a.foo1.call())call()without arguments setsthisto the global object (or undefined in strict mode).- In non-strict mode, this increments and returns the global
bar(1), then sets it to 2. - Output: 1
console.log(a.foo1())- This calls
foo1as a method ofa, sothisrefers toa. - It increments and returns
a.bar(10), then sets it to 11. - Output: 10
- This calls
console.log(a.foo2.call())call()is used onfoo2, butfoo2internally callsfoo()without any context.- In
foo(),thisrefers to the global object again. - It increments and returns the global
bar(2), then sets it to 3. - Output: 2
console.log(a.foo2())- This calls
foo2as a method ofa, butfoo2internally callsfoo()without any context. - In
foo(),thisagain refers to the global object. - It increments and returns the global
bar(3), then sets it to 4. - Output: 3
- This calls
So, the final output will be:
1
10
2
3
Key points:
- The value of
thisis determined by how a function is called, not where it's defined. - Using
call()without arguments or withnull/undefinedsetsthisto the global object in non-strict mode. - When a function is called as a method (
object.method()),thisrefers to the object. - When a function is called without any context (like inside
foo2),thisdefaults to the global object in non-strict mode. - The global
baranda.barare separate variables that are modified independently.
This quiz tests understanding of this binding in JavaScript, which is a crucial and often confusing concept for developers.
97. this V
97.https://bigfrontend.dev/quiz/this-V
const obj = {
prefix: 'BFE',
list: ['1', '2', '3'],
log() {
this.list.forEach(function (item) {
console.log(this.prefix + item)
})
},
}
obj.log()
Solution: Certainly, I'll explain this code in detail:
const obj = {
prefix: 'BFE',
list: ['1', '2', '3'],
log() {
this.list.forEach(function (item) {
console.log(this.prefix + item)
})
},
}
obj.log()
Let's break it down:
We define an object
objwith three properties:prefix: a string with value 'BFE'list: an array of strings ['1', '2', '3']log: a method
The
logmethod usesthis.list.forEach()to iterate over thelistarray.Inside
forEach, there's an anonymous function that takes eachitemas an argument.This function attempts to log
this.prefix + itemfor each item in the list.Finally, we call
obj.log().
Now, here's the tricky part and the likely point of the quiz:
The forEach method creates a new execution context for its callback function. In this new context, this is not bound to obj, but instead defaults to the global object (or undefined in strict mode).
As a result, when the code runs, it will try to access this.prefix inside the forEach callback, but this doesn't refer to obj in this context. So, this.prefix will be undefined.
The output will be:
undefined1
undefined2
undefined3
To fix this and make it work as intended, you could use an arrow function, which doesn't create its own this context:
log() {
this.list.forEach((item) => {
console.log(this.prefix + item);
});
}
Or you could explicitly bind this to the forEach callback:
log() {
this.list.forEach(function (item) {
console.log(this.prefix + item);
}.bind(this));
}
Both of these solutions would correctly output:
BFE1
BFE2
BFE3
This quiz tests understanding of this context in JavaScript, particularly how it behaves differently in regular functions versus arrow functions, and within methods of objects.
107. this VI
107.https://bigfrontend.dev/quiz/this-VI
const a = {
dev: 'BFE.dev',
update: (name) => {
this.dev = name
},
}
a.update('bigfrontend.dev')
console.log(a.dev)
Solution:
Let's break down this code and explain what's happening:
const a = {
dev: 'BFE.dev',
update: (name) => {
this.dev = name
},
}
a.update('bigfrontend.dev')
console.log(a.dev)
This code demonstrates a common misconception about how this works with arrow functions in JavaScript. Let's go through it step-by-step:
We define an object
awith two properties:dev: a string with the value 'BFE.dev'update: an arrow function that takes anameparameter and attempts to updatethis.dev
We call
a.update('bigfrontend.dev'), attempting to update thedevproperty.We then log
a.devto the console.
Now, here's the key point: The arrow function update does not behave as you might expect. Here's why:
Arrow functions do not have their own
thiscontext. Instead, they inheritthisfrom the enclosing lexical scope at the time they are defined.In this case, the arrow function is defined in the global scope (or whatever scope
ais defined in), not as a method ofa.Therefore, when
this.dev = nameis executed inside the arrow function, it's not updating thedevproperty ofa. Instead, it's either:- In non-strict mode: creating or updating a
devproperty on the global object (e.g.,windowin a browser). - In strict mode: throwing a TypeError because
thisisundefined.
- In non-strict mode: creating or updating a
The original
devproperty ofaremains unchanged.
As a result, when we log a.dev, we still get the original value:
BFE.dev
To fix this and make it work as intended, you could use a regular function instead of an arrow function:
const a = {
dev: 'BFE.dev',
update: function (name) {
this.dev = name
},
}
Or use the method shorthand syntax:
const a = {
dev: 'BFE.dev',
update(name) {
this.dev = name
},
}
Both of these would correctly update a.dev when called.
This code demonstrates several important concepts in JavaScript:
- The behavior of
thisin different function types. - How arrow functions inherit
thisfrom their enclosing lexical scope. - The difference between arrow functions and regular functions when used as object methods.
- The importance of understanding
thisbinding for proper object manipulation.
It's a common pitfall when working with objects and arrow functions in JavaScript, making it a valuable example for understanding these concepts.
117. this VII
117.https://bigfrontend.dev/quiz/this-VII
class A {
static dev = 'BFE'
dev = 'bigfrontend'
}
class B extends A {
log() {
console.log(this.dev)
}
static log() {
console.log(this.dev)
}
}
B.log()
new B().log()
Solution: Certainly! Let's break down this code and explain what's happening:
class A {
static dev = 'BFE'
dev = 'bigfrontend'
}
class B extends A {
log() {
console.log(this.dev)
}
static log() {
console.log(this.dev)
}
}
B.log()
new B().log()
Class A:
- It has a static property
devset to 'BFE'. - It also has an instance property
devset to 'bigfrontend'.
- It has a static property
Class B:
- It extends class A, inheriting its properties and methods.
- It has an instance method
log()that logsthis.dev. - It also has a static method
log()that logsthis.dev.
B.log():- This calls the static
log()method on class B. - In a static method,
thisrefers to the class itself. - Class B inherits the static
devproperty from class A. - So, this will log 'BFE' to the console.
- This calls the static
new B().log():- This creates a new instance of class B and calls its
log()method. - In an instance method,
thisrefers to the instance itself. - The instance inherits the instance property
devfrom class A. - So, this will log 'bigfrontend' to the console.
- This creates a new instance of class B and calls its
To summarize the output:
BFE
bigfrontend
Key points:
- Static properties and methods are associated with the class itself, not instances.
- Instance properties and methods are associated with instances of the class.
- When a class extends another, it inherits both static and instance members.
- The
thiskeyword behaves differently in static and instance contexts.
64. reference type
64.https://bigfrontend.dev/quiz/reference-type
const obj = {
msg: 'BFE',
foo() {
console.log(this.msg)
},
bar() {
console.log('dev')
},
}
obj.foo()
obj.foo()
;(obj.foo || obj.bar)()
Solution: This code demonstrates the behavior of function calls and how the this context is determined in JavaScript. Let's break it down line by line:
Object definition:
const obj = { msg: 'BFE', foo() { console.log(this.msg) }, bar() { console.log('dev') }, }This creates an object with a property
msgand two methodsfooandbar.obj.foo();- This is a straightforward method call on the object.
thisinsidefoorefers toobj.- Output:
'BFE'
(obj.foo)();- The parentheses around
obj.foodon't change anything here. - It's still a method call on the object.
thisinsidefoostill refers toobj.- Output:
'BFE'
- The parentheses around
(obj.foo || obj.bar)();- This is where things get tricky.
obj.foo || obj.baris evaluated first. Sinceobj.fooexists and is truthy, it's selected.- However, this expression results in the function itself, detached from the object.
- The function is then called without any context.
- In non-strict mode,
thiswill be the global object (e.g.,windowin browsers). - In strict mode,
thiswill beundefined. - Either way,
this.msgwill beundefined. - Output:
undefined
So, the final output will be:
BFE
BFE
undefined
Key points:
- Method calls (like
obj.foo()) setthisto the object the method is called on. - Parentheses around a method reference (
(obj.foo)()) don't affect thethisbinding. - When a method is passed around or used in an expression (like
obj.foo || obj.bar), it loses itsthisbinding. - Functions called without any context (like in the third case) have
thisset to the global object in non-strict mode, orundefinedin strict mode.
This quiz tests understanding of how this works in JavaScript, particularly the difference between method calls and function calls, and how the context can be lost when methods are passed around or used in expressions.
73. window name
73.https://bigfrontend.dev/quiz/window-name
var a = 1
;(function () {
console.log(a + this.a)
var a = '2'
console.log(a + this.a)
})()
var name = 1
;(function () {
console.log(name + this.name)
var name = '2'
console.log(name + this.name)
})()
Solution: This code demonstrates several important concepts in JavaScript, including variable hoisting, scope, and the behavior of this in different contexts. Let's break it down:
- First IIFE (Immediately Invoked Function Expression):
var a = 1
;(function () {
console.log(a + this.a)
var a = '2'
console.log(a + this.a)
})()
var a = 1sets a global variableato 1.- Inside the IIFE,
var ais hoisted, but its assignment is not. thisin a regular function call (not a method call) refers to the global object (orundefinedin strict mode).- First
console.log:- Local
aisundefineddue to hoisting this.ais 1 (globala)undefined + 1results inNaN
- Local
a = '2'assigns the locala- Second
console.log:- Local
ais now '2' this.ais still 1- '2' + 1 results in '21' (string concatenation)
- Local
- Second IIFE:
var name = 1
;(function () {
console.log(name + this.name)
var name = '2'
console.log(name + this.name)
})()
This behaves similarly to the first IIFE, but with a key difference:
- In many environments,
nameis a special property of the global object. var name = 1might not create a new global variable, but instead set the existing globalnameproperty.- implicitly coerced
nameto string "1" The output will be:
NaN
"21"
"undefined1"
"21"
Key points:
- Variable declarations with
varare hoisted, but initializations are not. thisin a regular function call refers to the global object (in non-strict mode).- Global variables become properties of the global object.
- The
nameproperty is special and may behave differently from other global variables.
This quiz tests understanding of variable hoisting, scope, this context, and some nuances of global variables in JavaScript.
100. var vs let
100.https://bigfrontend.dev/quiz/var-vs-let
var a = 'BFE'
let b = 'bigfrontend'
console.log(this.a)
console.log(this.b)
Solution: Let's break down this code and explain its behavior:
var a = 'BFE'
let b = 'bigfrontend'
console.log(this.a)
console.log(this.b)
var a = 'BFE'This declares a variableausingvarand assigns it the value 'BFE'.let b = 'bigfrontend'This declares a variablebusingletand assigns it the value 'bigfrontend'.console.log(this.a)This attempts to log the value ofaas a property ofthis.console.log(this.b)This attempts to log the value ofbas a property ofthis.
The key to understanding the output of this code lies in how var and let behave differently in terms of creating properties on the global object, and what this refers to in different contexts. Let's break it down:
Variables declared with
varin the global scope become properties of the global object (which iswindowin browsers orglobalin Node.js).Variables declared with
let(orconst) do not become properties of the global object, even when declared in the global scope.The behavior of
thisdepends on where this code is run:- In a browser's global scope,
thisrefers to thewindowobject. - In Node.js at the top level of a module,
thisis equivalent tomodule.exports. - In strict mode or in a module,
thismay beundefined.
- In a browser's global scope,
Given these points, here's what would happen in different environments:
In a browser (non-strict mode, global scope):
BFE
undefined
In Node.js (at the top level of a module):
undefined
undefined
The reason for this difference is that in a browser, var a creates a property on the global window object, which this refers to. But let b does not create such a property. In Node.js, neither var nor let create properties on this at the module level.
This code demonstrates the differences between var and let in terms of their interaction with the global object, as well as the behavior of this in different JavaScript environments. It's a good example for understanding variable scoping and the global execution context in JavaScript.
114. constructor
114.https://bigfrontend.dev/quiz/constructor
function A() {
this.dev1 = 'BFE'
this.dev2 = 'dev'
return {
dev1: 'bigfrontend',
}
}
const a = new A()
console.log(a.dev1)
console.log(a.dev2)
Solution:
Let's break down this code and explain what's happening:
function A() {
this.dev1 = 'BFE'
this.dev2 = 'dev'
return {
dev1: 'bigfrontend',
}
}
const a = new A()
console.log(a.dev1)
console.log(a.dev2)
We define a function
A(). In JavaScript, functions can be used as constructors when called with thenewkeyword.Inside the function, we set two properties on
this:this.dev1 = 'BFE'this.dev2 = 'dev'
However, the function then explicitly returns an object:
return { dev1: 'bigfrontend', }We create a new instance of
Ausing thenewkeyword:const a = new A()Now, here's the key point: When a constructor function returns an object, that object becomes the result of the
newexpression, instead of thethisobject that was being built up.So,
ais not the object that haddev1: 'BFE'anddev2: 'dev'. Instead,ais the object that was explicitly returned:{ dev1: 'bigfrontend' }.console.log(a.dev1)will output'bigfrontend', because that's thedev1property of the returned object.console.log(a.dev2)will outputundefined, because the returned object doesn't have adev2property.
The this.dev1 = 'BFE' and this.dev2 = 'dev' lines effectively do nothing in this case, because the object they're modifying is discarded in favor of the explicitly returned object.
This behavior is a bit unusual and not typically how constructor functions are used. Normally, constructor functions modify this and implicitly return this, rather than returning a new object.
7.Symbol
112. Symbol
112.https://bigfrontend.dev/quiz/Symbol
const symbol = Symbol('BFE')
const a = {
[symbol]: 'BFE',
BFE: 'BFE',
}
console.log(symbol in a)
console.log('BFE' in a)
console.log(Object.keys(a).length)
Solution:
Let's break down this code and explain what's happening:
const symbol = Symbol('BFE')
This line creates a new Symbol with the description 'BFE'. Symbols are unique identifiers in JavaScript.
const a = {
[symbol]: 'BFE',
BFE: 'BFE',
}
Here, we're creating an object a with two properties:
- A property with the Symbol as its key and 'BFE' as its value.
- A property with the string 'BFE' as its key and 'BFE' as its value.
Now, let's look at the console.log statements:
console.log(symbol in a)
This will output true. The in operator checks if a property exists in an object or its prototype chain. Since we added the Symbol as a property to a, this check returns true.
console.log('BFE' in a)
This will also output true. We added a property with the key 'BFE' to the object, so this check returns true.
console.log(Object.keys(a).length)
This will output 1. Object.keys() returns an array of a given object's own enumerable property names (as strings). Symbol properties are not included in this array. So, only the 'BFE' string property is counted, resulting in a length of 1.
In summary:
- Symbol properties are valid object properties and can be checked with the
inoperator. - String properties behave as expected.
Object.keys()does not include Symbol properties, which is why the length is 1 instead of 2.
This code demonstrates some of the unique behaviors of Symbols in JavaScript, particularly how they differ from string keys when it comes to object property enumeration.
8.Promise
Key points:
- Promise executors run synchronously.
.thencallbacks are added to the microtask queue andsetTimeoutcallbacks are added to the macrotask queue. The microtask is executed firstly before the macrotask. .thenonly takes the callback as parameter. Other type parameter will be ignored. If the.thencallback does not return, aundefinedwill be passed forward..finallycallbacks do not carry any value.
1.Promise order
1.https://bigfrontend.dev/quiz/1-promise-order
console.log(1)
const promise = new Promise((resolve) => {
console.log(2)
resolve()
console.log(3)
})
console.log(4)
promise
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
console.log(7)
setTimeout(() => {
console.log(8)
}, 10)
setTimeout(() => {
console.log(9)
}, 0)
Solution: This code demonstrates the concept of JavaScript's event loop, promises, and the order of execution. Let's break it down step by step:
console.log(1)
This logs 1 immediately.
const promise = new Promise((resolve) => {
console.log(2)
resolve()
console.log(3)
})
This creates a new Promise. The executor function runs synchronously, so it logs 2, calls resolve(), and then logs 3.
console.log(4)
This logs 4 immediately after the Promise is created.
promise
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
This adds two callbacks to the microtask queue. They won't run yet, as the main script is still executing.
console.log(7)
This logs 7 immediately.
setTimeout(() => {
console.log(8)
}, 10)
This schedules a callback to be added to the macrotask queue after 10ms.
setTimeout(() => {
console.log(9)
}, 0)
This schedules a callback to be added to the macrotask queue as soon as possible (but after the current script finishes).
Now, let's look at the order of execution:
- 1 is logged (synchronous)
- 2 is logged (synchronous, inside Promise executor)
- 3 is logged (synchronous, inside Promise executor)
- 4 is logged (synchronous)
- 7 is logged (synchronous)
- The main script finishes, so the event loop checks the microtask queue
- 5 is logged (from the first .then() callback)
- 6 is logged (from the second .then() callback)
- The event loop checks the macrotask queue
- 9 is logged (from the setTimeout with 0ms delay)
- After at least 10ms, 8 is logged (from the setTimeout with 10ms delay)
So the final output order will be:
1
2
3
4
7
5
6
9
8
Key points to understand:
- Synchronous code runs immediately.
- Promise executors run synchronously.
- .then() callbacks are added to the microtask queue.
- setTimeout callbacks are added to the macrotask queue.
- Microtasks are processed before macrotasks.
- Among macrotasks, the order is determined by the specified delay (0ms runs before 10ms).
This code effectively demonstrates the complex interplay between synchronous code, promises, and the event loop in JavaScript.
2. Promise executor
2.https://bigfrontend.dev/quiz/2-promise-executor
new Promise((resolve, reject) => {
resolve(1)
resolve(2)
reject('error')
}).then(
(value) => {
console.log(value)
},
(error) => {
console.log('error')
}
)
Solution:
Here's what's going on step by step:
We create a new Promise. The executor function (the function passed to the Promise constructor) runs immediately.
Inside the executor function:
resolve(1)is called first. This sets the state of the promise to "fulfilled" with a value of 1.resolve(2)is called next. This has no effect because the promise has already been resolved.reject('error')is called last. This also has no effect because the promise has already been resolved.
The key point here is that once a promise is settled (either resolved or rejected), its state cannot change. The first
resolveorrejectcall determines the promise's final state and value.We chain a
.then()method to the promise. This method takes two arguments:- The first is a function to handle the fulfilled state (when the promise is resolved).
- The second is a function to handle the rejected state (when the promise is rejected).
Since our promise was resolved (with the value 1), the first function passed to
.then()will be called.This function receives the resolved value (1) as its argument and logs it to the console.
So, the output of this code will be:
1
Key points to understand:
A promise can only be settled once. Once it's resolved or rejected, subsequent calls to
resolve()orreject()are ignored.The first
resolve()orreject()call determines the promise's fate.In this case,
resolve(1)is the first call, so it sets the promise's state to fulfilled with a value of 1.The
.then()method's first argument (the fulfillment handler) is called because the promise was fulfilled, not rejected.The rejection handler (the second argument to
.then()) is not called, so we never see 'error' logged.
This example demonstrates how promises maintain their state once settled, which is a crucial aspect of their design for managing asynchronous operations.
3. Promise then callbacks
3.https://bigfrontend.dev/quiz/3-promise-then-callbacks
Promise.resolve(1)
.then(() => 2)
.then(3)
.then((value) => value * 3)
.then(Promise.resolve(4))
.then(console.log)
Solution:
Step-by-Step Breakdown:
Promise.resolve(1):- This creates a resolved promise with the value
1.
- This creates a resolved promise with the value
First
.then(() => 2):- The first
thencallback doesn't use the value1from the resolved promise. Instead, it returns2. - The promise chain now has the value
2.
- The first
Second
.then(3):- This
thenis passed3, which is a value, not a function. - When you pass a non-function value to
.then, it doesn't do anything useful. The previous value (2) is passed through unchanged. - The promise chain still has the value
2.
- This
Third
.then((value) => value * 3):- This
thentakes the value2from the previous step and multiplies it by3. - The promise chain now has the value
6.
- This
Fourth
.then(Promise.resolve(4)):- Similar to the second
then, passing a promise (Promise.resolve(4)) directly to.thendoesn't do what you might expect. - Since
Promise.resolve(4)is not a function, it does nothing with the value from the previousthen. The value6is passed through unchanged. - The promise chain still has the value
6.
- Similar to the second
Fifth
.then(console.log):- This logs the final value in the promise chain to the console.
- The final value is
6.
Result:
// Output
6
Explanation:
- The final output is
6because thethen(3)andthen(Promise.resolve(4))do not alter the value of the promise chain. Only the operations on the values in the first and third.thencalls affect the final result.
4. Promise then callbacks II
4.https://bigfrontend.dev/quiz/4-Promise-then-callbacks-II
Promise.resolve(1)
.then((val) => {
console.log(val)
return val + 1
})
.then((val) => {
console.log(val)
})
.then((val) => {
console.log(val)
return Promise.resolve(3).then((val) => {
console.log(val)
})
})
.then((val) => {
console.log(val)
return Promise.reject(4)
})
.catch((val) => {
console.log(val)
})
.finally((val) => {
console.log(val)
return 10
})
.then((val) => {
console.log(val)
})
Solution: Let's break down the code step by step to understand its behavior:
Promise.resolve(1)
.then((val) => {
console.log(val) // Step 1: Logs 1
return val + 1 // Returns 2
})
.then((val) => {
console.log(val) // Step 2: Logs 2
})
.then((val) => {
console.log(val) // Step 3: Logs undefined (previous .then didn't return anything)
return Promise.resolve(3).then((val) => {
console.log(val) // Step 4: Logs 3
})
})
.then((val) => {
console.log(val) // Step 5: Logs undefined (previous .then didn't return anything)
return Promise.reject(4) // Returns a rejected promise with value 4
})
.catch((val) => {
console.log(val) // Step 6: Logs 4 (handles the rejection)
})
.finally((val) => {
console.log(val) // Step 7: Logs undefined (finally doesn't receive any value)
return 10 // This return value is ignored, but next .then will receive 10
})
.then((val) => {
console.log(val) // Step 8: Logs undefined (Unable to receive from the resolved promise after finally)
})
Step-by-Step Explanation:
First
.then((val) => {...}):- The resolved value from
Promise.resolve(1)is1. - It logs
1. - Returns
val + 1, which is2.
- The resolved value from
Second
.then((val) => {...}):- Receives the value
2from the previous step. - Logs
2. - Doesn't return anything, so the next
valisundefined.
- Receives the value
Third
.then((val) => {...}):- Receives
undefinedbecause the previous.thendidn't return anything. - Logs
undefined. - Returns a promise from
Promise.resolve(3). - Inside this promise,
3is logged. - The returned promise resolves to
undefined(since the innermost.thendoesn't return anything).
- Receives
Fourth
.then((val) => {...}):- Receives
undefinedfrom the previous.then. - Logs
undefined. - Returns a rejected promise with the value
4usingPromise.reject(4).
- Receives
.catch((val) => {...}):- Catches the rejection from the previous step.
- Logs
4.
.finally((val) => {...}):.finallydoesn't receive any argument, sovalisundefined.- Logs
undefined. - Returns
10, but this value is ignored for the purposes of thefinallyblock itself. - The next
.thenreceives the resolved value10.
Last
.then((val) => {...}):- Unable to receive the value
10from the resolved promise after thefinally. - Logs
undefined.
- Unable to receive the value
Final Output:
1
2
undefined
3
undefined
4
undefined
undefined
18. Promise executor II
18.https://bigfrontend.dev/quiz/Promise-executor-II
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => resolve(p1))
const p3 = Promise.resolve(p1)
const p4 = p2.then(() => new Promise((resolve) => resolve(p3)))
const p5 = p4.then(() => p4)
console.log(p1 == p2)
console.log(p1 == p3)
console.log(p3 == p4)
console.log(p4 == p5)
Solution:
When Promise.resolve() receives a Promise object, it doesn't wrap it in a new Promise. Instead, it returns the input Promise itself. By following const p3 = Promise.resolve(p1), p1 is as same as p3.
false
true
false
false
23. Promise.all()
23.https://bigfrontend.dev/quiz/Promise-all
;(async () => {
await Promise.all([]).then(
(value) => {
console.log(value)
},
(error) => {
console.log(error)
}
)
await Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(
(value) => {
console.log(value)
},
(error) => {
console.log(error)
}
)
await Promise.all([1, 2, Promise.resolve(3), Promise.reject('error')]).then(
(value) => {
console.log(value)
},
(error) => {
console.log(error)
}
)
})()
Solution: The code is wrapped in an Immediately Invoked Async Function Expression (IIAFE). Let's examine each Promise.all() call:
First case:
await Promise.all([]).then( (value) => { console.log(value) }, (error) => { console.log(error) } )Output:
[]Explanation:
Promise.all([])resolves immediately with an empty array when given an empty array.- The
thenblock is executed with this empty array.
Second case:
await Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then( (value) => { console.log(value) }, (error) => { console.log(error) } )Output:
[1, 2, 3, 4]Explanation:
Promise.all()waits for all promises to resolve.- Non-promise values (1 and 2) are treated as already resolved promises.
- The result is an array of all resolved values in the original order.
Third case:
await Promise.all([1, 2, Promise.resolve(3), Promise.reject('error')]).then( (value) => { console.log(value) }, (error) => { console.log(error) } )Output:
errorExplanation:
- If any promise in the array rejects,
Promise.all()immediately rejects with that error. - The rejection handler (second argument to
then) is called with the rejection reason. - Other promises that may still be pending are not affected, but their results are ignored.
- If any promise in the array rejects,
Key points to remember about Promise.all():
- It takes an iterable (usually an array) of promises as input.
- It returns a single Promise that resolves to an array of the results of the input promises.
- The returned promise resolves when all input promises have resolved, or rejects if any of the input promises reject.
- If the input array is empty, it resolves immediately with an empty array.
- Non-promise values in the input array are treated as already resolved promises.
- If any promise rejects,
Promise.all()rejects immediately with that reason, ignoring the results of other promises.
This example demonstrates how Promise.all() is useful for handling multiple asynchronous operations concurrently and collecting their results, while also showing how it behaves with different types of inputs and in error scenarios.
;[][(1, 2, 3, 4)]
;('error')
36. Promise.prototype.finally()
36.https://bigfrontend.dev/quiz/Promise-prototype-finally
Promise.resolve(1)
.finally((data) => {
console.log(data)
return Promise.reject('error')
})
.catch((error) => {
console.log(error)
throw 'error2'
})
.finally((data) => {
console.log(data)
return Promise.resolve(2).then(console.log)
})
.then(console.log)
.catch(console.log)
Solution:
This code demonstrates the behavior of Promise chaining, particularly focusing on the finally, catch, and then methods. Let's break it down step by step:
Promise.resolve(1)- Creates a Promise that immediately resolves with the value 1.
.finally((data) => { ... })finallyis called, but its callback doesn't receive any data (data is undefined).- It logs
undefinedto the console. - It returns a rejected Promise with 'error'.
- The return value of
finallyis ignored unless it's a rejected Promise.
.catch((error) => { ... })- Catches the 'error' from the rejected Promise in the
finally. - Logs 'error' to the console.
- Throws a new error 'error2'.
- Catches the 'error' from the rejected Promise in the
.finally((data) => { ... })- Again,
finallyis called with no data (data is undefined). - Logs
undefinedto the console. - Returns a new Promise that resolves to 2 and logs it.
- This return value is ignored as it's not a rejected Promise.
- Again,
.then(console.log)- This
thenwould log the result of the previous operation, but there isn't one because the previousfinallydoesn't pass anything along.
- This
.catch(console.log)- Catches the 'error2' thrown in the earlier
catchblock. - Logs 'error2' to the console.
- Catches the 'error2' thrown in the earlier
The output will be:
undefined // from the first finally
error // from the catch
undefined // from the second finally
2 // from the Promise.resolve(2) in the second finally
error2 // from the final catch
Key points:
finallycallbacks don't receive any parameters, which is whydataisundefinedin both cases.- The return value of a
finallyblock is generally ignored, unless it's a rejected Promise. catchblocks can throw new errors, which will be caught by subsequentcatchblocks.- Promises chain sequentially, with each step potentially modifying the data or error being passed along.
- This example demonstrates how complex Promise chains can lead to potentially confusing behavior, emphasizing the importance of careful error handling and understanding of Promise mechanics.
47. Promise Order II
47.https://bigfrontend.dev/quiz/promise-order-II
console.log(1)
setTimeout(() => {
console.log(2)
}, 10)
setTimeout(() => {
console.log(3)
}, 0)
new Promise((_, reject) => {
console.log(4)
reject(5)
console.log(6)
})
.then(() => console.log(7))
.catch(() => console.log(8))
.then(() => console.log(9))
.catch(() => console.log(10))
.then(() => console.log(11))
.then(console.log)
.finally(() => console.log(12))
console.log(13)
Solution:
This code demonstrates the concept of JavaScript's event loop, promise chaining, and the order of execution for different types of operations. Let's break it down step by step:
console.log(1)- This is synchronous and executes immediately.
First
setTimeoutwith 10ms delay- This is queued to run after 10ms.
Second
setTimeoutwith 0ms delay- This is queued to run in the next event loop iteration.
Promise creation and execution
console.log(4)runs synchronously.- The promise is rejected with value 5.
console.log(6)runs synchronously.- The
thencallback is not executed because the promise was rejected. - The
catchcallback is queued as a microtask.
console.log(13)- This runs synchronously.
After all synchronous code, microtasks are processed:
- The
catchcallback logs 8. - The following
thenlogs 9. - The next
thenlogs 11. - The next
thenlogsundefined. finallylogs 12.
- The
After microtasks, the event loop moves to macrotasks:
- The 0ms setTimeout callback runs, logging 3.
- The 10ms setTimeout callback runs, logging 2.
So, the final output order will be:
1
4
6
13
8
9
11
undefined
12
3
2
Key points:
- Synchronous code runs first.
- Promise callbacks (then, catch, finally) are microtasks and run after all synchronous code but before the next event loop iteration.
- setTimeout callbacks are macrotasks and run in subsequent event loop iterations.
- Even a 0ms setTimeout runs after microtasks.
- In a promise chain, if a promise is rejected, it skips
thencallbacks until it finds acatch. - After a
catch, the promise chain continues normally. finallyalways runs, regardless of whether the promise was fulfilled or rejected.
This quiz tests understanding of JavaScript's event loop, promise mechanics, and the order of execution for different types of asynchronous operations.
98. Promise executor IV
98.https://bigfrontend.dev/quiz/Promise-executor-IV
const promise = new Promise((resolve, reject) => {
const promise2 = Promise.reject('error').then(
() => {
console.log(1)
},
() => {
console.log(2)
}
)
resolve(promise2)
})
promise.then(console.log)
Solution: Let's break down this code step by step:
- We create a new Promise called
promise:
const promise = new Promise((resolve, reject) => {
// ... inner code ...
})
- Inside this Promise, we create another Promise called
promise2:
const promise2 = Promise.reject('error').then(
() => {
console.log(1)
},
() => {
console.log(2)
}
)
This promise2 is created by immediately rejecting a Promise with the value 'error', and then chaining a .then() method to it. The .then() method has two callback functions:
- The first callback (for resolved promises) logs 1
- The second callback (for rejected promises) logs 2
- We resolve the outer
promisewithpromise2:
resolve(promise2)
- Finally, we chain a
.then()method to the outerpromise:
promise.then(console.log)
Now, let's see what happens when this code runs:
promise2is created. Since it starts withPromise.reject('error'), it will immediately trigger the rejection handler in its.then()method.This rejection handler logs 2 to the console.
After logging 2, the rejection is considered handled, and
promise2resolves successfully (with no value, which means it resolves toundefined).The outer
promiseis resolved withpromise2. Sincepromise2is already settled (resolved toundefined), this effectively meanspromiseresolves toundefined.The
.then(console.log)on the outerpromisewill therefore logundefined.
So, the output of this code will be:
2
undefined
The 2 comes from the rejection handler of promise2, and the undefined comes from the final .then(console.log) on the outer promise.
This code demonstrates several important concepts in JavaScript Promises:
- Promise chaining
- How rejection handlers in
.then()can "recover" from rejections - How resolving a Promise with another Promise works
103. Promise Order III
103.https://bigfrontend.dev/quiz/Promise-Order-III
const createPromise = () => Promise.resolve(1)
function func1() {
createPromise().then(console.log)
console.log(2)
}
async function func2() {
await createPromise()
console.log(3)
}
console.log(4)
func1()
func2()
Solution:
Let's break down this code and explain its execution:
const createPromise = () => Promise.resolve(1)
function func1() {
createPromise().then(console.log)
console.log(2)
}
async function func2() {
await createPromise()
console.log(3)
}
console.log(4)
func1()
func2()
Let's go through this step-by-step:
We define
createPromise(), which returns a Promise that immediately resolves with the value 1.We define
func1(), which creates a promise and attaches athenhandler to log the result, then immediately logs 2.We define
func2(), an async function that awaits the promise fromcreatePromise(), then logs 3.We log 4.
We call
func1().We call
func2().
Now, let's look at the execution order:
console.log(4)executes first, logging 4.func1()is called:- It creates a promise and attaches a
thenhandler to log the result. - This
thenhandler is scheduled to run in the next event loop iteration. - It immediately logs 2.
- It creates a promise and attaches a
func2()is called:- It creates a promise and awaits it.
- The rest of
func2()(logging 3) is scheduled to run after the promise resolves, in a future event loop iteration.
The main script finishes executing.
In the next event loop iteration:
- The
thenhandler fromfunc1()runs, logging 1. - The remainder of
func2()runs, logging 3.
- The
Therefore, the output will be:
4
2
1
3
This code demonstrates several important concepts in JavaScript:
- Asynchronous execution and the event loop.
- The difference between
then()callbacks andasync/awaitsyntax. - How promises are scheduled and resolved.
- The order of execution between synchronous and asynchronous code.
It's a great example to understand how JavaScript handles asynchronous operations and the order in which different types of code (synchronous, promise-based, and async/await) are executed.
106. Promise.all() II
106.https://bigfrontend.dev/quiz/Promise-all-II
const promise1 = Promise.resolve(1)
const promise2 = Promise.resolve(2)
const promise3 = Promise.resolve(3)
const promise4 = Promise.reject(4)
const promiseAll = async () => {
const group1 = await Promise.all([promise1, promise2])
const group2 = await Promise.all([promise3, promise4])
return [group1, group2]
}
promiseAll().then(console.log).catch(console.log)
Solution: Let's break down this code and explain what's happening:
const promise1 = Promise.resolve(1)
const promise2 = Promise.resolve(2)
const promise3 = Promise.resolve(3)
const promise4 = Promise.reject(4)
const promiseAll = async () => {
const group1 = await Promise.all([promise1, promise2])
const group2 = await Promise.all([promise3, promise4])
return [group1, group2]
}
promiseAll().then(console.log).catch(console.log)
Let's go through this step-by-step:
We create four promises:
promise1,promise2, andpromise3are immediately resolved with values 1, 2, and 3 respectively.promise4is immediately rejected with value 4.
We define an async function
promiseAll:- It uses
Promise.all()to wait for two groups of promises. group1waits forpromise1andpromise2to resolve.group2waits forpromise3andpromise4to resolve.- The function attempts to return an array containing both groups.
- It uses
We call
promiseAll()and chain.then(console.log)and.catch(console.log)to handle the result or error.
Now, let's consider the execution:
group1 = await Promise.all([promise1, promise2]):- This will succeed because both promises resolve.
group1will be[1, 2].
group2 = await Promise.all([promise3, promise4]):- This will fail because
promise4is rejected. - When any promise in
Promise.all()is rejected, the entirePromise.all()is immediately rejected. - This rejection will be caught by the
catchblock of the async function.
- This will fail because
Because of the rejection in step 2, the function will not reach the
returnstatement.The rejection from
Promise.all([promise3, promise4])will be propagated to thecatchmethod chained topromiseAll().
Therefore, the output will be:
4
This is because:
- The rejection value of
promise4(which is 4) is caught by thecatchmethod. console.login thecatchmethod prints this rejection value.
Key points demonstrated by this code:
Promise.all()resolves only if all promises in the array resolve, and rejects if any promise rejects.- In an async function, a rejected promise will cause the function to immediately return a rejected promise, skipping any code after the rejection.
- Error handling in promise chains: rejections are caught by
catchmethods. - The difference between resolved and rejected promises in
Promise.all().
This example is excellent for understanding how Promise.all() works with mixed resolved and rejected promises, and how async/await handles promise rejections.
50. async await
50.https://bigfrontend.dev/quiz/async-await
async function async1() {
console.log(1)
await async2()
console.log(2)
}
async function async2() {
console.log(3)
}
console.log(4)
setTimeout(function () {
console.log(5)
}, 0)
async1()
new Promise(function (resolve) {
console.log(6)
resolve()
}).then(function () {
console.log(7)
})
console.log(8)
Solution:
This code demonstrates the execution order of different types of operations in JavaScript, including synchronous code, asynchronous functions, Promises, and the event loop. Let's break it down step by step:
console.log(4)- This is synchronous and executes immediately.
setTimeout(function(){ console.log(5) }, 0)- This schedules a callback to run in the next event loop iteration (macrotask).
async1()is calledconsole.log(1)runs synchronously inside async1.await async2()is encountered:async2()is called, which logs 3 synchronously.- The rest of
async1()is suspended and scheduled as a microtask.
New Promise creation
console.log(6)runs synchronously inside the Promise executor.resolve()is called, scheduling thethencallback as a microtask.
console.log(8)- This runs synchronously.
After all synchronous code, microtasks are processed:
- The suspended part of
async1()resumes, logging 2. - The Promise's
thencallback runs, logging 7.
- The suspended part of
After microtasks, the event loop moves to macrotasks:
- The setTimeout callback runs, logging 5.
So, the final output order will be:
4
1
3
6
8
2
7
5
Key points:
- Synchronous code runs first, in the order it appears.
asyncfunctions run synchronously until the firstawait, then the rest is scheduled as a microtask.- Promise callbacks (then, catch, finally) are microtasks and run after all synchronous code but before the next event loop iteration.
- setTimeout callbacks are macrotasks and run in subsequent event loop iterations, after all microtasks are completed.
- Even a 0ms setTimeout runs after microtasks.
This quiz tests understanding of JavaScript's event loop, async/await mechanics, promise execution, and the order of execution for different types of operations. It's a comprehensive test of JavaScript's asynchronous behavior.
130. async
130.https://bigfrontend.dev/quiz/async
const promise = Promise.resolve()
function f() {
return promise
}
async function a() {
return f()
}
async function b() {
return await f()
}
function c() {
return f()
}
console.log(a() === b())
console.log(b() === c())
console.log(a() === c())
Solution: Certainly! Let's break down this code and explain what's happening:
const promise = Promise.resolve()
function f() {
return promise
}
async function a() {
return f()
}
async function b() {
return await f()
}
function c() {
return f()
}
console.log(a() === b())
console.log(b() === c())
console.log(a() === c())
Let's go through each part:
const promise = Promise.resolve()creates a resolved promise.function f()returns this promise.async function a() { return f() }:- This is an async function that returns the result of
f(). - When an async function returns a value that is not a promise, it automatically wraps it in a promise.
- However, since
f()already returns a promise, it doesn't get wrapped again.
- This is an async function that returns the result of
async function b() { return await f() }:- This async function awaits the result of
f()before returning. - The
awaitkeyword unpacks the promise returned byf(), then repacks it into a new promise (because it's an async function).
- This async function awaits the result of
function c() { return f() }:- This is a regular function that simply returns the result of
f(), which is the original promise.
- This is a regular function that simply returns the result of
Now, let's look at the comparisons:
console.log(a() === b())- This logs
false. - Both
a()andb()return promises, but they are different promise objects. - Even though they resolve to the same value, they are not the same promise instance.
- This logs
console.log(b() === c())- This logs
false. - For the same reason as above,
b()returns a new promise, whilec()returns the original promise.
- This logs
console.log(a() === c())- This logs
false. a()returns a new promise (because it's an async function), whilec()returns the original promise.
- This logs
Key points to understand:
- Async functions always return promises. If the return value is not a promise, it gets wrapped in one.
- The
awaitkeyword unwraps a promise, and then the async function rewraps the result in a new promise. - Regular functions (like
c) that return promises don't create new promise objects; they just pass along the existing promise. - Promise equality (
===) compares object identity, not the resolved values of the promises. - Each call to an async function creates a new promise object, even if it's returning the same underlying promise each time.
This code demonstrates some subtle but important aspects of how promises and async functions work in JavaScript.
131. return await
131.https://bigfrontend.dev/quiz/return-await
async function a() {
try {
return await Promise.reject(1)
} catch (e) {
console.log(e)
}
}
async function b() {
try {
return Promise.reject(2)
} catch (e) {
console.log(e)
}
}
async function start() {
await a()
await b()
}
start()
Solution: Let's break down what this code does, focusing on the differences between using await with Promise.reject inside an async function and returning Promise.reject directly.
async Function a
async function a() {
try {
return await Promise.reject(1)
} catch (e) {
console.log(e)
}
}
await Promise.reject(1): This line creates a rejected promise with the value1. Sinceawaitis used, JavaScript waits for the promise to resolve or reject. In this case, it rejects, and control immediately jumps to thecatchblock.catch (e): The rejection (1) is caught here, andeis logged, so1will be printed to the console.- Return value: After the
catchblock executes, the function completes without returning anything, which means it returnsundefined.
async Function b
async function b() {
try {
return Promise.reject(2)
} catch (e) {
console.log(e)
}
}
return Promise.reject(2): Here, a rejected promise is returned without usingawait. The rejection is not immediately caught by thecatchblock withinb. Instead, the rejected promise is returned, and any code that callsb()is responsible for handling the rejection.catch (e): Thecatchblock will not be executed because the rejection happens after the return statement, meaning the error is not caught within the functionb.- Return value: The function returns a promise that is rejected with the value
2.
async Function start
async function start() {
await a()
await b()
}
await a(): This callsa(). Sincea()handles its rejection internally and returnsundefined, thisawaitwill resolve withundefinedwithout throwing an error.await b(): This callsb(), which returns a rejected promise. Sincestart()doesn't have atry...catcharound this call, the rejected promise will propagate, and thestart()function will return a rejected promise with the value2.
start() Execution
start()
start()begins by callinga(). This results in1being logged to the console, asa()catches the rejection.- Next,
b()is called. Sinceb()returns a rejected promise and doesn't catch the error,start()'s promise will be rejected with2. If there is no external error handling (e.g., anothertry...catchorcatchblock chained to the promise returned bystart()), this rejection will result in an unhandled promise rejection.
Summary:
- Output:
1will be printed from thecatchblock ina().- Nothing will be printed for
b()because the rejection is not caught withinb().
- Behavior:
a()handles the rejection internally, sostart()continues execution.b()doesn't handle the rejection, sostart()results in an unhandled promise rejection with the value2, unless caught elsewhere.
To summarize, await combined with try...catch inside a() captures and handles the rejection, while b()'s rejection is not caught within the function, leading to a propagated rejected promise.
60. postMessage
60.https://bigfrontend.dev/quiz/postMessage
console.log(1)
window.onmessage = () => {
console.log(2)
}
Promise.resolve().then(() => {
console.log(3)
})
setTimeout(() => {
console.log(4)
}, 0)
console.log(5)
window.postMessage('')
console.log(6)
Solution: This code demonstrates the order of execution in JavaScript, including synchronous code, asynchronous operations, and the event loop. Let's break it down step by step:
console.log(1)- This runs immediately, outputting 1.
window.onmessage = () => { console.log(2) }- This sets up an event listener for the 'message' event.
Promise.resolve().then(() => { console.log(3) })- This creates a microtask that will log 3.
setTimeout(() => { console.log(4) }, 0)- This schedules a macrotask to log 4 after a minimum delay.
console.log(5)- This runs immediately, outputting 5.
window.postMessage('')- This posts a message to the window, which will trigger the onmessage event handler.
- However, the event is added to the macrotask queue.
console.log(6)- This runs immediately, outputting 6.
Now, let's consider the execution order:
All synchronous code runs first, so we see outputs: 1, 5, 6.
After synchronous code, microtasks are processed. The Promise's then callback is a microtask, so 3 is logged next.
After microtasks, the event loop moves to macrotasks. There are two macrotasks queued:
- The setTimeout callback
- The message event triggered by postMessage
The order of these can vary between browsers, but typically, the message event is processed before the setTimeout callback.
So, the final output order will likely be:
1
5
6
3
2
4
Key points:
- Synchronous code runs first.
- Microtasks (like Promise callbacks) run after all synchronous code but before the next event loop iteration.
- Macrotasks (like setTimeout callbacks and most DOM events) run in subsequent event loop iterations.
- postMessage triggers an event asynchronously, even if called with a delay of 0.
- The exact order of macrotasks can sometimes vary between browsers, especially for events triggered in the same synchronous code block.
This quiz tests understanding of JavaScript's event loop, the difference between microtasks and macrotasks, and how various asynchronous operations are scheduled and executed.
UPDATE: Apparently, this behavior is different in different browsers.
Chrome: 1 -> 5 -> 6 -> 3 -> 4 -> 2
Safari: 1 -> 5 -> 6 -> 3 -> 2 -> 4
61. onClick
61.https://bigfrontend.dev/quiz/messsage-channel-is-async
console.log(1)
document.body.addEventListener('click', () => {
console.log(2)
})
Promise.resolve().then(() => {
console.log(3)
})
setTimeout(() => {
console.log(4)
}, 0)
console.log(5)
document.body.click()
console.log(6)
Solution: This code demonstrates the order of execution in JavaScript, including synchronous code, asynchronous operations, microtasks, macrotasks, and event handling. Let's break it down step by step:
console.log(1)- This runs immediately, outputting 1.
document.body.addEventListener('click', () => { console.log(2) })- This sets up an event listener for the 'click' event on the body.
Promise.resolve().then(() => { console.log(3) })- This creates a microtask that will log 3.
setTimeout(() => { console.log(4) }, 0)- This schedules a macrotask to log 4 after a minimum delay.
console.log(5)- This runs immediately, outputting 5.
document.body.click()- This triggers the click event on the body synchronously.
console.log(6)- This runs immediately, outputting 6.
Now, let's consider the execution order:
All synchronous code runs first, so we see outputs: 1, 5, 6.
The
document.body.click()is a synchronous method callAfter synchronous code, microtasks are processed. The Promise's then callback is a microtask, so 3 is logged next.
After microtasks, the event loop moves to macrotasks. There are two macrotasks queued:
- The setTimeout callback
So, the final output order will be:
1
5
2
6
3
4
Key points:
- Synchronous code runs first.
- Microtasks (like Promise callbacks) run after all synchronous code but before the next event loop iteration.
- Macrotasks (like setTimeout callbacks and most DOM events) run in subsequent event loop iterations.
This quiz tests understanding of JavaScript's event loop, the difference between microtasks and macrotasks, how events are handled, and the order of execution for various types of operations. It's a comprehensive test of JavaScript's asynchronous behavior.
62. MessageChannel
62.https://bigfrontend.dev/quiz/message-channel
console.log(1)
const mc = new MessageChannel()
mc.port1.onmessage = () => {
console.log(2)
}
Promise.resolve().then(() => {
console.log(3)
})
setTimeout(() => {
console.log(4)
}, 0)
console.log(5)
mc.port2.postMessage('')
console.log(6)
Solution:
This code demonstrates the interplay between different types of asynchronous operations in JavaScript, including Promises, MessageChannel, and setTimeout. Let's break it down step by step:
console.log(1)- This runs immediately, outputting 1.
const mc = new MessageChannel()- This creates a new MessageChannel with two ports: port1 and port2.
mc.port1.onmessage = () => { console.log(2) }- This sets up a message event handler on port1.
Promise.resolve().then(() => { console.log(3) })- This creates a microtask that will log 3.
setTimeout(() => { console.log(4) }, 0)- This schedules a macrotask to log 4 after a minimum delay.
console.log(5)- This runs immediately, outputting 5.
mc.port2.postMessage('')- This posts a message to port2, which will trigger the onmessage event handler on port1.
- However, this operation is asynchronous and will be queued as a task.
console.log(6)- This runs immediately, outputting 6.
Now, let's consider the execution order:
All synchronous code runs first, so we see outputs: 1, 5, 6.
After synchronous code, microtasks are processed. The Promise's then callback is a microtask, so 3 is logged next.
After microtasks, the event loop moves to tasks. There are two tasks queued:
- The setTimeout callback
- The MessageChannel message event
The MessageChannel message event is typically processed after the setTimeout callback.
So, the final output order will be:
1
5
6
3
4
2
Key points:
- Synchronous code runs first.
- Microtasks (like Promise callbacks) run after all synchronous code but before the next event loop iteration.
- MessageChannel operations are asynchronous and are queued as tasks.
- Tasks (like setTimeout callbacks and MessageChannel events) run in subsequent event loop iterations.
- MessageChannel events typically run after setTimeout callbacks, even with a 0ms delay.
This quiz tests understanding of JavaScript's event loop, the difference between microtasks and tasks, how MessageChannel operates, and the order of execution for various types of operations. It's a comprehensive test of JavaScript's asynchronous behavior, including some less commonly used APIs like MessageChannel.
9.Iterator and Generator
71. two-way generator
71.https://bigfrontend.dev/quiz/generator-2-way
function* gen() {
yield 2 * (yield 100)
}
const generator = gen()
console.log(generator.next().value)
console.log(generator.next(1).value)
console.log(generator.next(1).value)
Solution: Certainly! This code demonstrates the behavior of generator functions in JavaScript. Let's break it down step by step:
Generator Function Definition:
function* gen() { yield 2 * (yield 100) }This defines a generator function that uses the
yieldkeyword twice.Creating a Generator Instance:
const generator = gen()This creates an instance of the generator.
First
next()call:console.log(generator.next().value)- This starts the generator and runs until the first
yield. - It yields 100 and pauses.
- Output:
100
- This starts the generator and runs until the first
Second
next()call:console.log(generator.next(1).value)- This resumes the generator, passing 1 as the value for the previous
yield. - The expression becomes
2 * 1. - It yields 2 and pauses.
- Output:
2
- This resumes the generator, passing 1 as the value for the previous
Third
next()call:console.log(generator.next(1).value)- This resumes the generator, but there's nothing left to execute.
- It returns
undefinedas the value property of the result object. - Output:
undefined
So, the final output will be:
100
2
undefined
Key points:
- The first
next()call starts the generator and runs until the firstyield. - Subsequent
next()calls resume the generator from where it last paused. - The value passed to
next()becomes the result of theyieldexpression where the generator last paused. - When there's nothing left to yield,
next()returns an object withvalue: undefinedanddone: true.
This quiz tests understanding of:
- Generator functions and their behavior
- How
yieldworks in generators - The relationship between
next()calls andyieldexpressions - The lifecycle of a generator
It's a good example of how generators can maintain state between yields and how values can be passed into the generator through next() calls.
119. return in Generator
119.https://bigfrontend.dev/quiz/return-in-generator
function* gen() {
try {
yield 1
yield 2
return 3
yield 4
} finally {
yield 5
return 6
yield 7
}
}
console.log([...gen()])
Solution: Certainly! Let's break down this generator function and explain its behavior:
function* gen() {
try {
yield 1
yield 2
return 3
yield 4
} finally {
yield 5
return 6
yield 7
}
}
console.log([...gen()])
Here's what's happening:
function* gen()defines a generator function. Generator functions useyieldto pause execution and return a value.Inside the
tryblock:yield 1returns 1 and pauses the generator.yield 2returns 2 and pauses the generator.return 3would normally end the generator, but because there's afinallyblock, execution continues there.yield 4is never reached due to thereturnstatement before it.
The
finallyblock:- This block always executes, even if there's a
returnin thetryblock. yield 5returns 5 and pauses the generator.return 6ends the generator.yield 7is never reached due to thereturnstatement before it.
- This block always executes, even if there's a
console.log([...gen()]):- The spread operator
...is used with the generator. - This creates an array with all yielded values from the generator.
- The spread operator
Now, let's look at the output:
;[1, 2, 5]
Here's why:
- The generator yields 1, then 2.
- The
return 3in thetryblock doesn't add to the array (spread ignoresreturnvalues). - The
finallyblock executes, yielding 5. - The
return 6in thefinallyblock ends the generator, but its value is not included in the array.
Key points:
yieldpauses the generator and provides a value to the iterator.returnin a generator ends the generator's execution.- The
finallyblock always executes, even after areturn. - The spread operator
...collects all yielded values but ignores returned values. - Any code after a
returnstatement in a block is not executed.
This example demonstrates the complex interplay between generators, try/finally blocks, and how the spread operator interacts with generators.
121. Generator return()
121.https://bigfrontend.dev/quiz/generator-return
function* gen() {
yield 1
try {
yield 2
yield 3
} finally {
yield 4
}
yield 5
}
const g = gen()
console.log(g.next().value)
console.log(g.next().value)
console.log(g.return(6).value)
console.log(g.next().value)
console.log(g.next().value)
Solution: Let's break down this generator function and its usage step by step:
function* gen() {
yield 1
try {
yield 2
yield 3
} finally {
yield 4
}
yield 5
}
const g = gen()
console.log(g.next().value)
console.log(g.next().value)
console.log(g.return(6).value)
console.log(g.next().value)
console.log(g.next().value)
Here's what happens:
const g = gen()creates a generator object.console.log(g.next().value):- This yields 1.
- Output:
1
console.log(g.next().value):- This enters the try block and yields 2.
- Output:
2
console.log(g.return(6).value):return(6)is called, which attempts to terminate the generator.- However, because we're inside a try block with a finally clause, the finally block executes before the generator terminates.
- The finally block yields 4.
- The generator yields the value 4 from the finally block, ignoring 6 at this stage.
- Output:
4
console.log(g.next().value):- The next call to
g.next()continues after the finally block. - Since the generator has already been finalized, it immediately returns the value 6 that was passed to
g.return(6). - Output:
6
- The next call to
console.log(g.next().value):- The generator is now completed, so the next call to
g.next()returnsundefined. - Output:
undefined
- The generator is now completed, so the next call to
So, the final output will be:
1
2
4
6
undefined
Key points to note:
- The
yield 3andyield 5are never reached due to thereturn(6)call. - The
finallyblock always executes, even whenreturn()is called on the generator. - After
return()is called, subsequentnext()calls always return{value: undefined, done: true}. - The
return(6)call causes the generator to terminate, but not before executing thefinallyblock. - The value passed to
return()(6 in this case) becomes thevalueproperty of the returned object, overriding anyyieldin thefinallyblock.
This example demonstrates the complex interplay between generators, try/finally blocks, and the return() method of generators.
122. Iterable
122.https://bigfrontend.dev/quiz/iterable
const entries = [1, 2, 3, 4].entries()
for (const [, item] of entries) {
console.log(item)
break
}
for (const [, item] of entries) {
console.log(item)
break
}
Solution:
Let's break down this code and explain what's happening:
const entries = [1, 2, 3, 4].entries()
for (const [, item] of entries) {
console.log(item)
break
}
for (const [, item] of entries) {
console.log(item)
break
}
Let's go through it step by step:
const entries = [1,2,3,4].entries()- The
entries()method returns an iterator that gives array entries in the form of[index, value]pairs. entriesis now an iterator, not an array.
- The
First
for...ofloop:for (const [, item] of entries)starts iterating over the entries.- The destructuring
[, item]ignores the index (first element) and assigns the value toitem. console.log(item)prints the first item, which is1.breakimmediately exits the loop after the first iteration.
Second
for...ofloop:- This loop starts where the previous one left off because
entriesis an iterator that keeps its state. - It gets the next entry, which is
[1, 2]. console.log(item)prints2.breakexits the loop after the first iteration.
- This loop starts where the previous one left off because
The output of this code will be:
1
2
Key points to understand:
entries()returns an iterator, not an array. Iterators maintain their state across usage.- The destructuring
[, item]in thefor...ofloop ignores the index and only uses the value. breakin each loop ensures only one item is processed per loop.- The second loop continues from where the first loop left off in the iterator.
- If there were more loops, they would continue through the remaining entries (3 and 4) before the iterator is exhausted.
This code demonstrates how iterators maintain state across multiple uses and how destructuring can be used to select specific parts of each entry in an iterator.
123. return in Generator 2
123.https://bigfrontend.dev/quiz/return-gen
function* g() {
console.log(1)
try {
console.log(2)
yield 2
console.log(3)
throw new Error('error')
} finally {
console.log(4)
}
}
const obj = g()
obj.next()
obj.return()
Solution: Let's break down this code and explain its behavior step by step:
function* g() {
console.log(1)
try {
console.log(2)
yield 2
console.log(3)
throw new Error('error')
} finally {
console.log(4)
}
}
const obj = g()
obj.next()
obj.return()
Here's what happens:
const obj = g()- This creates a generator object but doesn't start executing the generator function yet.
obj.next()- This starts the execution of the generator function.
console.log(1)executes, printing1.- Enters the try block.
console.log(2)executes, printing2.- Hits
yield 2, which pauses the generator and returns{value: 2, done: false}to the caller. - The function is paused at this point, waiting for the next
next()call.
obj.return()- This is called instead of
next(), which attempts to terminate the generator prematurely. - Because there's a
finallyblock, it must be executed before the generator can fully terminate. - The
finallyblock runs, executingconsole.log(4)and printing4. - The generator then terminates.
- This is called instead of
The output of this code will be:
1
2
4
Key points to note:
The
console.log(3)and thethrow new Error('error')never execute becauseobj.return()is called before reaching that point.The
finallyblock always executes, even when the generator is terminated early withreturn().If
obj.next()had been called again instead ofobj.return(), it would have continued execution, printed 3, then thrown an error.The
return()method on a generator object is used to terminate the generator prematurely. It will still triggerfinallyblocks if they exist.After
obj.return(), the generator is in a completed state. Any further calls tonext()would return{value: undefined, done: true}.
This example demonstrates how generators behave with try/finally blocks, and how the return() method interacts with this structure, ensuring that cleanup code in finally blocks is always executed, even when a generator is terminated early.
110. yield
110.https://bigfrontend.dev/quiz/yield
function* genA() {
yield [1, 2, 3]
}
function* genB() {
yield* [1, 2, 3]
}
console.log(genA().next().value)
console.log(genB().next().value)
Solution:
Let's break down this code and explain what's happening:
function* genA() {
yield [1, 2, 3]
}
function* genB() {
yield* [1, 2, 3]
}
console.log(genA().next().value)
console.log(genB().next().value)
This code demonstrates the difference between yield and yield* in generator functions. Let's go through it step-by-step:
We define two generator functions,
genA()andgenB().genA()usesyield [1, 2, 3]:- This yields the entire array
[1, 2, 3]as a single value.
- This yields the entire array
genB()usesyield* [1, 2, 3]:- The
yield*expression delegates to another iterable object (in this case, the array[1, 2, 3]). - It yields each value in the iterable one by one.
- The
We call
genA().next().value:- This creates an instance of the
genAgenerator, immediately callsnext()on it, and accesses thevalueproperty of the resulting object. - Since
genAyields the entire array at once, this will return[1, 2, 3].
- This creates an instance of the
We call
genB().next().value:- This creates an instance of the
genBgenerator, immediately callsnext()on it, and accesses thevalueproperty of the resulting object. - Since
genBusesyield*, it yields the first value from the array, which is1.
- This creates an instance of the
Therefore, the output will be:
[1, 2, 3]
1
Key points to understand:
yield:- Yields a single value.
- When used with an array, it yields the entire array as one value.
yield*:- Delegates to another iterable.
- When used with an array, it yields each element of the array individually.
- It's equivalent to writing a loop that yields each value in the iterable.
Generator's
next()method:- Returns an object with
value(the yielded value) anddone(boolean indicating if the generator is finished) properties.
- Returns an object with
Accessing
.valueon the result ofnext():- Gives us the yielded value directly.
This code demonstrates:
- The difference between
yieldandyield*in generator functions. - How generators handle different types of yields.
- The behavior of the
next()method on generators.
It's a great example to understand the nuances of generator functions in JavaScript, particularly how they can yield single values or delegate to other iterables. This concept is useful for creating complex sequences or processing streams of data.
10.Operator
7. Increment Operator
7.https://bigfrontend.dev/quiz/Increment-Operator
let a = 1
const b = ++a
const c = a++
console.log(a) // 3
console.log(b) // 2
console.log(c) // 2
Solution:
let a = 1This initializes a variableawith the value 1.const b = ++aThis uses the pre-increment operator (++a). It incrementsaby 1 before assigning the value tob. So:abecomes 2bis assigned the new value ofa, which is 2
const c = a++This uses the post-increment operator (a++). It assigns the current value ofatoc, then incrementsaby 1. So:cis assigned the current value ofa, which is 2ais then incremented to 3
console.log(a) // 3At this point,ahas been incremented twice, so its value is 3.console.log(b) // 2bwas assigned the value ofaafter the first increment, so its value is 2.console.log(c) // 2cwas assigned the value ofabefore the second increment, so its value is also 2.
The key difference to note here is between the pre-increment (++a) and post-increment (a++) operators:
- Pre-increment (++a) increments the value and then returns it.
- Post-increment (a++) returns the value and then increments it.
This is why b and c both end up with the value 2, even though a ends up as 3.
13. Operator precedence
13.https://bigfrontend.dev/quiz/operator-precedence
console.log((0 == 1) == 2)
console.log((2 == 1) == 0)
console.log(0 < 1 < 2)
console.log(1 < 2 < 3)
console.log(2 > 1 > 0)
console.log(3 > 2 > 1)
Solution: Let's break it down line by line:
console.log(0 == 1 == 2)- First,
0 == 1is evaluated, which isfalse - Then,
false == 2is evaluated.falseis coerced to 0, so it's0 == 2 - This is
false - Output:
false
- First,
console.log(2 == 1 == 0)- First,
2 == 1is evaluated, which isfalse - Then,
false == 0is evaluated.falseis coerced to 0, so it's0 == 0 - This is
true - Output:
true
- First,
console.log(0 < 1 < 2)- First,
0 < 1is evaluated, which istrue - Then,
true < 2is evaluated.trueis coerced to 1, so it's1 < 2 - This is
true - Output:
true
- First,
console.log(1 < 2 < 3)- First,
1 < 2is evaluated, which istrue - Then,
true < 3is evaluated.trueis coerced to 1, so it's1 < 3 - This is
true - Output:
true
- First,
console.log(2 > 1 > 0)- First,
2 > 1is evaluated, which istrue - Then,
true > 0is evaluated.trueis coerced to 1, so it's1 > 0 - This is
true - Output:
true
- First,
console.log(3 > 2 > 1)- First,
3 > 2is evaluated, which istrue - Then,
true > 1is evaluated.trueis coerced to 1, so it's1 > 1 - This is
false - Output:
false
- First,
Key points:
- JavaScript evaluates these expressions from left to right.
- Each comparison returns either
trueorfalse, which is then used in the next comparison. - When
trueorfalseis used in a comparison with a number,trueis coerced to 1 andfalseto 0. - This chaining behavior can lead to unexpected results, especially when mixing different comparison operators.
- It's generally safer and more readable to use explicit boolean logic (e.g.,
0 < 1 && 1 < 2) rather than chaining comparisons like this.
14. Addition vs Unary Plus
14.https://bigfrontend.dev/quiz/Addition-vs-Unary-Plus
What does the code snippet to the right output by console.log?
There is a difference between Addition Operator(+) and Unary plus operator(+), even though they use the same '+'.
console.log(1 + 2)
console.log(1 + +2)
console.log(1 + +(+2))
console.log(1 + '2')
console.log(1 + +'2')
console.log('1' + 2)
console.log('1' + +2)
console.log(1 + true)
console.log(1 + +true)
console.log('1' + true)
console.log('1' + +true)
console.log(1 + null)
console.log(1 + +null)
console.log('1' + null)
console.log('1' + +null)
console.log(1 + undefined)
console.log(1 + +undefined)
console.log('1' + undefined)
console.log('1' + +undefined)
console.log('1' + +(+undefined))
Solution:
This code explores various arithmetic and string concatenation operations in JavaScript, demonstrating type coercion and the behavior of the unary plus operator. Let's go through each line:
console.log(1 + 2)- Simple addition: 1 + 2 = 3
- Output:
3
console.log(1 + + 2)- The second + is a unary plus, which doesn't change 2
- 1 + 2 = 3
- Output:
3
console.log(1 + + + 2)- Multiple unary plus operators, still doesn't change 2
- 1 + 2 = 3
- Output:
3
console.log(1 + '2')- Number + String, coerces to string concatenation
- Output:
'12'
console.log(1 + + '2')- The unary plus converts '2' to number 2
- 1 + 2 = 3
- Output:
3
console.log('1' + 2)- String + Number, coerces to string concatenation
- Output:
'12'
console.log('1' + + 2)- The unary plus doesn't affect 2 here
- String + Number, still string concatenation
- Output:
'12'
console.log(1 + true)- true is coerced to 1
- 1 + 1 = 2
- Output:
2
console.log(1 + + true)- The unary plus doesn't change true's coercion to 1
- 1 + 1 = 2
- Output:
2
console.log('1' + true)- String concatenation with true
- Output:
'1true'
console.log('1' + + true)- The unary plus converts true to 1
- String concatenation: '1' + 1
- Output:
'11'
console.log(1 + null)- null is coerced to 0
- 1 + 0 = 1
- Output:
1
console.log(1 + + null)- The unary plus converts null to 0
- 1 + 0 = 1
- Output:
1
console.log('1' + null)- String concatenation with null
- Output:
'1null'
console.log('1' + + null)- The unary plus converts null to 0
- String concatenation: '1' + 0
- Output:
'10'
console.log(1 + undefined)- undefined in addition results in NaN
- Output:
NaN
console.log(1 + + undefined)- The unary plus on undefined results in NaN
- 1 + NaN = NaN
- Output:
NaN
console.log('1' + undefined)- String concatenation with undefined
- Output:
'1undefined'
console.log('1' + + undefined)- The unary plus on undefined results in NaN
- String concatenation: '1' + NaN
- Output:
'1NaN'
console.log('1' + + + undefined)- Multiple unary plus operators on undefined still result in NaN
- String concatenation: '1' + NaN
- Output:
'1NaN'
Key points:
- The unary plus operator attempts to convert its operand to a number.
- String concatenation takes precedence over addition when a string is involved.
- true is coerced to 1, null to 0, and undefined results in NaN in numeric operations.
- These examples demonstrate the importance of understanding type coercion in JavaScript to avoid unexpected results.
15. instanceOf
15.https://bigfrontend.dev/quiz/instanceOf
console.log(typeof null)
console.log(null instanceof Object)
console.log(typeof 1)
console.log(1 instanceof Number)
console.log(1 instanceof Object)
console.log(Number(1) instanceof Object)
console.log(new Number(1) instanceof Object)
console.log(typeof true)
console.log(true instanceof Boolean)
console.log(true instanceof Object)
console.log(Boolean(true) instanceof Object)
console.log(new Boolean(true) instanceof Object)
console.log([] instanceof Array)
console.log([] instanceof Object)
console.log((() => {}) instanceof Object)
Solution: Certainly. This code explores the behavior of the typeof operator and the instanceof operator in JavaScript, as well as the differences between primitive values and their object wrapper counterparts. Let's go through each line:
console.log(typeof null)- Output:
'object' - This is a known quirk in JavaScript.
nullis not actually an object, buttypeof nullreturns 'object' due to a historical bug.
- Output:
console.log(null instanceof Object)- Output:
false - Despite the
typeofquirk,nullis not an instance ofObject.
- Output:
console.log(typeof 1)- Output:
'number' - 1 is a primitive number.
- Output:
console.log(1 instanceof Number)- Output:
false - Primitive numbers are not instances of the
Numberobject.
- Output:
console.log(1 instanceof Object)- Output:
false - Primitive numbers are not instances of
Object.
- Output:
console.log(Number(1) instanceof Object)- Output:
false Number(1)returns a primitive number, not aNumberobject.
- Output:
console.log(new Number(1) instanceof Object)- Output:
true new Number(1)creates aNumberobject, which is an instance ofObject.
- Output:
console.log(typeof true)- Output:
'boolean' trueis a primitive boolean.
- Output:
console.log(true instanceof Boolean)- Output:
false - Primitive booleans are not instances of the
Booleanobject.
- Output:
console.log(true instanceof Object)- Output:
false - Primitive booleans are not instances of
Object.
- Output:
console.log(Boolean(true) instanceof Object)- Output:
false Boolean(true)returns a primitive boolean, not aBooleanobject.
- Output:
console.log(new Boolean(true) instanceof Object)- Output:
true new Boolean(true)creates aBooleanobject, which is an instance ofObject.
- Output:
console.log([] instanceof Array)- Output:
true - An array is an instance of
Array.
- Output:
console.log([] instanceof Object)- Output:
true - Arrays are also instances of
Object(inheritance).
- Output:
console.log((() => {}) instanceof Object)- Output:
true - Functions are instances of
Objectin JavaScript.
- Output:
Key points:
typeofreturns a string indicating the type of the unevaluated operand.instanceoftests whether the prototype property of a constructor appears anywhere in the prototype chain of an object.- Primitive values (numbers, booleans, strings) are not instances of their corresponding object types or
Object. - Object wrappers created with
new(likenew Number(1)) are instances of both their type andObject. - Arrays and functions are objects in JavaScript.
- The behavior of
nullwithtypeofis a historical quirk in JavaScript. - Understanding these concepts is crucial for proper type checking and understanding object relationships in JavaScript.
87. instanceOf 2
87.https://bigfrontend.dev/quiz/instanceOf-2
console.log(Function instanceof Object)
console.log(Object instanceof Function)
console.log(Function instanceof Function)
console.log(Object instanceof Object)
Solution: This code explores the relationship between Function, Object, and the instanceof operator in JavaScript. Let's break it down line by line:
console.log(Function instanceof Object)- Output:
true - In JavaScript, functions are objects.
Functionis an instance ofObjectbecause all functions inherit fromObject.prototype.
- Output:
console.log(Object instanceof Function)- Output:
true Objectis a constructor function.- All constructor functions in JavaScript are instances of
Function.
- Output:
console.log(Function instanceof Function)- Output:
true Functionis a constructor function for creating function objects.- It's also an instance of itself, as it's a function object.
- Output:
console.log(Object instanceof Object)- Output:
true Objectis a function object (constructor function).- All function objects are instances of
Object.
- Output:
Key points to understand:
In JavaScript, functions are first-class objects. They are instances of
Object.Functionis the constructor for all functions, including itself andObject.Objectis a constructor function, which makes it an instance ofFunction.The
instanceofoperator checks if the prototype property of the constructor appears anywhere in the prototype chain of the object.This creates a circular relationship:
Functionis an instance ofObjectObjectis an instance ofFunction- Both are instances of themselves
This circular nature is a unique aspect of JavaScript's object model and prototype system. It's important to note that while this behavior is consistent across JavaScript environments, it's more of a curiosity than something you'd typically rely on in everyday programming.
These relationships demonstrate the deep interconnection between objects and functions in JavaScript, highlighting how the language blurs the line between these concepts in ways that other object-oriented languages typically don't.
25. zero
25.https://bigfrontend.dev/quiz/zero
console.log(1 / 0)
console.log(-1 / 0)
console.log(0 / 0)
console.log(0 === -0)
console.log(Object.is(0, -0))
console.log(Object.is(0, Math.round(-0.5)))
console.log(Object.is(0, Math.round(0.5)))
console.log(0 * Infinity)
console.log(Infinity / Infinity)
console.log(Object.is(0, Math.sign(0)))
console.log(Object.is(0, Math.sign(-0)))
console.log(1 / -0)
console.log(1 / 0)
console.log(1n / 0n)
Solution: This code explores some interesting and sometimes counterintuitive behaviors in JavaScript, particularly around division by zero, negative zero, and the Infinity value. Let's break it down line by line:
console.log(1 / 0)Output:Infinity- Division by zero results in
Infinityin JavaScript.
- Division by zero results in
console.log(-1 / 0)Output:-Infinity- Negative number divided by zero gives
-Infinity.
- Negative number divided by zero gives
console.log(0 / 0)Output:NaN- Zero divided by zero is undefined, so JavaScript returns
NaN(Not a Number).
- Zero divided by zero is undefined, so JavaScript returns
console.log(0 === -0)Output:true- JavaScript treats 0 and -0 as equal under normal comparison.
console.log(Object.is(0, -0))Output:falseObject.is()can distinguish between 0 and -0.
console.log(Object.is(0, Math.round(-0.5)))Output:falseMath.round(-0.5)returns -0, whichObject.is()distinguishes from 0.
console.log(Object.is(0, Math.round(0.5)))Output:falseMath.round(0.5)returns 1, not 0.
console.log(0 * Infinity)Output:NaN- Multiplying 0 by Infinity is indeterminate, so it results in
NaN.
- Multiplying 0 by Infinity is indeterminate, so it results in
console.log(Infinity / Infinity)Output:NaN- Dividing Infinity by Infinity is indeterminate, resulting in
NaN.
- Dividing Infinity by Infinity is indeterminate, resulting in
console.log(Object.is(0, Math.sign(0)))Output:trueMath.sign(0)returns +0, which is indistinguishable from 0 even withObject.is().
console.log(Object.is(0, Math.sign(-0)))Output:falseMath.sign(-0)returns -0, whichObject.is()can distinguish from +0.
console.log(1 / -0)Output:-Infinity- Division by negative zero results in negative Infinity.
console.log(1 / 0)Output:Infinity- Division by positive zero results in positive Infinity.
console.log(1n / 0n)Output: Uncaught RangeError: Division by zero- BigInt division by zero throws an error, unlike regular number division.
Key takeaways:
- Division by zero with regular numbers results in
Infinityor-Infinity, not an error. - JavaScript distinguishes between +0 and -0 in some operations, but treats them as equal in comparisons.
Object.is()can distinguish between +0 and -0, unlike===.- Operations involving
Infinitycan result inNaNwhen the result is indeterminate. Math.sign()preserves the sign of zero, which can be detected byObject.is().- BigInt division behaves differently from regular number division, throwing an error on division by zero.
These examples highlight some of the nuances and special cases in JavaScript's handling of numbers, especially around zero, infinity, and numerical comparisons. Understanding these behaviors is crucial for writing robust JavaScript code, especially when dealing with mathematical operations or precise numerical comparisons.
console.log(1 / 0) // Infinity
console.log(-1 / 0) // -Infinity
console.log(0 / 0) // NaN
console.log(0 === -0) // true
console.log(Object.is(0, -0)) // false
console.log(Object.is(0, Math.round(-0.5))) // false
console.log(Object.is(0, Math.round(0.5))) // false
console.log(0 * Infinity) // NaN
console.log(Infinity / Infinity) // NaN
console.log(Object.is(0, Math.sign(0))) // true
console.log(Object.is(0, Math.sign(-0))) // false
console.log(1 / -0) // -Infinity
console.log(1 / 0) // Infinity
console.log(1n / 0n) // Error
34. precedence
34.https://bigfrontend.dev/quiz/precedence
let a = 1
console.log(a++ + a)
let b = 1
console.log(b + +(+b))
let c = 1
console.log(c-- - c)
let d = 1
console.log(d - -(-d))
Solution:
This code demonstrates some interesting behavior with JavaScript's increment/decrement operators and the unary plus operator. Let's break it down line by line:
let a = 1console.log(a +++ a)- Output:
3 - This is parsed as
(a++) + a - First,
a++is evaluated. It returns 1 (the original value ofa) and then incrementsato 2. - Then, we add the new value of
a(which is 2) - So, 1 + 2 = 3
- Output:
let b = 1console.log(b + + + b)- Output:
2 - This is parsed as
b + (+(+b)) - The rightmost
+bis the unary plus operator, which doesn't change the value ofb - So it's equivalent to
b + (+b) - Both
band+bevaluate to 1 - Therefore, 1 + 1 = 2
- Output:
let c = 1console.log(c --- c)- Output:
1 - This is parsed as
(c--) - c - First,
c--is evaluated. It returns 1 (the original value ofc) and then decrementscto 0. - Then, we subtract the new value of
c(which is 0) - So, 1 - 0 = 1
- Output:
let d = 1console.log(d - - - d)- Output:
0 - This is parsed as
d - (-(-d)) - The rightmost
-dnegatesd, resulting in -1 - The next
-negates that, resulting in 1 - So it's equivalent to
d - (1) - Therefore, 1 - (1) = 0
- Output:
Key points:
- In
a +++ a, the++is treated as a postfix increment operator, and the last+as a binary addition operator. - In
b + + + b, all+are treated as unary plus operators, except the leftmost one which is binary addition. - In
c --- c, the--is treated as a postfix decrement operator, and the last-as a binary subtraction operator. - In
d - - - d, all-are treated as unary negation operators, except the leftmost one which is binary subtraction.
These examples demonstrate how JavaScript parses consecutive + and - operators, and the difference between unary operators (like ++, --, unary + and -) and binary operators (like addition and subtraction). It's a good reminder to use parentheses or spaces to make your intentions clear when using multiple operators in succession.
31. Math
31.https://bigfrontend.dev/quiz/Math
console.log(1 / 0)
console.log(0 / 0)
console.log(-1 / 0)
console.log((1 / 0) * 0)
console.log((1 / 0) * 1)
console.log((1 / 0) * -1)
console.log((1 / 0) * 1 + (1 / 0) * 1)
console.log((1 / 0) * 1 - (1 / 0) * 1)
console.log((1 / 0) * 1 * ((1 / 0) * 1))
console.log(((1 / 0) * 1) / ((1 / 0) * 1))
console.log(0 / Infinity)
console.log(0 * Infinity)
Solution:
Let's break down each operation:
1 / 0: In JavaScript, this results inInfinity.0 / 0: This is an undefined mathematical operation, resulting inNaN(Not a Number).-1 / 0: This results in-Infinity.1 / 0 * 0:Infinity * 0is undefined mathematically, so it results inNaN.1 / 0 * 1:Infinity * 1is stillInfinity.1 / 0 * -1: This is equivalent to-Infinity.1 / 0 * 1 + 1 / 0 * 1: This isInfinity + Infinity, which is stillInfinity.1 / 0 * 1 - 1 / 0 * 1: This isInfinity - Infinity, which is undefined mathematically, resulting inNaN.1 / 0 * 1 * (1 / 0 * 1): This isInfinity * Infinity, which isInfinity.1 / 0 * 1 / (1 / 0 * 1): This isInfinity / Infinity, which is undefined mathematically, resulting inNaN.0 / Infinity: In mathematics, this is interpreted as a limit approaching 0, so JavaScript returns0.0 * Infinity: This is undefined mathematically, resulting inNaN.
Key points to understand:
- Division by zero results in
Infinityor-Infinity, depending on the sign of the numerator. 0 / 0and other undefined mathematical operations result inNaN.- Operations with
Infinityfollow mathematical rules where possible, but undefined operations (likeInfinity - Infinity) result inNaN. - JavaScript has a built-in
Infinityvalue that can be used directly. NaNis the result of any operation that doesn't produce a meaningful numeric value.
This code demonstrates JavaScript's handling of edge cases in mathematical operations, which can be important to understand when dealing with numerical computations or when validating numerical inputs.
console.log(1 / 0) // Infinity
console.log(0 / 0) // NaN
console.log(-1 / 0) // -Infinity
console.log((1 / 0) * 0) // NaN => Infinity * 0 = NaN
console.log((1 / 0) * 1) // Infinity(NaN) => Infinity * 1 = Infinity
console.log((1 / 0) * -1) // -Infinity(NaN) => Infinity * -1 = -Infinity
console.log((1 / 0) * 1 + (1 / 0) * 1) // Infinity(NaN) => Infinity + Infinity = Infinity
console.log((1 / 0) * 1 - (1 / 0) * 1) // NaN => Infinity - Infinity = NaN
console.log((1 / 0) * 1 * ((1 / 0) * 1)) // Infinity(NaN) => Infinity * Infinity = Infinity
console.log(((1 / 0) * 1) / ((1 / 0) * 1)) // NaN => Infinity / Infinity = NaN
console.log(0 / Infinity) // 0
console.log(0 * Infinity) // NaN
75. meaningless calculation
75.https://bigfrontend.dev/quiz/meaningless-calculation
const num = +(~~!+[] + ~~!+[] + [] + ~~!+[])
console.log(num)
Solution: This code is a complex example of JavaScript type coercion and bitwise operations. Let's break it down step by step:
+[]:- An empty array coerced to a number is 0.
!+[]:!0istrue.
~~!+[]:~~trueis 1. The double tilde (~~) is a bitwise NOT operation performed twice, effectively coercing to an integer.
(~~!+[])+(~~!+[]):- This is 1 + 1 = 2.
(~~!+[])+(~~!+[])+[]:- 2 + [] results in string concatenation: "2" (the array is coerced to an empty string).
(~~!+[])+(~~!+[])+[]+(~~!+[]):- "2" + 1 = "21" (string concatenation)
+((~~!+[])+(~~!+[])+[]+(~~!+[])):- The outer
+coerces the string "21" back to a number.
- The outer
So, the final result is the number 21.
Therefore, console.log(num) will output:
21
Key points:
+can be used for both numeric addition and string concatenation, depending on the types of its operands.!negates a value and returns a boolean.~~is a shorthand for converting a value to an integer (similar to Math.floor() for positive numbers).- Empty arrays coerce to 0 when used as numbers, and to "" when used as strings.
- The outer
+at the beginning of the expression coerces the final result to a number.
This code is an example of obfuscation and is not recommended for practical use. It's hard to read and maintain. It's primarily used as a puzzle or to demonstrate JavaScript's type coercion rules and bitwise operations in an extreme way.
83. Plus Plus
83.https://bigfrontend.dev/quiz/Plus-Plus
console.log(1 + 1)
console.log(1 + +1)
console.log(1 + +1 + 1)
console.log(1 + +1 + +1)
console.log(1 + +(+1))
console.log(1 + +'1' + +'1')
console.log('1' + +'1' + +'1')
console.log('a' + +'b')
console.log('a' + +'b' + 'c')
console.log('a' + +'b' + +'c')
Solution: This code demonstrates various uses of the addition operator (+) and the unary plus operator (+) in JavaScript. Let's break it down line by line:
console.log(1 + 1)- Simple addition: 1 + 1 = 2
- Output: 2
console.log(1 + + 1)- The second + is a unary plus, which doesn't change 1
- Equivalent to 1 + 1 = 2
- Output: 2
console.log(1 + + 1 + 1)- Equivalent to 1 + 1 + 1 = 3
- Output: 3
console.log(1 + + 1 + + 1)- Equivalent to 1 + 1 + 1 = 3
- Output: 3
console.log(1 + + + 1)- Multiple unary plus operators, still doesn't change 1
- Equivalent to 1 + 1 = 2
- Output: 2
console.log(1 + + '1' + + '1')- Unary plus converts '1' to number 1
- Equivalent to 1 + 1 + 1 = 3
- Output: 3
console.log('1' + + '1' + + '1')- First + is string concatenation
- Unary plus converts '1' to number 1
- Equivalent to '1' + 1 + 1 = '111'
- Output: '111'
console.log('a' + + 'b')- Unary plus tries to convert 'b' to a number, resulting in NaN
- Equivalent to 'a' + NaN = 'aNaN'
- Output: 'aNaN'
console.log('a' + + 'b' + 'c')- Similar to previous, but with 'c' concatenated
- Equivalent to 'a' + NaN + 'c' = 'aNaNc'
- Output: 'aNaNc'
console.log('a' + + 'b' + + 'c')- Both 'b' and 'c' are attempted to be converted to numbers, resulting in NaN
- Equivalent to 'a' + NaN + NaN = 'aNaNNaN'
- Output: 'aNaNNaN'
Key points:
- The unary plus (+) attempts to convert its operand to a number.
- When used with numbers, unary plus doesn't change the value.
- When used with strings that represent numbers, it converts them to actual numbers.
- When used with strings that don't represent numbers, it results in NaN.
- String concatenation takes precedence over addition when a string is involved.
This code demonstrates the intricacies of JavaScript's type coercion and operator behavior, particularly the interaction between numeric operations and string concatenation.
92. NaN
92.https://bigfrontend.dev/quiz/NaN
console.log(NaN == NaN)
console.log(NaN === NaN)
console.log(Object.is(NaN, NaN))
console.log([NaN].indexOf(NaN))
console.log([NaN].includes(NaN)) // true
console.log(Math.max(NaN, 1))
console.log(Math.min(NaN, 1))
console.log(Math.min(NaN, Infinity))
Solution: This code demonstrates various comparisons and operations involving NaN (Not-a-Number) in JavaScript. Let's break it down line by line:
console.log(NaN == NaN)- Output:
false NaNis not equal to itself when using loose equality (==).
- Output:
console.log(NaN === NaN)- Output:
false NaNis not equal to itself even when using strict equality (===).
- Output:
console.log(Object.is(NaN, NaN))- Output:
true Object.is()considersNaNequal to itself.
- Output:
console.log([NaN].indexOf(NaN))- Output:
-1 indexOf()uses strict equality (===), so it can't findNaNin the array.
- Output:
console.log([NaN].includes(NaN))- Output:
true includes()usesSameValueZeroalgorithm, which considersNaNequal to itself.
- Output:
console.log(Math.max(NaN, 1))- Output:
NaN - If any argument to
Math.max()isNaN, the result isNaN.
- Output:
console.log(Math.min(NaN, 1))- Output:
NaN - If any argument to
Math.min()isNaN, the result isNaN.
- Output:
console.log(Math.min(NaN, Infinity))- Output:
NaN - Same as above,
NaNas an argument results inNaN.
- Output:
Key points:
NaNis the only value in JavaScript that is not equal to itself using == or ===.Object.is()andArray.prototype.includes()use a different comparison algorithm that considersNaNequal to itself.indexOf()uses strict equality and thus can't findNaNin an array.- Any mathematical operation involving
NaN(including comparisons inMath.max()andMath.min()) results inNaN.
This code highlights the special behavior of NaN in JavaScript, which can be a source of confusion and bugs if not properly understood. It's important to use appropriate methods when working with NaN, especially in comparisons and array operations.
109. isNaN()
109.https://bigfrontend.dev/quiz/isNaN
const a = 'BFE.dev'
const b = 1
console.log(Number.isNaN(a))
console.log(Number.isNaN(b))
console.log(isNaN(a))
console.log(isNaN(b))
Solution: Certainly! Let's break down this code and explain what's happening:
const a = 'BFE.dev'
const b = 1
console.log(Number.isNaN(a))
console.log(Number.isNaN(b))
console.log(isNaN(a))
console.log(isNaN(b))
This code demonstrates the difference between Number.isNaN() and the global isNaN() function. Let's go through each line:
- We define
aas a string 'BFE.dev'. - We define
bas a number 1.
Now, let's look at each console.log statement:
console.log(Number.isNaN(a))Number.isNaN()checks if the passed value is exactly theNaNvalue.- The string 'BFE.dev' is not
NaN, so this returnsfalse.
console.log(Number.isNaN(b))- 1 is a number, but it's not
NaN, so this returnsfalse.
- 1 is a number, but it's not
console.log(isNaN(a))- The global
isNaN()function first attempts to convert its argument to a number. - 'BFE.dev' cannot be converted to a valid number, so it becomes
NaNwhen converted. - Therefore, this returns
true.
- The global
console.log(isNaN(b))- 1 is already a number and it's not
NaN, so this returnsfalse.
- 1 is already a number and it's not
Therefore, the output will be:
false
false
true
false
Key points to understand:
Number.isNaN():- Introduced in ECMAScript 6 (ES6).
- It does not attempt to convert the argument to a number.
- It returns
trueonly if the argument is exactly theNaNvalue.
Global
isNaN():- It's an older function, available since the early days of JavaScript.
- It first attempts to convert its argument to a number.
- It returns
trueif the argument isNaNor if it becomesNaNafter conversion to a number.
This code demonstrates:
- The difference between
Number.isNaN()and the globalisNaN()function. - How type coercion works in the global
isNaN()function. - The behavior of these functions with different types of values (strings and numbers in this case).
It's a good example to understand the nuances of NaN checking in JavaScript and how different methods handle type conversion. In general, Number.isNaN() is considered more reliable for strictly checking for NaN, while the global isNaN() can be useful when you want to check if a value is not a valid number after conversion.
95. number format
95.https://bigfrontend.dev/quiz/number-format
console.log(017 - 011)
console.log(018 - 011)
console.log(019 - 011)
Solution: This code demonstrates the behavior of octal numbers in JavaScript and how they interact with decimal numbers. Let's break it down line by line:
console.log(017 - 011)- In JavaScript, numbers starting with
0are interpreted as octal (base-8) numbers, but only if all digits are 0-7. 017in octal is 15 in decimal (1*8 + 7 = 15)011in octal is 9 in decimal (1*8 + 1 = 9)- So this operation is equivalent to 15 - 9 = 6
- Output:
6
- In JavaScript, numbers starting with
console.log(018 - 011)018is not a valid octal number because 8 is not a valid octal digit.- In this case, JavaScript treats
018as the decimal number 18. 011is still interpreted as octal (9 in decimal)- So this operation is equivalent to 18 - 9 = 9
- Output:
9
console.log(019 - 011)- Similar to the previous case,
019is not a valid octal number. - JavaScript treats
019as the decimal number 19. 011is still interpreted as octal (9 in decimal)- So this operation is equivalent to 19 - 9 = 10
- Output:
10
- Similar to the previous case,
Key points:
- In JavaScript, numbers starting with
0are interpreted as octal, but only if all digits are valid octal digits (0-7). - If a number starts with
0but contains 8 or 9, it's treated as a decimal number. - Octal literals (starting with
0) are considered legacy and are not allowed in strict mode or in ECMAScript 6 modules. - In modern JavaScript, it's recommended to use the prefix
0ofor octal numbers (e.g.,0o17) to avoid confusion.
This behavior can lead to unexpected results and bugs if not properly understood. It's generally best to avoid using leading zeros for decimal numbers to prevent any ambiguity or unintended octal interpretation.
96. comparison
96.https://bigfrontend.dev/quiz/comparison
console.log(10 > 9)
console.log(10 > '9')
console.log('10' > 9)
console.log('10' > '9')
Solution: Let's break it down line by line:
console.log(10 > 9)
This compares two numbers. 10 is greater than 9, so this will output true.
console.log(10 > '9')
Here we're comparing a number (10) to a string ('9'). JavaScript will try to convert the string to a number before comparing. '9' becomes 9, and 10 is still greater than 9, so this outputs true.
console.log('10' > 9)
This compares a string ('10') to a number (9). Again, JavaScript converts the string to a number for comparison. '10' becomes 10, which is greater than 9, so this outputs true.
console.log('10' > '9')
This compares two strings. When comparing strings, JavaScript uses lexicographic (dictionary) order. In this order, '10' comes after '9', so it's considered "greater". This will also output true.
It's worth noting that while these all output true, the comparisons are happening in different ways. The first is a simple numeric comparison, the middle two involve type coercion (converting strings to numbers), and the last is a string comparison.
105. typeof
105.https://bigfrontend.dev/quiz/typeof
const a = 'BFE.dev'
if (!typeof a === 'string') {
console.log('string')
} else {
console.log('not a string')
}
Solution: Let's break down this code and explain what's happening:
const a = 'BFE.dev'
if (!typeof a === 'string') {
console.log('string')
} else {
console.log('not a string')
}
This code is attempting to check if a is not a string, but there's a subtle issue with the condition. Let's analyze it step by step:
We declare a constant
aand assign it the string value 'BFE.dev'.The
ifstatement condition is!typeof a === 'string'.The
typeofoperator has higher precedence than the logical NOT (!) operator, so this condition is evaluated as follows:- First,
typeof ais evaluated. Sinceais a string,typeof areturns 'string'. - Then, the NOT operator is applied:
!('string'). - The NOT operator converts its operand to a boolean and then negates it. Any non-empty string is truthy, so
!'string'evaluates tofalse. - Finally, we have:
false === 'string'
- First,
false === 'string'is alwaysfalse, because we're comparing a boolean to a string using strict equality.Since the condition evaluates to
false, theelseblock is executed.
Therefore, the output will be:
not a string
This is counterintuitive because a is indeed a string, but due to the incorrect use of the NOT operator, the condition always evaluates to false.
To correctly check if a is not a string, the condition should be written as:
if (typeof a !== 'string') {
console.log('not a string')
} else {
console.log('string')
}
This code demonstrates several important concepts in JavaScript:
- Operator precedence:
typeofhas higher precedence than!. - Type coercion: how the
!operator coerces its operand to a boolean. - Strict equality (
===): no type coercion is performed. - The importance of parentheses in complex conditions to ensure correct evaluation order.
It's a tricky example that highlights how easy it is to write conditions that don't behave as intended due to operator precedence and type coercion rules in JavaScript.
111. falsy
111.https://bigfrontend.dev/quiz/falsy
console.log(!!'')
console.log(!!{})
console.log(!![])
Solution: Let's break down this code and explain what's happening:
console.log(!!'')
console.log(!!{})
console.log(!![])
This code demonstrates the concept of double negation (!!) in JavaScript, which is often used as a shorthand way to convert a value to its boolean equivalent. Let's go through each line:
console.log(!!''):- The empty string
''is a falsy value in JavaScript. - The first
!negates this falsy value, resulting intrue. - The second
!negatestrue, resulting infalse. - So,
!!''evaluates tofalse.
- The empty string
console.log(!!{}):- An empty object
{}is a truthy value in JavaScript. - The first
!negates this truthy value, resulting infalse. - The second
!negatesfalse, resulting intrue. - So,
!!{}evaluates totrue.
- An empty object
console.log(!![]):- An empty array
[]is a truthy value in JavaScript. - The first
!negates this truthy value, resulting infalse. - The second
!negatesfalse, resulting intrue. - So,
!![]evaluates totrue.
- An empty array
Therefore, the output will be:
false
true
true
Key points to understand:
Truthy and Falsy values in JavaScript:
- Falsy values include:
false,0,''(empty string),null,undefined, andNaN. - All other values are considered truthy, including empty objects
{}and empty arrays[].
- Falsy values include:
The
!operator:- It negates a value, converting it to a boolean and then inverting it.
- When applied to a truthy value, it returns
false. - When applied to a falsy value, it returns
true.
Double negation
!!:- It's equivalent to converting a value to its boolean representation.
- It's often used as a shorthand for
Boolean(x).
This code demonstrates:
- How different types of empty values (
'',{},[]) are treated in boolean contexts. - The use of double negation as a technique to convert values to booleans.
- The difference between primitives (like strings) and objects (including arrays) in terms of their "truthiness".
It's a concise way to illustrate some fundamental concepts about type coercion and boolean logic in JavaScript. Understanding these concepts is crucial for writing clear and predictable conditional logic in JavaScript programs.
132. banana
131.https://bigfrontend.dev/quiz/banana
console.log(('b' + 'a' + +'b' + 'a').toLowerCase())
Solution: Let's break down this expression step by step:
console.log(('b' + 'a' + +'b' + 'a').toLowerCase())
First, let's look at the expression inside the parentheses:
'b' + 'a' + + 'b' + 'a'Breaking it down from left to right:
'b' + 'a'concatenates to'ba'- Now we have:
'ba' + + 'b' + 'a'
The key part is
+ 'b':- The
+before'b'is the unary plus operator - It attempts to convert
'b'to a number 'b'can't be converted to a number, so it becomesNaN(Not a Number)
- The
So now we have:
'ba' + NaN + 'a'When we add
NaNto a string, it gets converted to the string'NaN''ba' + 'NaN' + 'a'This all concatenates to:
'baNaNa'Finally, we call
.toLowerCase()on this string:'banana'
Therefore, console.log(('b' + 'a' + + 'b' + 'a').toLowerCase()) outputs:
banana
Key points:
- The unary
+operator attempts to convert its operand to a number NaNwhen used in string concatenation becomes the string'NaN'- This is a tricky bit of JavaScript that relies on type coercion and the behavior of the
+operator in different contexts
This is often used as a JavaScript joke or puzzle because it unexpectedly spells "banana" despite not having all the letters explicitly in the code.
11.Function
12. arguments
12.https://bigfrontend.dev/quiz/arguments
function log(a, b, c, d) {
console.log(a, b, c, d)
arguments[0] = 'bfe'
arguments[3] = 'dev'
console.log(a, b, c, d)
}
log(1, 2, 3)
Solution: Certainly. This code demonstrates some interesting behavior of the arguments object in JavaScript. Let's break it down:
First, we define a function
logthat takes four parameters:a,b,c, andd.Inside the function, we have two
console.logstatements and some manipulations of theargumentsobject.We call the function
log(1,2,3), passing only three arguments.
Now, let's go through what happens when we run this code:
console.log(a,b,c,d)- This will output:
1 2 3 undefined a,b, andcare assigned the values 1, 2, and 3 respectively.dis undefined because no fourth argument was provided.
- This will output:
arguments[0] = 'bfe'- This changes the first argument to 'bfe'.
- In non-strict mode, this also changes the value of
ato 'bfe'.
arguments[3] = 'dev'- This assigns 'dev' to the fourth position in the arguments object.
- However, it doesn't create a new parameter or affect the
dparameter.
console.log(a,b,c,d)- This will output:
'bfe' 2 3 undefined ais now 'bfe' due to the change inarguments[0].bandcremain unchanged.dis still undefined, despitearguments[3]being set.
- This will output:
Key points:
- In non-strict mode, changing
arguments[i]also changes the corresponding named parameter, and vice versa (fori< number of parameters). - Adding values to
argumentsbeyond the original number of arguments doesn't create new parameters or change undefined parameters. - The
argumentsobject is array-like but not an actual array. - This behavior can be confusing and is one reason why using the
argumentsobject is generally discouraged in modern JavaScript. Instead, rest parameters (...args) are often preferred for functions that need to handle a variable number of arguments.
16. parseInt
16.https://bigfrontend.dev/quiz/parseInt
console.log(['0'].map(parseInt))
console.log(['0', '1'].map(parseInt))
console.log(['0', '1', '1'].map(parseInt))
console.log(['0', '1', '1', '1'].map(parseInt))
Solution: This code demonstrates an interesting and often misunderstood interaction between the map() function and parseInt() in JavaScript. Let's break it down:
console.log(['0'].map(parseInt))- Output:
[0] - Here,
parseIntis called with just one argument '0', so it works as expected.
- Output:
console.log(['0','1'].map(parseInt))- Output:
[0, NaN] - This is where things get tricky.
mappasses three arguments to the callback function: the current value, the index, and the array. - So it's effectively doing:
parseInt('0', 0, ['0','1'])-> 0 (parsed in base 10, as 0 is not a valid radix)parseInt('1', 1, ['0','1'])-> NaN (1 is not a valid radix)
- Output:
console.log(['0','1','1'].map(parseInt))- Output:
[0, NaN, 1] - This is doing:
parseInt('0', 0, ['0','1','1'])-> 0parseInt('1', 1, ['0','1','1'])-> NaN (1 is not a valid radix)parseInt('1', 2, ['0','1','1'])-> 1 (valid binary digit)
- Output:
console.log(['0','1','1','1'].map(parseInt))- Output:
[0, NaN, 1, 1] - This is doing:
parseInt('0', 0, ['0','1','1','1'])-> 0parseInt('1', 1, ['0','1','1','1'])-> NaN (1 is not a valid radix)parseInt('1', 2, ['0','1','1','1'])-> 1 (valid binary digit)parseInt('1', 3, ['0','1','1','1'])-> 1 (valid ternary digit)
- Output:
Key points:
parseInt(string, radix)parses a string and returns an integer of the specified radix (base).map(callback(currentValue[, index[, array]]))calls the provided function once for each element in an array, in order.- When
parseIntis used as the callback formap, it receives not just the string to parse, but also the index as the radix. - This leads to unexpected results because the index is being used as the radix for
parseInt. - To correctly use
parseIntwithmap, you should wrap it in a function that only passes the string argument:This would correctly return;['0', '1', '1', '1'].map((num) => parseInt(num))[0, 1, 1, 1].
This example highlights the importance of understanding how functions work when passed as callbacks, especially when they accept multiple parameters. It's a common pitfall in JavaScript.
77. parseInt 2
77.https://bigfrontend.dev/quiz/parseInt-2
console.log(parseInt(0.00001))
console.log(parseInt(0.000001))
console.log(parseInt(0.0000001))
console.log(parseInt('0x12'))
console.log(parseInt('1e2'))
Solution: This code demonstrates the behavior of JavaScript's parseInt() function with different types of input. Let's break it down line by line:
console.log(parseInt(0.00001))- Output:
0 parseInt()converts its argument to a string first, so this becomesparseInt("0.00001").parseInt()stops parsing at the first non-digit character (the decimal point).- Therefore, it returns 0.
- Output:
console.log(parseInt(0.000001))- Output:
0 - Same reasoning as above.
- Output:
console.log(parseInt(0.0000001))- Output:
1 - This number in JavaScript is represented in scientific notation: "1e-7".
- When converted to a string, it becomes "1e-7".
parseInt()stops at the 'e', so it parses just the '1'.
- Output:
console.log(parseInt("0x12"))- Output:
18 - The "0x" prefix is recognized as a hexadecimal number.
- 12 in hexadecimal is 18 in decimal.
- Output:
console.log(parseInt("1e2"))- Output:
1 parseInt()stops parsing at the first non-digit character ('e').- So it only parses the '1'.
- Output:
Key points:
parseInt()first converts its argument to a string if it's not already a string.- It parses from left to right and stops at the first character it can't parse as part of the number.
- It recognizes and correctly parses hexadecimal numbers with the "0x" prefix.
- It does not recognize scientific notation (e.g., "1e2") as a single number.
- Very small floating-point numbers may be represented in scientific notation internally, which can lead to unexpected results when parsed.
This quiz demonstrates the importance of understanding how parseInt() works, especially with edge cases like very small numbers or numbers in different formats. It's a good reminder to always be cautious when parsing numerical input and to consider using other methods (like Number() or parseFloat()) when appropriate.
115. parseInt II
115.https://bigfrontend.dev/quiz/parseInt-II
console.log(parseInt(' 1'))
console.log(parseInt(' 00001'))
console.log(parseInt(' 0100'))
console.log(parseInt(' 1e2 '))
Solution:
Let's break down each of these parseInt() calls:
console.log(parseInt(' 1'))- Result:
1 - Explanation:
parseInt()ignores leading whitespace. It then parses the string until it encounters a character that isn't part of a valid integer. Here, it simply parses the '1'.
- Result:
console.log(parseInt(' 00001'))- Result:
1 - Explanation: Again, leading whitespace is ignored. Leading zeros are also ignored in decimal interpretation. The function parses this as a decimal number (base 10 by default) and returns 1.
- Result:
console.log(parseInt(' 0100'))- Result:
100 - Explanation: Leading whitespace and zeros are ignored. The function reads this as the decimal number 100.
- Result:
console.log(parseInt(' 1e2 '))- Result:
1 - Explanation:
parseInt()stops parsing when it encounters a character it can't interpret as part of an integer. In this case, it parses the '1', but stops at 'e' because 'e' is not a valid digit. The scientific notation 'e2' is not recognized byparseInt().
- Result:
Key points to remember about parseInt():
- It ignores leading whitespace.
- It parses until it encounters a character that can't be part of the number in the given base (10 by default).
- It ignores leading zeros in decimal interpretation.
- It doesn't recognize scientific notation (like 1e2) - for that, you'd use
parseFloat()or the unary plus operator.
If you need to parse numbers in different bases or with specific formatting, you might need to use additional parameters or different methods.
17. reduce
17.https://bigfrontend.dev/quiz/reduce
;[1, 2, 3].reduce((a, b) => {
console.log(a, b)
})
;[1, 2, 3].reduce((a, b) => {
console.log(a, b)
}, 0)
Solution:
This code demonstrates the behavior of the reduce() method in JavaScript, specifically highlighting the difference between using reduce() with and without an initial value. Let's break it down:
- First
reduce()call:
;[1, 2, 3].reduce((a, b) => {
console.log(a, b)
})
This will output:
1 2
undefined 3
Explanation:
- When
reduce()is called without an initial value, the first element of the array (1) becomes the initial accumulator value. - In the first iteration,
ais 1 (the first element) andbis 2 (the second element). - In the second iteration,
aisundefined(because the previous iteration didn't return anything) andbis 3. - The function doesn't return anything, so the final result of
reduce()isundefined.
- Second
reduce()call:
;[1, 2, 3].reduce((a, b) => {
console.log(a, b)
}, 0)
This will output:
0 1
undefined 2
undefined 3
Explanation:
- Here,
reduce()is called with an initial value of 0. - In the first iteration,
ais 0 (the initial value) andbis 1 (the first element). - In the second iteration,
aisundefined(because the previous iteration didn't return anything) andbis 2. - In the third iteration,
ais againundefinedandbis 3. - The function doesn't return anything, so the final result of
reduce()isundefined.
Key points:
- When
reduce()is called without an initial value, the first element of the array is used as the initial value, and reduction starts from the second element. - When an initial value is provided,
reduce()starts from the first element of the array. - In both cases, because the callback function doesn't return a value,
undefinedis used as the accumulator in subsequent iterations. - Typically, you'd want to return a value from each iteration to properly accumulate results. For example:This would log each step and correctly sum the array.
;[1, 2, 3].reduce((a, b) => { console.log(a, b) return a + b }, 0)
This example highlights the importance of understanding how reduce() works, particularly with regard to the initial value and the return value of the callback function.
20. name for Function expression
20.https://bigfrontend.dev/quiz/name-for-Function-expression
function a() {}
const b = function () {}
const c = function d() {
console.log(typeof d)
d = 'e'
console.log(typeof d)
}
console.log(typeof a)
console.log(typeof b)
console.log(typeof c)
console.log(typeof d)
c()
Solution:
Let's go through the code and its output:
Function Definitions:
function a() {} const b = function () {} const c = function d() { console.log(typeof d) d = 'e' console.log(typeof d) }ais a function declaration.bis an anonymous function expression assigned to a variable.cis a named function expression assigned to a variable. The function's internal name isd.
Console Output:
console.log(typeof a) // Output: "function" console.log(typeof b) // Output: "function" console.log(typeof c) // Output: "function" console.log(typeof d) // Output: "undefined"a,b, andcare all functions, sotypeofreturns "function" for each.dis not defined in the global scope, sotypeof dreturns "undefined".
Function
cExecution:c()This calls the function stored in
c. Inside this function:- First
console.log(typeof d)outputs: "function" This is becausedrefers to the function itself within its own scope. d = 'e'attempts to reassignd, but this doesn't affect the function name.- Second
console.log(typeof d)still outputs: "function" The assignmentd = 'e'doesn't change the type ofdwithin the function scope.
- First
Key points to understand:
Function declarations (
a) and function expressions (bandc) are all of type "function".In a named function expression (
function d() {...}), the function name (din this case) is only accessible within the function's own scope. It's not added to the surrounding scope, which is whytypeof doutside the function returns "undefined".Inside the named function expression, the function name (
d) is read-only. Attempts to reassign it (liked = 'e') are ignored in strict mode or fail silently in non-strict mode. This is why the type ofdremains "function" even after the attempted reassignment.The variable
cholds a reference to the function. The internal nameddoesn't create a variable in the outer scope.
This code demonstrates some subtle but important aspects of function definitions, scope, and the immutability of function names in JavaScript. It's a good example of why understanding these concepts is crucial for avoiding unexpected behavior in more complex scenarios.
22.min max
22.https://bigfrontend.dev/quiz/min-max
console.log(Math.min())
console.log(Math.max())
console.log(Math.min(1))
console.log(Math.max(1, 2))
console.log(Math.min([1, 2, 3]))
Solution: Let's break it down line by line.
console.log(Math.min())Output:InfinityExplanation: When
Math.min()is called with no arguments, it returnsInfinity. This might seem counterintuitive, but it's designed this way so that any number you compare with this result will be considered smaller.console.log(Math.max())Output:-InfinityExplanation: Similarly, when
Math.max()is called with no arguments, it returns-Infinity. This ensures that any number compared with this result will be considered larger.console.log(Math.min(1))Output:1Explanation: When
Math.min()is called with a single argument, it simply returns that argument.console.log(Math.max(1,2))Output:2Explanation: This is the standard use case.
Math.max()compares the given arguments and returns the largest one.console.log(Math.min([1,2,3]))Output:NaNExplanation: This is a tricky one.
Math.min()doesn't automatically unpack arrays. When given an array as an argument, it tries to convert the array to a number, which results inNaN(Not a Number).
Key points to remember:
Math.min()andMath.max()with no arguments returnInfinityand-Infinityrespectively.These functions work as expected when given multiple numeric arguments.
They don't work directly with arrays. To use an array, you need to spread its elements:
console.log(Math.min(...[1, 2, 3])) // This would correctly output 1Both functions can handle any number of arguments:
Math.min(5, 2, 8, 1, 9) // returns 1 Math.max(5, 2, 8, 1, 9) // returns 9If any argument is
NaNor can't be converted to a number, the result isNaN:Math.min(1, 'hello', 3) // returns NaN
These behaviors highlight the importance of understanding how JavaScript functions handle different types of inputs and edge cases, especially when working with mathematical operations.
Infinity - Infinity
1
2
NaN
43. JSON.stringify()
43.https://bigfrontend.dev/quiz/json-stringify
// attention that for console.log('"a"'), you should enter ""a""
// please refer to format guide
console.log(JSON.stringify(['false', false]))
console.log(JSON.stringify([NaN, null, Infinity, undefined]))
console.log(JSON.stringify({ a: null, b: NaN, c: undefined }))
Solution:
This code demonstrates how JSON.stringify() handles different JavaScript values. Let's break it down line by line:
console.log(JSON.stringify(['false', false]))Output:'["false",false]'- The string 'false' is encoded as a JSON string.
- The boolean false is encoded as the JSON literal false.
- Both are kept in the array structure.
console.log(JSON.stringify([NaN, null, Infinity, undefined]))Output:'[null,null,null,null]'- NaN is converted to null in JSON.
- null remains as null.
- Infinity is converted to null in JSON.
- undefined is converted to null when it's in an array.
console.log(JSON.stringify({a: null, b: NaN, c: undefined}))Output:'{"a":null,"b":null}'- The property a with value null is kept as-is.
- The property b with value NaN is converted to null.
- The property c with value undefined is omitted entirely from the JSON output.
Key points about JSON.stringify():
- Strings are always valid JSON and are enclosed in double quotes.
- Booleans (true and false) are valid JSON literals.
- Numbers are generally valid JSON, but with some exceptions:
- NaN and Infinity are converted to null.
- null is a valid JSON literal.
- undefined is handled differently depending on context:
- In arrays, it's converted to null.
- As an object property value, the entire property is omitted.
- Object properties with undefined values are omitted from the JSON output.
- JSON doesn't have a representation for JavaScript's NaN or Infinity, so they're converted to null.
This behavior ensures that the resulting JSON string is always valid according to the JSON specification, even if it means losing some information from the original JavaScript value. It's important to be aware of these conversions when working with data that might contain special values like NaN, Infinity, or undefined.
127. JSON.stringify() II
127.https://bigfrontend.dev/quiz/json-stringify-ii
console.log(JSON.stringify(undefined))
console.log(JSON.stringify([undefined]))
Solution:
Let's break down this code and explain what's happening:
console.log(JSON.stringify(undefined))
console.log(JSON.stringify([undefined]))
Let's examine each line separately:
console.log(JSON.stringify(undefined))JSON.stringify()is a method that converts a JavaScript value to a JSON string.- When
undefinedis passed directly toJSON.stringify(), it returnsundefined(not as a string, but as the actualundefinedvalue). - When
console.log()receivesundefined, it printsundefinedto the console. - Output:
undefined
console.log(JSON.stringify([undefined]))- Here, we're passing an array containing a single
undefinedelement toJSON.stringify(). - In JSON, there is no
undefineddata type. WhenJSON.stringify()encountersundefinedwithin an array, it replaces it withnull. - The resulting JSON string will be
"[null]". console.log()prints this string to the console.- Output:
[null]
- Here, we're passing an array containing a single
So, the complete output will be:
undefined
[null]
Key points to understand:
JSON.stringify()handlesundefineddifferently depending on where it appears:- When
undefinedis the root value,JSON.stringify()returnsundefined(not a string). - When
undefinedis found within an array, it's converted tonullin the resulting JSON string. - When
undefinedis found as a property value in an object, the property is omitted entirely from the JSON output.
- When
JSON does not have an
undefinedtype, which is why it's handled specially byJSON.stringify().This behavior helps maintain valid JSON structure, as
undefinedis not a valid JSON value, butnullis.It's important to be aware of these transformations when working with
JSON.stringify(), especially when dealing with data that might containundefinedvalues.
This example demonstrates the nuanced behavior of JSON.stringify() when dealing with undefined values in different contexts.
44. Function call
44.https://bigfrontend.dev/quiz/Function-call
function a() {
console.log(1)
return {
a: function () {
console.log(2)
return a()
},
}
}
a().a()
Solution:
Let's break down this code step by step:
First, we define a function
a:function a() { console.log(1) return { a: function () { console.log(2) return a() }, } }This function does two things:
- It logs the number 1 to the console.
- It returns an object with a method also named
a.
The returned object's
amethod:- Logs the number 2 to the console.
- Calls the original
afunction again.
Now, let's look at the function call:
a().a()This is executed as follows:
a. First,
a()is called:- It logs 1 to the console.
- It returns the object with the
amethod.
b. Then,
.a()is called on the returned object:- It logs 2 to the console.
- It calls
a()again. b. Then,a()is again: - It logs 1 to the console.
- It return
{a: function () {...}}. However, the function inside is not executed.
So, the execution flow is:
a()is called- 1 is logged
- The object is returned
- The
amethod of the returned object is called - 2 is logged
a()is called again (go back to step 1)- 1 is logged and returned an object. This creates an infinite loop, alternately logging 1 and 2:
1
2
1
Key points:
- This is an example of recursion, where a function indirectly calls itself.
- The name
ais used for both the outer function and the method of the returned object, but they are distinct functions. - This code demonstrates how function returns and method calls can be chained.
65. Function name
65.https://bigfrontend.dev/quiz/Function-name
var foo = function bar() {
return 'BFE'
}
console.log(foo())
console.log(bar())
Solution: This code demonstrates named function expressions and scoping rules in JavaScript. Let's break it down:
Function Declaration:
var foo = function bar() { return 'BFE' }This is a named function expression. It creates a function with the name 'bar' and assigns it to the variable 'foo'.
console.log(foo());- This calls the function stored in the variable 'foo'.
- It will execute successfully and return 'BFE'.
- Output:
'BFE'
console.log(bar());- This attempts to call a function named 'bar'.
- However, 'bar' is not defined in the current scope.
- This will result in a ReferenceError.
Key points:
In a named function expression, the function name (in this case, 'bar') is only available within the function's own scope. It's not created as a variable in the outer scope.
The variable to which the function is assigned (in this case, 'foo') is what's used to reference the function from the outside.
Attempting to use the function's name ('bar') outside of its own scope will result in a ReferenceError.
The expected behavior:
- The first
console.log(foo())will output'BFE'. - The second
console.log(bar())will throw a ReferenceError: "bar is not defined".
So, if we were to run this code, we would see:
'BFE'
Uncaught ReferenceError: bar is not defined
This quiz tests understanding of:
- Named function expressions
- Variable scoping in JavaScript
- The difference between a function's name and the variable it's assigned to
It's a good example of how function names in named function expressions behave differently from function declarations, highlighting some nuanced aspects of JavaScript's scoping rules.
69. undefined
69.https://bigfrontend.dev/quiz/undefined
function foo(a, b, undefined, undefined) {
console.log('BFE.dev')
}
console.log(foo.length)
Solution:
Certainly! This code demonstrates an interesting aspect of JavaScript functions related to their parameters and the length property. Let's break it down:
Function Declaration:
function foo(a, b, undefined, undefined) { console.log('BFE.dev') }- This declares a function named
foowith four parameters. - Interestingly, two of the parameters are named
undefined. - In JavaScript, it's valid to have multiple parameters with the same name, though it's not a recommended practice.
- This declares a function named
console.log(foo.length)- This logs the
lengthproperty of the functionfoo.
- This logs the
The key point here is understanding what function.length represents:
- The
lengthproperty of a function object indicates the number of parameters expected by the function. - It returns the number of parameters in the function declaration, excluding rest parameters.
- Importantly, it counts the number of parameters up to the first parameter with a default value.
In this case:
- The function
foohas four parameters in its declaration. - None of these parameters have default values.
- The fact that two parameters have the same name (
undefined) doesn't affect thelengthproperty.
Therefore, console.log(foo.length) will output:
4
Key points to note:
- The
lengthproperty of a function returns the number of parameters in its declaration. - Duplicate parameter names are allowed in non-strict mode (though they should be avoided).
- The actual names of the parameters (even if they're
undefined) don't affect thelengthproperty. - This behavior can be useful for introspection or when writing higher-order functions that need to know about the arity (number of arguments) of other functions.
This quiz tests understanding of:
- Function parameters in JavaScript
- The
lengthproperty of function objects - JavaScript's permissiveness with parameter naming
It's a subtle example that highlights some of the more unusual aspects of JavaScript's function syntax and properties.
12. Array
21. Array I
21.https://bigfrontend.dev/quiz/Array-I
const a = [0]
console.log(a.length)
a[3] = 3
console.log(a.length)
for (let item of a) {
console.log(item)
}
a.map((item) => {
console.log(item)
})
a.forEach((item) => {
console.log(item)
})
console.log(Object.keys(a))
delete a[3]
console.log(a.length)
a[2] = 2
a.length = 1
console.log(a[0], a[1], a[2])
Solution: Let's analyze each part of the code:
const a = [0]- Initializes an array with one element, 0.
console.log(a.length)Output:1- The array has one element, so its length is 1.
a[3] = 3- Assigns the value 3 to index 3, creating a sparse array.
console.log(a.length)Output:4- The length is now 4, as it's always one more than the highest index.
for (let item of a) { console.log(item) }Output:0 undefined undefined 3- This loop iterates over all items, including empty slots (as undefined).
a.map(item => {console.log(item)})Output:0 3mapskips empty slots.
a.forEach(item => {console.log(item)})Output:0 3forEachskips empty slots.
console.log(Object.keys(a))Output:['0', '3']Object.keysreturns only the indices of existing elements.
delete a[3]- Removes the element at index 3, leaving an empty slot.
console.log(a.length)Output:4- Deleting an element doesn't change the array's length.
a[2] = 2- Assigns 2 to index 2, filling one of the empty slots.
a.length = 1- Explicitly sets the array length to 1, truncating the array.
console.log(a[0],a[1],a[2])Output:0 undefined undefined- a[0] is still 0, but a[1] and a[2] are now undefined as they were truncated.
Key takeaways:
- Array length is always one more than the highest index.
- Assigning to an index higher than the current length creates a sparse array.
- Different array methods handle empty slots differently:
for...ofandmaptreat empty slots as undefined.forEachskips empty slots.
Object.keysonly returns indices of existing elements.deleteleaves an empty slot but doesn't change the array length.- Setting
lengthto a smaller value truncates the array.
This code demonstrates the flexibility and sometimes unexpected behavior of JavaScript arrays, particularly when dealing with sparse arrays and manipulating array length directly.
1
4
0
undefined
undefined
3
0
3
0
;(3)[('0', '3')]
4
0, undefined, undefined
37. push unshift
37.https://bigfrontend.dev/quiz/push-unshift
const arr = [1, 2]
arr.push(3, 4)
arr.unshift(5, 6)
console.log(arr)
Solution: Let's break down this code step by step:
const arr = [1,2]- This creates a constant array
arrwith initial elements 1 and 2. - At this point,
arris [1, 2].
- This creates a constant array
arr.push(3,4)- The
push()method adds one or more elements to the end of an array. - It adds 3 and 4 to the end of the array.
- After this operation,
arris [1, 2, 3, 4].
- The
arr.unshift(5,6)- The
unshift()method adds one or more elements to the beginning of an array. - It adds 5 and 6 to the beginning of the array.
- After this operation,
arris [5, 6, 1, 2, 3, 4].
- The
console.log(arr)- This prints the final state of the array to the console.
So, the output will be:
[5, 6, 1, 2, 3, 4]
Key points:
push()adds elements to the end of an array.unshift()adds elements to the beginning of an array.- Both methods can take multiple arguments to add multiple elements at once.
- These methods modify the original array; they don't create a new array.
- Even though
arris declared withconst, we can still modify its contents. Theconstdeclaration prevents reassignment of the variablearr, but doesn't make the array itself immutable.
This quiz tests understanding of basic array manipulation methods in JavaScript and how they affect the array's contents.
55. sparse array
55.https://bigfrontend.dev/quiz/sparse-array
const arr = [1, , , 2]
// forEach
arr.forEach((i) => console.log(i))
// map
console.log(arr.map((i) => i * 2))
// for ... of
for (const i of arr) {
console.log(i)
}
// spread
console.log([...arr])
Solution: Certainly! This code demonstrates how different JavaScript array methods and operations handle sparse arrays (arrays with empty slots). Let's break it down:
const arr = [1,,,2]This creates a sparse array with four elements:- Index 0: 1
- Index 1: empty
- Index 2: empty
- Index 3: 2
Now, let's see how different methods handle this array:
arr.forEach(i => console.log(i))forEachskips empty slots. It will output:1 2console.log(arr.map(i => i * 2))mappreserves the empty slots. It will output:[2,empty,empty,4]for (const i of arr) { console.log(i) }for...ofincludes undefined for empty slots. It will output:1 undefined undefined 2console.log([...arr])The spread operator treats empty slots as undefined. It will output:[1, undefined, undefined, 2]
Key points:
Sparse arrays: JavaScript allows arrays to have "holes" or empty slots. These are different from slots containing
undefined.forEach: Skips empty slots entirely.map: Preserves the structure of the original array, including empty slots.for...of: Treats empty slots asundefined.Spread operator: Converts empty slots to
undefined.
This behavior can lead to unexpected results when working with sparse arrays. It's generally a good practice to avoid creating sparse arrays, as their behavior can be inconsistent across different array methods and operations.
This quiz tests understanding of:
- The concept of sparse arrays in JavaScript
- How different array methods and operations handle empty slots
- The difference between an empty slot and a slot containing
undefined
Understanding these nuances is important for writing robust JavaScript code, especially when dealing with arrays that might have gaps or when working with data from external sources that might produce sparse arrays.
72. Array length
72.https://bigfrontend.dev/quiz/array-length
class MyArray extends Array {
get length() {
return 3
}
}
const arr1 = new MyArray(10)
console.log(arr1.length)
const arr2 = new Array(10)
console.log(arr2.length)
Solution: Subclasses cannot override parentClass properties. This is by design.
10
10
74. Typed Array length
74.https://bigfrontend.dev/quiz/Typed-Array-length
class MyArray extends Uint8Array {
get length() {
return 3
}
}
const arr1 = new MyArray(10)
console.log(arr1.length)
const arr2 = new Uint8Array(10)
console.log(arr2.length)
Solution: Certainly! This code demonstrates class inheritance in JavaScript, specifically extending a typed array (Uint8Array), and the potential pitfalls of overriding core properties. Let's break it down:
Class Definition:
class MyArray extends Uint8Array { get length() { return 3 } }MyArrayis a subclass ofUint8Array, which is a typed array representing an array of 8-bit unsigned integers.- It overrides the
lengthgetter to always return 3, regardless of the actual array content.
Creating an instance of MyArray:
const arr1 = new MyArray(10) console.log(arr1.length)- This creates a new
MyArrayinstance with an initial size of 10. - However, due to the overridden
lengthgetter,arr1.lengthwill always return 3. - Output:
3
- This creates a new
Creating a regular Uint8Array:
const arr2 = new Uint8Array(10) console.log(arr2.length)- This creates a standard Uint8Array with length 10.
- Output:
10
Key points:
- Typed arrays like Uint8Array are specialized array-like objects for handling binary data.
- Extending typed arrays is possible in JavaScript, just like extending regular arrays.
- Overriding core properties like
lengthcan lead to unexpected behavior, especially with typed arrays. - The
lengthproperty is crucial for many array operations, so overriding it can break normal functionality. - The overridden
lengthgetter inMyArrayignores the actual content or initialization of the array.
Important considerations:
- While this code demonstrates an interesting point about inheritance and property overriding, it's generally not a good practice to override
lengthin this way for arrays, especially typed arrays. - Typed arrays are designed for performance and efficiency in handling binary data. Overriding
lengthcould interfere with this intended use. - Many built-in methods of Uint8Array rely on the
lengthproperty behaving normally, so this override could cause issues with those methods. - This example shows how JavaScript's class system allows for powerful customizations, but also how it can be misused to create confusing or potentially broken behavior.
This quiz tests understanding of:
- Class inheritance in JavaScript, particularly with built-in types like typed arrays
- Overriding getters in subclasses
- The behavior of typed array constructors
- How overriding core properties can affect object behavior, especially in specialized types like typed arrays
It's a tricky example that highlights the potential pitfalls of subclassing built-in objects, especially more specialized ones like typed arrays, and overriding their core properties.
This is quite similar to this previous problem but the difference being we are extending from Uint8Array typed array that uses ArrayBuffer and in this case Subclass constructors may over-ride it to change the constructor assignment See this
84. Array.prototype.sort()
84.https://bigfrontend.dev/quiz/Array-prototype-sort
const a = [999, 1111, 111, 2, 0]
const b = a.sort()
console.log(a)
console.log(b)
Solution: Certainly! This code demonstrates the behavior of JavaScript's built-in sort() method on arrays. Let's break it down:
Array Declaration:
const a = [999, 1111, 111, 2, 0]This creates an array
awith five numeric elements.Sorting:
const b = a.sort()This sorts the array
aand assigns the result tob.Console Output:
console.log(a) console.log(b)This logs both
aandbto the console.
Now, let's discuss the key points:
The
sort()method sorts the elements of an array in place and returns the sorted array. This means it modifies the original array.When
sort()is called without arguments, it sorts the elements as strings in alphabetical and ascending order.For numbers, this leads to an unexpected result because the numbers are converted to strings before sorting.
Both
aandbreference the same sorted array after the operation.
The output will be:
[0, 111, 1111, 2, 999]
[0, 111, 1111, 2, 999]
Explanation of the sorting:
- When converted to strings, the numbers are ordered as: "0", "111", "1111", "2", "999"
- This is why 1111 comes before 2 in the sorted array
Important notes:
- To sort numbers correctly, you should provide a comparison function to
sort():a.sort((x, y) => x - y) constdoesn't make the array immutable; it just prevents reassignment of the variable.bis not a new array; it's the same array asa, just sorted.
This example highlights the importance of understanding how built-in methods work in JavaScript, especially when dealing with different data types. It also demonstrates that arrays in JavaScript are reference types, and operations like sort() modify the original array.
90. array keys
90.https://bigfrontend.dev/quiz/array-keys
console.log(Reflect.ownKeys([]))
console.log(Reflect.ownKeys([,]))
console.log(Reflect.ownKeys([1, , 2]))
console.log(Reflect.ownKeys([...[1, , 2]]))
Solution: Certainly! This code demonstrates the use of Reflect.ownKeys() on different array configurations in JavaScript. Let's break it down line by line:
console.log(Reflect.ownKeys([]))- This creates an empty array and gets its own keys.
- Output:
['length'] - Even an empty array has a 'length' property.
console.log(Reflect.ownKeys([,]))- This creates an array with one empty slot.
- Output:
['length'] - The empty slot doesn't create an own property.
console.log(Reflect.ownKeys([1,,2]))- This creates an array with elements at indices 0 and 2, and an empty slot at index 1.
- Output:
['0', '2', 'length'] - The indices of non-empty slots become own properties.
console.log(Reflect.ownKeys([...[1,,2]]))- This spreads the array
[1,,2]into a new array. - The spread operator turns empty slots into
undefinedvalues. - Output:
['0', '1', '2', 'length'] - All indices, including the one that was an empty slot, become own properties.
- This spreads the array
Key points:
Reflect.ownKeys()returns an array of all the own property names of an object, including non-enumerable properties.For arrays, this includes:
- Numeric indices of elements that are not empty slots
- The 'length' property
Empty slots in arrays (often called "holes") do not create own properties.
The spread operator (
...) converts empty slots toundefined, which does create own properties.Array indices are converted to strings in the returned array of keys.
This code highlights some interesting aspects of JavaScript arrays:
- The difference between empty slots and
undefinedvalues - How array indices become object properties
- The effect of the spread operator on array holes
It's a good demonstration of the internal workings of JavaScript arrays and how they differ from simple lists in other languages.
91. largest Array index
91.https://bigfrontend.dev/quiz/largest-Array-index
const arr = []
arr[2 ** 32 - 2] = 1
arr[2 ** 32 - 1] = 2
console.log(arr.at(-1))
Solution: This code demonstrates some interesting behavior related to array indexing and the limits of JavaScript arrays. Let's break it down:
const arr = []- Creates an empty array.
arr[(2 ** 32) - 2] = 12 ** 32is 4,294,967,296(2 ** 32) - 2is 4,294,967,294- This sets the element at index 4,294,967,294 to 1
- This is the largest valid index for a JavaScript array
arr[(2 ** 32) - 1] = 2(2 ** 32) - 1is 4,294,967,295- This attempts to set an element at index 4,294,967,295
- However, this index is beyond the maximum allowed array index in JavaScript
console.log(arr.at(-1))- The
at()method accesses array elements using negative indices, counting from the end -1refers to the last element of the array
- The
Now, let's consider what happens:
- The array is created with a length of 4,294,967,295 (the largest valid index + 1)
- The element at index 4,294,967,294 is set to 1
- The attempt to set an element at index 4,294,967,295 is ignored because it's beyond the array limit
arr.at(-1)tries to access the last element of the array
The output will be:
1
Key points:
- JavaScript arrays have a maximum length of 2^32 - 1 (4,294,967,295)
- The highest valid index is one less than this: 2^32 - 2 (4,294,967,294)
- Attempts to set elements beyond this limit are silently ignored
- The
at()method provides a convenient way to access elements from the end of the array - Despite the array appearing to have two elements set, only the one at the highest valid index is actually in the array
This code demonstrates the limits of JavaScript arrays and how they behave at these limits. It's an edge case that you're unlikely to encounter in typical programming scenarios, but it's useful for understanding the underlying implementation of arrays in JavaScript.
113. push
113.https://bigfrontend.dev/quiz/push
const a = [1, 2, 3]
const b = a.push(4)
const c = b.push(5)
console.log(c)
Solution: Let's break down this code step by step:
const a = [1, 2, 3]
This line creates an array a with three elements: 1, 2, and 3.
const b = a.push(4)
The push() method adds one or more elements to the end of an array and returns the new length of the array. It modifies the original array.
So, this line does two things:
- It adds the number 4 to the end of array
a. Nowais [1,2,3,4]. - It assigns the new length of the array (which is 4) to the variable
b.
const c = b.push(5)
This line is problematic. b is not an array; it's a number (the length of array a after pushing 4). Numbers don't have a push() method.
console.log(c)
When this line runs, it will throw a TypeError because we tried to call push() on a number in the previous line.
The correct output of this code would be a TypeError, something like:
TypeError: b.push is not a function
If the intention was to add 5 to the original array a, the code should have been:
const a = [1, 2, 3]
a.push(4)
a.push(5)
console.log(a) // This would output [1,2,3,4,5]
This example illustrates a common misunderstanding about the push() method. While it modifies the original array, it returns the new length, not the modified array itself.
13.time
54. setTimeout(0ms)
54.https://bigfrontend.dev/quiz/setTimeout-0ms
// This snippet's result may vary on browsers
setTimeout(() => {
console.log(2)
}, 2)
setTimeout(() => {
console.log(1)
}, 1)
setTimeout(() => {
console.log(0)
}, 0)
Solution:
This code snippet demonstrates the behavior of JavaScript's setTimeout function with very small delay values. Let's break it down:
First
setTimeout:setTimeout(() => { console.log(2) }, 2)This schedules a callback to log
2after a 2 millisecond delay.Second
setTimeout:setTimeout(() => { console.log(1) }, 1)This schedules a callback to log
1after a 1 millisecond delay.Third
setTimeout:setTimeout(() => { console.log(0) }, 0)This schedules a callback to log
0after a 0 millisecond delay.
Now, here's why the result may vary on different browsers:
Minimum delay: Most browsers have a minimum delay that they enforce for
setTimeout, even if you specify a shorter time. This is typically around 4ms, but it can vary.Timer resolution: The actual resolution of timers can vary between browsers and operating systems.
Task queue and event loop: All these
setTimeoutcallbacks are placed in the task queue, and their exact order of execution can be influenced by how the browser handles the event loop and task queue.Browser optimizations: Some browsers might optimize the handling of very short timeouts differently.
Given these factors, there are a few possible outcomes:
If the browser respects the exact times (which is rare), the output would be:
0 1 2If the browser treats all these as "minimum delay" timeouts, they might execute in the order they were scheduled:
2 1 0Some browsers might group these together and execute them in a somewhat unpredictable order.
The key takeaways from this quiz are:
setTimeoutwith very small delays (0-4ms) often doesn't behave exactly as the numbers suggest.- There's no guarantee of precise timing for very short delays with
setTimeout. - The order of execution for nearly simultaneous timeouts can be unpredictable and may vary between browsers.
- When working with timeouts, it's generally safer to use larger delays if you need more predictable behavior.
This quiz highlights the importance of understanding the limitations and potential inconsistencies of JavaScript's timing functions across different environments.
81. setTimeout II
81.https://bigfrontend.dev/quiz/setTimeout-2
let num
for (let i = 0; i < 5; i++) {
num = i
setTimeout(() => {
console.log(num)
}, 100)
}
Solution: This code demonstrates the interaction between loop iterations, variable scoping, and asynchronous functions in JavaScript. Let's break it down:
let numdeclares a variable outside the loop.The
forloop runs 5 times (0 to 4).In each iteration:
num = iassigns the current value ofitonum.setTimeout()schedules a function to run after 100 milliseconds.
The loop completes almost instantly, much faster than the 100ms delay.
After the loop,
numhas the value 4 (the last value ofi).After 100ms, all the scheduled
console.log(num)calls execute.
The key point here is that all the setTimeout callbacks reference the same num variable. By the time these callbacks execute, the loop has long finished, and num has its final value of 4.
Therefore, the output will be:
4
4
4
4
4
This happens because:
- JavaScript's event loop: The setTimeout callbacks are queued to run after the main script execution.
- Closure: Each callback closes over the same
numvariable, not a copy of its value at each iteration. - Variable mutation:
numis repeatedly reassigned in the loop, ending with the value 4.
If you wanted to log 0, 1, 2, 3, 4 instead, you could either:
- Use
letin the loop:for (let i = 0; i < 5; i++)and useidirectly in the setTimeout callback. - Use an IIFE (Immediately Invoked Function Expression) to create a new scope for each iteration.
This example highlights the importance of understanding variable scope, closures, and the asynchronous nature of JavaScript when working with loops and timers.
86. setTimeout III
86.https://bigfrontend.dev/quiz/setTimeout-III
let func = () => {
console.log(1)
}
setTimeout(() => {
func = () => {
console.log(2)
}
}, 0)
setTimeout(func, 100)
Solution: This code demonstrates the interaction between function reassignment, closures, and the JavaScript event loop. Let's break it down step by step:
Initial function definition:
let func = () => { console.log(1) }This defines
funcas an arrow function that logs 1.First setTimeout:
setTimeout(() => { func = () => { console.log(2) } }, 0)This schedules a function to run as soon as possible (but after the current script finishes). This function will reassign
functo a new arrow function that logs 2.Second setTimeout:
setTimeout(func, 100)This schedules the current value of
functo be called after 100 milliseconds.
Now, let's consider the sequence of events:
- The initial
funcis defined. - Both setTimeouts are scheduled.
- The main script finishes executing.
- The first setTimeout callback runs (almost immediately), changing
functo log 2. - After 100ms, the second setTimeout callback runs, executing the function that
funcreferenced when the setTimeout was created.
The key point here is that when setTimeout(func, 100) is called, it doesn't schedule "whatever func will be in 100ms". Instead, it schedules "the function that func is right now".
Therefore, even though func is reassigned to a new function that logs 2, the originally scheduled function (which logs 1) is what actually runs after 100ms.
The output will be:
1
This example illustrates:
- How closures work in JavaScript (the setTimeout captures the current value of
func, not a reference to the variable itself). - The asynchronous nature of setTimeout and the JavaScript event loop.
- The difference between reassigning a variable and mutating an object (if
funcwere an object and we changed one of its properties, the scheduled setTimeout would see that change).
It's a tricky example that tests understanding of JavaScript's handling of function references and asynchronous operations.
128. setTimeout IV
128.https://bigfrontend.dev/quiz/settimeout-iv
function block(duration = 1000) {
const start = Date.now()
while (Date.now() - start < duration) {
window.timestamp = Date.now()
}
}
function a() {
console.log(1)
block()
setTimeout(() => console.log(2), 0)
setTimeout(() => console.log(3), 1)
}
function b() {
console.log(4)
}
console.log(5)
setTimeout(a, 0)
setTimeout(b, 500)
Solution: Let's break down this code and explain its behavior:
function block(duration = 1000) {
const start = Date.now()
while (Date.now() - start < duration) {
window.timestamp = Date.now()
}
}
function a() {
console.log(1)
block()
setTimeout(() => console.log(2), 0)
setTimeout(() => console.log(3), 1)
}
function b() {
console.log(4)
}
console.log(5)
setTimeout(a, 0)
setTimeout(b, 500)
Here's what happens:
console.log(5)executes immediately, printing 5.setTimeout(a, 0)schedules functionato run as soon as possible (but after the current script finishes).setTimeout(b, 500)schedules functionbto run after 500ms.The main script finishes, and the event loop starts processing the task queue.
Function
aruns (almost immediately, as it was scheduled with a 0ms delay):- It prints 1.
- It calls
block(), which runs a tight loop for 1000ms (1 second), blocking the main thread. - It schedules two more timeouts (for 2 and 3).
After
afinishes (which is about 1000ms after it started due to the blocking call):- The timeout for
bhas already passed its 500ms delay, sobruns next, printing 4. - Then the two timeouts from
arun, printing 2 and 3 in quick succession.
- The timeout for
The output will be:
5
1
4
2
3
Key points to understand:
JavaScript is single-threaded. The
blockfunction demonstrates a "busy wait" that occupies the main thread, preventing any other JavaScript from running during that time.setTimeoutschedules a task to run after a delay, but it doesn't guarantee exact timing. If the main thread is busy, the scheduled task will have to wait.Tasks scheduled with
setTimeout(fn, 0)don't run immediately; they're queued to run as soon as possible after the current script finishes.The
blockfunction inadelays everything else from running for about 1 second.Even though
bwas scheduled to run after 500ms, it doesn't actually run until afterafinishes blocking the main thread.The timeouts scheduled within
arun afterb, even though they were scheduled earlier, because they were scheduled after the blocking operation.
This code demonstrates how blocking operations can affect the timing of asynchronous tasks in JavaScript, and how the event loop and task queue work together to schedule and execute code.
Browsers may have different strategies for handling tasks scheduled with setTimeout and other asynchronous operations, leading to variations in the order of execution
In Chrome -> (5,1,2,4,3)
In Safari -> (5,1,4,2,3)
14.Object
57. non-writable
57.https://bigfrontend.dev/quiz/inherit-writable-flag
const a = {}
Object.defineProperty(a, 'foo1', {
value: 1,
})
const b = Object.create(a)
b.foo2 = 1
console.log(b.foo1)
console.log(b.foo2)
b.foo1 = 2
b.foo2 = 2
console.log(b.foo1)
console.log(b.foo2)
Solution: This code demonstrates object creation, property definition, and property inheritance in JavaScript. Let's break it down step by step:
const a = {}- Creates an empty object
a.
- Creates an empty object
Object.defineProperty(a, 'foo1', { value: 1 })- Defines a property 'foo1' on object
awith a value of 1. - By default, properties defined this way are not writable, not enumerable, and not configurable.
- Defines a property 'foo1' on object
const b = Object.create(a)- Creates a new object
bwithaas its prototype. binherits properties froma.
- Creates a new object
b.foo2 = 1- Adds a new property 'foo2' to
bwith a value of 1. - This property is created directly on
b, not on its prototype.
- Adds a new property 'foo2' to
console.log(b.foo1)- Outputs: 1
bdoesn't have its own 'foo1', so it looks up the prototype chain and finds it ona.
console.log(b.foo2)- Outputs: 1
- This is the 'foo2' property directly on
b.
b.foo1 = 2- Attempts to set 'foo1' on
bto 2. - This fails silently because 'foo1' on
ais not writable. - It doesn't create a new 'foo1' on
b.
- Attempts to set 'foo1' on
b.foo2 = 2- Sets 'foo2' on
bto 2. - This succeeds because 'foo2' is a regular property on
b.
- Sets 'foo2' on
console.log(b.foo1)- Outputs: 1
- Still returns 1 from
a.foo1because the attempt to change it failed.
console.log(b.foo2)- Outputs: 2
- Returns the new value of 'foo2' on
b.
Key points:
Object.defineProperty()creates non-writable properties by default.Object.create()sets up prototype inheritance.- Properties are looked up in the prototype chain if not found on the object itself.
- Attempting to modify a non-writable inherited property doesn't throw an error, but silently fails.
- Properties created with direct assignment (like
b.foo2 = 1) are writable by default.
The output of this code will be:
1
1
1
2
This example illustrates important concepts in JavaScript's object-oriented programming model, including prototypal inheritance and property attributes.
58. inherit getter setter
58.https://bigfrontend.dev/quiz/inherit-getter-setter
let val = 0
class A {
set foo(_val) {
val = _val
}
get foo() {
return val
}
}
class B extends A {}
class C extends A {
get foo() {
return val
}
}
const b = new B()
console.log(b.foo)
b.foo = 1
console.log(b.foo)
const c = new C()
console.log(c.foo)
c.foo = 2
console.log(c.foo)
console.log(b.foo)
Solution: This code demonstrates the behavior of getters and setters in JavaScript classes, along with inheritance. Let's break it down step by step:
let val = 0- Initializes a variable
valto 0. This will be used by the classes.
- Initializes a variable
Class A:
class A { set foo(_val) { val = _val } get foo() { return val } }- Defines a class with a getter and setter for
foo. - The setter updates the global
val. - The getter returns the global
val.
- Defines a class with a getter and setter for
class B extends A { }- B inherits from A without modifying anything.
Class C:
class C extends A { get foo() { return val } }- C inherits from A but overrides the getter for
foo. - It still returns the global
val, effectively doing the same thing as A's getter. - When we override the
getmethod, it appears that thesetmethod must also be overridden, otherwise undefined is returned See this
- C inherits from A but overrides the getter for
const b = new B()- Creates an instance of B.
console.log(b.foo)- Outputs: 0 (initial value of
val)
- Outputs: 0 (initial value of
b.foo = 1- Calls the inherited setter, setting
valto 1.
- Calls the inherited setter, setting
console.log(b.foo)- Outputs: 1 (new value of
val)
- Outputs: 1 (new value of
const c = new C()- Creates an instance of C.
console.log(c.foo)- Outputs: 1 (current value of
val)
- Outputs: 1 (current value of
c.foo = 2- Because C only overrode the getter, not the setter, this will not update val as there is no setter defined
console.log(c.foo)- Outputs: 1 (old value of
val)
- Outputs: 1 (old value of
console.log(b.foo)- Outputs: 1 (b.foo also reflects the old value of
val)
- Outputs: 1 (b.foo also reflects the old value of
Key points:
- All instances share the same
valvariable. - B inherits both getter and setter from A.
- C inherits the setter from A but has its own getter (which does the same thing as A's getter).
- Changing
fooon either b or c affects the sharedval.
The output will be:
0
1
1
1
1
This quiz tests understanding of:
- Class inheritance in JavaScript
- Getter and setter methods
- How inherited properties work
- The difference between overriding a getter and not overriding it.
- Because C only overrode the getter, not the setter, this will not update val as there is no setter defined
- The concept of shared state across instances when using a global variable
59. override setter
59.https://bigfrontend.dev/quiz/override-setter
class A {
val = 1
get foo() {
return this.val
}
}
class B {
val = 2
set foo(val) {
this.val = val
}
}
const a = new A()
const b = new B()
console.log(a.foo)
console.log(b.foo)
b.foo = 3
console.log(b.val)
console.log(b.foo)
Solution: This code demonstrates the use of getter and setter methods in JavaScript classes, as well as some nuances of property access. Let's break it down step by step:
Class A:
class A { val = 1 get foo() { return this.val } }- Class A has a property
valinitialized to 1. - It has a getter method for
foothat returns the value ofval.
- Class A has a property
Class B:
class B { val = 2 set foo(val) { this.val = val } }- Class B has a property
valinitialized to 2. - It has a setter method for
foothat sets the value ofval.
- Class B has a property
const a = new A()- Creates an instance of A.
const b = new B()- Creates an instance of B.
console.log(a.foo)- Outputs: 1
- This calls the getter method of
fooin A, which returnsthis.val(1).
console.log(b.foo)- Outputs: undefined
- B only has a setter for
foo, not a getter. When you try to accessfoo, it returnsundefined.
b.foo = 3- This calls the setter method of
fooin B, settingthis.valto 3.
- This calls the setter method of
console.log(b.val)- Outputs: 3
- This directly accesses the
valproperty of b, which was set to 3 by the setter.
console.log(b.foo)- Outputs: undefined
- Even though we set a value using the setter, trying to access
foostill returnsundefinedbecause there's no getter defined.
Key points:
- Getters are used to read values, setters are used to write values.
- If a property only has a getter, it's read-only. If it only has a setter, you can set its value but reading it directly will return
undefined. - Getters and setters can operate on other properties of the object (like
valin this case). - The
valproperty can be accessed directly, bypassing the getter/setter.
The output will be:
1
undefined
3
undefined
This quiz tests understanding of:
- How getters and setters work in JavaScript classes
- The behavior when a property has only a getter or only a setter
- The difference between accessing a property directly vs. through a getter/setter
63. in
63.https://bigfrontend.dev/quiz/in-coercion
const obj = {
foo: 'bar',
}
console.log('foo' in obj)
console.log(['foo'] in obj)
Solution: This code demonstrates the usage of the in operator in JavaScript and how it behaves with different types of operands. Let's break it down:
const obj = { foo: 'bar' }- This creates an object
objwith a propertyfoothat has the value'bar'.
- This creates an object
console.log('foo' in obj)- The
inoperator checks if the property'foo'exists in the objectobj. - This will output
truebecause'foo'is indeed a property ofobj.
- The
console.log(['foo'] in obj)- This line is tricky. The left operand of
inis an array['foo']. - When the
inoperator is used with a non-string left operand, JavaScript automatically converts it to a string. ['foo'].toString()evaluates to'foo'.- So this line is equivalent to
console.log('foo' in obj). - This will also output
true.
- This line is tricky. The left operand of
The output of this code will be:
true
true
Key points:
- The
inoperator checks for the existence of a property in an object or its prototype chain. - The left operand of
inshould typically be a string or a symbol representing the property name. - If the left operand is not a string or symbol, JavaScript will convert it to a string.
- Arrays are converted to strings by joining their elements with commas. However, in this case, there's only one element, so
['foo']becomes'foo'when converted to a string. - This behavior can be counter-intuitive and is a good reason to always use string literals or variables containing strings with the
inoperator to avoid unexpected type coercion.
This quiz tests understanding of the in operator and JavaScript's type coercion rules, particularly how non-string values are converted to strings in certain contexts.
108. property key
108.https://bigfrontend.dev/quiz/property-key
const obj = {}
const a = { name: 'a' }
const b = { name: 'b' }
obj[a] = { ...a }
obj[b] = { ...b }
console.log(obj[a].name)
console.log(obj[b].name)
Solution: Let's break down this code and explain what's happening:
const obj = {}
const a = { name: 'a' }
const b = { name: 'b' }
obj[a] = { ...a }
obj[b] = { ...b }
console.log(obj[a].name)
console.log(obj[b].name)
This code demonstrates some interesting behavior when using objects as keys in other objects. Let's go through it step-by-step:
We create an empty object
obj.We create two objects
aandb, each with anameproperty.We use
aas a key inobjand set its value to a new object created by spreadinga.We do the same with
b.We then try to log
obj[a].nameandobj[b].name.
Now, here's the key point: When you use an object as a key in another object, JavaScript automatically converts that object to a string. The default string representation of an object is "[object Object]".
So, what's actually happening is:
obj[a]is equivalent toobj["[object Object]"]obj[b]is also equivalent toobj["[object Object]"]
This means that obj[a] and obj[b] are referring to the same property of obj. The second assignment (obj[b] = {...b}) overwrites the first one.
Therefore, obj ends up looking like this:
{
"[object Object]": { name: "b" }
}
When we log obj[a].name and obj[b].name, we're actually accessing the same object both times. This object is { name: "b" }, which was the last one assigned.
So, the output will be:
b
b
This code demonstrates several important concepts in JavaScript:
- Object-to-string conversion: When used as a key, objects are converted to strings.
- The default string representation of objects:
"[object Object]". - Property assignment and overwriting in objects.
- The spread operator (
...) for creating shallow copies of objects. - The potential pitfalls of using objects as keys in other objects.
It's a tricky example that highlights some of the nuances of working with objects in JavaScript, especially when it comes to using them as keys in other objects. In practice, if you need to use objects as keys, it's often better to use a Map instead of a regular object, as Map can use objects as keys without converting them to strings.
126. Object.keys()
126.https://bigfrontend.dev/quiz/object-keys
console.log([...Object.keys({ a: 1, b: 2 }), ...Object.keys({ b: 2, a: 1 })])
Solution:
Let's break down this code step by step:
console.log([...Object.keys({ a: 1, b: 2 }), ...Object.keys({ b: 2, a: 1 })])
Here's what's happening:
Object.keys({a: 1, b: 2}):- This creates an array of the enumerable property names of the object
{a: 1, b: 2}. - The result is
['a', 'b'].
- This creates an array of the enumerable property names of the object
Object.keys({b: 2, a: 1}):- This creates an array of the enumerable property names of the object
{b: 2, a: 1}. - The result is
['b', 'a'].
- This creates an array of the enumerable property names of the object
The spread operator
...is used on both of these arrays inside a new array literal[ ].- This spreads the elements of both arrays into the new array.
The resulting array is then logged to the console.
The output of this code will be:
['a', 'b', 'b', 'a']
Key points to understand:
Object.keys()returns an array of a given object's own enumerable property names, in the same order as a normal loop would.The order of keys in JavaScript objects is not guaranteed to be the same as the order in which they were defined, except for certain cases:
- Integer keys are sorted in ascending order.
- String keys are inserted in the order they were added if the object was created as an empty object literal.
- In modern JavaScript engines (ES2015+), the order of string keys is generally preserved as they were defined in an object literal.
The spread operator
...is used to expand an array into individual elements.In this case, even though the objects have the same keys in different orders,
Object.keys()returns them in the order they were defined in each object literal, resulting in['a', 'b']for the first object and['b', 'a']for the second.When these are spread into a new array, we get all four elements in the order they were returned by
Object.keys()for each object.
This example demonstrates how Object.keys() works with different object literals, and how the spread operator can be used to combine arrays. It also shows that the order of keys in an object can affect the order of the array returned by Object.keys().
15.String
85. String.raw()
85.https://bigfrontend.dev/quiz/String-raw
console.log(String.raw`BFE\n.${'dev'}`)
console.log(String.raw({ raw: 'BFE' }, 'd', 'e', 'v'))
// when you enter your input in text box below
// keep in mind it is treated as raw string and will be escaped
Solution: This code demonstrates the use of the String.raw tag function in JavaScript. Let's break it down:
First
console.log:console.log(String.raw`BFE\n.${'dev'}`)- This uses
String.rawas a tagged template literal. \nis treated as a raw string, not as a newline character.${'dev'}is a template literal placeholder, which is evaluated.
Output:
BFE\n.dev- This uses
Second
console.log:console.log(String.raw({ raw: 'BFE' }, 'd', 'e', 'v'))- This uses
String.rawas a function, not a tagged template. - The first argument is an object with a
rawproperty containing the string 'BFE'. - Subsequent arguments ('d', 'e', 'v') are interpolated between each character of 'BFE'.
Output:
BdFevE- This uses
Key points:
String.rawas a tagged template:- It returns a string where backslashes are treated as literal characters.
- Template literal substitutions
(${...})are still processed.
String.rawas a function:- The first argument should be an object with a
rawproperty containing a string. - Subsequent arguments are interpolated between each character of the
rawstring.
- The first argument should be an object with a
In both cases,
String.rawprevents escape sequences (like\n) from being interpreted.The comment about input being treated as a raw string and escaped is relevant if this quiz has an interactive component where users can input their own strings.
This quiz tests understanding of:
- Tagged template literals
- The
String.rawfunction and its different usage patterns - How JavaScript handles raw strings and escape sequences
It's a good demonstration of some of the more advanced string manipulation features in modern JavaScript.
93. string
93.https://bigfrontend.dev/quiz/string
let a = 'bfe.dev'
a[0] = 'c'
console.log(a)
Solution: This code demonstrates an important characteristic of strings in JavaScript. Let's break it down:
let a = 'bfe.dev'- This creates a string variable
awith the value 'bfe.dev'.
- This creates a string variable
a[0] = 'c'- This attempts to change the first character of the string from 'b' to 'c'.
console.log(a)- This prints the value of
ato the console.
- This prints the value of
The output will be:
bfe.dev
Key points:
Strings in JavaScript are immutable. This means that once a string is created, its individual characters cannot be changed.
When you try to modify a character in a string using bracket notation (like
a[0] = 'c'), JavaScript will not throw an error in non-strict mode. Instead, it will silently fail to make the change.In strict mode, attempting to modify a string character would throw a TypeError.
The original string remains unchanged after the attempted modification.
If you need to modify a string, you have to create a new string. For example:
a = 'c' + a.slice(1)This would create a new string with 'c' as the first character and the rest of the original string.
This behavior is different from some other programming languages where strings are mutable and can be changed character by character. In JavaScript, the immutability of strings is a fundamental characteristic that affects how string manipulation is performed.
Understanding this behavior is crucial for avoiding bugs when working with strings in JavaScript. It's also a common topic in JavaScript interviews and quizzes.
94. emoji
94.https://bigfrontend.dev/quiz/emoji
console.log('👍'.length)
Solution: Certainly! This code demonstrates an interesting aspect of how JavaScript handles Unicode characters, particularly emoji. Let's break it down:
console.log('👍'.length)
The output of this code will be:
2
Here's why:
The thumbs-up emoji '👍' is a single visual character, but in JavaScript's internal representation, it's actually composed of two code units.
JavaScript uses UTF-16 encoding for strings. In UTF-16, characters are represented by one or two 16-bit code units.
The thumbs-up emoji '👍' has the Unicode code point U+1F44D, which is outside the Basic Multilingual Plane (BMP).
Characters outside the BMP are represented in UTF-16 using two 16-bit code units, known as a surrogate pair.
The
lengthproperty of a string in JavaScript returns the number of 16-bit code units in the string, not the number of visual characters or Unicode code points.
Key points:
- Many emojis and other characters from non-BMP Unicode planes will have a
lengthof 2 or more in JavaScript. - This behavior can lead to unexpected results when working with strings containing emojis or other complex Unicode characters.
- If you need to work with the actual number of visual characters or Unicode code points, you might need to use more advanced string handling techniques or libraries.
For example, to get the correct length of a string with emojis, you could use:
console.log([...'👍'].length) // Output: 1
This spread operator technique splits the string into an array of its actual characters, giving the correct visual length.
Understanding this behavior is important for proper string manipulation, especially in applications that deal with international text or emojis.
16.Proxy
Creating a Simple Proxy Let's jump in and see how to create a Proxy. We'll start with a really basic example, in case you haven't seen Proxies before.
type MessageObject = {
message: string
}
let target: MessageObject = {
message: 'Hello, world!',
}
let handler: ProxyHandler<MessageObject> = {
get: (obj, prop) => {
return `Property ${String(prop)} is: ${obj[prop]}`
},
}
let proxy: MessageObject = new Proxy(target, handler)
console.log(proxy.message) // Output: Property message is: Hello, world!
- Auto-populating Properties
type LazyProfile = {
firstName: string
lastName: string
fullName?: string
}
let lazyProfileHandler = {
get: (target: LazyProfile, property: keyof LazyProfile) => {
if (property === 'fullName' && !target[property]) {
target[property] = `${target.firstName} ${target.lastName}`
}
return target[property]
},
}
let profile: LazyProfile = new Proxy({ firstName: 'John', lastName: 'Doe' }, lazyProfileHandler)
console.log(profile.fullName) // Output: John Doe
- Operation Counting
type Counter = {
[key: string]: any
_getCount: number
}
let countHandler = {
get: (target: Counter, property: keyof Counter) => {
if (property === '_getCount') {
return target[property]
}
target._getCount++
return target[property]
},
}
let counter: Counter = new Proxy({ a: 1, b: 2, _getCount: 0 }, countHandler)
counter.a
counter.b
console.log(counter._getCount) // Output: 2
- Immutable Objects
function createImmutable<T extends object>(obj: T): T {
return new Proxy(obj, {
set: () => {
throw new Error('This object is immutable')
},
})
}
const immutableObject = createImmutable({ name: 'Jane', age: 25 })
// immutableObject.age = 26; // Throws error
- Method Chaining and Fluent Interfaces
type FluentPerson = {
setName(name: string): FluentPerson
setAge(age: number): FluentPerson
save(): void
}
function FluentPerson(): FluentPerson {
let person: any = {}
return new Proxy(
{},
{
get: (target, property) => {
if (property === 'save') {
return () => {
console.log(person)
}
}
return (value: any) => {
person[property] = value
return target
}
},
}
) as FluentPerson
}
const person = FluentPerson()
person.setName('Alice').setAge(30).save() // Output: { setName: 'Alice', setAge: 30 }
- Smart Caching
function smartCache<T extends object>(obj: T, fetcher: (key: keyof T) => any): T {
const cache: Partial<T> = {}
return new Proxy(obj, {
get: (target, property: keyof T) => {
if (!cache[property]) {
cache[property] = fetcher(property)
}
return cache[property]
},
})
}
const userData = smartCache({ userId: 1 }, (prop) => {
console.log(`Fetching data for ${String(prop)}`)
return { name: 'Bob' } // Simulated fetch
})
console.log(userData.userId) // Output: Fetching data for userId, then returns { name: "Bob" }
- Dynamic Property Validation
let user = {
age: 25,
}
let validator = {
set: (obj, prop, value) => {
if (prop === 'age' && (typeof value !== 'number' || value < 18)) {
throw new Error('User must be at least 18 years old.')
}
obj[prop] = value
return true // Indicate success
},
}
let userProxy = new Proxy(user, validator)
userProxy.age = 30 // Works fine
console.log(userProxy.age) // Output: 30
// userProxy.age = 'thirty'; // Throws error
// userProxy.age = 17; // Throws error
- Watching for Changes
function onChange(obj, onChange) {
const handler = {
set: (target, property, value, receiver) => {
onChange(`Property ${String(property)} changed to ${value}`)
return Reflect.set(target, property, value, receiver)
},
}
return new Proxy(obj, handler)
}
const person = { name: 'John', age: 30 }
const watchedPerson = onChange(person, console.log)
watchedPerson.age = 31 // Console: Property age changed to 31
- Tracing: Logging function calls
function trace(originalFunction) {
return new Proxy(originalFunction, {
apply(target, thisArg, args) {
console.log('Calling function:', target.name, 'with arguments:', args);
// Important - don't forget the return statement or else the function's
// return value is lost!
return target.apply(thisArg, args);
}
});
}
- Data binding: Syncing multiple objects
<input type="text" id="username" />
const inputState = {
id: 'username',
value: ''
}
const input = document.querySelector('#username')
const handler = {
set: function(target, key, value) {
if (target.id && key === 'username') {
target[key] = value;
document.querySelector(`#${target.id}`).value = value;
return true
}
return false
}
}
const proxy = new Proxy(inputState, handler)
proxy.value = 'John Doe'
console.log(proxy.value, input.value)
// 'John Doe' will be printed for both
Reference: https://dev.to/mattlewandowski93/7-use-cases-for-javascript-proxies-3b29
80. Proxy I
80.https://bigfrontend.dev/quiz/proxy-i
const obj = new Map()
const map = new Map()
obj.foo = 1
map.set('foo', 2)
console.log(obj.foo)
console.log(map.get('foo'))
const proxyObj = new Proxy(obj, {})
const proxyMap = new Proxy(map, {})
console.log(proxyObj.foo)
console.log(proxyMap.get('foo'))
Solution: Certainly! This code demonstrates the usage of regular objects, Map objects, and Proxies in JavaScript. Let's break it down step by step:
Creating objects:
const obj = new Map() const map = new Map()objis created as a Map but is then used as a regular object.mapis created as a Map and used as a Map.
Setting properties/values:
obj.foo = 1 map.set('foo', 2)- For
obj, we're setting a propertyfoodirectly, which works because JavaScript objects can have properties added dynamically. - For
map, we're using thesetmethod to add a key-value pair.
- For
Logging values:
console.log(obj.foo) // Output: 1 console.log(map.get('foo')) // Output: 2- We access
obj.foodirectly as a property. - We use
map.get('foo')to retrieve the value from the Map.
- We access
Creating Proxies:
const proxyObj = new Proxy(obj, {}) const proxyMap = new Proxy(map, {})- We create Proxies for both
objandmapwith empty handler objects. - These Proxies will behave exactly like the original objects because no custom behavior is defined.
- We create Proxies for both
Logging values through Proxies:
console.log(proxyObj.foo) // Output: 1 console.log(proxyMap.get('foo')) // Output: undefined which is the unexpected behavior.We access the values through the Proxies, which pass through to the original objects.
The reason for
proxyMap.get('foo')returnsundefined:
- When we create a Proxy for a Map object, the Proxy doesn't automatically handle the special methods of Map like
get,set, etc. - When we call
proxyMap.get('foo'), it's looking for a propertygeton the Proxy object itself, not calling thegetmethod of the underlying Map. - Since there's no
getproperty on the Proxy (or its prototype chain), it returnsundefined.
To fix this and make the Proxy work correctly with Map methods, we need to use a handler with a
gettrap:const proxyMap = new Proxy(map, { get(target, prop) { if (typeof target[prop] === 'function') { return target[prop].bind(target) } return target[prop] }, })With this handler,
proxyMap.get('foo')would correctly return 2.
The output will be:
1
2
1
Error
Key points:
obj, despite being created as a Map, is used as a regular object. This works because in JavaScript, you can add properties to any object.mapis used correctly as a Map object, usingset()andget()methods.- Proxies with empty handlers behave identically to their target objects.
- This code demonstrates the flexibility of JavaScript objects and the transparent nature of basic Proxies.
This example highlights the differences between regular objects and Map objects, as well as showing how Proxies can be used to wrap objects without changing their behavior (when using an empty handler). It's a good demonstration of different ways to store and access key-value pairs in JavaScript.
82. Proxy II
82.https://bigfrontend.dev/quiz/Proxy-II
class Dev {
#name
constructor(name) {
this.#name = name
}
get name() {
return this.#name
}
}
const dev = new Dev('BFE')
console.log(dev.name)
const proxyDev = new Proxy(dev, {})
console.log(proxyDev.name)
Solution: This code demonstrates the use of private class fields, getters, and Proxies in JavaScript. Let's break it down:
Class Definition:
class Dev { #name constructor(name) { this.#name = name } get name() { return this.#name } }#nameis a private field, only accessible within the class.- The constructor initializes this private field.
- A getter
nameis defined to access the private field.
Creating an instance:
const dev = new Dev('BFE')- This creates a new
Devinstance with the name "BFE".
- This creates a new
Logging the name:
console.log(dev.name)- This calls the getter
name, which returns the private#name. - Output:
BFE
- This calls the getter
Creating a Proxy:
const proxyDev = new Proxy(dev, {})- This creates a Proxy for the
devobject with an empty handler.
- This creates a Proxy for the
Logging the name through the Proxy:
console.log(proxyDev.name)- This attempts to access the
nameproperty through the Proxy. - Important: This will throw an error.
- This attempts to access the
The output will be:
BFE
Uncaught TypeError: Cannot read private member #name from an object whose class did not declare it
Key points:
- Private fields (
#name) are not accessible outside the class, even through getters. - The getter works fine when called directly on the instance (
dev.name). - When using a Proxy, accessing private fields (even through getters) throws an error.
The error occurs because:
- JavaScript's private fields are truly private and are not part of the object's property set.
- Proxies work by intercepting property access, but they can't intercept access to private fields.
- When the Proxy tries to forward the
nameaccess to the target object, it can't do so for the private field.
To fix this and make the Proxy work, you would need to explicitly handle the getter in the Proxy handler:
const proxyDev = new Proxy(dev, {
get(target, prop, receiver) {
if (prop === 'name') {
return target.name
}
return Reflect.get(target, prop, receiver)
},
})
This quiz highlights the interaction between modern JavaScript features (private fields) and more established ones (Proxies), showing that they don't always work together seamlessly without additional handling.
17.Other
52. requestAnimationFrame
52.https://bigfrontend.dev/quiz/requestanimationframe
console.log(1)
setTimeout(() => {
console.log(2)
}, 100)
requestAnimationFrame(() => {
console.log(3)
})
requestAnimationFrame(() => {
console.log(4)
setTimeout(() => {
console.log(5)
}, 10)
})
const end = Date.now() + 200
while (Date.now() < end) {}
console.log(6)
Solution: This code demonstrates the interaction between different types of asynchronous operations in JavaScript, including setTimeout, requestAnimationFrame, and a blocking while loop. Let's break it down:
console.log(1)- This runs immediately, outputting 1.
setTimeout(() => { console.log(2) }, 100)- This schedules a callback to run after 100ms.
First
requestAnimationFrame(() => { console.log(3) })- This schedules a callback to run before the next repaint.
Second
requestAnimationFrame(() => { ... })- This schedules another callback to run before the next repaint.
- Inside this callback, there's another setTimeout for 10ms.
const end = Date.now() + 200; while (Date.now() < end) { }- This creates a blocking loop that runs for 200ms.
console.log(6)- This runs immediately after the blocking loop.
The order of execution will be:
console.log(1)runs immediately.- The blocking while loop runs for 200ms.
console.log(6)runs after the blocking loop.- The first
requestAnimationFramecallback runs, logging 3. - The second
requestAnimationFramecallback runs, logging 4. - The 100ms setTimeout callback runs, logging 2.
- The 10ms setTimeout (from inside the second rAF) runs, logging 5.
So, the final output order will likely be:
1
6
3
4
2
5
Key points:
- Synchronous code (including blocking loops) runs before any asynchronous callbacks.
requestAnimationFramecallbacks typically run before the next repaint, which usually occurs every 16.7ms (60 fps).- setTimeout callbacks are pushed to the task queue and run after the current execution context is empty.
- The blocking while loop delays all other asynchronous operations.
- The exact timing can vary slightly depending on the browser and system performance.
This quiz tests understanding of JavaScript's event loop, the difference between microtasks and macrotasks, and how blocking operations affect the execution of asynchronous code. It's a complex example that demonstrates the intricacies of JavaScript's asynchronous behavior.
66. comma
66.https://bigfrontend.dev/quiz/comma
var obj = {
a: 'BFE',
b: 'dev',
func:
(function foo() {
return this.a
},
function bar() {
return this.b
}),
}
console.log(obj.func())
Solution: This code demonstrates some interesting aspects of JavaScript, including object literals, function expressions, and the behavior of this. Let's break it down:
Object Definition:
var obj = { a: 'BFE', b: 'dev', func: (function foo() { return this.a }, function bar() { return this.b }), }objis an object with propertiesa,b, andfunc.- The
funcproperty is assigned the result of a comma-separated expression.
The
funcproperty:- It's a comma-separated expression containing two function expressions.
- In JavaScript, a comma-separated expression evaluates all its parts but returns only the last part.
- So,
funcwill actually be assigned only the second function:function bar(){ return this.b; }
console.log(obj.func())- This calls the function stored in
obj.func. - When a function is called as a method of an object,
thisinside the function refers to that object. - In this case,
thisrefers toobj.
- This calls the function stored in
So, when obj.func() is called:
- It's actually calling the
barfunction. this.brefers toobj.b, which is "dev".
Therefore, the output will be:
"dev"
Key points:
- In a comma-separated expression, only the last part is returned/assigned.
- When a function is called as a method (
obj.func()),thisinside the function refers to the object it's called on. - The first function (
foo) in the comma expression is effectively discarded. - This code might be confusing because it looks like
funcmight be an array of functions, but it's not - it's just a single function due to the comma operator's behavior.
This quiz tests understanding of:
- The comma operator in JavaScript
- How
thisis determined in method calls - Function expressions in object literals
It's a tricky example that combines several JavaScript concepts in a way that might not be immediately obvious.
124. comma 2
124.https://bigfrontend.dev/quiz/comma-2
const a = (1, 2, 3)
console.log(a)
Solution: Let's break down this code:
const a = (1, 2, 3)
console.log(a)
This code demonstrates the use of the comma operator in JavaScript. Here's what's happening:
const a = (1,2,3)- This line uses the comma operator, which is enclosed in parentheses.
- The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
- In this case, it evaluates 1, then 2, and finally 3.
- The result of the entire expression is the value of the last operand, which is 3.
- So,
ais assigned the value 3.
console.log(a)- This simply prints the value of
ato the console.
- This simply prints the value of
The output of this code will be:
3
Key points to understand:
The comma operator
,in JavaScript evaluates multiple expressions, separated by commas, and returns the value of the last expression.When used in an assignment, only the final value is assigned to the variable.
The parentheses
()are necessary here. Without them, this would be interpreted as three separate statements, andawould be assigned the value 1.This usage of the comma operator is not common in practice and can lead to confusing code. It's generally better to write clearer, more explicit code.
The comma operator has very low precedence, lower than assignment, which is why the parentheses are needed in this case.
While this example demonstrates a feature of JavaScript, it's worth noting that using the comma operator in this way is generally not considered good practice in real-world code, as it can make the code harder to read and understand.
78. RegExp
78.https://bigfrontend.dev/quiz/RegExp
const arr = ['a', 'b', 'c', '1']
const regExp = /^[a-z]$/gi
const chars = arr.filter((elem) => regExp.test(elem))
console.log(chars)
Solution: Intuitively, it seems that output should be ["a","b","c"] since these items match the regular expression.
But when a regex has the global flag set, test() will advance the lastIndex of the regex. lastIndex is a property of RegExp that specifies the index at which to start the next match.
Basically,
As long as test() returns true, lastIndex will not reset—even when testing a different string! When test() returns false, the calling regex's lastIndex property will reset to 0. /^[a-z]$/gi 👉🏻 Any character from character set [a-z], g means global flag for multiple matches, i means character insensitive same as [a-zA-Z]
So the loop effectively becomes,
regExp.test('a') // true and it sets lastIndex = 1
regExp.test('b') // false as the lastIndex i.e. staring point is not 0, lastIndex resets
regExp.test('c') // true as lastIndex is 0 and regex satisfies
regExp.test('1') // false
Reference: https://bigfrontend.dev/quiz/RegExp/discuss
118. structuredClone()
118.https://bigfrontend.dev/quiz/structuredclone
const error = new Error('😢')
error.name = 'SyntaxError'
console.log(error instanceof Error)
console.log(error instanceof SyntaxError)
const clonededError = structuredClone(error)
console.log(clonededError instanceof Error)
console.log(clonededError instanceof SyntaxError)
Solution: I'd be happy to explain this code. Let's go through it step by step:
const error = new Error('😢')
error.name = 'SyntaxError'
console.log(error instanceof Error)
console.log(error instanceof SyntaxError)
const clonededError = structuredClone(error)
console.log(clonededError instanceof Error)
console.log(clonededError instanceof SyntaxError)
const error = new Error('😢'):- Creates a new Error object with the message "😢".
error.name = 'SyntaxError':- Changes the
nameproperty of the error to 'SyntaxError'. - This doesn't actually change the type of the error, just its name.
- Changes the
console.log(error instanceof Error):- This will log
truebecauseerroris indeed an instance of the Error class.
- This will log
console.log(error instanceof SyntaxError):- This will log
false. Even though we changed the name to 'SyntaxError', the error is still an instance of Error, not SyntaxError.
- This will log
const clonededError = structuredClone(error):structuredClone()has built-in support for cloning Error objects.- When cloning an Error, it preserves the Error type and creates a new Error instance.
- It also preserves the
nameproperty of the Error.
console.log(clonededError instanceof Error):- This will log
true.
- This will log
console.log(clonededError instanceof SyntaxError):- In JavaScript, when an Error's
nameproperty is set to 'SyntaxError', it becomes an instance ofSyntaxErroras well. - This will also log
truefor the same reason as above.
- In JavaScript, when an Error's
To summarize the output:
true
false
true
true
Key points:
- Changing the
nameproperty of an Error doesn't change its type. instanceofchecks if an object is an instance of a particular constructor function.structuredClone()creates a deep copy of an object
The structuredClone function in JavaScript is used to create a deep clone of a given value. This method can handle complex objects, including those with nested structures, circular references, and non-serializable properties (like Map, Set, Date, ArrayBuffer, etc.), which can't be properly cloned using simpler methods like JSON.stringify and JSON.parse.
Key Characteristics of structuredClone:
Deep Cloning:
structuredClonecreates a deep copy of the original object or value. This means all nested objects and properties are also cloned, and the resulting clone is completely independent of the original.- Changes made to the clone do not affect the original object and vice versa.
Support for Complex Data Types:
- It supports cloning of various built-in JavaScript objects like
Array,Object,Map,Set,Date,RegExp,Blob,File,ImageData,ArrayBuffer,TypedArray, and others. - Unlike
JSON.stringify/JSON.parse,structuredClonecan clone objects containing non-enumerable properties, properties with getters and setters, and objects with prototype chains.
- It supports cloning of various built-in JavaScript objects like
Handling Circular References:
structuredClonecan clone objects with circular references without throwing an error, something that would cause issues withJSON.stringify.
Performance:
structuredCloneis generally faster and more reliable for deep cloning compared to manual deep cloning or usingJSONmethods.
Syntax:
const clone = structuredClone(value)
value: The object or value you want to clone.- Returns: A deep clone of the provided
value.
Example Usage:
// Example with simple objects
const original = { a: 1, b: { c: 2 } }
const clone = structuredClone(original)
clone.b.c = 3
console.log(original.b.c) // Output: 2
console.log(clone.b.c) // Output: 3
// Example with complex objects
const originalComplex = new Map([['key', { a: 1, b: new Set([2, 3]) }]])
const cloneComplex = structuredClone(originalComplex)
console.log(cloneComplex.get('key').b.has(2)) // Output: true
console.log(cloneComplex.get('key').b === originalComplex.get('key').b) // Output: false
Limitations:
- Functions:
structuredClonedoes not clone functions. If the object contains functions, they will not be copied. - Symbol properties: Properties keyed by
Symbolare not cloned. - Error objects: While it can clone error objects, the non-enumerable properties (like
stack) may not be preserved.
Conclusion: structuredClone is a robust and versatile tool for deep cloning objects and values in JavaScript, making it preferable over other cloning techniques for most use cases, especially when dealing with complex or circular data structures.
88. try...catch
88.https://bigfrontend.dev/quiz/try-catch
var a = 'a'
try {
throw new Error('BFE.dev')
} catch {
var a = 'a1'
}
console.log(a)
var b = 'b'
try {
throw new Error('BFE.dev')
} catch (b) {
var b = 'b1'
}
console.log(b)
var c = 'c'
try {
throw new Error('BFE.dev')
} catch (error) {
var c = 'c1'
}
console.log(c)
Solution: This code demonstrates the behavior of variable declarations and scope in JavaScript, particularly within try...catch blocks. Let's break it down case by case:
Case with variable
a:var a = 'a' try { throw new Error('BFE.dev') } catch { var a = 'a1' } console.log(a)var ais hoisted to the top of its scope (function or global).- In the
catchblock,var a = 'a1'is treated as a re-declaration and assignment. - The
catchblock without a parameter doesn't create a new scope forvardeclarations. - Output:
'a1'
Case with variable
b:var b = 'b' try { throw new Error('BFE.dev') } catch (b) { var b = 'b1' } console.log(b)var bis hoisted.- The
catch (b)creates a block-scopedbthat shadows the outerb. var b = 'b1'insidecatchis hoisted to the function/global scope, not thecatchblock.- The block-scoped
bis not affected by thevardeclaration. - Output:
'b'(the outerbis unchanged)
Case with variable
c:var c = 'c' try { throw new Error('BFE.dev') } catch (error) { var c = 'c1' } console.log(c)- Similar to case 1,
var cis hoisted. - The
catch (error)doesn't interfere withc. var c = 'c1'in thecatchblock reassigns the value of the outerc.- Output:
'c1'
- Similar to case 1,
Key points:
vardeclarations are hoisted to the top of their containing function or global scope.catchblocks with a parameter create a new scope for that parameter, which can shadow outer variables.vardeclarations insidecatchblocks are not block-scoped; they affect the outer scope.- The behavior can be confusing and is one of the reasons why
letandconstare preferred in modern JavaScript for better scoping control.
This quiz highlights some of the quirks of var declarations and scoping in JavaScript, especially in try...catch contexts. It's a good reminder of why understanding these behaviors is important for avoiding unexpected results in JavaScript code.
120. try..catch..finally
120.https://bigfrontend.dev/quiz/try-catch-finally
function func() {
try {
console.log(1)
return
} catch (e) {
console.log(2)
} finally {
console.log(3)
}
console.log(4)
}
func()
Solution: Certainly! Let's break down this code and explain its execution:
function func() {
try {
console.log(1)
return
} catch (e) {
console.log(2)
} finally {
console.log(3)
}
console.log(4)
}
func()
Here's what happens when func() is called:
The function enters the
tryblock:console.log(1)executes, printing1to the console.- The
returnstatement is encountered. This would normally exit the function immediately, but because there's afinallyblock, its execution is delayed.
The
catchblock is skipped entirely because no error was thrown in thetryblock.The
finallyblock executes:console.log(3)runs, printing3to the console.- After the
finallyblock completes, the function returns (due to the earlierreturnstatement in thetryblock).
console.log(4)is never reached because the function has already returned.
The output of this function will be:
1
3
Key points to note:
- The
tryblock is where the main code execution happens. - The
catchblock only executes if an error is thrown in thetryblock. - The
finallyblock always executes, regardless of whether an error occurred or if there was areturnstatement. - A
returnstatement in thetryblock doesn't immediately exit the function if there's afinallyblock. Thefinallyblock will still execute before the function returns. - Code after the
try-catch-finallystructure (in this case,console.log(4)) will not execute if the function has already returned.
This example demonstrates how try, catch, and finally blocks interact with return statements, and shows that finally always executes, even when there's an early return in the try block.
125. try...catch...finally 2
125.https://bigfrontend.dev/quiz/try-catch-finally-2
const prettify = (str) => {
try {
if (typeof str === 'string') {
JSON.parse(str)
return 'prettified'
}
} catch (e) {
return 'errored'
} finally {
return str
}
}
console.log(prettify('BFE.dev'))
console.log(prettify('123'))
Solution: Certainly! Let's break down this code and explain its behavior:
const prettify = (str) => {
try {
if (typeof str === 'string') {
JSON.parse(str)
return 'prettified'
}
} catch (e) {
return 'errored'
} finally {
return str
}
}
console.log(prettify('BFE.dev'))
console.log(prettify('123'))
Here's what's happening:
The
prettifyfunction:- It takes a parameter
str. - It has a
try-catch-finallystructure.
- It takes a parameter
Inside the
tryblock:- It checks if
stris a string. - If it is, it attempts to parse
stras JSON usingJSON.parse(str). - If parsing succeeds, it would return "prettified" (but this return is overridden by the
finallyblock).
- It checks if
The
catchblock:- If
JSON.parse(str)throws an error, it would return "errored" (but again, this is overridden by thefinallyblock).
- If
The
finallyblock:- This block always executes, regardless of whether an error was thrown or not.
- It returns the original
strparameter.
console.log(prettify('BFE.dev')):- 'BFE.dev' is not valid JSON, so
JSON.parsewill throw an error. - The
catchblock is entered, but its return value is ignored. - The
finallyblock executes, returning 'BFE.dev'. - Output:
BFE.dev
- 'BFE.dev' is not valid JSON, so
console.log(prettify('123')):- '123' is valid JSON (it parses to the number 123).
- No error is thrown, so the
tryblock completes. - The
finallyblock still executes, returning '123'. - Output:
123
Key points:
- The
finallyblock always executes last, and its return value overrides any return values from thetryorcatchblocks. - In this function, the logic in the
tryandcatchblocks effectively does nothing because their return values are always overridden by thefinallyblock. - The function will always return the original input string, regardless of whether it's valid JSON or not.
- The
JSON.parse()call is essentially a no-op in this function, as its result is never used and any errors it throws are caught and then ignored.
This code demonstrates how finally blocks interact with return statements, but it's worth noting that the function doesn't actually "prettify" anything - it just returns the input string unchanged. The name prettify is misleading given the function's actual behavior.