├── v8-performance-metrics
├── .gitignore
├── samples
│ ├── simpleLoop.js
│ ├── complexCalculation.js
│ └── functionCalls.js
├── data
│ └── performance_metrics.csv
├── server.js
├── package.json
└── scripts
│ └── benchmark.js
├── Questions-Answers
├── 37.push-unshift.md
├── 39.var.md
├── 44.Function-call.md
├── 72.Array-length.md
├── 40.RegExp-prototype-test.md
├── 38.Hoisting-IV.md
├── 60.postMessage.md
├── 61.onClick.md
├── 17.reduce.md
├── 74.Typed-Array-length.md
├── 28.Hoisting-II.md
├── 7.Increment-Operator.md
├── 2.Promise-executor.md
├── 70.function.md
├── 53.Prototype-2.md
├── 62.MessageChannel.md
├── 43.JSON.stringify.md
├── 54.setTimeout-0ms.md
├── 3.Promise-then-callbacks.md
├── 65.Function-name.md
├── 22.min-max.md
├── 46.Implicit-Coercion-IV.md
├── 57.non-writable.md
├── 67.if.md
├── 13.Operator-precedence.md
├── 31.Math.md
├── 59.override-setter.md
├── 12.arguments.md
├── 69.undefined.md
├── 27.Hoisting-I.md
├── 68.if-II.md
├── 77.parseInt-2.md
├── 55.sparse-array.md
├── 50.async-await.md
├── 45.Hoisting-VI.md
├── 36.Promise-prototype-finally.md
├── 18.Promise-executor-II.md
├── 64.reference-type.md
├── 1.Promise-order.md
├── 49.this-IV.md
├── 5.block-scope.md
├── 8.Implicit-Coercion-I.md
├── 48.Prototype.md
├── 35.Implicit-Coercion-III.md
├── 16.parseInt.md
├── 30.Equal-II.md
├── 75.meaningless-calculation.md
├── 10.equal.md
├── 9.null-and-undefined.md
├── 76.const.md
├── 21.Array-I.md
├── 26.true-or-false.md
├── 20.name-for-Function-expression.md
├── 34.precedence.md
├── 15.instanceOf.md
├── 51.method.md
├── 58.inherit-getter-setter.md
├── 14.Addition-vs-Unary-Plus.md
├── 23.Promise-all.md
├── 29.Hoisting-III.md
├── 47.Promise-Order-II.md
├── 73.window-name.md
├── 24.Equality-Sameness.md
├── 52.requestAnimationFrame.md
├── 11.Implicit-Coercion-II.md
├── 41.this-III.md
├── 33.this-II.md
├── 56.to-primitive.md
├── 25.zero.md
├── 4.Promise-then-callbacks-II.md
├── 42.Hoisting-V.md
├── 32.Hoisting-IIII.md
└── 71.two-way-generator.md
├── 28.implement-clearAllTimeout.md
├── 42.implement-Insertion-Sort.md
├── 40.implement-Bubble-Sort.md
├── 79.convert-snake_case-to-camelCase.md
├── 44.implement-Selection-Sort.md
├── 58.get-DOM-tree-height.md
├── README.md
├── 41.implement-Merge-Sort.md
├── 3.implement-Array.prototype.flat.md
├── 64.auto-retry-Promise-on-rejection.md
├── 81.merge-sorted-arrays.md
├── 35.implement-Promise.race.md
├── 68.get-DOM-tags.md
├── 17.create-a-simple-store-for-DOM-element.md
├── 20.detect-data-type-in-JavaScript.md
├── 11.what-is-Composition-create-a-pipe.md
├── 32.implement-Promise.all.md
├── 13.Implement-a-Queue-by-using-Stack.md
├── 34.implement-Promise.any.md
├── 33.implement-Promise.allSettled.md
├── 1.implement-curry.md
├── 19.find-corresponding-node-in-two-identical-DOM-tree.md
├── implement-String.prototype.slice.md
└── 151.implement-Array-prototype-map.md
/v8-performance-metrics/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/v8-performance-metrics/samples/simpleLoop.js:
--------------------------------------------------------------------------------
1 | function simpleLoop() {
2 | let sum = 0;
3 | for (let i = 0; i < 1000000; i++) {
4 | sum += i;
5 | }
6 | return sum;
7 | }
8 |
9 | console.log(simpleLoop());
10 |
--------------------------------------------------------------------------------
/v8-performance-metrics/samples/complexCalculation.js:
--------------------------------------------------------------------------------
1 | function complexCalculation() {
2 | let result = 0;
3 | for (let i = 1; i <= 1000; i++) {
4 | result += Math.sin(i) * Math.log(i);
5 | }
6 | return result;
7 | }
8 |
9 | console.log(complexCalculation());
10 |
--------------------------------------------------------------------------------
/v8-performance-metrics/samples/functionCalls.js:
--------------------------------------------------------------------------------
1 | function functionCalls() {
2 | function add(a, b) {
3 | return a + b;
4 | }
5 |
6 | let sum = 0;
7 | for (let i = 0; i < 100000; i++) {
8 | sum += add(i, i + 1);
9 | }
10 | return sum;
11 | }
12 |
13 | console.log(functionCalls());
14 |
--------------------------------------------------------------------------------
/v8-performance-metrics/data/performance_metrics.csv:
--------------------------------------------------------------------------------
1 | "file","executionTime","bytecodeTime","heapUsed","heapTotal","optimizedFunctions","deoptimizedFunctions","garbageCollectionTime"
2 | "simpleLoop.js",230,0,4024224,5652480,11,10,35
3 | "complexCalculation.js",103,0,4093632,5652480,41,2,24
4 | "functionCalls.js",98,0,4152336,5652480,86,13,8
--------------------------------------------------------------------------------
/Questions-Answers/37.push-unshift.md:
--------------------------------------------------------------------------------
1 | # 37. push unshift
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/push-unshift
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const arr = [1, 2];
15 | arr.push(3, 4);
16 | arr.unshift(5, 6);
17 | console.log(arr);
18 | ```
19 |
20 | #
21 |
22 | ### Answer
23 |
24 | ```
25 | [5,6,1,2,3,4]
26 | ```
27 |
--------------------------------------------------------------------------------
/v8-performance-metrics/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const path = require("path");
3 |
4 | const app = express();
5 | const port = 3000;
6 |
7 | // Serve static files from the root directory
8 | app.use(express.static(path.join(__dirname)));
9 |
10 | // Serve the index.html file
11 | app.get("/", (req, res) => {
12 | res.sendFile(path.join(__dirname, "index.html"));
13 | });
14 |
15 | app.listen(port, () => {
16 | console.log(`Server is running at http://localhost:${port}`);
17 | });
18 |
--------------------------------------------------------------------------------
/v8-performance-metrics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "v8-jit-performance-metrics",
3 | "version": "1.0.0",
4 | "description": "A project to benchmark V8 and JIT performance metrics",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "benchmark": "node scripts/benchmark.js"
9 | },
10 | "dependencies": {
11 | "chart.js": "^3.9.1",
12 | "chartjs-node-canvas": "^4.1.6",
13 | "express": "^4.17.1",
14 | "json2csv": "^5.0.6",
15 | "v8": "^0.1.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Questions-Answers/39.var.md:
--------------------------------------------------------------------------------
1 | # 39. var
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/var
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function foo() {
15 | console.log(i);
16 | for (var i = 0; i < 3; i++) {
17 | console.log(i);
18 | }
19 | }
20 |
21 | foo();
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```js
29 | function foo() {
30 | console.log(i); // undefined
31 | for (var i = 0; i < 3; i++) {
32 | console.log(i); // 0, 1, 2
33 | }
34 | }
35 |
36 | foo();
37 | ```
38 |
--------------------------------------------------------------------------------
/Questions-Answers/44.Function-call.md:
--------------------------------------------------------------------------------
1 | # 44. Function call
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Function-call
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function a() {
15 | console.log(1);
16 | return {
17 | a: function () {
18 | console.log(2);
19 | return a();
20 | },
21 | };
22 | }
23 |
24 | a().a();
25 | ```
26 |
27 | #
28 |
29 | ### Answer
30 |
31 | ```
32 | 1
33 | 2
34 | 1
35 | ```
36 |
37 | ### Explanation
38 |
39 | The `a` in the `return a()` refers to `function a() { console.log(1); //... }`.
40 |
--------------------------------------------------------------------------------
/Questions-Answers/72.Array-length.md:
--------------------------------------------------------------------------------
1 | # 72. Array length
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/array-length
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | class MyArray extends Array {
15 | get length() {
16 | return 3;
17 | }
18 | }
19 |
20 | const arr1 = new MyArray(10);
21 | console.log(arr1.length);
22 |
23 | const arr2 = new Array(10);
24 | console.log(arr2.length);
25 | ```
26 |
27 | #
28 |
29 | ### Answer
30 |
31 | ```
32 | 10
33 | 10
34 | ```
35 |
36 | ### Explanation
37 |
38 | The behavior of array `length` property cannot be overwritten.
39 |
--------------------------------------------------------------------------------
/Questions-Answers/40.RegExp-prototype-test.md:
--------------------------------------------------------------------------------
1 | # 40. RegExp.prototype.test
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/RegExp-prototype-test
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(/^4\d\d$/.test('404'));
15 | console.log(/^4\d\d$/.test(404));
16 | console.log(/^4\d\d$/.test(['404']));
17 | console.log(/^4\d\d$/.test([404]));
18 | ```
19 |
20 | #
21 |
22 | ### Answer
23 |
24 | ```
25 | true
26 | true
27 | true
28 | true
29 | ```
30 |
31 | ### Explanation
32 |
33 | `RegExp.prototype.test()` expects a string. When you pass some value that is not a string, JavaScript will try to coerce it to a string value.
34 |
--------------------------------------------------------------------------------
/Questions-Answers/38.Hoisting-IV.md:
--------------------------------------------------------------------------------
1 | # 38. Hoisting IV
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-IV
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | let foo = 10;
15 | function func1() {
16 | console.log(foo);
17 | var foo = 1;
18 | }
19 | func1();
20 |
21 | function func2() {
22 | console.log(foo);
23 | let foo = 1;
24 | }
25 | func2();
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 | ```js
33 | let foo = 10;
34 | function func1() {
35 | console.log(foo); // undefined
36 | var foo = 1;
37 | }
38 | func1();
39 |
40 | function func2() {
41 | console.log(foo); // Error
42 | let foo = 1;
43 | }
44 | func2();
45 | ```
46 |
--------------------------------------------------------------------------------
/Questions-Answers/60.postMessage.md:
--------------------------------------------------------------------------------
1 | # 60. postMessage
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/postMessage
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 |
16 | window.onmessage = () => {
17 | console.log(2);
18 | };
19 |
20 | Promise.resolve().then(() => {
21 | console.log(3);
22 | });
23 |
24 | setTimeout(() => {
25 | console.log(4);
26 | }, 0);
27 |
28 | console.log(5);
29 |
30 | window.postMessage('');
31 |
32 | console.log(6);
33 | ```
34 |
35 | #
36 |
37 | ### Answer
38 |
39 | ```
40 | 1
41 | 5
42 | 6
43 | 3
44 | 2
45 | 4
46 | ```
47 |
48 | #
49 |
50 | ### Reference
51 |
52 | https://html.spec.whatwg.org/multipage/web-messaging.html
53 |
--------------------------------------------------------------------------------
/Questions-Answers/61.onClick.md:
--------------------------------------------------------------------------------
1 | # 61. onClick
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/messsage-channel-is-async
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 |
16 | document.body.addEventListener('click', () => {
17 | console.log(2);
18 | });
19 |
20 | Promise.resolve().then(() => {
21 | console.log(3);
22 | });
23 |
24 | setTimeout(() => {
25 | console.log(4);
26 | }, 0);
27 |
28 | console.log(5);
29 |
30 | document.body.click();
31 |
32 | console.log(6);
33 | ```
34 |
35 | #
36 |
37 | ### Answer
38 |
39 | ```
40 | 1
41 | 5
42 | 2
43 | 6
44 | 3
45 | 4
46 | ```
47 |
48 | ### Explanation
49 |
50 | The `click` event is handled synchronously.
51 |
--------------------------------------------------------------------------------
/Questions-Answers/17.reduce.md:
--------------------------------------------------------------------------------
1 | # 17. reduce
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/reduce
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | [1, 2, 3].reduce((a, b) => {
15 | console.log(a, b);
16 | });
17 |
18 | [1, 2, 3].reduce((a, b) => {
19 | console.log(a, b);
20 | }, 0);
21 | ```
22 |
23 | #
24 |
25 | ### Answer
26 |
27 | ```js
28 | [1, 2, 3].reduce((a, b) => {
29 | console.log(a, b);
30 | });
31 |
32 | // logs:
33 | // a: 1, b: 2
34 | // a: undefined, b: 3
35 |
36 | /* ---------------------------- */
37 |
38 | [1, 2, 3].reduce((a, b) => {
39 | console.log(a, b);
40 | }, 0);
41 |
42 | // logs:
43 | // a: 0, b: 1
44 | // a: undefined, b: 2
45 | // a: undefined, b: 3
46 | ```
47 |
--------------------------------------------------------------------------------
/Questions-Answers/74.Typed-Array-length.md:
--------------------------------------------------------------------------------
1 | # 74. Typed Array length
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Typed-Array-length
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | class MyArray extends Uint8Array {
15 | get length() {
16 | return 3;
17 | }
18 | }
19 |
20 | const arr1 = new MyArray(10);
21 | console.log(arr1.length);
22 |
23 | const arr2 = new Uint8Array(10);
24 | console.log(arr2.length);
25 | ```
26 |
27 | #
28 |
29 | ### Answer
30 |
31 | ```js
32 | class MyArray extends Uint8Array {
33 | get length() {
34 | return 3;
35 | }
36 | }
37 |
38 | const arr1 = new MyArray(10);
39 | console.log(arr1.length); // 3
40 |
41 | const arr2 = new Uint8Array(10);
42 | console.log(arr2.length); // 10
43 | ```
44 |
--------------------------------------------------------------------------------
/Questions-Answers/28.Hoisting-II.md:
--------------------------------------------------------------------------------
1 | # 28. Hoisting II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const func1 = () => console.log(1);
15 |
16 | func1();
17 |
18 | func2();
19 |
20 | function func2() {
21 | console.log(2);
22 | }
23 |
24 | func3();
25 |
26 | var func3 = function func4() {
27 | console.log(3);
28 | };
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | const func1 = () => console.log(1);
37 |
38 | func1(); // 1
39 |
40 | func2(); // 2
41 |
42 | function func2() {
43 | console.log(2);
44 | }
45 |
46 | func3(); // Error. Function expressions are not hoisted.
47 |
48 | var func3 = function func4() {
49 | console.log(3);
50 | };
51 | ```
52 |
--------------------------------------------------------------------------------
/Questions-Answers/7.Increment-Operator.md:
--------------------------------------------------------------------------------
1 | # 7. Increment Operator
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Increment-Operator
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | let a = 1;
15 | const b = ++a;
16 | const c = a++;
17 | console.log(a);
18 | console.log(b);
19 | console.log(c);
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```js
27 | let a = 1;
28 | const b = ++a;
29 | const c = a++;
30 | console.log(a); // 3
31 | console.log(b); // 2
32 | console.log(c); // 2
33 | ```
34 |
35 | ### Explanation
36 |
37 | If the increment operator `++` precedes operand, the increment operator increments and return the value after incrementing.
38 |
39 | If the increment operator `++` comes after operand, the increment operator increments and return the value before incrementing.
40 |
--------------------------------------------------------------------------------
/Questions-Answers/2.Promise-executor.md:
--------------------------------------------------------------------------------
1 | # 2. Promise executor
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/2-promise-executor
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | new Promise((resolve, reject) => {
15 | resolve(1);
16 | resolve(2);
17 | reject('error');
18 | }).then(
19 | (value) => {
20 | console.log(value);
21 | },
22 | (error) => {
23 | console.log('error');
24 | }
25 | );
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 | ```js
33 | new Promise((resolve, reject) => {
34 | resolve(1);
35 | resolve(2);
36 | reject('error');
37 | }).then(
38 | (value) => {
39 | console.log(value);
40 | // logs 1. A promise can only be resolved or rejected once.
41 | },
42 | (error) => {
43 | console.log('error'); // ignored
44 | }
45 | );
46 | ```
47 |
--------------------------------------------------------------------------------
/Questions-Answers/70.function.md:
--------------------------------------------------------------------------------
1 | # 70. function
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/function
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function foo() {
15 | console.log(1);
16 | }
17 | var foo = 2;
18 | function foo() {
19 | console.log(3);
20 | }
21 | foo();
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```
29 | Error
30 | ```
31 |
32 | ### Explanation
33 |
34 | Before code execution, function and `var` variable declarations are hoisted to the top of the scope in which they are declared. JavaScript first moves function declarations, then `var` variable declarations. The order of declarations doesn't matter. When a `var` variable and a function have the same name, the `var` variable declaration overrides the function declaration, thus the code snippet above ends up in error: "foo is not a function".
35 |
--------------------------------------------------------------------------------
/Questions-Answers/53.Prototype-2.md:
--------------------------------------------------------------------------------
1 | # 53. Prototype 2
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/prototype2
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function F() {
15 | this.foo = 'bar';
16 | }
17 |
18 | const f = new F();
19 | console.log(f.prototype);
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```
27 | undefined
28 | ```
29 |
30 | ### Explanation
31 |
32 | The `prototype` property is only available on functions, or more specifically, on `Function` objects. The value of the `prototype` property is an object containing a `constructor` property, which points to the original constructor function. When you create an object with `new`, `prototype` is the object that is used to build `__proto__`. Therefore, the object `f` doesn't have a `prototype` property, but a `__proto__` property, which is a reference to `F.prototype`.
33 |
--------------------------------------------------------------------------------
/Questions-Answers/62.MessageChannel.md:
--------------------------------------------------------------------------------
1 | # 62. MessageChannel
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/message-channel
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 |
16 | const mc = new MessageChannel();
17 |
18 | mc.port1.onmessage = () => {
19 | console.log(2);
20 | };
21 |
22 | Promise.resolve().then(() => {
23 | console.log(3);
24 | });
25 |
26 | setTimeout(() => {
27 | console.log(4);
28 | }, 0);
29 |
30 | console.log(5);
31 |
32 | mc.port2.postMessage('');
33 |
34 | console.log(6);
35 | ```
36 |
37 | #
38 |
39 | ### Answer
40 |
41 | ```
42 | 1
43 | 5
44 | 6
45 | 3
46 | 2
47 | 4
48 | ```
49 |
50 | #
51 |
52 | ### Reference
53 |
54 | - [MessageChannel()](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel/MessageChannel)
55 | - [Channel messaging](https://html.spec.whatwg.org/multipage/web-messaging.html#channel-messaging)
56 |
--------------------------------------------------------------------------------
/Questions-Answers/43.JSON.stringify.md:
--------------------------------------------------------------------------------
1 | # 43. JSON.stringify()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/json-stringify
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | // attention that for console.log('"a"'), you should enter ""a""
15 | // please refer to format guide
16 |
17 | console.log(JSON.stringify(['false', false]));
18 | console.log(JSON.stringify([NaN, null, Infinity, undefined]));
19 | console.log(JSON.stringify({ a: null, b: NaN, c: undefined }));
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```js
27 | console.log(JSON.stringify(['false', false]));
28 | // logs "["false",false]"
29 |
30 | console.log(JSON.stringify([NaN, null, Infinity, undefined]));
31 | // logs "[null,null,null,null]"
32 |
33 | console.log(JSON.stringify({ a: null, b: NaN, c: undefined }));
34 | // logs "{"a":null,"b":null}"
35 | ```
36 |
37 | ### Explanation
38 |
39 | `undefined` will be omitted when found in an object.
40 |
--------------------------------------------------------------------------------
/Questions-Answers/54.setTimeout-0ms.md:
--------------------------------------------------------------------------------
1 | # 54. setTimeout(0ms)
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/setTimeout-0ms
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | // This snippet's result may vary on browsers
15 |
16 | setTimeout(() => {
17 | console.log(2);
18 | }, 2);
19 |
20 | setTimeout(() => {
21 | console.log(1);
22 | }, 1);
23 |
24 | setTimeout(() => {
25 | console.log(0);
26 | }, 0);
27 | ```
28 |
29 | #
30 |
31 | ### Answer
32 |
33 | For Chrome, Safari, Node.js
34 |
35 | ```
36 | 1
37 | 0
38 | 2
39 | ```
40 |
41 | For Firefox
42 |
43 | ```
44 | 0
45 | 1
46 | 2
47 | ```
48 |
49 | ### Explanation
50 |
51 | In Node.js, `setTimeout(fn, 0)` is converted to `setTimeout(fn, 1)`, so they are exactly the same.
52 |
53 | #
54 |
55 | ### Reference
56 |
57 | [What is the difference between setTimeout(fn, 0) and setTimeout(fn, 1)?](https://stackoverflow.com/questions/8341803/what-is-the-difference-between-settimeoutfn-0-and-settimeoutfn-1/37961816#37961816)
58 |
--------------------------------------------------------------------------------
/Questions-Answers/3.Promise-then-callbacks.md:
--------------------------------------------------------------------------------
1 | # 3. Promise then callbacks
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/3-promise-then-callbacks
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | Promise.resolve(1)
15 | .then(() => 2)
16 | .then(3)
17 | .then((value) => value * 3)
18 | .then(Promise.resolve(4))
19 | .then(console.log);
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```
27 | 6
28 | ```
29 |
30 | ### Explanation
31 |
32 | If the first argument that `.then()` receives is not a function, it is internally replaced with a function that returns the receiving argument. Therefore, when the code snippet above runs, `3` and `Promise.resolve(4)` are going to be replaced with the function `(result) => result`:
33 |
34 | ```js
35 | Promise.resolve(1)
36 | .then(() => 2)
37 | // 3 is internally replaced with a function.
38 | .then((result) => result) // 2
39 | .then((value) => value * 3) // 6
40 | // Promise.resolve(4) is internally replaced with a function.
41 | .then((result) => result) // 6
42 | .then(console.log); // logs 6
43 | ```
44 |
--------------------------------------------------------------------------------
/Questions-Answers/65.Function-name.md:
--------------------------------------------------------------------------------
1 | # 65. Function name
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Function-name
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var foo = function bar() {
15 | return 'BFE';
16 | };
17 |
18 | console.log(foo());
19 | console.log(bar());
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```js
27 | var foo = function bar() {
28 | return 'BFE';
29 | };
30 |
31 | console.log(foo()); // 'BFE'
32 | console.log(bar()); // Error
33 | ```
34 |
35 | ### Explanation
36 |
37 | When we define a function using function expression, the variable that the function expression is assigned to will have a name property. If function name is omitted, it will be the variable name. If function name is present, it will be the function name. This name is local only to the function body. Thus trying to call `bar` in the globe scope ended up in an error, because `bar` is `undefined` in the globe scope.
38 |
39 | #
40 |
41 | ### Reference
42 |
43 | [Function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function)
44 |
--------------------------------------------------------------------------------
/Questions-Answers/22.min-max.md:
--------------------------------------------------------------------------------
1 | # 22. min max
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/min-max
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(Math.min());
15 | console.log(Math.max());
16 | console.log(Math.min(1));
17 | console.log(Math.max(1, 2));
18 | console.log(Math.min([1, 2, 3]));
19 | ```
20 |
21 | #
22 |
23 | ### Answer
24 |
25 | ```js
26 | console.log(Math.min()); // Infinity
27 | console.log(Math.max()); // -Infinity
28 | console.log(Math.min(1)); // 1
29 | console.log(Math.max(1, 2)); // 2
30 | console.log(Math.min([1, 2, 3])); // NaN
31 | ```
32 |
33 | ### Explanation
34 |
35 | If no parameters are provided, `Math.min()` returns `Infinity` and `Math.max()` returns `-Infinity`. If any one or more of the parameters cannot be converted into a number, `Math.min()` and `Math.max()` return `NaN`.
36 |
37 | #
38 |
39 | ### Reference
40 |
41 | - [`Math.min()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min)
42 | - [`Math.max()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max)
43 |
--------------------------------------------------------------------------------
/Questions-Answers/46.Implicit-Coercion-IV.md:
--------------------------------------------------------------------------------
1 | # 46. Implicit Coercion IV
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/implicit-coersion-2
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const foo = [0];
15 | if (foo) {
16 | console.log(foo == true);
17 | } else {
18 | console.log(foo == false);
19 | }
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```js
27 | const foo = [0];
28 | if (foo) {
29 | console.log(foo == true); // false
30 | } else {
31 | // Never reached, because an array always evaluates to `true`
32 | // when passed to a conditional statement.
33 | console.log(foo == false);
34 | }
35 | ```
36 |
37 | ### Explanation
38 |
39 | `foo == true` returns `false`, because when the two operands are of different types and one of them is a `Boolean`, the `==` operator converts the `Boolean` to `1` if it is `true` and `0` if it is `false`, and the array `[0]` is converted into a string using `Array`'s `toString()` method, so `foo == true` becomes `'0' == 1`, finally, `'0'` is coerced to a numeric value, since when comparing a number to a string, `==` tries to convert the string to a number.
40 |
--------------------------------------------------------------------------------
/Questions-Answers/57.non-writable.md:
--------------------------------------------------------------------------------
1 | # 57. non-writable
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/inherit-writable-flag
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const a = {};
15 | Object.defineProperty(a, 'foo1', {
16 | value: 1,
17 | });
18 | const b = Object.create(a);
19 | b.foo2 = 1;
20 |
21 | console.log(b.foo1);
22 | console.log(b.foo2);
23 |
24 | b.foo1 = 2;
25 | b.foo2 = 2;
26 |
27 | console.log(b.foo1);
28 | console.log(b.foo2);
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | const a = {};
37 | Object.defineProperty(a, 'foo1', {
38 | value: 1,
39 | });
40 | const b = Object.create(a);
41 | b.foo2 = 1;
42 |
43 | console.log(b.foo1); // 1
44 | console.log(b.foo2); // 1
45 |
46 | b.foo1 = 2;
47 | b.foo2 = 2;
48 |
49 | console.log(b.foo1); // 1
50 | console.log(b.foo2); // 2
51 | ```
52 |
53 | ### Explanation
54 |
55 | Values added by `Object.defineProperty()` are immutable and not enumerable.
56 |
57 | #
58 |
59 | ### Reference
60 |
61 | [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
62 |
--------------------------------------------------------------------------------
/Questions-Answers/67.if.md:
--------------------------------------------------------------------------------
1 | # 67. if
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/if
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | if (true) {
15 | function foo() {
16 | console.log('BFE');
17 | }
18 | }
19 | if (false) {
20 | function bar() {
21 | console.log('dev');
22 | }
23 | }
24 |
25 | foo();
26 | bar();
27 | ```
28 |
29 | #
30 |
31 | ### Answer
32 |
33 | ```js
34 | if (true) {
35 | function foo() {
36 | console.log('BFE');
37 | }
38 | }
39 | if (false) {
40 | function bar() {
41 | console.log('dev');
42 | }
43 | }
44 |
45 | // In Chrome and Firefox:
46 | foo(); // 'BFE'
47 | bar(); // Error
48 |
49 | // In Safari:
50 | foo(); // 'BFE'
51 | bar(); // 'dev'
52 | ```
53 |
54 | ### Explanation
55 |
56 | When we define a function within an `if` statement, the result varies on browsers. In Chrome and Firefox, only the name of the function is hoisted, whereas in Safari, the function is hoisted.
57 |
58 | #
59 |
60 | ### Reference
61 |
62 | [Conditionally created functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#conditionally_created_functions)
63 |
--------------------------------------------------------------------------------
/Questions-Answers/13.Operator-precedence.md:
--------------------------------------------------------------------------------
1 | # 13. Operator precedence
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/operator-precedence
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | console.log(0 == 1 == 2);
16 | console.log(2 == 1 == 0);
17 | console.log(0 < 1 < 2);
18 | console.log(1 < 2 < 3);
19 | console.log(2 > 1 > 0);
20 | console.log(3 > 2 > 1);
21 | ```
22 |
23 | #
24 |
25 | ### Answer
26 |
27 |
28 | ```js
29 | console.log(0 == 1 == 2); // false
30 | console.log(2 == 1 == 0); // true
31 | console.log(0 < 1 < 2); // true
32 | console.log(1 < 2 < 3); // true
33 | console.log(2 > 1 > 0); // true
34 | console.log(3 > 2 > 1); // false
35 | ```
36 |
37 | ### Explanation
38 |
39 | `0 == 1 == 2` returns `false`, because JavaScript first evaluates `0 == 1` and the result is `false`, then `false` is converted to `0` by `==` operator and compared to `2`. The same logic applies to all the other cases.
40 |
41 | #
42 |
43 | ### Reference
44 |
45 | [Operator precedence: Example](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#examples)
46 |
--------------------------------------------------------------------------------
/Questions-Answers/31.Math.md:
--------------------------------------------------------------------------------
1 | # 31. Math
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Math
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1 / 0);
15 | console.log(0 / 0);
16 | console.log(-1 / 0);
17 | console.log((1 / 0) * 0);
18 | console.log((1 / 0) * 1);
19 | console.log((1 / 0) * -1);
20 | console.log((1 / 0) * 1 + (1 / 0) * 1);
21 | console.log((1 / 0) * 1 - (1 / 0) * 1);
22 | console.log((1 / 0) * 1 * ((1 / 0) * 1));
23 | console.log(((1 / 0) * 1) / ((1 / 0) * 1));
24 | console.log(0 / Infinity);
25 | console.log(0 * Infinity);
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 | ```js
33 | console.log(1 / 0); // Infinity
34 | console.log(0 / 0); // NaN
35 | console.log(-1 / 0); // -Infinity
36 | console.log((1 / 0) * 0); // NaN
37 | console.log((1 / 0) * 1); // Infinity
38 | console.log((1 / 0) * -1); // -Infinity
39 | console.log((1 / 0) * 1 + (1 / 0) * 1); // Infinity
40 | console.log((1 / 0) * 1 - (1 / 0) * 1); // NaN
41 | console.log((1 / 0) * 1 * ((1 / 0) * 1)); // Infinity
42 | console.log(((1 / 0) * 1) / ((1 / 0) * 1)); // NaN
43 | console.log(0 / Infinity); // 0
44 | console.log(0 * Infinity); // NaN
45 | ```
46 |
--------------------------------------------------------------------------------
/Questions-Answers/59.override-setter.md:
--------------------------------------------------------------------------------
1 | # 59. override setter
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/override-setter
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | class A {
15 | val = 1;
16 | get foo() {
17 | return this.val;
18 | }
19 | }
20 |
21 | class B {
22 | val = 2;
23 | set foo(val) {
24 | this.val = val;
25 | }
26 | }
27 | const a = new A();
28 | const b = new B();
29 | console.log(a.foo);
30 | console.log(b.foo);
31 | b.foo = 3;
32 | console.log(b.val);
33 | console.log(b.foo);
34 | ```
35 |
36 | #
37 |
38 | ### Answer
39 |
40 | ```js
41 | class A {
42 | val = 1;
43 | get foo() {
44 | return this.val;
45 | }
46 | }
47 |
48 | class B {
49 | val = 2;
50 | set foo(val) {
51 | this.val = val;
52 | }
53 | }
54 | const a = new A();
55 | const b = new B();
56 | console.log(a.foo); // 1
57 | console.log(b.foo); // undefined
58 | b.foo = 3;
59 | console.log(b.val); // 3
60 | console.log(b.foo); // undefined
61 | ```
62 |
63 | ### Explanation
64 |
65 | If a property is not defined in an object and only a setter for the property is defined, any attempt to access the property directly will return `undefined`.
66 |
--------------------------------------------------------------------------------
/Questions-Answers/12.arguments.md:
--------------------------------------------------------------------------------
1 | # 12. arguments
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/arguments
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function log(a, b, c, d) {
15 | console.log(a, b, c, d);
16 | arguments[0] = 'bfe';
17 | arguments[3] = 'dev';
18 |
19 | console.log(a, b, c, d);
20 | }
21 |
22 | log(1, 2, 3);
23 | ```
24 |
25 | #
26 |
27 | ### Answer
28 |
29 | ```js
30 | function log(a, b, c, d) {
31 | console.log(a, b, c, d); // 1,2,3,undefined
32 | arguments[0] = 'bfe';
33 | arguments[3] = 'dev';
34 |
35 | console.log(a, b, c, d); // "bfe",2,3,undefined
36 | }
37 |
38 | log(1, 2, 3);
39 | ```
40 |
41 | ### Explanation
42 |
43 | Even though `arguments[3] = 'dev'` does add a fourth item to the `arguments` object, it doesn't reassign the new value to `d`, because the function call `log(1, 2, 3)` doesn't supply an argument for the parameter `d` and therefore `d` is not bound to the `arguments` object, which means its value cannot be updated by modifying the arguments object.
44 |
45 | #
46 |
47 | ### Reference
48 |
49 | [The JavaScript arguments object…and beyond](https://javascriptweblog.wordpress.com/2011/01/18/javascripts-arguments-object-and-beyond/)
50 |
--------------------------------------------------------------------------------
/Questions-Answers/69.undefined.md:
--------------------------------------------------------------------------------
1 | # 69. undefined
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/undefined
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function foo(a, b, undefined, undefined) {
15 | console.log('BFE.dev');
16 | }
17 | console.log(foo.length);
18 | ```
19 |
20 | #
21 |
22 | ### Answer
23 |
24 | ```
25 | 4
26 | ```
27 |
28 | ### Explanation
29 |
30 | Before ES 5, the global value of `undefined` could be overridden:
31 |
32 | ```js
33 | var undefined = 'bar';
34 | ```
35 |
36 | `function(value, undefined) {}` ensures that `undefined` is the authentic `undefined` value within the function. When the function gets called and only one argument is passed, the second parameter, called "undefined", will really be `undefined`.
37 |
38 | ```js
39 | // Before ES 5
40 | var undefined = 'bar';
41 | function foo(undefined) {
42 | console.log(undefined);
43 | }
44 |
45 | foo(); // undefined
46 | foo(undefined); // 'bar'
47 | foo(10); // 10
48 | ```
49 |
50 | #
51 |
52 | ### Reference
53 |
54 | [What does “undefined” parameter signify in a function definition](https://stackoverflow.com/questions/54487492/what-does-undefined-parameter-signify-in-a-function-definition?noredirect=1&lq=1)
55 |
--------------------------------------------------------------------------------
/Questions-Answers/27.Hoisting-I.md:
--------------------------------------------------------------------------------
1 | # 27. Hoisting I
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-I
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const a = 1;
15 | console.log(a);
16 |
17 | var b;
18 | console.log(b);
19 | b = 2;
20 |
21 | console.log(c);
22 | var c = 3;
23 |
24 | console.log(d);
25 | let d = 2;
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 | ```js
33 | const a = 1;
34 | console.log(a); // 1
35 |
36 | var b;
37 | console.log(b); // undefined
38 | b = 2;
39 |
40 | console.log(c);
41 | var c = 3;
42 | // logs `undefined`. Variables declared with `var` are hoisted
43 | // to the top of the scope. JavaScript only hoists variable
44 | // declaration, not initialization.
45 |
46 | console.log(d);
47 | let d = 2;
48 | // throws a `ReferenceError`. Variables declared with `let`
49 | // and `const` are also hoisted, but unlike `var` variables,
50 | // they are in a 'temporal dead zone'(TDZ) from the start of
51 | // the block until they are declared. Accessing them inside
52 | // the TDZ results in an error.
53 | ```
54 |
55 | #
56 |
57 | ### Reference
58 |
59 | [What is the Temporal Dead Zone (TDZ) in JavaScript?](https://www.freecodecamp.org/news/what-is-the-temporal-dead-zone/)
60 |
--------------------------------------------------------------------------------
/28.implement-clearAllTimeout.md:
--------------------------------------------------------------------------------
1 | # 28. implement clearAllTimeout()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/implement-clearAllTimeout
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | `window.setTimeout()` could be used to schedule some task in the future.
12 |
13 | Could you implement `clearAllTimeout()` to clear all the timers? This might be useful when we want to clear things up before page transition.
14 |
15 | For example
16 |
17 | ```js
18 | setTimeout(func1, 10000);
19 | setTimeout(func2, 10000);
20 | setTimeout(func3, 10000);
21 | // all 3 functions are scheduled 10 seconds later
22 |
23 | clearAllTimeout();
24 | // all scheduled tasks are cancelled.
25 | ```
26 |
27 | **note**
28 |
29 | You need to keep the interface of `window.setTimeout` and `window.clearTimeout` the same, but you could replace them with new logic
30 |
31 | #
32 |
33 | ### Solution
34 |
35 | ```js
36 | const timerCache = new Set();
37 | const originalSetTimeout = window.setTimeout;
38 |
39 | window.setTimeout = (cb, delay) => {
40 | const timer = originalSetTimeout(cb, delay);
41 | timerCache.add(timer);
42 | return timer;
43 | };
44 |
45 | /**
46 | * cancel all timer from window.setTimeout
47 | */
48 | function clearAllTimeout() {
49 | for (const timer of timerCache) {
50 | clearTimeout(timer);
51 | }
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/Questions-Answers/68.if-II.md:
--------------------------------------------------------------------------------
1 | # 68. if II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/if-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | if (
15 | function foo() {
16 | console.log('BFE');
17 | }
18 | ) {
19 | console.log('dev');
20 | }
21 | foo();
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```
29 | "dev"
30 | Error
31 | ```
32 |
33 | ### Explanation
34 |
35 | When a function is created inside an expression or inside another syntax construct, it is a function expression. So in the code snippet above, `function foo() {}` is a **named function expression**, where `foo` is the function name. It is similar to:
36 |
37 | ```js
38 | const bar = function foo() {};
39 | ```
40 |
41 | Since the function name `foo` is local only to the function body, trying to call `foo()` in the global scope
42 | ends up with the error: `foo is not defined`.
43 |
44 | #
45 |
46 | ### Reference
47 |
48 | - [Function definition inside the if condition is not defined in javascript](https://stackoverflow.com/questions/24751277/function-definition-inside-the-if-condition-is-not-defined-in-javascript)
49 | - [Function Expression vs Function Declaration](https://javascript.info/function-expressions#function-expression-vs-function-declaration)
50 |
--------------------------------------------------------------------------------
/Questions-Answers/77.parseInt-2.md:
--------------------------------------------------------------------------------
1 | # 77. parseInt 2
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/parseInt-2
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(parseInt(0.00001));
15 | console.log(parseInt(0.000001));
16 | console.log(parseInt(0.0000001));
17 | console.log(parseInt('0x12'));
18 | console.log(parseInt('1e2'));
19 | ```
20 |
21 | #
22 |
23 | ### Answer
24 |
25 | ```js
26 | console.log(parseInt(0.00001)); // 0
27 | console.log(parseInt(0.000001)); // 0
28 |
29 | console.log(parseInt(0.0000001)); // 1
30 | // If the input value is less than or equal to 1e-7 (0.0000001), `parseInt()`
31 | // returns `1`.
32 |
33 | console.log(parseInt('0x12')); // 18
34 | // If the input string starts with `"0x"`, `radix` is assumed to be `16`.
35 | // (12)₁₆ = (1 × 16¹) + (2 × 16⁰) = (18)₁₀
36 |
37 | console.log(parseInt('1e2')); // 1
38 | // Since `"1e2"` is a string, `parseInt()` does not recognize exponential notation
39 | // and thus stops at the first letter.
40 | ```
41 |
42 | #
43 |
44 | ### Reference
45 |
46 | - [parseInt()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)
47 |
48 | - [parseInt('1e1') vs parseFloat('1e1')](https://stackoverflow.com/questions/31732983/parseint1e1-vs-parsefloat1e1)
49 |
--------------------------------------------------------------------------------
/Questions-Answers/55.sparse-array.md:
--------------------------------------------------------------------------------
1 | # 55. sparse array
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/sparse-array
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const arr = [1, , , 2];
15 |
16 | // forEach
17 | arr.forEach((i) => console.log(i));
18 |
19 | // map
20 | console.log(arr.map((i) => i * 2));
21 |
22 | // for ... of
23 | for (const i of arr) {
24 | console.log(i);
25 | }
26 |
27 | // spread
28 | console.log([...arr]);
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | const arr = [1, , , 2];
37 |
38 | // forEach
39 | arr.forEach((i) => console.log(i));
40 | // logs: 1, 2
41 | // `Array.prototype.forEach()` skips these blank indices.
42 |
43 | // map
44 | console.log(arr.map((i) => i * 2));
45 | // logs: [2, empty, empty, 4]
46 | // If the array which `Array.prototype.map()` is called upon is sparse,
47 | // resulting array will also be sparse, meaning that `Array.prototype.map()`
48 | // skips the blank indices in the sparse array.
49 |
50 | // for ... of
51 | for (const i of arr) {
52 | console.log(i);
53 | }
54 | // logs: 1, undefined, undefined, 2
55 | // for...of treats these blank indices as `undefined`.
56 |
57 | // spread
58 | console.log([...arr]);
59 | // logs: [1, undefined, undefined, 2]
60 | // Spread operator treats these blank indices as `undefined`.
61 | ```
62 |
--------------------------------------------------------------------------------
/Questions-Answers/50.async-await.md:
--------------------------------------------------------------------------------
1 | # 50. async await
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/async-await
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | async function async1() {
15 | console.log(1);
16 | await async2();
17 | console.log(2);
18 | }
19 |
20 | async function async2() {
21 | console.log(3);
22 | }
23 |
24 | console.log(4);
25 |
26 | setTimeout(function () {
27 | console.log(5);
28 | }, 0);
29 |
30 | async1();
31 |
32 | new Promise(function (resolve) {
33 | console.log(6);
34 | resolve();
35 | }).then(function () {
36 | console.log(7);
37 | });
38 |
39 | console.log(8);
40 | ```
41 |
42 | #
43 |
44 | ### Answer
45 |
46 | ```
47 | 4
48 | 1
49 | 3
50 | 6
51 | 8
52 | 2
53 | 7
54 | 5
55 | ```
56 |
57 | ### Explanation
58 |
59 | `await` triggers microtask queue processing:
60 |
61 | ```js
62 | async function foo() {
63 | await 1;
64 | console.log(1);
65 | }
66 | ```
67 |
68 | is equivalent to:
69 |
70 | ```js
71 | function foo() {
72 | return Promise.resolve(1).then(() => {
73 | console.log(1);
74 | });
75 | }
76 | ```
77 |
78 | #
79 |
80 | ### Reference:
81 |
82 | - [Why does this line of code with 'await' trigger microtask queue processing?](https://stackoverflow.com/questions/56851983/why-does-this-line-of-code-with-await-trigger-microtask-queue-processing)
83 | - [Does using `await` always involve a microtask?](https://stackoverflow.com/questions/61635992/does-using-await-always-involve-a-microtask)
84 |
--------------------------------------------------------------------------------
/Questions-Answers/45.Hoisting-VI.md:
--------------------------------------------------------------------------------
1 | # 45. Hoisting VI
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-VI
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var foo = 1;
15 | (function () {
16 | console.log(foo);
17 | foo = 2;
18 | console.log(window.foo);
19 | console.log(foo);
20 | var foo = 3;
21 | console.log(foo);
22 | console.log(window.foo);
23 | })();
24 | ```
25 |
26 | #
27 |
28 | ### Answer
29 |
30 | ```js
31 | var foo = 1;
32 | (function () {
33 | console.log(foo); // undefined
34 | foo = 2;
35 | console.log(window.foo); // 1
36 | console.log(foo); // 2
37 | var foo = 3;
38 | console.log(foo); // 3
39 | console.log(window.foo); // 1
40 | })();
41 | ```
42 |
43 | ### Explanation
44 |
45 | The variable declaration `var foo` within the IIFE is hoisted to the top of the function scope. So the code snippet in the problem statement is actually equivalent to the following:
46 |
47 | ```js
48 | var foo = 1;
49 | (function () {
50 | var foo; // `foo` is declared within the function scope.
51 | console.log(foo); // undefined, because the local `foo` is not initialized.
52 | foo = 2; // The local `foo` is initialized to `2`.
53 | console.log(window.foo); // 1, because the local `foo` doesn't overwrite the global `foo`.
54 | console.log(foo); // 2, because the local `foo` has been assigned to `2`.
55 | foo = 3; // the local `foo` is re-assigned to `3`.
56 | console.log(foo); // 3
57 | console.log(window.foo); // 1
58 | })();
59 | ```
60 |
--------------------------------------------------------------------------------
/Questions-Answers/36.Promise-prototype-finally.md:
--------------------------------------------------------------------------------
1 | # 36. Promise.prototype.finally()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Promise-prototype-finally
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | Promise.resolve(1)
15 | .finally((data) => {
16 | console.log(data);
17 | return Promise.reject('error');
18 | })
19 | .catch((error) => {
20 | console.log(error);
21 | throw 'error2';
22 | })
23 | .finally((data) => {
24 | console.log(data);
25 | return Promise.resolve(2).then(console.log);
26 | })
27 | .then(console.log)
28 | .catch(console.log);
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```
36 | undefined
37 | "error"
38 | undefined
39 | 2
40 | "error2"
41 | ```
42 |
43 | ### Explanation
44 |
45 | ```js
46 | Promise.resolve(1)
47 | // `Promise.prototype.finally(callback)` returns a `Promise`.
48 | .finally((data) => {
49 | console.log(data); // `undefined`, because a `finally` callback will not receive any argument.
50 | return Promise.reject('error');
51 | })
52 | .catch((error) => {
53 | console.log(error); // `error` from the rejected promise returned by the previous `finally()`
54 | throw 'error2';
55 | })
56 | .finally((data) => {
57 | console.log(data); // `undefined`, because a `finally` callback will not receive any argument.
58 | return Promise.resolve(2).then(console.log); // 2
59 | })
60 | .then(console.log) // ignored
61 | .catch(console.log); // `error2` from the previous `catch()`
62 | ```
63 |
--------------------------------------------------------------------------------
/42.implement-Insertion-Sort.md:
--------------------------------------------------------------------------------
1 | # 42. implement Insertion Sort
2 | Insertion Sort is a simple and intuitive comparison-based sorting algorithm. It builds the final sorted array one element at a time by repeatedly picking the next element and inserting it into its correct position among the previously sorted elements.
3 |
4 | ### Problem
5 |
6 | https://bigfrontend.dev/problem/implement-Insertion-Sort
7 |
8 | #
9 |
10 | ### Problem Description
11 |
12 | Even for Front-End Engineer, it is a must to understand how basic sorting algorithms work.
13 |
14 | Now you are asked to implement [Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort), which sorts an integer array in ascending order.
15 |
16 | Do it **in-place**, no need to return anything.
17 |
18 | **Follow-up**
19 |
20 | What is time cost for average / worst case ? Is it stable?
21 |
22 | #
23 |
24 | ### Solution
25 |
26 | ```js
27 | /**
28 | * @param {number[]} arr
29 | */
30 | function insertionSort(arr) {
31 | for (let i = 1; i < arr.length; i++) {
32 | let currentVal = arr[i];
33 | let j = i - 1;
34 | while (j >= 0 && currentVal < arr[j]) {
35 | arr[j + 1] = arr[j];
36 | j--;
37 | }
38 | arr[j + 1] = currentVal;
39 | }
40 | }
41 | ```
42 |
43 | #### Use Cases:
44 |
45 | 1. **Small Data Sets**: Efficient for small datasets due to its simplicity and low overhead.
46 | 2. **Nearly Sorted Data**: Performs very well on nearly sorted data or when the data is already sorted.
47 | 3. **Online Sorting**: Suitable for scenarios where the list is being received in a streaming fashion, as it can sort the list as elements arrive.
--------------------------------------------------------------------------------
/Questions-Answers/18.Promise-executor-II.md:
--------------------------------------------------------------------------------
1 | # 18. Promise executor II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Promise-executor-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const p1 = Promise.resolve(1);
15 | const p2 = new Promise((resolve) => resolve(p1));
16 | const p3 = Promise.resolve(p1);
17 | const p4 = p2.then(() => new Promise((resolve) => resolve(p3)));
18 | const p5 = p4.then(() => p4);
19 |
20 | console.log(p1 == p2);
21 | console.log(p1 == p3);
22 | console.log(p3 == p4);
23 | console.log(p4 == p5);
24 | ```
25 |
26 | #
27 |
28 | ### Answer
29 |
30 | ```js
31 | const p1 = Promise.resolve(1);
32 | const p2 = new Promise((resolve) => resolve(p1));
33 | const p3 = Promise.resolve(p1);
34 | const p4 = p2.then(() => new Promise((resolve) => resolve(p3)));
35 | const p5 = p4.then(() => p4);
36 |
37 | console.log(p1 == p2); // false
38 | console.log(p1 == p3); // true
39 | console.log(p3 == p4); // false
40 | console.log(p4 == p5); // false
41 | ```
42 |
43 | ### Explanation
44 |
45 | - `p1 == p2` is `false`, because `p1` and `p2` reference two different Promise objects.
46 | - `p1 == p3` is `true`, because when `Promise.resolve(value)` receives a Promise object as argument, it returns that Promise object.
47 | - `p3 == p4` is `false`, because `Promise.prototype.then()` returns a new Promise. If the `onFulfilled` handler returns an already resolved Promise, the Promise returned by `then()` gets resolved with that Promise's value as its value.
48 | - `p4 == p5` is `false`, because `Promise.prototype.then()` returns a new Promise.
49 |
--------------------------------------------------------------------------------
/Questions-Answers/64.reference-type.md:
--------------------------------------------------------------------------------
1 | # 64. reference type
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/reference-type
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | const obj = {
16 | msg: 'BFE',
17 | foo() {
18 | console.log(this.msg)
19 | },
20 | bar() {
21 | console.log('dev')
22 | }
23 | }
24 |
25 | obj.foo();
26 | (obj.foo)();
27 | (obj.foo || obj.bar)();
28 | ```
29 |
30 | #
31 |
32 | ### Answer
33 |
34 |
35 | ```js
36 | const obj = {
37 | msg: 'BFE',
38 | foo() {
39 | console.log(this.msg)
40 | },
41 | bar() {
42 | console.log('dev')
43 | }
44 | }
45 |
46 | obj.foo(); // 'BFE'
47 | (obj.foo)(); // 'BFE'
48 | (obj.foo || obj.bar)(); // undefined
49 | ```
50 |
51 | ### Explanation
52 |
53 | - `(obj.foo)();` returns `'BFE'`, because `(obj.foo)()` is identical to `obj.foo()`. The [grouping operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping) only changes expression priority and doesn't trigger extra expression value return.
54 |
55 | - `(obj.foo || obj.bar)()` returns `undefined`, because the [grouping operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping) changes expression priority, so the `obj.foo || obj.bar` is evaluated first and the function call becomes secondary, `obj.foo || obj.bar` returns `function() { console.log(this.msg) }`, which `obj.foo` points to, and then `function() { console.log(this.msg) }` gets invoked, since it is a plain, undecorated function invocation, `this` refers to the global object and `undefined` is returned.
56 |
--------------------------------------------------------------------------------
/Questions-Answers/1.Promise-order.md:
--------------------------------------------------------------------------------
1 | # 1. Promise order
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/1-promise-order
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 | const promise = new Promise((resolve) => {
16 | console.log(2);
17 | resolve();
18 | console.log(3);
19 | });
20 |
21 | console.log(4);
22 |
23 | promise
24 | .then(() => {
25 | console.log(5);
26 | })
27 | .then(() => {
28 | console.log(6);
29 | });
30 |
31 | console.log(7);
32 |
33 | setTimeout(() => {
34 | console.log(8);
35 | }, 10);
36 |
37 | setTimeout(() => {
38 | console.log(9);
39 | }, 0);
40 | ```
41 |
42 | #
43 |
44 | ### Answer
45 |
46 | ```
47 | 1
48 | 2
49 | 3
50 | 4
51 | 7
52 | 5
53 | 6
54 | 9
55 | 8
56 | ```
57 |
58 | ### Explanation
59 |
60 | - The console logs `2` right after `1`, because the function passed to the `Promise` constructor is executed immediately when a new `Promise` is created.
61 |
62 | - Although a promise can only be resolved or rejected once, codes that come after `resolve()` or `reject()` will still get executed:
63 |
64 | ```js
65 | const promise = new Promise((resolve, reject) => {
66 | console.log(2);
67 | reject();
68 | console.log(3);
69 | });
70 |
71 | // Logs:
72 | // 2
73 | // 3
74 | ```
75 |
76 | - The [then handlers](<(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)>) of a promise are enqueued onto microtask queue. Immediately after every macrotask, JavaScript engine executes all tasks from the microtask queue, prior to running any other macrotasks. Since `setTimeout()` handlers are macrotasks, the console logs `5` and `6` before `9` and `8`.
77 |
--------------------------------------------------------------------------------
/Questions-Answers/49.this-IV.md:
--------------------------------------------------------------------------------
1 | # 49. `this` IV
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/this-4
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var bar = 1;
15 |
16 | function foo() {
17 | return this.bar++;
18 | }
19 |
20 | const a = {
21 | bar: 10,
22 | foo1: foo,
23 | foo2: function () {
24 | return foo();
25 | },
26 | };
27 |
28 | console.log(a.foo1.call());
29 | console.log(a.foo1());
30 | console.log(a.foo2.call());
31 | console.log(a.foo2());
32 | ```
33 |
34 | #
35 |
36 | ### Answer
37 |
38 | ```js
39 | var bar = 1;
40 |
41 | function foo() {
42 | return this.bar++;
43 | }
44 |
45 | const a = {
46 | bar: 10,
47 | foo1: foo,
48 | foo2: function () {
49 | return foo();
50 | },
51 | };
52 |
53 | console.log(a.foo1.call()); // 1
54 | console.log(a.foo1()); // 10
55 | console.log(a.foo2.call()); // 2
56 | console.log(a.foo2()); // 3
57 | ```
58 |
59 | ### Explanation
60 |
61 | - When using `call()` to invoke a function and without providing the first argument, the value of `this` is set to the global object.
62 |
63 | - If the increment operator `++` precedes the operand, the increment operator increments and return the value before incrementing. If `++` comes after the operand, `++` increments and return the value after incrementing.
64 |
65 | ```js
66 | let x = 3;
67 | y = x++;
68 |
69 | // y = 3
70 | // x = 4
71 | ```
72 |
73 | ```js
74 | let a = 2;
75 | b = ++a;
76 |
77 | // a = 3
78 | // b = 3
79 | ```
80 |
81 | #
82 |
83 | ### Reference
84 |
85 | - [Function.prototype.call()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call)
86 | - [Increment (++)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Increment)
87 |
--------------------------------------------------------------------------------
/40.implement-Bubble-Sort.md:
--------------------------------------------------------------------------------
1 | # 40. implement Bubble Sort
2 | Bubble Sort is a simple comparison-based sorting algorithm. It repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. This process continues until the list is sorted.
3 |
4 |
5 | ### Problem
6 |
7 | https://bigfrontend.dev/problem/implement-Bubble-Sort
8 |
9 | #
10 |
11 | ### Problem Description
12 |
13 | Even for Front-End Engineer, it is a must to understand how basic sorting algorithms work.
14 |
15 | Now you are asked to implement [Bubble Sort](https://en.wikipedia.org/wiki/Bubble_sort), which sorts an integer array in ascending order.
16 |
17 | Do it **in-place**, no need to return anything.
18 |
19 | **Follow-up**
20 |
21 | What is time cost for average / worst case ? Is it stable?
22 |
23 | #
24 |
25 | ### Solution
26 |
27 | ```js
28 | /**
29 | * @param {number[]} arr
30 | */
31 | function bubbleSort(arr) {
32 | let hasNoSwaps;
33 | for (let i = arr.length; i >= 0; i--) {
34 | hasNoSwaps = true;
35 | for (let j = 0; j < i - 1; j++) {
36 | if (arr[j] > arr[j + 1]) {
37 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
38 | hasNoSwaps = false;
39 | }
40 | }
41 | if (hasNoSwaps) {
42 | break;
43 | }
44 | }
45 | }
46 |
47 | // Usage
48 | console.log(bubbleSort([37, 45, 29, 8, 12, 88, -3])); // [-3, 8, 12, 29, 37, 45, 88]
49 | console.log(bubbleSort([8, 1, 2, 3, 4, 5, 6, 7])); // [1, 2, 3, 4, 5, 6, 7, 8]
50 | console.log(bubbleSort([1, 2, 3, 4, 5, 6, 7, 8])); // [1, 2, 3, 4, 5, 6, 7, 8]
51 | ```
52 |
53 | #### Use Cases:
54 |
55 | 1. **Small Data Sets**: It can be useful for small datasets or when the simplicity of the implementation is more critical than performance.
56 | 2. **Nearly Sorted Data**: If the data is nearly sorted or has only a few elements out of order, Bubble Sort can be efficient.
57 |
--------------------------------------------------------------------------------
/Questions-Answers/5.block-scope.md:
--------------------------------------------------------------------------------
1 | # 5. block scope
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/block-scope-1
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | for (var i = 0; i < 5; i++) {
15 | setTimeout(() => console.log(i), 0);
16 | }
17 |
18 | for (let i = 0; i < 5; i++) {
19 | setTimeout(() => console.log(i), 0);
20 | }
21 | ```
22 |
23 | #
24 |
25 | ### Answer
26 |
27 | ```js
28 | for (var i = 0; i < 5; i++) {
29 | setTimeout(() => console.log(i), 0); // 5 5 5 5 5
30 | }
31 |
32 | for (let i = 0; i < 5; i++) {
33 | setTimeout(() => console.log(i), 0); // 0 1 2 3 4
34 | }
35 | ```
36 |
37 | ### Explanation
38 |
39 | Because of how Event loop works in JavaScript, the timeout function callbacks always run after the completion of the for loop, even if it is `setTimeout(..., 0)` on each iteration.
40 |
41 | `var` variables are function scoped. `var` in the for loop header creates a single binding for `i`. When the timeout function callbacks execute, they share a reference to the same `i`, which is global scoped. Thus it logs: `5 5 5 5 5`.
42 |
43 | `let` variables are block scoped. `let` in the for loop header not only binds `i` to the for loop body, but also creates a new binding for each loop iteration. In other words, each iteration of the loop captures its own copy of `i`. Hence it prints: `0 1 2 3 4`.
44 |
45 | We can use closures to make the first one works like the second one:
46 |
47 | ```js
48 | for (var i = 0; i < 5; i++) {
49 | (function (j) {
50 | setTimeout(() => console.log(j), 0);
51 | })(i);
52 | }
53 | ```
54 |
55 | or declare a variable with `let` inside the for loop body to create a per-iteration block scope:
56 |
57 | ```js
58 | for (var i = 0; i < 5; i++) {
59 | let j = i;
60 | setTimeout(() => console.log(j), 0);
61 | }
62 | ```
63 |
--------------------------------------------------------------------------------
/Questions-Answers/8.Implicit-Coercion-I.md:
--------------------------------------------------------------------------------
1 | # 8. Implicit Coercion I
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Implicit-Conversion-1
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(Boolean('false'));
15 | console.log(Boolean(false));
16 | console.log('3' + 1);
17 | console.log('3' - 1);
18 | console.log('3' - ' 02 ');
19 | console.log('3' * ' 02 ');
20 | console.log(Number('1'));
21 | console.log(Number('number'));
22 | console.log(Number(null));
23 | console.log(Number(false));
24 | ```
25 |
26 | #
27 |
28 | ### Answer
29 |
30 | ```js
31 | console.log(Boolean('false')); // true
32 | console.log(Boolean(false)); // false
33 | console.log('3' + 1); // "31"
34 | console.log('3' - 1); // 2
35 | console.log('3' - ' 02 '); // 1
36 | console.log('3' * ' 02 '); // 6
37 | console.log(Number('1')); // 1
38 | console.log(Number('number')); // NaN
39 | console.log(Number(null)); // 0
40 | console.log(Number(false)); // 0
41 | ```
42 |
43 | ### Explanation
44 |
45 | - When using `Boolean()` as a function, it converts the input value to a boolean value. Since `'false'` is a non-empty string, it evaluates to `true`.
46 |
47 | 🙋♀️🙋♂️ However, if we use `new Boolean()`, e.g. `new Boolean(false)`, a `Boolean` object will be created. Since any object of which the value is not `undefined` or `null`, evaluates to `true` when passed to a conditional statement.
48 |
49 | ```js
50 | var x = new Boolean(false);
51 | if (x) {
52 | // this code is executed
53 | }
54 | ```
55 |
56 | - If the `+` operator is used with a string, it acts as the concatenation operator. When concatenating a number on to a string, the number will be converted to a string.
57 | - When using non-numeric operands with the arithmetic operators, JavaScript will attempt to convert it to a numeric value.
58 |
--------------------------------------------------------------------------------
/Questions-Answers/48.Prototype.md:
--------------------------------------------------------------------------------
1 | # 48. Prototype
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/prototype
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function Foo() {}
15 | Foo.prototype.bar = 1;
16 | const a = new Foo();
17 | console.log(a.bar);
18 |
19 | Foo.prototype.bar = 2;
20 | const b = new Foo();
21 | console.log(a.bar);
22 | console.log(b.bar);
23 |
24 | Foo.prototype = { bar: 3 };
25 | const c = new Foo();
26 | console.log(a.bar);
27 | console.log(b.bar);
28 | console.log(c.bar);
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | function Foo() {}
37 | Foo.prototype.bar = 1;
38 | const a = new Foo();
39 | console.log(a.bar); // 1
40 |
41 | Foo.prototype.bar = 2;
42 | const b = new Foo();
43 | console.log(a.bar);
44 | // 2, because reassigning the `Foo.prototype.bar` to a new
45 | // value also affects the old instance `a`.
46 | console.log(b.bar);
47 | // 2
48 |
49 | Foo.prototype = { bar: 3 };
50 | const c = new Foo();
51 | console.log(a.bar); // 2
52 | console.log(b.bar); // 2
53 | console.log(c.bar); // 3
54 | ```
55 |
56 | ### Explanation
57 |
58 | Modifying the `bar` property of `Foo.prototype` does affect the old instances, but if we point the `prototype` property of `Foo` to a new object, the old instances' `__proto__` property will still point to the old `Foo.prototype`:
59 |
60 | ```js
61 | function Foo() {}
62 | Foo.prototype.bar = 1;
63 | const a = new Foo();
64 |
65 | Foo.prototype = { bar: 3 };
66 | const c = new Foo();
67 |
68 | console.log(a.__proto__); // { bar: 1, constructor: ƒ Foo() }
69 | console.log(c.__proto__); // { bar: 3 }
70 | ```
71 |
72 | #
73 |
74 | ### Reference
75 |
76 | [Javascript : Modifying Prototype doesn't affect existing Instances](https://stackoverflow.com/questions/43765005/javascript-modifying-prototype-doesnt-affect-existing-instances)
77 |
--------------------------------------------------------------------------------
/Questions-Answers/35.Implicit-Coercion-III.md:
--------------------------------------------------------------------------------
1 | # 35. Implicit Coercion III
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Implicit-Conversion-III
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | console.log( [] + {} )
16 | console.log( + {} )
17 | console.log( + [] )
18 | console.log( {} + [])
19 | console.log( ({}) + [])
20 | console.log( ({}) + [])
21 | console.log( ({}) + [])
22 | console.log( {} + + [])
23 | console.log( {} + + [] + {} )
24 | console.log( {} + + [] + {} + [])
25 | ```
26 |
27 | #
28 |
29 | ### Answer
30 |
31 |
32 | ```js
33 | console.log( [] + {} ) // "[object Object]"
34 | console.log( + {} ) // NaN
35 | console.log( + [] ) // 0
36 | console.log( {} + []) // "[object Object]"
37 | console.log( ({}) + []) // "[object Object]"
38 | console.log( ({}) + []) // "[object Object]"
39 | console.log( ({}) + []) // "[object Object]"
40 | console.log( {} + + []) // "[object Object]0"
41 | console.log( {} + + [] + {} ) // "[object Object]0[object Object]"
42 | console.log( {} + + [] + {} + []) // "[object Object]0[object Object]"
43 | ```
44 |
45 | ### Explanation
46 |
47 | `{} + + []` returns `"[object Object]0"`, because:
48 |
49 | - First, the unary operator `+` in `+ []` tries to convert its operand `[]` into a number. Since `Array`'s `valueOf()` method returns the array itself, so `+` uses `Array`'s `toString()` method as a fallback. `toString()` returns an empty string and `+` converts the empty string into `0`.
50 |
51 | - Then, the addition `+` operator tries to convert `{}` into a primitive, since `Object`'s `valueOf()` method returns the object itself, so `toString()` method is used as a fallback, which returns `"[object Object]"`. **When one of the operands is a string, `+` operator tries string concatenation before numeric addition**.
52 |
--------------------------------------------------------------------------------
/Questions-Answers/16.parseInt.md:
--------------------------------------------------------------------------------
1 | # 16. parseInt
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/parseInt
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(['0'].map(parseInt));
15 | console.log(['0', '1'].map(parseInt));
16 | console.log(['0', '1', '1'].map(parseInt));
17 | console.log(['0', '1', '1', '1'].map(parseInt));
18 | ```
19 |
20 | #
21 |
22 | ### Answer
23 |
24 | ```js
25 | console.log(['0'].map(parseInt)); // [0]
26 | console.log(['0', '1'].map(parseInt)); // [0,NaN]
27 | console.log(['0', '1', '1'].map(parseInt)); // [0,NaN,1]
28 | console.log(['0', '1', '1', '1'].map(parseInt)); // [0,NaN,1,1]
29 | ```
30 |
31 | ### Explanation
32 |
33 | When a callback function is passed into `Array.prototype.map(callback)`, **it will receive three arguments**: `currentValue`, `currentIndex` and the full `array`. `['0', '1'].map(parseInt)` is equivalent to `['0', '1'].map((val, index, array) => parseInt(val, index, array))`. The function `parseInt()` takes in two arguments, a string to parse and an integer representing the radix, and the second argument is optional. If the radix provided is falsy, then by default, radix is set to `10`; if the radix is smaller than `2` or bigger than `36`, then `parseInt()` returns `NaN`.
34 |
35 | In `['0', '1'].map(parseInt)`, at first iteration `parseInt()` is invoked with `'0'`, `0` and `['0', '1']` as arguments. Since the radix provided is falsy, it is set to `10`, and `parseInt()` returns `0`. At second iteration, `parseInt()` receives `'1'`, `1` and `['0', '1']`. Since the radix is smaller than `2`, `NaN` is returned. Thus `console.log(['0', '1'].map(parseInt));` logs `[0,NaN]`. The same logic applies to the other cases.
36 |
37 | #
38 |
39 | ### Reference
40 |
41 | [Why `['1', '7', '11'].map(parseInt)` returns `[1, NaN, 3]` in Javascript](https://medium.com/dailyjs/parseint-mystery-7c4368ef7b21)
42 |
--------------------------------------------------------------------------------
/79.convert-snake_case-to-camelCase.md:
--------------------------------------------------------------------------------
1 | # 79. convert snake_case to camelCase
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/convert-snake_case-to-camelCase
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Do you prefer [snake_case](https://en.wikipedia.org/wiki/Snake_case) or [camelCase](https://en.wikipedia.org/wiki/Camel_case) ?
12 |
13 | Anyway, please create a function to convert snake_case to camcelCase.
14 |
15 | ```js
16 | snakeToCamel('snake_case');
17 | // 'snakeCase'
18 | snakeToCamel('is_flag_on');
19 | // 'isFlagOn'
20 | snakeToCamel('is_IOS_or_Android');
21 | // 'isIOSOrAndroid'
22 | snakeToCamel('_first_underscore');
23 | // '_firstUnderscore'
24 | snakeToCamel('last_underscore_');
25 | // 'lastUnderscore_'
26 | snakeToCamel('_double__underscore_');
27 | // '_double__underscore_'
28 | ```
29 |
30 | contiguous underscore `__`, leading underscore `_a`, and trailing underscore `a_` should be kept untouched.
31 |
32 | #
33 |
34 | ### Solution
35 |
36 | ```js
37 | /**
38 | * @param {string} str
39 | * @return {string}
40 | */
41 | function snakeToCamel(str) {
42 | return str.replace(/[a-z]_[a-z]/gi, (match) => {
43 | return match[0] + match[2].toUpperCase();
44 | });
45 | }
46 | ```
47 |
48 | #
49 |
50 | ### Refactored Solution
51 |
52 | ```js
53 | /**
54 | * @param {string} str
55 | * @return {string}
56 | */
57 | function snakeToCamel(str) {
58 | return str.replace(/([^_])_([^_])/g, (_, before, after) => {
59 | return before + after.toUpperCase();
60 | });
61 | }
62 | ```
63 |
64 | #
65 |
66 | ### Solution with Lookbehind
67 |
68 | ```js
69 | /**
70 | * @param {string} str
71 | * @return {string}
72 | */
73 | function snakeToCamel(str) {
74 | return str.replace(/(?<=[a-z])_[a-z]/gi, (match) => {
75 | return match[1].toUpperCase();
76 | });
77 | }
78 | ```
79 |
80 | #
81 |
82 | ### Reference
83 |
84 | [Problem Discuss](https://bigfrontend.dev/problem/convert-snake_case-to-camelCase/discuss)
85 |
--------------------------------------------------------------------------------
/44.implement-Selection-Sort.md:
--------------------------------------------------------------------------------
1 | # 44. implement Selection Sort
2 | Selection Sort is a simple comparison-based sorting algorithm. It divides the input list into two parts: a sorted sublist of items which is built up from left to right at the front (left) of the list, and a sublist of the remaining unsorted items that occupy the rest of the list. The algorithm repeatedly selects the smallest (or largest, depending on sorting order) element from the unsorted sublist, swaps it with the leftmost unsorted element, and moves the sublist boundaries one element to the right.
3 |
4 |
5 | ### Problem
6 |
7 | https://bigfrontend.dev/problem/implement-Selection-Sort
8 |
9 | #
10 |
11 | ### Problem Description
12 |
13 | Even for Front-End Engineer, it is a must to understand how basic sorting algorithms work.
14 |
15 | Now you are asked to implement [Selection sort](https://en.wikipedia.org/wiki/Selection_sort), which sorts an integer array in ascending order.
16 |
17 | Do it **in-place**, no need to return anything.
18 |
19 | **Follow-up**
20 |
21 | What is time cost for average / worst case ? Is it stable?
22 |
23 | #
24 |
25 | ### Solution
26 |
27 | ```js
28 | /**
29 | * @param {number[]} arr
30 | */
31 | function selectionSort(arr) {
32 | for (let i = 0; i < arr.length; i++) {
33 | let smallestIndex = i;
34 | for (let j = i + 1; j < arr.length; j++) {
35 | if (arr[j] < arr[smallestIndex]) {
36 | smallestIndex = j;
37 | }
38 | }
39 | if (smallestIndex !== i) {
40 | [arr[smallestIndex], arr[i]] = [arr[i], arr[smallestIndex]];
41 | }
42 | }
43 | }
44 | ```
45 |
46 | #### Use Cases:
47 |
48 | 1. **Small Data Sets**: Suitable for small datasets where the overhead of more complex algorithms is not justified.
49 | 2. **Memory-Constrained Environments**: Due to its in-place nature, it is useful when memory space is at a premium.
50 | 3. **Simple Requirements**: When the simplicity of implementation is more important than performance.
--------------------------------------------------------------------------------
/Questions-Answers/30.Equal-II.md:
--------------------------------------------------------------------------------
1 | # 30. Equal II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Equal-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 | ref to the [The Abstract Equality Comparison Algorithm](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3)
13 |
14 | ```js
15 | console.log([1] == 1);
16 | console.log([1] == '1');
17 | console.log(['1'] == '1');
18 | console.log(['1'] == 1);
19 | console.log([1] == ['1']);
20 | console.log(new Boolean(true) == 1);
21 | console.log(new Boolean(true) == new Boolean(true));
22 | console.log(Boolean(true) == '1');
23 | console.log(Boolean(false) == [0]);
24 | console.log(new Boolean(true) == '1');
25 | console.log(new Boolean(false) == [0]);
26 | console.log(null == undefined);
27 | ```
28 |
29 | #
30 |
31 | ### Answer
32 |
33 | ```js
34 | console.log([1] == 1); // true
35 | console.log([1] == '1'); // true
36 | console.log(['1'] == '1'); // true
37 | console.log(['1'] == 1); // true
38 | console.log([1] == ['1']); // false
39 | console.log(new Boolean(true) == 1); // true
40 | console.log(new Boolean(true) == new Boolean(true)); // false
41 | console.log(Boolean(true) == '1'); // true
42 | console.log(Boolean(false) == [0]); // true
43 | console.log(new Boolean(true) == '1'); // true
44 | console.log(new Boolean(false) == [0]); // false
45 | console.log(null == undefined); // true
46 | ```
47 |
48 | ### Explanation
49 |
50 | - `==` tries to convert the operands if they are of different types. In `[1] == ['1']`, the operands are of the same type, so type coercion doesn't happen. Since arrays are the reference data types, we are comparing two references that point to different memory location, thus `[1] === ['1']` returns `false`. The same logic applies to the `new Boolean(true) == new Boolean(true)` case and the `new Boolean(false) == [0]` case.
51 |
52 | - `new Boolean(true).valueOf()` returns `true`, so `new Boolean(true) == 1` returns `true`.
53 |
--------------------------------------------------------------------------------
/Questions-Answers/75.meaningless-calculation.md:
--------------------------------------------------------------------------------
1 | # 75. meaningless calculation
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/meaningless-calculation
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | const num = +((~~!+[])+(~~!+[])+[]+(~~!+[]))
16 | console.log(num)
17 | ```
18 |
19 | #
20 |
21 | ### Answer
22 |
23 | ```
24 | 21
25 | ```
26 |
27 | ### Explanation
28 |
29 | `+((~~!+[])+(~~!+[])+[]+(~~!+[]))` is equivalent to `+(~~!+[] + ~~!+[] + [] + ~~!+[])`.
30 |
31 | 1. First, JavaScript evaluates `~~!+[]`, starting with `+[]`.
32 |
33 | 1. `+[]` evaluates to `0`, because the unary plus `+` convert a operand into a number. When the operand is an array, the array is first converted to a string and then to a number. The empty array is first converted to `''`, then the empty string is converted to `0`.
34 |
35 | 2. Then `!0` evaluates to `true`, because the logical NOT(`!`) operator returns `true` if the operand is not a Boolean value and cannot be converted to `true`.
36 |
37 | 3. `~~` is a double [NOT bitwise operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT). The first `~` operator forces the operand to an integer, then inverts the lowest 31 bits. Then the second `~` operator inverts the bits back, so we will get an integer. In our case, `~true` returns `-2`, then `~-2` returns `1`.
38 |
39 | 2. Now we get `1 + 1 + [] + 1`. JavaScript then evaluates from left to right.
40 |
41 | 1. `1 + 1 + [] + 1` = `2 + [] + 1`.
42 |
43 | 2. The empty array is converted to a string value: `''`. When one of the operands is a string, the addition operator `+` tries string concatenation before numeric addition. Therefore, we got `'2' + 1`.
44 |
45 | 3. Finally, `'2' + 1` returns `'21'`.
46 |
47 | #
48 |
49 | ### Reference
50 |
51 | [What does ~~ ("double tilde") do in Javascript?](https://stackoverflow.com/questions/4055633/what-does-double-tilde-do-in-javascript)
52 |
--------------------------------------------------------------------------------
/Questions-Answers/10.equal.md:
--------------------------------------------------------------------------------
1 | # 10. Equal
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Equal-1
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(0 == false);
15 | console.log('' == false);
16 | console.log([] == false);
17 | console.log(undefined == false);
18 | console.log(null == false);
19 | console.log('1' == true);
20 | console.log(1n == true);
21 | console.log(' 1 ' == true);
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```js
29 | console.log(0 == false); // true
30 | console.log('' == false); // true
31 | console.log([] == false); // true
32 | console.log(undefined == false); // false
33 | console.log(null == false); // false
34 | console.log('1' == true); // true
35 | console.log(1n == true); // true
36 | console.log(' 1 ' == true); // true
37 | ```
38 |
39 | ### Explanation
40 |
41 | When two values are compared with `==` (and `!=`), the [Abstract Equality Comparison Algorithm](http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3) is used.
42 |
43 | If the two values are of different types, JavaScript converts them to the same type before comparing:
44 |
45 | - If one of the operands is `Boolean`, the Boolean is converted to 1 if it is `true` and +0 if it is `false`.
46 | - When comparing a number to a string, JavaScript tries to convert the string to a numeric value.
47 | - If one of the operands is an object or an array, JavaScript tries to convert it to a primitive value using the `valueOf()` or `toString()` method.
48 |
49 | `[]` is converted to `''` using `toString()`, so `[] == false` returns `true`.
50 |
51 | `null` is only comparable to `undefined` in the [Abstract Equality Comparison Algorithm](http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3), therefore `undefined == false` and `null == false` both return `false`.
52 |
53 | Since `true` is converted to `1`, `2 == true` returns `false`.
54 |
55 | #
56 |
57 | ### Reference
58 |
59 | [Equality (==)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality)
60 |
--------------------------------------------------------------------------------
/Questions-Answers/9.null-and-undefined.md:
--------------------------------------------------------------------------------
1 | # 9. null and undefined
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/null-and-undefined
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(JSON.stringify([1, 2, null, 3]));
15 | console.log(JSON.stringify([1, 2, undefined, 3]));
16 | console.log(null === undefined);
17 | console.log(null == undefined);
18 | console.log(null == 0);
19 | console.log(null < 0);
20 | console.log(null > 0);
21 | console.log(null <= 0);
22 | console.log(null >= 0);
23 | console.log(undefined == 0);
24 | console.log(undefined < 0);
25 | console.log(undefined > 0);
26 | console.log(undefined <= 0);
27 | console.log(undefined >= 0);
28 | ```
29 |
30 | #
31 |
32 | ### Answer
33 |
34 | ```js
35 | // `null` and `undefined` are not JSON values. If such values
36 | // are encountered in an array, they will be changed to `null`.
37 | console.log(JSON.stringify([1, 2, null, 3])); // "[1,2,null,3]"
38 | console.log(JSON.stringify([1, 2, undefined, 3])); // "[1,2,null,3]"
39 |
40 | console.log(null === undefined); // false
41 |
42 | // `null` is only comparable to `undefined` when using `==`.
43 | // Therefore `null == undefined` returns `true`, and `null == 0`
44 | // returns `false`.
45 | console.log(null == undefined); // true
46 | console.log(null == 0); // false
47 |
48 | // `>`, `<`, `>=` and `<=` perform type coercion with a hint type
49 | // of `Number`. Therefore `null` is converted to `0` before comparison.
50 | console.log(null < 0); // false
51 | console.log(null > 0); // false
52 | console.log(null <= 0); // true
53 | console.log(null >= 0); // true
54 |
55 | console.log(undefined == 0); // false
56 |
57 | // `undefined` becomes `NaN` when coerced to numeric type.
58 | console.log(undefined < 0); // false
59 | console.log(undefined > 0); // false
60 | console.log(undefined <= 0); // false
61 | console.log(undefined >= 0); // false
62 | ```
63 |
64 | #
65 |
66 | ### Reference
67 |
68 | [Equality (==)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality)
69 |
--------------------------------------------------------------------------------
/Questions-Answers/76.const.md:
--------------------------------------------------------------------------------
1 | # 76. const
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/const
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | function func() {
16 | const a = b = c = 1;
17 | }
18 | func();
19 | console.log(typeof a, typeof b, typeof c);
20 | ```
21 |
22 | #
23 |
24 | ### Answer
25 |
26 | ```
27 | "undefined","number","number"
28 | ```
29 |
30 | ### Explanation
31 |
32 | - `typeof a` returns `"undefined"`, because constants are block-scoped. We declared `a` with `const` inside the function `func()`, therefore `a` is local only to the `func() {}` block. When we access `a` outside of that scope, i.e., in the global scope, `a` is `undefined`.
33 |
34 | - `typeof b` and `typeof c` both return `"number"`, because `b` and `c` are declared globally.
35 |
36 | `const a = b = c = 1` is equivalent to the following:
37 |
38 | ```js
39 | c = 1;
40 | b = c;
41 |
42 | const a = b;
43 | ```
44 |
45 | Since there are no declarations for `b` and `c`, both of them are created as global variables. Therefore, the code snippet in the problem statement is essentially the same as the following:
46 |
47 | ```js
48 | var b;
49 | var c;
50 |
51 | function func() {
52 | c = 1;
53 | b = c;
54 |
55 | const a = b;
56 | }
57 | func();
58 | console.log(typeof a, typeof b, typeof c);
59 | ```
60 |
61 | First, we call `func()`; `c` and `b` are assigned to `1`. Then we log the type of `b` and `c`. Since now `b` and `c` are pointing to the number value `1`, `typeof b` and `typeof c` return `"number"`.
62 |
63 | It's worth noting, when declaring a constant, we must specify its value in the same statement in which it is declared:
64 |
65 | ```js
66 | // throws an error
67 | // Uncaught SyntaxError: Missing initializer in const declaration
68 |
69 | const a;
70 | ```
71 |
72 | #
73 |
74 | ### Reference
75 |
76 | [Initialization of several variables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#initialization_of_several_variables)
77 |
--------------------------------------------------------------------------------
/Questions-Answers/21.Array-I.md:
--------------------------------------------------------------------------------
1 | # 21. Array I
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Array-I
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const a = [0];
15 | console.log(a.length);
16 | a[3] = 3;
17 | console.log(a.length);
18 | for (let item of a) {
19 | console.log(item);
20 | }
21 | a.map((item) => {
22 | console.log(item);
23 | });
24 | a.forEach((item) => {
25 | console.log(item);
26 | });
27 | console.log(Object.keys(a));
28 | delete a[3];
29 | console.log(a.length);
30 | a[2] = 2;
31 | a.length = 1;
32 | console.log(a[0], a[1], a[2]);
33 | ```
34 |
35 | #
36 |
37 | ### Answer
38 |
39 | ```js
40 | const a = [0];
41 | console.log(a.length); // 1
42 | a[3] = 3;
43 | console.log(a.length); // 4
44 | for (let item of a) {
45 | console.log(item); // 0, undefined, undefined, 3
46 | }
47 | a.map((item) => {
48 | console.log(item); // 0, 3
49 | });
50 | a.forEach((item) => {
51 | console.log(item); // 0, 3
52 | });
53 | console.log(Object.keys(a)); // ["0","3"]
54 | delete a[3];
55 | console.log(a.length); // 4
56 | a[2] = 2;
57 | a.length = 1;
58 | console.log(a[0], a[1], a[2]); // 0,undefined,undefined
59 | ```
60 |
61 | ### Explanation
62 |
63 | - When run over a sparse array, both `Array.prototype.map()` and `Array.prototype.forEach()` skip the blank indices in the sparse array.
64 |
65 | - When you delete an array element with the `delete` operator, the array `length` is not affected.
66 |
67 | ```js
68 | const a = [0];
69 | a[3] = 3;
70 | delete a[3];
71 |
72 | console.log(a); // [0, empty, empty, empty]
73 | ```
74 |
75 | #
76 |
77 | ### Reference
78 |
79 | - [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#parameters_in_detail)
80 | - [Array.prototype.forEach()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#no_operation_for_uninitialized_values_sparse_arrays)
81 | - [delete operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#deleting_array_elements)
82 |
--------------------------------------------------------------------------------
/Questions-Answers/26.true-or-false.md:
--------------------------------------------------------------------------------
1 | # 26. true or false
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/true-or-false
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log([] == 0);
15 | console.log([] == false);
16 | console.log(!![]);
17 | console.log([1] == 1);
18 | console.log(!![1]);
19 | console.log(Boolean([]));
20 | console.log(Boolean(new Boolean([])));
21 | console.log(Boolean(new Boolean(false)));
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```js
29 | console.log([] == 0); // true
30 | console.log([] == false); // true
31 | console.log(!![]); // true;
32 | console.log([1] == 1); // true;
33 | console.log(!![1]); // true;
34 | console.log(Boolean([])); // true
35 | console.log(Boolean(new Boolean([]))); // true
36 | console.log(Boolean(new Boolean(false))); // true
37 | ```
38 |
39 | ### Explanation
40 |
41 | - The [double NOT operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT) explicitly converts any value to the corresponding [boolean primitive](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#boolean_type). The conversion is based on the "truthyness" or "falsyness" of the value.
42 | In the case of `!![]`, since any array is truthy, `[]` evaluates to `true`; `!true` results in `false`, and finally `!false` returns `true`.
43 |
44 | - `Boolean(value)` returns `false` if the value passed as argument is falsy, otherwise it returns `true`. An empty array is truthy, thus `Boolean([])` returns `true`.
45 |
46 | - `new Boolean(value)` returns a `Boolean` object. Any object is truthy including a `Boolean` object whose value is `false`, thus `Boolean(new Boolean(false))` returns `true`.
47 |
48 | #
49 |
50 | ### Reference
51 |
52 | - [Logical NOT (!)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT)
53 | - [Truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy)
54 | - [Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)
55 | - [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
56 |
--------------------------------------------------------------------------------
/Questions-Answers/20.name-for-Function-expression.md:
--------------------------------------------------------------------------------
1 | # 20. name for Function expression
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/name-for-Function-expression
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function a() {}
15 | const b = function () {};
16 |
17 | const c = function d() {
18 | console.log(typeof d);
19 | d = 'e';
20 | console.log(typeof d);
21 | };
22 |
23 | console.log(typeof a);
24 | console.log(typeof b);
25 | console.log(typeof c);
26 | console.log(typeof d);
27 | c();
28 | ```
29 |
30 | #
31 |
32 | ### Answer
33 |
34 | ```js
35 | function a() {}
36 | const b = function () {};
37 |
38 | const c = function d() {
39 | console.log(typeof d); // "function"
40 | d = 'e';
41 | console.log(typeof d); // "function"
42 | };
43 |
44 | console.log(typeof a); // "function"
45 | console.log(typeof b); // "function"
46 | console.log(typeof c); // "function"
47 | console.log(typeof d); // "undefined"
48 | c();
49 | ```
50 |
51 | ### Explanation
52 |
53 | The variable the function expression is assigned to will have a `name` property. If function name is present, the value of the `name` property will be the function name (explicit name); otherwise it will be the variable name (implicit name):
54 |
55 | ```js
56 | const b = function () {};
57 | console.log(b.name); // 'b'
58 |
59 | const c = function d() {};
60 | console.log(c.name); // 'd'
61 | ```
62 |
63 | The scope of the name is just the body of the function, not the surrounding scope, therefore it is not defined in the global scope:
64 |
65 | ```js
66 | const c = function d() {
67 | console.log(d); // function d() {...}
68 | };
69 |
70 | console.log(d); // Error: `d` is not defined.
71 | ```
72 |
73 | In addition, the name doesn't change if it is assigned to a different variable:
74 |
75 | ```js
76 | const c = function d() {
77 | d = 'e';
78 | console.log(c.name); // "d"
79 | console.log(d); // function d() {...}
80 | };
81 |
82 | c();
83 | ```
84 |
85 | #
86 |
87 | ### Reference
88 |
89 | [Function expression](https://developer.mozilla.org/en-US/docs/web/JavaScript/Reference/Operators/function)
90 |
--------------------------------------------------------------------------------
/Questions-Answers/34.precedence.md:
--------------------------------------------------------------------------------
1 | # 34. precedence
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/precedence
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | let a = 1
16 | console.log(a +++ a)
17 |
18 | let b = 1
19 | console.log(b + + + b)
20 |
21 | let c = 1
22 | console.log(c --- c)
23 |
24 | let d = 1
25 | console.log(d - - - d)
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 |
33 | ```js
34 | let a = 1
35 | console.log(a +++ a) // 3
36 |
37 | let b = 1
38 | console.log(b + + + b) // 2
39 |
40 | let c = 1
41 | console.log(c --- c) // 1
42 |
43 | let d = 1
44 | console.log(d - - - d) // 0
45 | ```
46 |
47 | ### Explanation
48 |
49 | - `a +++ a` returns `3`, because in this expression postfix increment operator `... ++` has the highest precedence, therefore JavaScript first evaluates `a ++`. `a +++ a` is same as `a++ + a`.
50 | Postfix increment increments and returns the value before incrementing:
51 |
52 | ```js
53 | let x = 3;
54 | let y = x++;
55 |
56 | console.log(x); // 4
57 | console.log(y); // 3
58 | ```
59 |
60 | Thus `a++` returns `1` and `a` becomes `2`, and `a++ + a` returns `3`.
61 |
62 | The same logic applies to the `c --- c` case. `c --- c` is same as `c-- - c`. `c--` returns `1` and `c` becomes `0`, so the final result is `1`.
63 |
64 | - `b + + + b` returns `2`, because `+ +` is not increment operator and the unary plus operator `+` has the highest precedence, therefore JavaScript evaluates `+ b` first. `b + + + b` is same as `b + + (+b)`.
65 | The unary plus operator `+` tries to convert its operand into a number if it isn't already. Since `b` is already a number, the expression becomes `b + + b`. `b + + b` is identical to `b + +b`, so the final result is `2`.
66 |
67 | The same logic applies to `d - - - d`, which is identical to `d - - (-d)`. The unary negation operator `-` negates its operand, so `-d` returns `-1` and the expression becomes `d - - (-1)`, which is same as `d - -(-1)`. So the final result is `0`.
68 |
69 | #
70 |
71 | ### Reference
72 |
73 | [Operator precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)
74 |
--------------------------------------------------------------------------------
/58.get-DOM-tree-height.md:
--------------------------------------------------------------------------------
1 | # 58. get DOM tree height
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/get-DOM-tree-height
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Height of a tree is the maximum depth from root node. Empty root node have a height of 0.
12 |
13 | If given DOM tree, can you create a function to get the height of it?
14 |
15 | For the DOM tree below, we have a height of 4.
16 |
17 | ```html
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | World!
26 |
27 |
28 | ```
29 |
30 | Can you solve this both recursively and iteratively?
31 |
32 | #
33 |
34 | ### Recursive Solution with DFS
35 |
36 | ```js
37 | /**
38 | * @param {HTMLElement | null} tree
39 | * @return {number}
40 | */
41 | function getHeight(tree) {
42 | if (tree === null) {
43 | return 0;
44 | }
45 | return 1 + Math.max(getTreeHeight(tree.left), getTreeHeight(tree.right));
46 | }
47 | ```
48 |
49 | #
50 |
51 | ### Iterative Solution using Stack
52 |
53 | ```js
54 | /**
55 | * @param {HTMLElement | null} tree
56 | * @return {number}
57 | */
58 | function getHeight(tree) {
59 | if (tree === null) {
60 | return 0;
61 | }
62 |
63 | let maxHeight = 0;
64 | const stack = [[tree, 1]];
65 |
66 | while (stack.length > 0) {
67 | const [el, height] = stack.pop();
68 | maxHeight = Math.max(height, maxHeight);
69 |
70 | for (const child of el.children) {
71 | stack.push([child, height + 1]);
72 | }
73 | }
74 |
75 | return maxHeight;
76 | }
77 | ```
78 |
79 | #
80 |
81 | ### Iterative Solution with BFS
82 |
83 | ```js
84 | /**
85 | * @param {HTMLElement | null} tree
86 | * @return {number}
87 | */
88 | function getHeight(tree) {
89 | if (tree === null) {
90 | return 0;
91 | }
92 |
93 | let maxHeight = 0;
94 | const queue = [[tree, 1]];
95 |
96 | while (queue.length > 0) {
97 | const [el, height] = queue.shift();
98 | maxHeight = Math.max(height, maxHeight);
99 |
100 | for (const child of el.children) {
101 | queue.push([child, height + 1]);
102 | }
103 | }
104 |
105 | return maxHeight;
106 | }
107 | ```
108 |
--------------------------------------------------------------------------------
/Questions-Answers/15.instanceOf.md:
--------------------------------------------------------------------------------
1 | # 15. instanceOf
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/instanceOf
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(typeof null);
15 | console.log(null instanceof Object);
16 | console.log(typeof 1);
17 | console.log(1 instanceof Number);
18 | console.log(1 instanceof Object);
19 | console.log(Number(1) instanceof Object);
20 | console.log(new Number(1) instanceof Object);
21 | console.log(typeof true);
22 | console.log(true instanceof Boolean);
23 | console.log(true instanceof Object);
24 | console.log(Boolean(true) instanceof Object);
25 | console.log(new Boolean(true) instanceof Object);
26 | console.log([] instanceof Array);
27 | console.log([] instanceof Object);
28 | console.log((() => {}) instanceof Object);
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | console.log(typeof null); // "object"
37 |
38 | console.log(null instanceof Object);
39 | // false. Primitive values are not objects.
40 |
41 | console.log(typeof 1); // "number"
42 | console.log(1 instanceof Number); // false
43 | console.log(1 instanceof Object); // false
44 |
45 | console.log(Number(1) instanceof Object);
46 | // false. When used as a function, `Number(value)` converts
47 | // `value` to the Number type.
48 |
49 | console.log(new Number(1) instanceof Object); // true
50 | console.log(typeof true); // "boolean"
51 | console.log(true instanceof Boolean); // false
52 | console.log(true instanceof Object); // false
53 |
54 | console.log(Boolean(true) instanceof Object);
55 | // false. When used as a function, `Boolean(value)` converts
56 | // `value` to the Boolean type.
57 |
58 | console.log(new Boolean(true) instanceof Object); // true
59 | console.log([] instanceof Array); // true
60 |
61 | console.log([] instanceof Object);
62 | // true. Since `Array` prototypically inherits from `Object`,
63 | // `Array` also belongs to `Object` class.
64 |
65 | console.log((() => {}) instanceof Object);
66 | // true. Every JavaScript function is a `Function` object.
67 | ```
68 |
69 | #
70 |
71 | ### Reference
72 |
73 | - [The instanceof operator](https://javascript.info/instanceof#ref-instanceof)
74 | - [Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
75 |
--------------------------------------------------------------------------------
/v8-performance-metrics/scripts/benchmark.js:
--------------------------------------------------------------------------------
1 | const { spawnSync } = require("child_process");
2 | const fs = require("fs");
3 | const { parse } = require("json2csv");
4 | const v8 = require("v8");
5 |
6 | function measureTime(file) {
7 | const start = process.hrtime.bigint();
8 |
9 | // Run the script and measure total execution time
10 | const result = spawnSync("node", [`samples/${file}`], { encoding: "utf-8" });
11 | const end = process.hrtime.bigint();
12 | const executionTime = (end - start) / BigInt(1000000); // Convert to milliseconds
13 |
14 | // Measure bytecode generation time
15 | const bytecodeStart = process.hrtime.bigint();
16 | const script = fs.readFileSync(`samples/${file}`, "utf-8");
17 | new Function(script);
18 | const bytecodeEnd = process.hrtime.bigint();
19 | const bytecodeTime = (bytecodeEnd - bytecodeStart) / BigInt(1000000); // Convert to milliseconds
20 |
21 | // Get V8 heap statistics
22 | const heapStats = v8.getHeapStatistics();
23 | const heapUsed = heapStats.used_heap_size;
24 | const heapTotal = heapStats.total_heap_size;
25 |
26 | // Mock data for optimized and deoptimized function counts
27 | const optimizedFunctions = Math.floor(Math.random() * 100);
28 | const deoptimizedFunctions = Math.floor(Math.random() * 20);
29 |
30 | // Mock data for garbage collection time (in ms)
31 | const garbageCollectionTime = Math.floor(Math.random() * 50);
32 |
33 | return {
34 | file,
35 | executionTime: Number(executionTime),
36 | bytecodeTime: Number(bytecodeTime), // Include bytecode time
37 | heapUsed,
38 | heapTotal,
39 | optimizedFunctions,
40 | deoptimizedFunctions,
41 | garbageCollectionTime,
42 | };
43 | }
44 |
45 | const files = ["simpleLoop.js", "complexCalculation.js", "functionCalls.js"];
46 | const results = files.map(measureTime);
47 |
48 | // Save results to CSV
49 | const csv = parse(results, {
50 | fields: [
51 | "file",
52 | "executionTime",
53 | "bytecodeTime", // Include bytecode time field
54 | "heapUsed",
55 | "heapTotal",
56 | "optimizedFunctions",
57 | "deoptimizedFunctions",
58 | "garbageCollectionTime",
59 | ],
60 | });
61 | fs.writeFileSync("data/performance_metrics.csv", csv);
62 |
63 | console.log(
64 | "Benchmarking complete. Results saved to data/performance_metrics.csv"
65 | );
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚀 Javascript Internals 🔥
2 |
3 | Welcome to **Javascript Internals**! This repository is your backstage pass to the world of JavaScript, revealing the magic behind its core functions and features. Whether you're a coding wizard or a curious learner, this repo is packed with insights to supercharge your JavaScript skills. Let's dive in! 🌟
4 |
5 | ## 🌟 What You'll Find Here
6 |
7 | * **🔧 Core Functions**: Detailed recreations of built-in JavaScript functions like `Array.prototype.map`, `String.prototype.slice`, `Object.assign`, and more.
8 | * **📚 Data Structures**: In-depth explanations and implementations of fundamental data structures such as arrays, objects, and sets.
9 | * **🧠 Execution Contexts**: Insights into how JavaScript manages execution contexts, scope chains, and the call stack.
10 | * **⏳ Event Loop**: A thorough exploration of the event loop, task queues, and asynchronous programming.
11 | * **🗑️ Memory Management**: An overview of how JavaScript handles memory allocation, garbage collection, and optimization techniques.
12 | * **🏗️ Prototypes and Inheritance**: Clear explanations of prototype-based inheritance and how JavaScript handles object creation and property lookup.
13 |
14 | ## 🎯 Goals of This Repository
15 |
16 | 1. **Educational Resource**: Provide a clear and detailed educational resource for developers wanting to understand JavaScript beyond its syntax.
17 | 2. **Practical Examples**: Offer practical examples and implementations that can be directly tested and modified.
18 | 3. **Collaborative Learning**: Encourage collaboration and contributions from the community to continuously improve and expand the content.
19 |
20 | ## 🚀 Getting Started
21 |
22 | 1. **Clone the repository**:
23 |
24 | ```bash
25 | git clone https://github.com/Shaban-Eissa/Javascript-Internals
26 | ```
27 |
28 | 2. **Navigate to the project directory**:
29 |
30 | ```bash
31 | cd Javascript-Internals
32 | ```
33 |
34 | 3. **Explore the different modules** and run the examples to see the inner workings of JavaScript functions and features.
35 |
36 | ## 🤝 How to Contribute
37 |
38 | We welcome contributions! If you have insights, improvements, or new implementations, feel free to fork the repository and submit a pull request. Let's make this a vibrant, collaborative learning community.
39 |
--------------------------------------------------------------------------------
/Questions-Answers/51.method.md:
--------------------------------------------------------------------------------
1 | # 51. method
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/method
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | // This is a trick question
15 |
16 | // case 1
17 | const obj1 = {
18 | foo() {
19 | console.log(super.foo());
20 | },
21 | };
22 |
23 | Object.setPrototypeOf(obj1, {
24 | foo() {
25 | return 'bar';
26 | },
27 | });
28 |
29 | obj1.foo();
30 |
31 | // case 2
32 |
33 | const obj2 = {
34 | foo: function () {
35 | console.log(super.foo());
36 | },
37 | };
38 |
39 | Object.setPrototypeOf(obj2, {
40 | foo() {
41 | return 'bar';
42 | },
43 | });
44 |
45 | obj2.foo();
46 | ```
47 |
48 | #
49 |
50 | ### Answer
51 |
52 | ```js
53 | // case 1
54 |
55 | const obj1 = {
56 | foo() {
57 | console.log(super.foo());
58 | },
59 | };
60 |
61 | Object.setPrototypeOf(obj1, {
62 | foo() {
63 | return 'bar';
64 | },
65 | });
66 |
67 | obj1.foo(); // didn't run because of the error caused by the second case
68 |
69 | // case 2
70 |
71 | const obj2 = {
72 | foo: function () {
73 | console.log(super.foo());
74 | },
75 | };
76 |
77 | Object.setPrototypeOf(obj2, {
78 | foo() {
79 | return 'bar';
80 | },
81 | });
82 |
83 | obj2.foo(); // Error: 'super' keyword unexpected here
84 | ```
85 |
86 | ### Explanation
87 |
88 | We can use either the ES6 shorthand syntax `propertyName() {}` or `propertyName: function() {}` to define "methods" on object literals. However, they are not fully identical. Only the shorthand syntax `propertyName() {}` is a [Method Definition](https://262.ecma-international.org/6.0/#sec-method-definitions). `propertyName: function() {}` is just a property, not a method. In JavaScript, starting with ES6, a method is defined as a function that has an internal `[[HomeObject]]` property containing the object to which the method belongs. The `[[HomeObject]]` property is used by `super` to resolve the parent prototype and its methods.
89 |
90 | #
91 |
92 | ### Reference
93 |
94 | - [super keyword unexpected here](https://stackoverflow.com/questions/39263358/super-keyword-unexpected-here)
95 | - [A Formal Method Definition](https://leanpub.com/understandinges6/read/#leanpub-auto-a-formal-method-definition)
96 | - [Super: internals, [[HomeObject]]](https://javascript.info/class-inheritance#super-internals-homeobject)
97 |
--------------------------------------------------------------------------------
/Questions-Answers/58.inherit-getter-setter.md:
--------------------------------------------------------------------------------
1 | # 58. inherit getter setter
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/inherit-getter-setter
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | let val = 0;
15 |
16 | class A {
17 | set foo(_val) {
18 | val = _val;
19 | }
20 | get foo() {
21 | return val;
22 | }
23 | }
24 |
25 | class B extends A {}
26 |
27 | class C extends A {
28 | get foo() {
29 | return val;
30 | }
31 | }
32 |
33 | const b = new B();
34 | console.log(b.foo);
35 | b.foo = 1;
36 | console.log(b.foo);
37 |
38 | const c = new C();
39 | console.log(c.foo);
40 | c.foo = 2;
41 | console.log(c.foo);
42 | console.log(b.foo);
43 | ```
44 |
45 | #
46 |
47 | ### Answer
48 |
49 | ```js
50 | let val = 0;
51 |
52 | class A {
53 | set foo(_val) {
54 | val = _val;
55 | }
56 | get foo() {
57 | return val;
58 | }
59 | }
60 |
61 | class B extends A {}
62 |
63 | class C extends A {
64 | get foo() {
65 | return val;
66 | }
67 | }
68 |
69 | const b = new B();
70 | console.log(b.foo); // 0
71 | b.foo = 1;
72 | console.log(b.foo); // 1
73 |
74 | const c = new C();
75 | console.log(c.foo); // 1
76 | c.foo = 2;
77 | console.log(c.foo); // 1
78 | console.log(b.foo); // 1
79 | ```
80 |
81 | ### Explanation
82 |
83 | The reason that both `c.foo` and `b.foo` return `1` after setting `c.foo` to `2` is that when we override the `get` method of an attribute in an inherited class, `set` method must also be overridden, and vice versa, because if an object has own property, this property will be used and not an inherited one.
84 |
85 | ```js
86 | let val = 0;
87 |
88 | class A {
89 | set foo(_val) {
90 | val = _val;
91 | }
92 | get foo() {
93 | return val;
94 | }
95 | }
96 |
97 | class C extends A {
98 | get foo() {
99 | return val;
100 | }
101 | set foo(_val) {
102 | super.foo = _val;
103 | }
104 | }
105 |
106 | const c = new C();
107 | console.log(c.foo); // 0
108 | c.foo = 2;
109 | console.log(c.foo); // 2
110 | ```
111 |
112 | #
113 |
114 | ### Reference
115 |
116 | [Override a setter, and the getter must also be overridden](https://stackoverflow.com/questions/28950760/override-a-setter-and-the-getter-must-also-be-overridden?noredirect=1&lq=1)
117 |
--------------------------------------------------------------------------------
/41.implement-Merge-Sort.md:
--------------------------------------------------------------------------------
1 | # 41. implement Merge Sort
2 | Merge Sort is a divide-and-conquer algorithm that splits the input list into smaller sublists, sorts those sublists, and then merges them back together to produce a sorted list.
3 |
4 |
5 | ### Problem
6 |
7 | https://bigfrontend.dev/problem/implement-Merge-Sort
8 |
9 | #
10 |
11 | ### Problem Description
12 |
13 | Even for Front-End Engineer, it is a must to understand how basic sorting algorithms work.
14 |
15 | Now you are asked to implement [Merge Sort](https://en.wikipedia.org/wiki/Merge_sort), which sorts an integer array in ascending order.
16 |
17 | Do it **in-place**, no need to return anything.
18 |
19 | **Follow-up**
20 |
21 | What is time cost for average / worst case ? Is it stable?
22 |
23 | #
24 |
25 | ### Solution
26 |
27 | ```js
28 | /**
29 | * @param {number[]} arr
30 | */
31 | function mergeSort(arr) {
32 | if (arr.length <= 1) return arr;
33 | const middle = Math.floor(arr.length / 2);
34 | const left = mergeSort(arr.slice(0, middle));
35 | const right = mergeSort(arr.slice(middle));
36 | return merge(left, right);
37 | }
38 |
39 | function merge(left, right) {
40 | let result = [];
41 | let leftIndex = 0;
42 | let rightIndex = 0;
43 |
44 | while (leftIndex < left.length && rightIndex < right.length) {
45 | if (left[leftIndex] < right[rightIndex]) {
46 | result.push(left[leftIndex]);
47 | leftIndex++;
48 | } else {
49 | result.push(right[rightIndex]);
50 | rightIndex++;
51 | }
52 | }
53 |
54 | return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
55 | }
56 |
57 | // Usage
58 | console.log(mergeSort([1, 5, 3, 8, 2, 6, 4, 7])); // [1, 2, 3, 4, 5, 6, 7, 8]
59 | console.log(mergeSort([1, 5, 3, 8, 2, 6, 4])); // [1, 2, 3, 4, 5, 6, 8]
60 | console.log(mergeSort([1, 5, 3, 8, 2, 6])); // [1, 2, 3, 5, 6, 8]
61 | console.log(mergeSort([1, 5, 3, 8, 2])); // [1, 2, 3, 5, 8]
62 | console.log(mergeSort([1, 5, 3, 8])); // [1, 3, 5, 8]
63 | console.log(mergeSort([1, 5, 3])); // [1, 3, 5]
64 | console.log(mergeSort([1, 5])); // [1, 5]
65 | console.log(mergeSort([1])); // [1]
66 | console.log(mergeSort([])); // []
67 | ```
68 |
69 | #### Use Cases:
70 |
71 | 1. **Large Data Sets**: Efficient for large datasets due to its O(nlogn)O(n \log n)O(nlogn) complexity.
72 | 2. **Linked Lists**: Works well with linked lists where random access is not required.
73 | 3. **External Sorting**: Useful for sorting large amounts of data that do not fit into memory.
74 |
--------------------------------------------------------------------------------
/Questions-Answers/14.Addition-vs-Unary-Plus.md:
--------------------------------------------------------------------------------
1 | # 14. Addition vs Unary Plus
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Addition-vs-Unary-Plus
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | There is a difference between [Addition Operator(+)](https://tc39.es/ecma262/#sec-addition-operator-plus) and [Unary plus operator(+)](https://tc39.es/ecma262/#sec-unary-plus-operator), even though they use the same '+'.
14 |
15 |
16 | ```js
17 | console.log(1 + 2)
18 | console.log(1 + + 2)
19 | console.log(1 + + + 2)
20 | console.log(1 + '2')
21 | console.log(1 + + '2')
22 | console.log('1' + 2)
23 | console.log('1' + + 2)
24 | console.log(1 + true)
25 | console.log(1 + + true)
26 | console.log('1' + true)
27 | console.log('1' + + true)
28 | console.log(1 + null)
29 | console.log(1 + + null)
30 | console.log('1' + null)
31 | console.log('1' + + null)
32 | console.log(1 + undefined)
33 | console.log(1 + + undefined)
34 | console.log('1' + undefined)
35 | console.log('1' + + undefined)
36 | console.log('1' + + + undefined)
37 | ```
38 |
39 | #
40 |
41 | ### Answer
42 |
43 |
44 | ```js
45 | console.log(1 + 2) // 3
46 | console.log(1 + + 2) // 3
47 | console.log(1 + + + 2) // 3
48 |
49 | console.log(1 + '2')
50 | // "12". When one of the operands is a string, `+` operator performs
51 | // string concatenation.
52 |
53 | console.log(1 + + '2') // 3
54 | console.log('1' + 2) // "12"
55 | console.log('1' + + 2) // "12"
56 | console.log(1 + true) // 2
57 | console.log(1 + + true) // 2
58 |
59 | console.log('1' + true)
60 | // "1true". When one of the operands is a string, `+` operator performs
61 | // string concatenation. Thus `true` is converted to `"true"`.
62 |
63 | console.log('1' + + true) // "11"
64 | console.log(1 + null) // 1
65 | console.log(1 + + null) // 1
66 |
67 | console.log('1' + null)
68 | // "1null". When one of the operands is a string, `+` operator performs
69 | // string concatenation. Thus `null` is converted to `"null"`.
70 |
71 |
72 | console.log('1' + + null) // "10"
73 | console.log(1 + undefined) // NaN
74 | console.log(1 + + undefined) // NaN
75 |
76 | console.log('1' + undefined)
77 | // "1undefined". When one of the operands is a string, `+` operator performs
78 | // string concatenation. Thus `undefined` is converted to `"undefined"`.
79 |
80 |
81 | console.log('1' + + undefined) // "1NaN"
82 | console.log('1' + + + undefined) // "1NaN"
83 | ```
84 |
--------------------------------------------------------------------------------
/Questions-Answers/23.Promise-all.md:
--------------------------------------------------------------------------------
1 | # 23. Promise.all()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Promise-all
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | (async () => {
15 | await Promise.all([]).then(
16 | (value) => {
17 | console.log(value);
18 | },
19 | (error) => {
20 | console.log(error);
21 | }
22 | );
23 |
24 | await Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(
25 | (value) => {
26 | console.log(value);
27 | },
28 | (error) => {
29 | console.log(error);
30 | }
31 | );
32 |
33 | await Promise.all([1, 2, Promise.resolve(3), Promise.reject('error')]).then(
34 | (value) => {
35 | console.log(value);
36 | },
37 | (error) => {
38 | console.log(error);
39 | }
40 | );
41 | })();
42 | ```
43 |
44 | #
45 |
46 | ### Answer
47 |
48 | ```js
49 | (async () => {
50 | await Promise.all([]).then(
51 | (value) => {
52 | console.log(value); // []
53 | },
54 | (error) => {
55 | console.log(error); // ignored
56 | }
57 | );
58 |
59 | await Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(
60 | (value) => {
61 | console.log(value); // [1, 2, 3, 4]
62 | },
63 | (error) => {
64 | console.log(error); // ignored
65 | }
66 | );
67 |
68 | await Promise.all([1, 2, Promise.resolve(3), Promise.reject('error')]).then(
69 | (value) => {
70 | console.log(value); // ignored
71 | },
72 | (error) => {
73 | console.log(error); // "error"
74 | }
75 | );
76 | })();
77 | ```
78 |
79 | ### Explanation
80 |
81 | The `Promise.all()` method takes an iterable of promises as input.
82 |
83 | - If an empty iterable is passed, then the promise returned by this method is fulfilled synchronously. The resolved value is an empty array.
84 | - If a nonempty iterable is passed, and all of the promises fulfill, or are not promises, then the promise returned by this method is fulfilled asynchronously. The resolved value is an array containing all the resolved values, including non-promise values.
85 | - If any of the passed-in promises reject, `Promise.all` asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved.
86 |
87 | #
88 |
89 | ### Reference
90 |
91 | [Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
92 |
--------------------------------------------------------------------------------
/Questions-Answers/29.Hoisting-III.md:
--------------------------------------------------------------------------------
1 | # 29. Hoisting III
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-III
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var a = 1;
15 |
16 | function func() {
17 | a = 2;
18 | console.log(a);
19 | var a;
20 | }
21 |
22 | func();
23 |
24 | console.log(a);
25 |
26 | if (!('b' in window)) {
27 | var b = 1;
28 | }
29 |
30 | console.log(b);
31 | ```
32 |
33 | #
34 |
35 | ### Answer
36 |
37 | ```js
38 | var a = 1;
39 |
40 | function func() {
41 | a = 2;
42 | console.log(a); // 2
43 | var a;
44 | }
45 |
46 | func();
47 |
48 | console.log(a); // 1
49 |
50 | if (!('b' in window)) {
51 | var b = 1;
52 | }
53 |
54 | console.log(b); // undefined
55 | ```
56 |
57 | ### Explanation
58 |
59 | Hoisting is a JavaScript mechanism where variable and function declarations are moved to the top of their scope before code execution. Variables declared with `var` are hoisted, but JavaScript only hoists declaration, i.e. `var b`, not initialization.
60 |
61 | ```js
62 | var a = 1;
63 |
64 | function func() {
65 | a = 2; // Initialization
66 | console.log(a);
67 | var a; // Declaration
68 | }
69 |
70 | func();
71 |
72 | console.log(a);
73 | ```
74 |
75 | Variable `a` in the function `func()` is moved to the top of the function scope before code execution. When `func()` gets called, variable `a` is initialized to `2`. Since `var` variables are function scope, it doesn't affect the global variable `a`. The global variable `a` will be affected, if we replace the function with a `if` statement:
76 |
77 | ```js
78 | var x = 1;
79 |
80 | if (x === 1) {
81 | var x = 2;
82 |
83 | console.log(x);
84 | // expected output: 2
85 | }
86 |
87 | console.log(x);
88 | // expected output: 2
89 | ```
90 |
91 | ---
92 |
93 | ```js
94 | if (!('b' in window)) {
95 | var b = 1;
96 | }
97 |
98 | console.log(b);
99 | ```
100 |
101 | When declaring a variable using `var` in a `if` statement, no matter the `if` condition pass or not pass, the declaration is always hoisted. So in the above case, when the code executes, `b` is already in the `window` object, thus the initialization never happens and the console logs `undefined`.
102 |
103 | #
104 |
105 | ### Reference
106 |
107 | [If statement and variable hoisting](https://stackoverflow.com/questions/34149693/if-statement-and-variable-hoisting)
108 |
--------------------------------------------------------------------------------
/Questions-Answers/47.Promise-Order-II.md:
--------------------------------------------------------------------------------
1 | # 47. Promise Order II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/promise-order-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 |
16 | setTimeout(() => {
17 | console.log(2);
18 | }, 10);
19 |
20 | setTimeout(() => {
21 | console.log(3);
22 | }, 0);
23 |
24 | new Promise((_, reject) => {
25 | console.log(4);
26 | reject(5);
27 | console.log(6);
28 | })
29 | .then(() => console.log(7))
30 | .catch(() => console.log(8))
31 | .then(() => console.log(9))
32 | .catch(() => console.log(10))
33 | .then(() => console.log(11))
34 | .then(console.log)
35 | .finally(() => console.log(12));
36 |
37 | console.log(13);
38 | ```
39 |
40 | #
41 |
42 | ### Answer
43 |
44 | ```
45 | 1
46 | 4
47 | 6
48 | 13
49 | 8
50 | 9
51 | 11
52 | undefined
53 | 12
54 | 3
55 | 2
56 | ```
57 |
58 | ### Explanation
59 |
60 | - The executor function passed to the `Promise` constructor is executed immediately. Since the callbacks in `setTimeout()` and `then()` will not be executed until the call stack is empty, the console logs `4` after `1`.
61 | - Although a promise can only be resolved/rejected once, code below `reject(value)` will be still executed. Therefore, console logs `6` after `4`.
62 | - `then` handlers and `catch` handler are queued in microtask queue. Immediately after every macrotask, the JavaScript engine will execute all tasks from the microtask queue, before running any other macrotasks.
63 | - Since the promise is rejected, `console.log(7)` is not executed, and the console outputs `8` from the `catch` handler after `13`.
64 | - The `catch()` method returns a promise, therefore the onFulfilled handler `() => console.log(9)` runs and the console logs `9`.
65 | - The `then()` method also returns a promise. Since the promise is resolved with a `undefined` value, `() => console.log(10)` from the `catch` handler is skipped, and the console logs `11`.
66 | - When the `then` onFulfilled handler gets executed, it receives the resolved promise's value as argument. Since the promise returned by `then(() => console.log(11))` is resolved with an `undefined` value, the `console.log` from `then(console.log)` receives `undefined` as argument and outputs `undefined`.
67 | - The callback function passed to `finally()` will be executed when a promise is settled, no matter whether the promise is resolved or rejected. There `12` is logged.
68 | - After all tasks from the microtask queue are executed, the callbacks from the `setTimeout()` run.
69 |
--------------------------------------------------------------------------------
/3.implement-Array.prototype.flat.md:
--------------------------------------------------------------------------------
1 | # 3. implement Array.prototype.flat()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/implement-Array-prototype.flat
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | There is already `Array.prototype.flat()` in JavaScript (ES2019), which reduces the nesting of Array.
12 |
13 | Could you manage to implement your own one?
14 |
15 | Here is an example to illustrate
16 |
17 | ```js
18 | console.log(flat([1, [2, [3, [4, [5]]]]], 1)); // [1, 2, [3, [4, [5]]]
19 | console.log(flat([1, [2, [3, [4, [5]]]]], 2)); // [1, 2, 3, [4, [5]]
20 | console.log(flat([1, [2, [3, [4, [5]]]]], 3)); // [1, 2, 3, 4, [5]
21 | console.log(flat([1, [2, [3, [4, [5]]]]], 4)); // [1, 2, 3, 4, 5]
22 | console.log(flat([1, 2, 3, [4, 5, [6, 7, 8, [9, 10]]]], Infinity)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
23 | ```
24 |
25 | follow up
26 |
27 | Are you able to solve it both recursively and iteratively?
28 |
29 | #
30 |
31 | ### Recursive Solution
32 |
33 | ```js
34 | /**
35 | * @param { Array } arr
36 | * @param { number } depth
37 | */
38 | const flat = (array, depth = 1) => {
39 | let flatArray = [];
40 | array.forEach((element) => {
41 | if (Array.isArray(element) && depth > 0) {
42 | flatArray.push(...flat(element, depth - 1));
43 | } else {
44 | flatArray.push(element);
45 | }
46 | });
47 | return flatArray;
48 | };
49 | ```
50 |
51 | #
52 |
53 | ### Recursive Solution with Reduce
54 |
55 | ```js
56 | /**
57 | * @param { Array } arr
58 | * @param { number } depth
59 | */
60 | function flat(arr, depth = 1) {
61 | return arr.reduce(
62 | (acc, item) =>
63 | Array.isArray(item) && depth > 0
64 | ? acc.concat(flat(item, depth - 1))
65 | : [...acc, item],
66 | []
67 | );
68 | }
69 | ```
70 |
71 | #
72 |
73 | ### Iterative Solution with Stack
74 |
75 | ```js
76 | /**
77 | * @param { Array } arr
78 | * @param { number } depth
79 | */
80 | function flat(arr, depth = 1) {
81 | const flatArray = [];
82 | let stack = [...arr.map((item) => [item, depth])];
83 |
84 | while (stack.length > 0) {
85 | const [item, depth] = stack.pop();
86 | if (Array.isArray(item) && depth > 0) {
87 | stack.push(...item.map((el) => [el, depth - 1]));
88 | } else {
89 | flatArray.push(item);
90 | }
91 | }
92 |
93 | return flatArray.reverse();
94 | }
95 | ```
96 |
97 | #
98 |
99 | ### Reference
100 |
101 | [Problem Discuss](https://bigfrontend.dev/problem/implement-Array-prototype.flat/discuss)
102 |
--------------------------------------------------------------------------------
/Questions-Answers/73.window-name.md:
--------------------------------------------------------------------------------
1 | # 73. window name
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/window-name
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var a = 1;
15 | (function () {
16 | console.log(a + this.a);
17 | var a = '2';
18 | console.log(a + this.a);
19 | })();
20 |
21 | var name = 1;
22 | (function () {
23 | console.log(name + this.name);
24 | var name = '2';
25 | console.log(name + this.name);
26 | })();
27 | ```
28 |
29 | #
30 |
31 | ### Answer
32 |
33 | ```js
34 | var a = 1;
35 | (function () {
36 | console.log(a + this.a); // NaN
37 | var a = '2';
38 | console.log(a + this.a); // "21"
39 | })();
40 |
41 | var name = 1;
42 | (function () {
43 | console.log(name + this.name); // "undefined1"
44 | var name = '2';
45 | console.log(name + this.name); // "21"
46 | })();
47 | ```
48 |
49 | ### Explanation
50 |
51 | ```js
52 | var a = 1;
53 | (function () {
54 | console.log(a + this.a);
55 | var a = '2';
56 | console.log(a + this.a);
57 | })();
58 | ```
59 |
60 | 1. First, we initialize a variable called `a` to `1` using `var`. In the global scope, a variable declared with `var` is added as a property of global object. In our case, the global object is `window`.
61 |
62 | 2. Then, we declare an IIFE. Inside the IIFE,
63 |
64 | 1. first, we log `a + this.a`. Because `(function(){})()` is a plain, undecorated invocation, `this` refers to the global object, which is `window`, so `this.a` returns `1`. `a` returns `undefined`, because we declare a variable `a` with `var` in the next line and the variable declaration is hoisted to the top of the function scope. Thus `a + this.a` returns `NaN`.
65 |
66 | 2. then, we initialize the local variable `a` to `'2'`, so `a + this.a` in the next line evaluates to `'2' + 1`. If one of the operands is a string, the `+` operator tries string concatenation before numeric addition, thus `a + this.a` returns `'21'`.
67 |
68 | ---
69 |
70 | ```js
71 | var name = 1;
72 | (function () {
73 | console.log(name + this.name); // "undefined1"
74 | var name = '2';
75 | console.log(name + this.name); // "21"
76 | })();
77 | ```
78 |
79 | The reason that the first `name + this.name` returns `'undefined1` is `window.name` converts all stored values to their string representations. For instance,
80 |
81 | ```js
82 | var name = { a: 1 };
83 |
84 | console.log(window.name); // '[object Object]'
85 | ```
86 |
87 | #
88 |
89 | ### Reference
90 |
91 | [Window.name](https://developer.mozilla.org/en-US/docs/Web/API/Window/name)
92 |
--------------------------------------------------------------------------------
/Questions-Answers/24.Equality-Sameness.md:
--------------------------------------------------------------------------------
1 | # 24. Equality & Sameness
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Equality-Sameness
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(0 == '0');
15 | console.log(0 === '0');
16 | console.log(Object.is(0, '0'));
17 |
18 | console.log(0 == 0);
19 | console.log(0 === 0);
20 | console.log(Object.is(0, 0));
21 |
22 | console.log(0 == -0);
23 | console.log(0 === -0);
24 | console.log(Object.is(0, -0));
25 |
26 | console.log(NaN == NaN);
27 | console.log(NaN === NaN);
28 | console.log(Object.is(NaN, NaN));
29 |
30 | console.log(0 == false);
31 | console.log(0 === false);
32 | console.log(Object.is(0, false));
33 | ```
34 |
35 | #
36 |
37 | ### Answer
38 |
39 | ```js
40 | console.log(0 == '0'); // true
41 | console.log(0 === '0'); // false
42 | console.log(Object.is(0, '0')); // false
43 |
44 | console.log(0 == 0); // true
45 | console.log(0 === 0); // true
46 | console.log(Object.is(0, 0)); // true
47 |
48 | console.log(0 == -0); // true
49 | console.log(0 === -0); // true
50 | console.log(Object.is(0, -0)); // false
51 |
52 | console.log(NaN == NaN); // false
53 | console.log(NaN === NaN); // false
54 | console.log(Object.is(NaN, NaN)); // true
55 |
56 | console.log(0 == false); // true
57 | console.log(0 === false); // false
58 | console.log(Object.is(0, false)); // false
59 | ```
60 |
61 | ### Explanation
62 |
63 | - When comparing a number to a string using the equality operator `==`, `==` tries to convert the string to a numeric value.
64 |
65 | - When comparing two values using `==` and one of the values is `Boolean`, `==` converts the `Boolean` to `1` if it is `true` and `+0` if it is `false`.
66 |
67 | - The `Object.is(value1, value2)` method determines whether two values are the same, and it doesn't coerce either value before comparing. The difference between `Object.is()` and the `===` operator is how they treat signed zeros and `NaN`s:
68 | - The `===` operator (and the `==` operator) treats `-0` and `+0` as equal, whereas `Object.is()` treats `-0` and `+0` as not equal.
69 | - The `===` operator treats `NaN` as not equal: `NaN === NaN` returns `false`, whereas `Object.is(NaN, NaN)` returns `true`. In addition, `Object.is(NaN, 0/0)` returns `true`, whereas
70 | `NaN === 0/0` returns `false`.
71 |
72 | #
73 |
74 | ### Reference
75 |
76 | - [Equality (==)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality)
77 | - [Object.is()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
78 |
--------------------------------------------------------------------------------
/Questions-Answers/52.requestAnimationFrame.md:
--------------------------------------------------------------------------------
1 | # 52. requestAnimationFrame
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/requestanimationframe
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1);
15 |
16 | setTimeout(() => {
17 | console.log(2);
18 | }, 100);
19 |
20 | requestAnimationFrame(() => {
21 | console.log(3);
22 | });
23 |
24 | requestAnimationFrame(() => {
25 | console.log(4);
26 | setTimeout(() => {
27 | console.log(5);
28 | }, 10);
29 | });
30 |
31 | const end = Date.now() + 200;
32 | while (Date.now() < end) {}
33 |
34 | console.log(6);
35 | ```
36 |
37 | #
38 |
39 | ### Answer
40 |
41 | ```
42 | 1
43 | 6
44 | 3
45 | 4
46 | 2
47 | 5
48 | ```
49 |
50 | ### Explanation
51 |
52 | The browser executes all queued `requestAnimationFrame` callbacks just before it repaints the page. Typically the browser repaints the page 60 times per second, if it is not blocked by the running task. The order of execution is specified in the [Event Loop Processing Model](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model) section:
53 |
54 | 1. Do the oldest macrotask
55 |
56 | 2. Do microtasks
57 |
58 | 3. If it is a good time to render:
59 |
60 | 1. Do preparation work
61 |
62 | 2. Run `requestAnimationFrame` callbacks
63 |
64 | 3. Render
65 |
66 | Thus the console logs `3` and `4` before `2` and `5`.
67 |
68 | The console logs `2` before `5` is because of the `while` loop. Even though the loop body is empty, the rest of the code has to wait for the while loop to finish before they can execute, which means it blocks the browser from repainting the page(?). When we remove the `while` loop, the console logs `5` before `2`: https://jsitor.com/WaSG_fzH3
69 |
70 | ```js
71 | console.log(1);
72 |
73 | setTimeout(() => {
74 | console.log(2);
75 | }, 100);
76 |
77 | requestAnimationFrame(() => {
78 | console.log(3);
79 | });
80 |
81 | requestAnimationFrame(() => {
82 | console.log(4);
83 | setTimeout(() => {
84 | console.log(5);
85 | }, 10);
86 | });
87 |
88 | const end = Date.now() + 200;
89 | while (Date.now() < end) {
90 | console.log('while loop runs');
91 | }
92 |
93 | console.log(6);
94 |
95 | // logs:
96 | // 1
97 | // 5164 times: while loop runs
98 | // 6
99 | // 3
100 | // 4
101 | // 2
102 | // 5
103 | ```
104 |
105 | #
106 |
107 | ### Reference
108 |
109 | [When will requestAnimationFrame be executed?](https://stackoverflow.com/questions/43050448/when-will-requestanimationframe-be-executed)
110 |
--------------------------------------------------------------------------------
/Questions-Answers/11.Implicit-Coercion-II.md:
--------------------------------------------------------------------------------
1 | # 11. Implicit Coercion II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Implicit-Conversion-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log([] + []);
15 | console.log([] + 1);
16 | console.log([[]] + 1);
17 | console.log([[1]] + 1);
18 | console.log([[[[2]]]] + 1);
19 | console.log([] - 1);
20 | console.log([[]] - 1);
21 | console.log([[1]] - 1);
22 | console.log([[[[2]]]] - 1);
23 | console.log([] + {});
24 | console.log({} + {});
25 | console.log({} - {});
26 | ```
27 |
28 | #
29 |
30 | ### Answer
31 |
32 | ```js
33 | console.log([] + []); // ""
34 | console.log([] + 1); // "1"
35 | console.log([[]] + 1); // "1"
36 | console.log([[1]] + 1); // "11"
37 | console.log([[[[2]]]] + 1); // "21"
38 | console.log([] - 1); // -1
39 | console.log([[]] - 1); // -1
40 | console.log([[1]] - 1); // 0
41 | console.log([[[[2]]]] - 1); // 1
42 | console.log([] + {}); // "[object Object]"
43 | console.log({} + {}); // "[object Object][object Object]"
44 | console.log({} - {}); // NaN
45 | ```
46 |
47 | ### Explanation
48 |
49 | - `[] + []` returns `""`, because `[]` is coerced to an empty string using `Array`'s `toString()` method. First, `+` operator tries to convert `[]` into a primitive value with `Array`'s `valueOf()` method, but it returns the array itself, so the `valueOf()` method is ignored. Then `+` uses `Array`'s `toString()` as fallback and it returns an empty string.
50 |
51 | - `[] + 1` returns `"1"`, because **`+` operator tries string concatenation before numeric addition, if one of the operands is a string.**. The same logic also applies to `[[]] + 1`, `[[1]] + 1` and `[[[[2]]]] + 1`.
52 |
53 | - `[] - 1` returns `-1`, because `-` operator triggers numeric conversion for `[]`: first, `[]` is converted to an empty string using the `toString()` method; then the empty string is converted to `0`. The same logic applies to `[[]] - 1`, `[[1]] - 1` and `[[[[2]]]] - 1`.
54 |
55 | - `[] + {}` returns `"[object Object]"`, because `+` operator tries to convert both `[]` and `{}` to primitive values. Since `Object`'s `valueOf()` method returns the object itself, `{}` is converted to `"[object Object]"` using the `toString()` method. The same logic applies to `{} + {}`.
56 |
57 | - `{} - {}` returns `NaN`, because `-` operator triggers numeric conversion for `{}`: first, `{}` is coerced to `"[object Object]"` using the `toString()` method; then `"[object Object]"` is converted to `NaN`.
58 |
59 | #
60 |
61 | ### Reference
62 |
63 | [JavaScript type coercion explained](https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/)
64 |
--------------------------------------------------------------------------------
/Questions-Answers/41.this-III.md:
--------------------------------------------------------------------------------
1 | # 41. `this` III
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/this-III
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | const obj = {
15 | a: 1,
16 | b: this.a + 1,
17 | c: () => this.a + 1,
18 | d() {
19 | return this.a + 1;
20 | },
21 | e() {
22 | return (() => this.a + 1)();
23 | },
24 | };
25 | console.log(obj.b);
26 | console.log(obj.c());
27 | console.log(obj.d());
28 | console.log(obj.e());
29 | ```
30 |
31 | #
32 |
33 | ### Answer
34 |
35 | ```js
36 | const obj = {
37 | a: 1,
38 | b: this.a + 1,
39 | c: () => this.a + 1,
40 | d() {
41 | return this.a + 1;
42 | },
43 | e() {
44 | return (() => this.a + 1)();
45 | },
46 | };
47 | console.log(obj.b); // NaN
48 | console.log(obj.c()); // NaN
49 | console.log(obj.d()); // 2
50 | console.log(obj.e()); // 2
51 | ```
52 |
53 | ### Explanation
54 |
55 | - `console.log(obj.b)` logs `NaN`, because object literals do not affect the value of `this` and **default binding** applies, which means `this` does not refer to `obj` but to the global object, i.e. `window` in browser. `window.a` is `undefined` and thus the result is `NaN`.
56 |
57 | - `console.log(obj.c())` logs `NaN`, because arrow functions do not have its own `this` binding and they adopt the value of `this` from the enclosed (function or global) scope.
58 |
59 | - `console.log(obj.d())` logs `2`, because the function `d()` is called as a method of `obj` and thus `this` is set to `obj`.
60 |
61 | - `console.log(obj.e())` logs `2`, because (() => this.a + 1)() is a arrow function and it is defined in the function `obj.e` which is not an arrow function. (() => this.a + 1)() captures whatever the function `e`'s `this` is at its call time.
62 |
63 | If we replace the arrow function with a normal function, we will get `NaN`, because `(function() {})()` is a plain, undecorated call:
64 |
65 | ```js
66 | const obj = {
67 | a: 1,
68 | e() {
69 | return (function () {
70 | return this.a + 1;
71 | })();
72 | },
73 | };
74 |
75 | console.log(obj.e()); // NaN
76 | ```
77 |
78 | We will also get `NaN`, if `obj.e` is not a method:
79 |
80 | ```js
81 | const obj = {
82 | a: 1,
83 | e: (() => {
84 | return this.a + 1;
85 | })(),
86 | };
87 |
88 | console.log(obj.e); // NaN
89 | ```
90 |
91 | ```js
92 | const obj = {
93 | a: 1,
94 | e: (function () {
95 | return this.a + 1;
96 | })(),
97 | };
98 |
99 | console.log(obj.e); // NaN
100 | ```
101 |
102 | #
103 |
104 | ### Reference
105 |
106 | [Understanding this, one example at a time](https://www.karenjs.com/blog/understanding-this-one-example-at-a-time/#arrow-function)
107 |
--------------------------------------------------------------------------------
/Questions-Answers/33.this-II.md:
--------------------------------------------------------------------------------
1 | # 33. `this` II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/this-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 |
14 | ```js
15 | const obj = {
16 | a: 1,
17 | b() {
18 | return this.a
19 | }
20 | }
21 | console.log(obj.b())
22 | console.log((true ? obj.b : a)())
23 | console.log((true, obj.b)())
24 | console.log((3, obj['b'])())
25 | console.log((obj.b)())
26 | console.log((obj.c = obj.b)())
27 | ```
28 |
29 | #
30 |
31 | ### Answer
32 |
33 |
34 | ```js
35 | const obj = {
36 | a: 1,
37 | b() {
38 | return this.a
39 | }
40 | }
41 | console.log(obj.b()) // 1
42 | console.log((true ? obj.b : a)()) // undefined
43 | console.log((true, obj.b)()) // undefined
44 | console.log((3, obj['b'])()) // undefined
45 | console.log((obj.b)()) // 1
46 | console.log((obj.c = obj.b)()) // undefined
47 | ```
48 |
49 | ### Explanation
50 |
51 | `()` is [grouping operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping). It overrides the normal precedence of evaluation in expressions, so that expressions with lower precedence can be evaluated before an expression with higher priority.
52 |
53 | - In `(true ? obj.b : a)()`, the condition expression is going to be evaluated first. The evaluation returns the function `function() { return this.a }`, that `obj.b` points to. Then `function() { return this.a }` is invoked. Since it is a plain, undecorated invocation, `this` points to the global object and we get `undefined` as result.
54 |
55 | - In `(true, obj.b)()`, we have a [comma sequence expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator). Comma sequence expression evaluates its operands from left to right and returns the evaluation of last operand:
56 |
57 | ```js
58 | let x = 1;
59 |
60 | x = (x++, x);
61 |
62 | console.log(x);
63 | // output: 2
64 |
65 | x = (2, 3);
66 |
67 | console.log(x);
68 | // output: 3
69 | ```
70 |
71 | In our case, the comma sequence expression returns `function() { return this.a }`, which is the value of the last operand `obj.b`. Then `function() { return this.a }` gets called and it is a plain, undecorated invocation.
72 |
73 | The same logic applies to `(3, obj['b'])()` and `(obj.c = obj.b)()`.
74 |
75 | - `(obj.b)()` is identical to `obj.b()`, because the grouping operator `()` only changes expression priority and doesn't trigger extra expression value return. In `(true, obj.b)()`, it is the comma sequence expression that triggers the expression value, and in `(obj.c = obj.b)()`, it is the assignment operator `=`.
76 |
77 | #
78 |
79 | ### Reference
80 |
81 | [Understanding this, one example at a time](https://www.karenjs.com/blog/understanding-this-one-example-at-a-time/#arrow-function)
82 |
--------------------------------------------------------------------------------
/64.auto-retry-Promise-on-rejection.md:
--------------------------------------------------------------------------------
1 | # 64. auto-retry Promise on rejection
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/retry-promise-on-rejection
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | For a web application, fetching API data is a common task.
12 |
13 | But the API calls might fail because of Network problems. Usually we could show a screen for Network Error and ask users to retry.
14 |
15 | One approach to handle this is **auto retry when network error occurs**.
16 |
17 | You are asked to create a `fetchWithAutoRetry(fetcher, count)`, which automatically fetch again when error happens, until the maximum count is met.
18 |
19 | For the problem here, there is no need to detect network error, you can just retry on all promise rejections.
20 |
21 | #
22 |
23 | ### Understanding the problem
24 |
25 | Write a function that retries fetch upon failure up to N times. The function takes two inputs, a callback function that fetches API data and returns a promise, and an integer representing maximum retry count.
26 |
27 | #
28 |
29 | ### Approach
30 |
31 | Fetching data is always asynchronous, so the function needs to return a promise. Within the promise, we can use a `try...catch` block to implement the auto retry. Call the `fetcher` function in the `try` block and then `resolve` the result. If an error is thrown in the `try` block, the statement in the `catch` block will be executed. So in the `catch` block, we can try to fetch the data again if the maximum count is not reached. We can use the variable `maximumRetryCount` to keep track of how many tries we have left. Every time we retry, we decrease the `maximumRetryCount` by 1. If `maximumRetryCount` is equal to 0, the returned promise gets rejected with the thrown error as its value.
32 |
33 | ### Solution
34 |
35 | ```js
36 | /**
37 | * @param {() => Promise} fetcher
38 | * @param {number} maximumRetryCount
39 | * @return {Promise}
40 | */
41 | function fetchWithAutoRetry(fetcher, maximumRetryCount) {
42 | return new Promise((resolve, reject) => {
43 | const retry = async (retriesLeft) => {
44 | try {
45 | const data = await fetcher();
46 | resolve(data);
47 | } catch (err) {
48 | if (retriesLeft === 0) {
49 | reject(err);
50 | return;
51 | }
52 | retry(retriesLeft - 1);
53 | }
54 | };
55 | retry(maximumRetryCount);
56 | });
57 | }
58 | ```
59 |
60 | #
61 |
62 | ### Usage
63 | ```js
64 | const fetcher = () => {
65 | return new Promise((resolve, reject) => {
66 | setTimeout(() => {
67 | fetch("https://jsonplaceholder.typicode.com/posts")
68 | .then((response) => response.json())
69 | .then((data) => resolve(data))
70 | .catch((err) => reject(err));
71 | }, 1000);
72 | });
73 | };
74 |
75 | console.log("Start");
76 | fetchWithAutoRetry(fetcher, 5)
77 | .then((data) => console.log(data))
78 | .catch((err) => console.log(err));
79 | console.log("End");
80 |
81 | ```
82 |
--------------------------------------------------------------------------------
/81.merge-sorted-arrays.md:
--------------------------------------------------------------------------------
1 | # 81. merge sorted arrays
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/merge-sorted-arrays
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | You are given a list of sorted non-descending integer arrays, write a function to merge them into one sorted non-descending array.
12 |
13 | ```js
14 | merge([
15 | [1, 1, 1, 100, 1000, 10000],
16 | [1, 2, 2, 2, 200, 200, 1000],
17 | [1000000, 10000001],
18 | [2, 3, 3],
19 | ]);
20 | // [1,1,1,1,2,2,2,2,3,3,100,200,200,1000,1000,10000,1000000,10000001]
21 | ```
22 |
23 | What is time complexity of your solution?
24 |
25 | #
26 |
27 | ### Understanding the problem
28 |
29 | Given a list of sorted arrays of integers, which are all in non-descending order, I am asked to write a function that is going to merge them into one array sorted in the same order.
30 |
31 | #
32 |
33 | ### Approach
34 |
35 | I can solve the problem by using divide-and-conquer approach. Divide the list of sorted arrays in half, then keep dividing the two sub-lists of sorted arrays in half, until the sub-list contains only one array, cause if there is only one sorted array in the list, I can simply return that array. Then I can merge the two sorted arrays from the previous step into one and return the merged array. Keep merging the two arrays from the previous step until all arrays are merged.
36 |
37 | To merge two sorted array I can use two pointers approach. Initially, point the first pointer to the start of the first array and the second pointer to the start of the second array, loop until both pointers point to the ends of the arrays. At each iteration, compare the elements in the two arrays the two pointers point to, if the element in the first array is smaller than the element in the second array, push the element from the first array into the empty array and move the pointer of that element to right by 1; otherwise push the element from the second array and move its pointer to right. If one of these two elements is `undefined`, set it to `Infinity`.
38 |
39 | ### Solution
40 |
41 | ```js
42 | /**
43 | * @param {number[][]} arrList
44 | * non-descending integer array
45 | * @return {number[]}
46 | */
47 | function merge(arrList) {
48 | return mergeImpl(arrList, 0, arrList.length - 1);
49 | }
50 |
51 | function mergeImpl(arrList, start, end) {
52 | if (start >= end) return arrList[end] || [];
53 |
54 | const mid = Math.floor((start + end) / 2);
55 |
56 | const left = mergeImpl(arrList, start, mid);
57 | const right = mergeImpl(arrList, mid + 1, end);
58 | return mergeSort(left, right);
59 | }
60 |
61 | function mergeSort(arrOne, arrTwo) {
62 | const mergedArr = [];
63 | let idxOne = 0;
64 | let idxTwo = 0;
65 |
66 | while (idxOne !== arrOne.length || idxTwo !== arrTwo.length) {
67 | const firstElement = arrOne[idxOne] || Infinity;
68 | const secondElement = arrTwo[idxTwo] || Infinity;
69 |
70 | if (firstElement < secondElement) {
71 | mergedArr.push(firstElement);
72 | idxOne++;
73 | } else {
74 | mergedArr.push(secondElement);
75 | idxTwo++;
76 | }
77 | }
78 |
79 | return mergedArr;
80 | }
81 | ```
82 |
--------------------------------------------------------------------------------
/Questions-Answers/56.to-primitive.md:
--------------------------------------------------------------------------------
1 | # 56. to primitive
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/primitive
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | // case 1
15 | const obj1 = {
16 | valueOf() {
17 | return 1;
18 | },
19 | toString() {
20 | return '100';
21 | },
22 | };
23 |
24 | console.log(obj1 + 1);
25 | console.log(parseInt(obj1));
26 |
27 | // case 2
28 | const obj2 = {
29 | [Symbol.toPrimitive]() {
30 | return 200;
31 | },
32 |
33 | valueOf() {
34 | return 1;
35 | },
36 | toString() {
37 | return '100';
38 | },
39 | };
40 |
41 | console.log(obj2 + 1);
42 | console.log(parseInt(obj2));
43 |
44 | // case 3
45 | const obj3 = {
46 | toString() {
47 | return '100';
48 | },
49 | };
50 |
51 | console.log(+obj3);
52 | console.log(obj3 + 1);
53 | console.log(parseInt(obj3));
54 |
55 | // case 4
56 | const obj4 = {
57 | valueOf() {
58 | return 1;
59 | },
60 | };
61 |
62 | console.log(obj4 + 1);
63 | console.log(parseInt(obj4));
64 |
65 | // case 5
66 | const obj5 = {
67 | [Symbol.toPrimitive](hint) {
68 | return hint === 'string' ? '100' : 1;
69 | },
70 | };
71 |
72 | console.log(obj5 + 1);
73 | console.log(parseInt(obj5));
74 | ```
75 |
76 | #
77 |
78 | ### Answer
79 |
80 | ```js
81 | /* ------- case 1 ------- */
82 |
83 | const obj1 = {
84 | valueOf() {
85 | return 1;
86 | },
87 | toString() {
88 | return '100';
89 | },
90 | };
91 |
92 | console.log(obj1 + 1); // 2
93 | // JavaScript uses `obj1`'s `valueOf()` to convert the object to a primitive.
94 |
95 | console.log(parseInt(obj1)); // 100
96 | // `parseInt()` expects a string value. If the argument is not a string,
97 | // then it is converted to one.
98 |
99 | /* ------- case 2 ------- */
100 |
101 | const obj2 = {
102 | [Symbol.toPrimitive]() {
103 | return 200;
104 | },
105 |
106 | valueOf() {
107 | return 1;
108 | },
109 | toString() {
110 | return '100';
111 | },
112 | };
113 |
114 | console.log(obj2 + 1); // 201
115 | console.log(parseInt(obj2)); // 200
116 | // When converting an object to a primitive value, JavaScript will first
117 | // call the method `Symbol.toPrimitive` if it is present.
118 | // The `Symbol.toPrimitive` takes in a string argument `hint`, which
119 | // gives us fine-grained control over the result primitive value.
120 |
121 | /* ------- case 3 ------- */
122 |
123 | const obj3 = {
124 | toString() {
125 | return '100';
126 | },
127 | };
128 |
129 | console.log(+obj3); // 100
130 | console.log(obj3 + 1); // '1001'
131 | console.log(parseInt(obj3)); // 100
132 |
133 | /* ------- case 4 ------- */
134 |
135 | const obj4 = {
136 | valueOf() {
137 | return 1;
138 | },
139 | };
140 |
141 | console.log(obj4 + 1); // 2
142 | console.log(parseInt(obj4)); // NaN
143 |
144 | /* ------- case 5 ------- */
145 |
146 | const obj5 = {
147 | [Symbol.toPrimitive](hint) {
148 | return hint === 'string' ? '100' : 1;
149 | },
150 | };
151 |
152 | console.log(obj5 + 1); // 2
153 | console.log(parseInt(obj5)); // 100
154 | ```
155 |
156 | #
157 |
158 | ### Reference
159 |
160 | - [parseInt()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)
161 | - [Symbol.toPrimitive](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive)
162 |
--------------------------------------------------------------------------------
/35.implement-Promise.race.md:
--------------------------------------------------------------------------------
1 | # 35. implement `Promise.race()`
2 | Promise.race is used to handle multiple promises concurrently, but it resolves or rejects as soon as the first promise settles (either resolves or rejects). It takes an iterable (such as an array) of promises and returns a single promise. This returned promise settles with the value or reason of the first promise that settles.
3 |
4 | ### Key Features of Promise.race
5 |
6 | 1. **First Settled Promise Wins**:
7 |
8 | * `Promise.race` returns a promise that resolves or rejects as soon as the first promise in the iterable settles.
9 | * This means the outcome (resolved value or rejection reason) is based on the earliest settling promise.
10 | 2. **Concurrent Execution**:
11 |
12 | * All promises in the iterable are executed concurrently.
13 | * The returned promise does not wait for the rest of the promises to settle once the first one settles.
14 | 3. **Flexible Handling**:
15 |
16 | * It can be used to manage both success and error scenarios quickly, depending on which promise settles first.
17 | 4. **Simplifying Timeouts**:
18 |
19 | * `Promise.race` is often used to implement timeouts for async operations by racing the main promise against a timeout promise that rejects after a certain period.
20 |
21 | ### Problem
22 |
23 | https://bigfrontend.dev/problem/implement-Promise-race
24 |
25 | #
26 |
27 | ### Problem Description
28 |
29 | This problem is similar to [31. implement async helper - race()](https://bigfrontend.dev/problem/implement-async-helper-race), but with Promise.
30 |
31 | > The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise. [source: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race)
32 |
33 | Can you create a `race()` which works the same as `Promise.race()`?
34 |
35 | #
36 |
37 | ### Solution
38 |
39 | ```js
40 | /**
41 | * @param {Array} promises
42 | * @return {Promise}
43 | */
44 | function race(promises) {
45 | return new Promise((resolve, reject) => {
46 | promises.forEach((promise) => {
47 | promise.then(resolve).catch(reject);
48 | });
49 | });
50 | }
51 | ```
52 |
53 |
54 | ### Use Cases
55 |
56 | 1. **Fastest Response Wins**
57 | When you want to proceed with the result of the fastest asynchronous operation, such as fetching data from multiple sources and using the quickest response.
58 |
59 | 2. **Timeouts**
60 | When you need to enforce a timeout on an asynchronous operation, combining the original promise with a timeout promise that rejects after a certain period.
61 |
62 |
63 | ### Example Usage
64 |
65 | ```javascript
66 | const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 500, 'First!'));
67 | const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'Second!'));
68 | const promise3 = new Promise((resolve, reject) => setTimeout(reject, 300, 'Error!'));
69 |
70 | Promise.race([promise1, promise2, promise3])
71 | .then((value) => {
72 | console.log(value); // 'Second!' (the fastest one to settle)
73 | })
74 | .catch((error) => {
75 | console.error('The first settled promise rejected:', error);
76 | });
77 | ```
78 |
79 | By leveraging `Promise.race`, developers can efficiently handle scenarios where the earliest completion (either resolve or reject) of any among several asynchronous tasks is needed, optimizing performance and responsiveness in cases where speed is crucial.
--------------------------------------------------------------------------------
/68.get-DOM-tags.md:
--------------------------------------------------------------------------------
1 | # 68. get DOM tags
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/get-DOM-tags
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Given a DOM tree, please return all the tag names it has.
12 |
13 | Your function should return a unique array of tags names in lowercase, order doesn't matter.
14 |
15 | #
16 |
17 | ### Understanding the problem
18 |
19 | We are given a DOM tree and we are asked to write a function that returns all the unique **tag names** in it in an array. The tag names should be returned in lowercase, and we can return them in any order.
20 |
21 | #
22 |
23 | ### Approach
24 |
25 | We could traverse the DOM tree using either DFS or BFS to get all of the tag names. To store only the unique tag names, we could make use of a `Set`, since a `Set` cannot have duplicate values.
26 |
27 | Because only an `Element` has the tag, as we traverse the DOM tree, we need to use `Element.children` rather than `Node.childNodes` to get only the child `Element`s.
28 |
29 | ### Implementation
30 |
31 | Recursive DFS:
32 |
33 | ```js
34 | /**
35 | * @param {HTMLElement} tree
36 | * @return {string[]}
37 | */
38 | function getTags(tree) {
39 | const tagNames = new Set();
40 |
41 | dfs(tree, tagNames);
42 | return [...tagNames];
43 | }
44 |
45 | function dfs(el, tagNames) {
46 | const tagName = el.tagName.toLowerCase();
47 | tagNames.add(tagName);
48 |
49 | for (const child of el.children) {
50 | dfs(child, tagNames);
51 | }
52 | }
53 | ```
54 |
55 | Iterative DFS:
56 |
57 | ```js
58 | /**
59 | * @param {HTMLElement} tree
60 | * @return {string[]}
61 | */
62 | function getTags(tree) {
63 | const tagNames = new Set();
64 | const stack = [tree];
65 |
66 | while (stack.length > 0) {
67 | const el = stack.pop();
68 | const tagName = el.tagName.toLowerCase();
69 | tagNames.add(tagName);
70 |
71 | stack.push(...el.children);
72 | }
73 |
74 | return [...tagNames];
75 | }
76 | ```
77 |
78 | Iterative BFS:
79 |
80 | ```js
81 | /**
82 | * @param {HTMLElement} tree
83 | * @return {string[]}
84 | */
85 | function getTags(tree) {
86 | const tagNames = new Set();
87 | // Assume we have a Queue data structure implemented.
88 | const queue = [tree];
89 |
90 | while (queue.length > 0) {
91 | const el = queue.shift();
92 | const tagName = el.tagName.toLowerCase();
93 | tagNames.add(tagName);
94 |
95 | queue.push(...el.children);
96 | }
97 |
98 | return [...tagNames];
99 | }
100 | ```
101 |
102 | #
103 |
104 | ### Usage
105 |
106 | ```js
107 | const DOM = {
108 | tagName: "div",
109 | children: [
110 | {
111 | tagName: "h1",
112 | children: [
113 | {
114 | tagName: "span",
115 | children: [
116 | {
117 | tagName: "a",
118 | children: [
119 | {
120 | tagName: "span",
121 | children: [],
122 | },
123 | ],
124 | },
125 | ],
126 | },
127 | ],
128 | },
129 |
130 | {
131 | tagName: "p",
132 | children: [
133 | {
134 | tagName: "span",
135 | children: [],
136 | },
137 | {
138 | tagName: "a",
139 | children: [
140 | {
141 | tagName: "span",
142 | children: [],
143 | },
144 | ],
145 | },
146 | ],
147 | },
148 | ],
149 | };
150 |
151 | console.log(getTags(DOM)); // ["div", "h1", "span", "a", "p"]
152 | ```
153 |
--------------------------------------------------------------------------------
/17.create-a-simple-store-for-DOM-element.md:
--------------------------------------------------------------------------------
1 | # 17. create a simple store for DOM element
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/create-a-simple-store-for-DOM-node
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | We have `Map` in es6, so we could use any data as key, such as DOM element.
12 |
13 | ```js
14 | const map = new Map();
15 | map.set(domNode, somedata);
16 | ```
17 |
18 | What if we need to support old JavaScript env like es5, how would you create your own Node Store as above?
19 |
20 | You are asked to implement a Node Store, which supports DOM element as key.
21 |
22 | ```js
23 | class NodeStore {
24 | set(node, value) {}
25 |
26 | get(node) {}
27 |
28 | has(node) {}
29 | }
30 | ```
31 |
32 | **note**
33 |
34 | `Map` is disabled when judging your code, it is against the goal of practicing.
35 |
36 | You can create a simple general `Map` polyfill. Or since you are asked to support specially for DOM element, what is special about DOM element?
37 |
38 | What is the Time / Space cost of your solution?
39 |
40 | #
41 |
42 | ### Solution
43 |
44 | ```js
45 | class NodeStore {
46 | constructor() {
47 | this.nodes = {};
48 | }
49 | /**
50 | * @param {Node} node
51 | * @param {any} value
52 | */
53 | set(node, value) {
54 | node.__id__ = Symbol();
55 | this.nodes[node.__id__] = value;
56 | }
57 | /**
58 | * @param {Node} node
59 | * @return {any}
60 | */
61 | get(node) {
62 | return this.nodes[node.__id__];
63 | }
64 |
65 | /**
66 | * @param {Node} node
67 | * @return {Boolean}
68 | */
69 | has(node) {
70 | // coerce to boolean value
71 | return !!this.nodes[node.__id__];
72 | }
73 | }
74 | ```
75 |
76 |
77 | #
78 |
79 | ### Usage
80 |
81 | ```js
82 | const store = new NodeStore();
83 |
84 | const Node = function () {};
85 | const node1 = new Node();
86 | const node2 = new Node();
87 |
88 | store.set(node1, "foo");
89 | store.set(node2, "bar");
90 |
91 | console.log(store.get(node1)); // 'foo'
92 | console.log(store.get(node2)); // 'bar'
93 |
94 | console.log(store.has(node1)); // true
95 | console.log(store.has(node2)); // true
96 | console.log(store.has(new Node())); // false
97 | ```
98 |
99 | #
100 |
101 | ### Explanation
102 |
103 | Let's break down the `NodeStore` class and its usage:
104 |
105 | 1. **Class Definition**: The `NodeStore` class is defined with a constructor and three methods: `set`, `get`, and `has`.
106 |
107 | 2. **Constructor**: The constructor initializes an empty object `this.nodes` that will be used to store nodes.
108 |
109 | 3. **`set` Method**: This method takes a `node` and a `value` as parameters. It assigns a unique identifier to the `node` using `Symbol()` and then stores the `value` in `this.nodes` using the unique identifier as the key.
110 |
111 | 4. **`get` Method**: This method takes a `node` as a parameter and returns the value associated with the `node` from `this.nodes`.
112 |
113 | 5. **`has` Method**: This method takes a `node` as a parameter and checks if `this.nodes` has a value for the `node`. It returns a boolean value.
114 |
115 | 6. **Usage**: The usage example shows how to use the `NodeStore` class. It creates a new `NodeStore` instance and two new `Node` instances. It uses the `set` method to store values for the nodes, the `get` method to retrieve the stored values, and the `has` method to check if values are stored for the nodes.
116 |
117 | This `NodeStore` class provides a way to associate values with nodes in a way that doesn't mutate the nodes themselves (except for adding a unique identifier). This can be useful in situations where you need to store metadata about nodes, for example in a graph or tree data structure.
118 |
119 |
--------------------------------------------------------------------------------
/20.detect-data-type-in-JavaScript.md:
--------------------------------------------------------------------------------
1 | # 20. Detect data type in JavaScript
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/detect-data-type-in-JavaScript
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | This is an easy problem.
12 |
13 | For [all the basic data types](https://javascript.info/types) in JavaScript, how could you write a function to detect the type of arbitrary data?
14 |
15 | Besides basic types, you need to also handle also commonly used complex data type including `Array`, `ArrayBuffer`, `Map`, `Set`, `Date` and `Function`
16 |
17 | The goal is not to list up all the data types but to show us how to solve the problem when we need to.
18 |
19 | The type should be lowercase
20 |
21 | ```js
22 | detectType(1); // 'number'
23 | detectType(new Map()); // 'map'
24 | detectType([]); // 'array'
25 | detectType(null); // 'null'
26 |
27 | // more in judging step
28 | ```
29 |
30 | #
31 |
32 | ### Solution
33 |
34 | ```js
35 | const dataTypes = new Map([
36 | [Number, 'number'],
37 | [String, 'string'],
38 | [Boolean, 'boolean'],
39 | [Array, 'array'],
40 | [ArrayBuffer, 'arraybuffer'],
41 | [Date, 'date'],
42 | [Map, 'map'],
43 | [Set, 'set'],
44 | ]);
45 |
46 | /**
47 | * @param {any} data
48 | * @return {string}
49 | */
50 | function detectType(data) {
51 | if (typeof data !== 'object') {
52 | return typeof data;
53 | }
54 |
55 | if (data === null) {
56 | return 'null';
57 | }
58 |
59 | for (const [type, name] of dataTypes.entries()) {
60 | if (data instanceof type) {
61 | return name;
62 | }
63 | }
64 |
65 | return 'object';
66 | }
67 | ```
68 |
69 | #
70 |
71 | ### Usage
72 |
73 | ```js
74 | console.log(detectType("hello")); // string
75 | console.log(detectType(1)); // number
76 | console.log(detectType(true)); // boolean
77 | console.log(detectType({})); // object
78 | console.log(detectType([])); // array
79 | console.log(detectType(null)); // null
80 | console.log(detectType(undefined)); // undefined
81 | console.log(detectType(() => {})); // function
82 | console.log(detectType(new Date())); // date
83 | console.log(detectType(new Map())); // map
84 | console.log(detectType(new Set())); // set
85 | ```
86 |
87 |
88 | #
89 |
90 | ### Explanation
91 |
92 | Let's break down the `detectType` function and its usage:
93 |
94 | 1. **Map Definition**: The `dataTypes` map is defined with JavaScript built-in types as keys and their corresponding string names as values.
95 |
96 | 2. **Function Definition**: The `detectType` function is defined to take one argument, `data`, and return a string representing its type.
97 |
98 | 3. **Non-object Types**: If `data` is not an object (i.e., it's a number, string, boolean, undefined, or function), it returns the result of `typeof data`, which will be a string representing its type.
99 |
100 | 4. **Null Type**: If `data` is `null`, it returns the string "null". This is necessary because `typeof null` incorrectly returns "object" in JavaScript.
101 |
102 | 5. **Object Types**: If `data` is an object, it iterates over the entries of `dataTypes`. If `data` is an instance of the current type, it returns the corresponding name. This covers array, arraybuffer, date, map, and set types.
103 |
104 | 6. **Default Type**: If `data` is an object but not an instance of any type in `dataTypes`, it returns the string "object". This covers plain objects and any other kinds of objects not covered by `dataTypes`.
105 |
106 | 7. **Usage**: The function is then called with various types of data, and the results are logged to the console. The output matches the comments after each call, demonstrating that the function correctly identifies the type of each piece of data.
107 |
108 | This `detectType` function provides a more accurate way to determine the type of a piece of data in JavaScript than the built-in `typeof` operator, especially for different kinds of objects.
--------------------------------------------------------------------------------
/11.what-is-Composition-create-a-pipe.md:
--------------------------------------------------------------------------------
1 | # 11. what is Composition? create a pipe()
2 |
3 | The concept of "pipes" or "pipelining" in JavaScript is a functional programming concept where you pass the output of one function as the input to the next function, forming a pipeline of functions. Here are some real-world examples:
4 |
5 | 1. **Data Transformation**: Suppose you have an array of objects representing users, and you want to transform this data in several steps (e.g., filter out inactive users, sort by last name, and then extract just the email addresses). Each of these steps could be a function, and you could use `pipes` to create a single function that performs all these transformations in order.
6 |
7 | 2. **Middleware in Express.js**: In Express.js, middleware functions are used to handle requests and responses. These middleware functions are essentially a pipeline where each function receives a request and response object, does something with them (like checking if a user is authenticated), and then passes them on to the next middleware function.
8 |
9 | 3. **Redux Middleware**: In Redux, middleware is used to handle side effects and asynchronous actions. Middleware functions in Redux form a pipeline where each function can inspect and modify actions before they reach the reducer.
10 |
11 | 4. **Data Validation**: Suppose you have a form and you want to validate the input in several steps (e.g., trim whitespace, check if the input is empty, validate against a regex). Each of these steps could be a function, and you could use `pipes` to create a single validation function.
12 |
13 | 5. **Mathematical Computations**: If you have a sequence of mathematical operations that need to be performed in a specific order, you can create a function for each operation and use `pipes` to combine them into a single function.
14 |
15 | In all these examples, the `pipes` function allows you to create modular, reusable functions and combine them in flexible ways.
16 |
17 | ### Problem
18 |
19 | https://bigfrontend.dev/problem/what-is-composition-create-a-pipe
20 |
21 | #
22 |
23 | ### Problem Description
24 |
25 | what is Composition? It is actually not that difficult to understand, see [@dan_abramov 's explanation](https://whatthefuck.is/composition).
26 |
27 | Here you are asked to create a `pipe()` function, which chains multiple functions together to create a new function.
28 |
29 | Suppose we have some simple functions like this
30 |
31 | ```js
32 | const times = (y) => (x) => x * y;
33 | const plus = (y) => (x) => x + y;
34 | const subtract = (y) => (x) => x - y;
35 | const divide = (y) => (x) => x / y;
36 | ```
37 |
38 | Your `pipe()` would be used to generate new functions
39 |
40 | ```js
41 | pipe([times(2), times(3)]);
42 | // x _ 2 _ 3
43 |
44 | pipe([times(2), plus(3), times(4)]);
45 | // (x _ 2 + 3) _ 4
46 |
47 | pipe([times(2), subtract(3), divide(4)]);
48 | // (x \* 2 - 3) / 4
49 | ```
50 |
51 | **notes**
52 |
53 | 1. to make things simple, functions passed to `pipe()` will all accept 1 argument
54 |
55 | #
56 |
57 | ### Solution
58 |
59 | ```js
60 | /**
61 | * @param {Array<(arg: any) => any>} funcs
62 | * @return {(arg: any) => any}
63 | */
64 | function pipe(funcs) {
65 | return function (n) {
66 | let result = n;
67 | for (let func of funcs) {
68 | result = func(result);
69 | }
70 | return result;
71 | };
72 | }
73 | ```
74 |
75 | #
76 |
77 | ### Usage
78 |
79 | ```js
80 | const addTwo = (n) => n + 2;
81 | const divideByFive = (n) => n / 5;
82 | const multiplyByTwo = (n) => n * 2;
83 | const multiplyByThree = (n) => n * 3;
84 |
85 | const addTwoMultiplyByThree = pipe([addTwo, multiplyByThree]);
86 | console.log(addTwoMultiplyByThree(5)); // 21
87 |
88 | const addTwoDivideByFive = pipe([addTwo, divideByFive]);
89 | console.log(addTwoDivideByFive(5)); // 1.4
90 |
91 | const addTwoMultiplyByThreeDivideByFive = pipe([
92 | addTwo,
93 | multiplyByThree,
94 | divideByFive,
95 | ]);
96 | console.log(addTwoMultiplyByThreeDivideByFive(5)); // 6
97 | ```
--------------------------------------------------------------------------------
/Questions-Answers/25.zero.md:
--------------------------------------------------------------------------------
1 | # 25. zero
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/zero
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | console.log(1 / 0);
15 | console.log(-1 / 0);
16 | console.log(0 / 0);
17 | console.log(0 === -0);
18 | console.log(Object.is(0, -0));
19 | console.log(Object.is(0, Math.round(-0.5)));
20 | console.log(Object.is(0, Math.round(0.5)));
21 | console.log(0 * Infinity);
22 | console.log(Infinity / Infinity);
23 | console.log(Object.is(0, Math.sign(0)));
24 | console.log(Object.is(0, Math.sign(-0)));
25 | console.log(1 / -0);
26 | console.log(1 / 0);
27 | console.log(1n / 0n);
28 | ```
29 |
30 | #
31 |
32 | ### Answer
33 |
34 | ```js
35 | console.log(1 / 0); // Infinity
36 | console.log(-1 / 0); // -Infinity
37 | console.log(0 / 0); // NaN
38 | console.log(0 === -0); // true
39 | console.log(Object.is(0, -0)); // false
40 | console.log(Object.is(0, Math.round(-0.5))); // false
41 | console.log(Object.is(0, Math.round(0.5))); // false
42 | console.log(0 * Infinity); // NaN
43 | console.log(Infinity / Infinity); // NaN
44 | console.log(Object.is(0, Math.sign(0))); // true
45 | console.log(Object.is(0, Math.sign(-0))); // false
46 | console.log(1 / -0); // -Infinity
47 | console.log(1 / 0); // Infinity
48 | console.log(1n / 0n); // Error
49 | ```
50 |
51 | ### Explanation
52 |
53 | - `1 / 0` returns `Infinity`, because any positive number divided by `0` returns `Infinity`. [The ECMAScript language specification states](http://es5.github.io/#x11.5.2):
54 |
55 | > division of a non-zero finite value by a zero results in a signed infinity.
56 |
57 | The specification author decided for division by zero to default to `Infinity`, because as the denominator trends toward zero, the result trends towards `Infinity`:
58 |
59 | ```
60 | 2 / 1 = 2
61 | 2 / 0.5 = 4
62 | ...
63 | 2 / 0.0000000000000005 = 4e+1
64 | ```
65 |
66 | - `0 / 0` returns `NaN`, because in mathematics, `0 / 0` is undefined as a [real number](https://en.wikipedia.org/wiki/Real_number), and is therefore represented by `NaN` in computing systems.
67 |
68 | - The `Object.is(value1, value2)` method determines whether two values are the same. `Object.is()` treats `-0` and `+0` as not equal, whereas the `===` operator (and the `==` operator) treats `-0` and `+0` as equal.
69 |
70 | - `0 * Infinity` returns `NaN`. The floating point zeros not only represent the real number `0`, but also all real numbers that would round to something smaller than the smallest [subnormal](https://en.wikipedia.org/wiki/Denormal_number). That is why `0` is signed. The floating point infinites represent all numbers that would round to something with a magnitude that would not fit in the finite range. `NaN` represents either 'No real number result' or 'Haven't a clue'. Something very big multiplied by something very small could be anything from very small to very bid, therefore `NaN` is the most reasonable answer. The same logic applies to `Infinity / Infinity` case(?).
71 |
72 | - `Math.sign(number)` method returns `+1` or `-1`, indicating the sign of the input number. If the input number is 0, it will return `+0` or `-0` depending on the sign of the `0` passed into `Math.sign()`.
73 |
74 | - `1n / 0n` ends up in an error: `RangeError: Division by zero`(?).
75 |
76 | #
77 |
78 | ### Reference
79 |
80 | - [Infinite from zero division](https://stackoverflow.com/questions/21893525/infinite-from-zero-division)
81 | - [In JavaScript, why does zero divided by zero return NaN, but any other divided by zero return Infinity?](https://stackoverflow.com/questions/18838301/in-javascript-why-does-zero-divided-by-zero-return-nan-but-any-other-divided-b)
82 | - [NaN](https://en.wikipedia.org/wiki/NaN#Creation)
83 | - [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
84 | - [Why is Infinity × 0 = NaN?](https://stackoverflow.com/questions/37841240/why-is-infinity-%C3%97-0-nan)
85 | - [Math.sign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign)
86 |
--------------------------------------------------------------------------------
/Questions-Answers/4.Promise-then-callbacks-II.md:
--------------------------------------------------------------------------------
1 | # 4. Promise then callbacks II
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/4-Promise-then-callbacks-II
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | Promise.resolve(1)
15 | .then((val) => {
16 | console.log(val);
17 | return val + 1;
18 | })
19 | .then((val) => {
20 | console.log(val);
21 | })
22 | .then((val) => {
23 | console.log(val);
24 | return Promise.resolve(3).then((val) => {
25 | console.log(val);
26 | });
27 | })
28 | .then((val) => {
29 | console.log(val);
30 | return Promise.reject(4);
31 | })
32 | .catch((val) => {
33 | console.log(val);
34 | })
35 | .finally((val) => {
36 | console.log(val);
37 | return 10;
38 | })
39 | .then((val) => {
40 | console.log(val);
41 | });
42 | ```
43 |
44 | #
45 |
46 | ### Answer
47 |
48 | ```js
49 | Promise.resolve(1)
50 | .then((val) => {
51 | console.log(val); // 1
52 | return val + 1;
53 | })
54 | .then((val) => {
55 | console.log(val); // 2
56 | })
57 | .then((val) => {
58 | console.log(val); // undefined
59 | return Promise.resolve(3).then((val) => {
60 | console.log(val); // 3
61 | });
62 | })
63 | .then((val) => {
64 | console.log(val); // undefined
65 | return Promise.reject(4);
66 | })
67 | .catch((val) => {
68 | console.log(val); // 4
69 | })
70 | .finally((val) => {
71 | console.log(val); // undefined
72 | return 10;
73 | })
74 | .then((val) => {
75 | console.log(val); // undefined
76 | });
77 | ```
78 |
79 | ### Explanation
80 |
81 | - `Promise.prototype.then()` returns a `Promise`. If a `then` handler:
82 |
83 | - returns a value, the `Promise` returned by `Promise.prototype.then()` gets resolved with the returned value.
84 |
85 | - doesn't return anything, the `Promise` returned by `Promise.prototype.then()` gets resolved with an `undefined` value.
86 |
87 | - returns an already fulfilled `Promise`, the `Promise` returned by `Promise.prototype.then()` gets resolved with that `Promise`'s value as its value.
88 |
89 | - returns an already rejected `Promise`, the `Promise` returned by `Promise.prototype.then()` gets rejected with that `Promise`'s value as its value.
90 |
91 | - returns a pending `Promise`, the resolution/rejection of the promise returned by `Promise.prototype.then()` will be subsequent to the resolution/rejection of the `Promise` returned by the `then` handler.
92 |
93 | - `Promise.prototype.finally()` returns a `Promise`. A `finally` handler will not receive any argument. In addition, it won't change the resolved value and just pass through it:
94 |
95 | ```js
96 | Promise.resolve(1)
97 | .finally(() => {
98 | console.log('Promise ready');
99 | return 10;
100 | })
101 | .then((value) => {
102 | console.log(value); // 1
103 | });
104 | ```
105 |
106 | The reason that the final `then` handler in the code snippet above logs `undefined` is that `Promise.prototype.catch()` also returns a `Promise` and it behaves the same as calling `Promise.prototype.then(undefined, onRejected)`. If the `catch` handler before `finally()` returns a value, then the final `then` handler would log that value:
107 |
108 | ```js
109 | Promise.reject(1)
110 | .catch((value) => {
111 | console.log(value);
112 | return 3;
113 | })
114 | .finally(() => {
115 | console.log('Promise ready');
116 | })
117 | .then((value) => {
118 | console.log(value); // 3
119 | });
120 | ```
121 |
122 | #
123 |
124 | ### Reference
125 |
126 | - [Promise.prototype.then()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)
127 | - [Promise.prototype.catch()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch)
128 | - [finally](https://javascript.info/promise-basics#finally)
129 |
--------------------------------------------------------------------------------
/32.implement-Promise.all.md:
--------------------------------------------------------------------------------
1 | # 32. implement `Promise.all()`
2 | Promise.all is a method in JavaScript used to handle multiple promises concurrently. It takes an iterable (such as an array) of promises and returns a single promise. This returned promise resolves when all the promises in the iterable have resolved, or rejects if any of the promises in the iterable reject.
3 |
4 | ### Key Features of `Promise.all`
5 |
6 | 1. **Concurrent Execution**:
7 |
8 | * `Promise.all` runs multiple promises in parallel.
9 | * It waits for all the promises to settle (either resolved or rejected).
10 | 2. **Resolved Value**:
11 |
12 | * When all promises resolve, `Promise.all` resolves with an array of their results, maintaining the order of the original promises.
13 | * Example: `Promise.all([promise1, promise2]).then((values) => { console.log(values); });`
14 | * If `promise1` resolves to `value1` and `promise2` resolves to `value2`, the output will be `[value1, value2]`.
15 | 3. **Rejection Handling**:
16 |
17 | * If any promise in the iterable rejects, `Promise.all` immediately rejects with the reason of the first promise that rejected.
18 | * Example: `Promise.all([promise1, promise2]).catch((error) => { console.log(error); });`
19 | * If `promise1` rejects with an `error1`, `Promise.all` will reject with `error1`.
20 |
21 |
22 | ### Problem
23 |
24 | https://bigfrontend.dev/problem/implement-Promise-all
25 |
26 | #
27 |
28 | ### Problem Description
29 |
30 | > The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises
31 |
32 | source - [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
33 |
34 | Could you write your own `all()` ? which should works the same as `Promise.all()`
35 |
36 | #
37 |
38 | ### Solution
39 |
40 | ```js
41 | /**
42 | * @param {Array} promises - notice input might have non-Promises
43 | * @return {Promise}
44 | */
45 | function all(promises) {
46 | if (promises.length === 0) {
47 | return Promise.resolve([]);
48 | }
49 |
50 | const results = Array(promises.length);
51 | let count = 0;
52 |
53 | return new Promise((resolve, reject) => {
54 | promises.forEach((promise, i) => {
55 | if (!(promise instanceof Promise)) {
56 | promise = Promise.resolve(promise);
57 | }
58 |
59 | promise
60 | .then((res) => {
61 | results[i] = res;
62 | count++;
63 |
64 | if (count === promises.length) {
65 | resolve(results);
66 | }
67 | })
68 | .catch((err) => {
69 | reject(err);
70 | });
71 | });
72 | });
73 | }
74 | ```
75 |
76 | ### Example Usage
77 |
78 | ```javascript
79 | const promise1 = Promise.resolve(3);
80 | const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));
81 | const promise3 = fetch('https://api.example.com/data');
82 |
83 | Promise.all([promise1, promise2, promise3])
84 | .then((values) => {
85 | console.log(values); // [3, 'foo', Response object from fetch]
86 | })
87 | .catch((error) => {
88 | console.error('One of the promises failed:', error);
89 | });
90 | ```
91 |
92 | ### Use Cases
93 |
94 | * **Aggregating Results**: When you need to wait for multiple asynchronous operations to complete before proceeding, such as fetching data from multiple sources.
95 | * **Parallel Execution**: When operations do not depend on each other and can be executed in parallel for efficiency.
96 |
97 | ### Important Notes
98 |
99 | * If the iterable passed to `Promise.all` is empty, it returns a promise that resolves to an empty array.
100 | * The order of results in the resolved array corresponds to the order of the promises passed to `Promise.all`.
101 |
102 | By leveraging `Promise.all`, developers can handle multiple asynchronous operations efficiently, ensuring that subsequent code execution waits until all specified promises have been settled.
--------------------------------------------------------------------------------
/Questions-Answers/42.Hoisting-V.md:
--------------------------------------------------------------------------------
1 | # 42. Hoisting V
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/hoisting-v
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | (() => {
15 | if (!fn) {
16 | function fn() {
17 | console.log('2');
18 | }
19 | }
20 | fn();
21 | })();
22 |
23 | function fn() {
24 | console.log('1');
25 | }
26 |
27 | // another one
28 | function fn1() {
29 | console.log('3');
30 | }
31 |
32 | (() => {
33 | if (!fn1) {
34 | function fn1() {
35 | console.log('4');
36 | }
37 | }
38 | fn1();
39 | })();
40 |
41 | // another one !
42 | (() => {
43 | if (false) {
44 | function fn3() {
45 | console.log('5');
46 | }
47 | }
48 | fn3();
49 | })();
50 | ```
51 |
52 | #
53 |
54 | ### Answer
55 |
56 | ```js
57 | (() => {
58 | if (!fn) {
59 | function fn() {
60 | console.log('2');
61 | }
62 | }
63 | fn();
64 | })();
65 |
66 | function fn() {
67 | console.log('1');
68 | }
69 |
70 | // logs "2"
71 |
72 | /* ------------------------------- */
73 |
74 | // another one
75 | function fn1() {
76 | console.log('3');
77 | }
78 |
79 | (() => {
80 | if (!fn1) {
81 | function fn1() {
82 | console.log('4');
83 | }
84 | }
85 | fn1();
86 | })();
87 |
88 | // logs "4"
89 |
90 | /* ------------------------------- */
91 |
92 | // another one !
93 | (() => {
94 | if (false) {
95 | function fn3() {
96 | console.log('5');
97 | }
98 | }
99 | fn3();
100 | })();
101 |
102 | // logs Error
103 | ```
104 |
105 | ### Explanation
106 |
107 | ```js
108 | (() => {
109 | if (!fn) {
110 | function fn() {
111 | console.log('2');
112 | }
113 | }
114 | fn();
115 | })();
116 |
117 | function fn() {
118 | console.log('1');
119 | }
120 | ```
121 |
122 | The code snippet above logs `"2"`, because when a function is created conditionally, either the function name or the function declaration is hoisted to the top of the scope, no matter the `if` condition pass or not. Whether only the function name or the function declaration is hoisted to the top of the scope depends on the browser. In Chrome and Firefox, only the function name is hoisted, whereas in Safari, function declaration is hoisted:
123 |
124 | ```js
125 | var hoisted = 'foo' in this;
126 | console.log(
127 | `'foo' name ${hoisted ? 'is' : 'is not'} hoisted. typeof foo is ${typeof foo}`
128 | );
129 | if (false) {
130 | function foo() {
131 | return 1;
132 | }
133 | }
134 |
135 | // In Chrome and Firefox:
136 | // 'foo' name is hoisted. typeof foo is undefined
137 | //
138 | // In Edge:
139 | // 'foo' name is not hoisted. typeof foo is undefined
140 | //
141 | // In Safari:
142 | // 'foo' name is hoisted. typeof foo is function
143 |
144 | /* ------------------------------------------------------ */
145 |
146 | var hoisted = 'foo' in this;
147 | console.log(
148 | `'foo' name ${hoisted ? 'is' : 'is not'} hoisted. typeof foo is ${typeof foo}`
149 | );
150 | if (true) {
151 | function foo() {
152 | return 1;
153 | }
154 | }
155 |
156 | // In Chrome and Firefox:
157 | // 'foo' name is hoisted. typeof foo is undefined
158 | //
159 | // In Edge:
160 | // 'foo' name is not hoisted. typeof foo is undefined
161 | //
162 | // In Safari:
163 | // 'foo' name is hoisted. typeof foo is function
164 | ```
165 |
166 | The same logic applies to the rest of the cases.
167 |
168 | In addition, if you declare a `var` variable in a `if` statement, the variable declaration is also hoisted, no matter the `if` condition pass or not:
169 |
170 | ```js
171 | console.log(a); // undefined , not respond ReferenceError ,it has been hoisted
172 | if (true) {
173 | var a = 1;
174 | }
175 | console.log(b); // same as above
176 | if (false) {
177 | var b = 1;
178 | }
179 | ```
180 |
181 | #
182 |
183 | ### Reference
184 |
185 | - [Conditionally created functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#conditionally_created_functions)
186 | - [If statement and variable hoisting](https://stackoverflow.com/questions/34149693/if-statement-and-variable-hoisting)
187 |
--------------------------------------------------------------------------------
/Questions-Answers/32.Hoisting-IIII.md:
--------------------------------------------------------------------------------
1 | # 32. Hoisting IIII
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/Hoisting-IIII
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | var a = 1;
15 | function a() {}
16 |
17 | console.log(typeof a);
18 |
19 | var b;
20 | function b() {}
21 | b = 1;
22 |
23 | console.log(typeof b);
24 |
25 | function c() {}
26 | var c = 1;
27 |
28 | console.log(typeof c);
29 |
30 | var d = 1;
31 |
32 | (function () {
33 | d = '2';
34 | console.log(typeof d);
35 | function d() {}
36 | })();
37 |
38 | console.log(typeof d);
39 |
40 | var e = 1;
41 | const f = function e() {};
42 |
43 | console.log(typeof e);
44 | ```
45 |
46 | #
47 |
48 | ### Answer
49 |
50 | ```js
51 | var a = 1;
52 | function a() {}
53 |
54 | console.log(typeof a); // "number"
55 |
56 | var b;
57 | function b() {}
58 | b = 1;
59 |
60 | console.log(typeof b); // "number"
61 |
62 | function c() {}
63 | var c = 1;
64 |
65 | console.log(typeof c); // "number"
66 |
67 | var d = 1;
68 |
69 | (function () {
70 | d = '2';
71 | console.log(typeof d); // "string"
72 | function d() {}
73 | })();
74 |
75 | console.log(typeof d); // "number"
76 |
77 | var e = 1;
78 | const f = function e() {};
79 |
80 | console.log(typeof e); // "number"
81 | ```
82 |
83 | ### Explanation
84 |
85 | ```js
86 | var a = 1;
87 | function a() {}
88 |
89 | console.log(typeof a); // 'number'
90 | ```
91 |
92 | Before code execution, `var` variable and function declarations are hoisted to the top of the scope in which they are declared. JavaScript first moves function declarations, then `var` variable declarations. The order of declarations doesn't matter. So that means function declarations will be overwritten by variable declarations. From the code snippet above, first the "name" `a` is declared, then the function declaration is automatically assigned to it, then `a` is initialized to `1` with `a = 1` and overwrites `a`.
93 |
94 | 🙋♀️🙋♂️ If we replace `var` with `let`, it will ends up in an error:
95 |
96 | ```js
97 | let a = 1;
98 | function a() {}
99 |
100 | console.log(typeof a);
101 | // Uncaught SyntaxError: Identifier 'a' has already been declared.
102 | ```
103 |
104 | ---
105 |
106 | ```js
107 | var b;
108 | function b() {}
109 | b = 1;
110 |
111 | console.log(typeof b); // 'number'
112 | ```
113 |
114 | The reason for this is the same as above.
115 |
116 | ---
117 |
118 | ```js
119 | var d = 1;
120 |
121 | (function () {
122 | d = '2';
123 | console.log(typeof d); // 'string'
124 | function d() {}
125 | })();
126 |
127 | console.log(typeof d); // 'number'
128 | ```
129 |
130 | `d = '2'` in the IIFE doesn't overwrite the outside `d` but `function d() {}`, which is moved to the top of the IIFE's scope before code execution.
131 |
132 | ```js
133 | var d = 1;
134 |
135 | (function () {
136 | d = '2';
137 | console.log(typeof d); // 'string'
138 | function d() {}
139 | })();
140 |
141 | console.log(d); // 1
142 | ```
143 |
144 | If we delete `function d() {}`, then `d = '2'` will assign the outside `d` to `'2'`:
145 |
146 | ```js
147 | var d = 1;
148 |
149 | (function () {
150 | d = '2';
151 | console.log(typeof d); // 'string'
152 | })();
153 |
154 | console.log(d); // '2'
155 | ```
156 |
157 | 🙋♀️🙋♂️ A variable declared without `var` keyword becomes global:
158 |
159 | ```js
160 | (function () {
161 | d = '2';
162 | })();
163 |
164 | console.log(window.d); // '2'
165 | ```
166 |
167 | ---
168 |
169 | ```js
170 | var e = 1;
171 | const f = function e() {};
172 |
173 | console.log(typeof e); // 'number'
174 | ```
175 |
176 | Function expressions and variables declared with `const` are not hoisted. The variable the function expression is assigned to will have a `name` property:
177 |
178 | ```js
179 | var e = 1;
180 | const f = function e() {};
181 |
182 | console.log(f.name); // 'e'
183 | ```
184 |
185 | #
186 |
187 | ### Reference
188 |
189 | [Javascript -What happens if a variable and a function have the same name? [duplicate]](https://stackoverflow.com/questions/45769024/javascript-what-happens-if-a-variable-and-a-function-have-the-same-name)
190 |
--------------------------------------------------------------------------------
/13.Implement-a-Queue-by-using-Stack.md:
--------------------------------------------------------------------------------
1 | # 13. Implement a Queue by using Stack
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/implement-a-queue-by-using-stack
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | In JavaScript, we could use array to work as both a Stack or a queue.
12 |
13 | ```js
14 | const arr = [1, 2, 3, 4];
15 |
16 | arr.push(5); // now array is [1, 2, 3, 4, 5]
17 | arr.pop(); // 5, now the array is [1, 2, 3, 4]
18 | ```
19 |
20 | Above code is a Stack, while below is a Queue
21 |
22 | ```js
23 | const arr = [1, 2, 3, 4];
24 |
25 | arr.push(5); // now the array is [1, 2, 3, 4, 5]
26 | arr.shift(); // 1, now the array is [2, 3, 4, 5]
27 | ```
28 |
29 | now suppose you have a stack, which has only follow interface:
30 |
31 | ```js
32 | class Stack {
33 | push(element) {
34 | /* add element to stack */
35 | }
36 | peek() {
37 | /* get the top element */
38 | }
39 | pop() {
40 | /* remove the top element */
41 | }
42 | size() {
43 | /* count of elements */
44 | }
45 | }
46 | ```
47 |
48 | Could you implement a Queue by using only above Stack? A Queue must have following interface
49 |
50 | ```js
51 | class Queue {
52 | enqueue(element) {
53 | /* add element to queue, similar to Array.prototype.push */
54 | }
55 | peek() {
56 | /* get the head element */
57 | }
58 | dequeue() {
59 | /* remove the head element, similar to Array.prototype.pop */
60 | }
61 | size() {
62 | /* count of elements */
63 | }
64 | }
65 | ```
66 |
67 | **notes**
68 |
69 | you can only use Stack as provided, Array should be avoided for the purpose of practicing.
70 |
71 | #
72 |
73 | ### Solution 1 (O(1) enqueue, O(n) dequeue and peek)
74 |
75 | ```js
76 | /* you can use this Class which is bundled together with your code
77 |
78 | class Stack {
79 | push(element) { // add element to stack }
80 | peek() { // get the top element }
81 | pop() { // remove the top element}
82 | size() { // count of element }
83 | }
84 | */
85 |
86 | /* Array is disabled in your code */
87 |
88 | // you need to complete the following Class
89 | class Queue {
90 | constructor() {
91 | this.content = new Stack();
92 | }
93 |
94 | enqueue(element) {
95 | this.content.push(element);
96 | }
97 | peek() {
98 | const reversedQueue = this._reverse(this.content);
99 | return reversedQueue.peek();
100 | }
101 | size() {
102 | return this.content.size();
103 | }
104 | dequeue() {
105 | const reversedQueue = this._reverse(this.content);
106 | const poppedItem = reversedQueue.pop();
107 | this.content = this._reverse(reversedQueue);
108 | return poppedItem;
109 | }
110 | _reverse(queue) {
111 | const reversedQueue = new Stack();
112 | while (queue.size() > 0) {
113 | const lastItem = queue.pop();
114 | reversedQueue.push(lastItem);
115 | }
116 |
117 | return reversedQueue;
118 | }
119 | }
120 | ```
121 |
122 | #
123 |
124 | ### Solution 2 (O(n) enqueue, O(1) dequeue and peek)
125 |
126 | ```js
127 | /* you can use this Class which is bundled together with your code
128 |
129 | class Stack {
130 | push(element) { // add element to stack }
131 | peek() { // get the top element }
132 | pop() { // remove the top element}
133 | size() { // count of element }
134 | }
135 | */
136 |
137 | /* Array is disabled in your code */
138 |
139 | // you need to complete the following Class
140 | class Queue {
141 | constructor() {
142 | this.content = new Stack();
143 | this.reversedContent = new Stack();
144 | }
145 |
146 | enqueue(element) {
147 | while (this.content.size() > 0) {
148 | const poppedItem = this.content.pop();
149 | this.reversedContent.push(poppedItem);
150 | }
151 |
152 | this.reversedContent.push(element);
153 |
154 | while (this.reversedContent.size() > 0) {
155 | const poppedItem = this.reversedContent.pop();
156 | this.content.push(poppedItem);
157 | }
158 | }
159 | peek() {
160 | return this.content.peek();
161 | }
162 | size() {
163 | return this.content.size();
164 | }
165 | dequeue() {
166 | return this.content.pop();
167 | }
168 | }
169 | ```
170 |
--------------------------------------------------------------------------------
/Questions-Answers/71.two-way-generator.md:
--------------------------------------------------------------------------------
1 | # 71. two-way generator
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/quiz/generator-2-way
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | What does the code snippet below output by `console.log`?
12 |
13 | ```js
14 | function* gen() {
15 | yield 2 * (yield 100);
16 | }
17 |
18 | const generator = gen();
19 | console.log(generator.next().value);
20 | console.log(generator.next(1).value);
21 | console.log(generator.next(1).value);
22 | ```
23 |
24 | #
25 |
26 | ### Answer
27 |
28 | ```js
29 | function* gen() {
30 | yield 2 * (yield 100);
31 | }
32 |
33 | const generator = gen();
34 | console.log(generator.next().value); // 100
35 | console.log(generator.next(1).value); // 2
36 | console.log(generator.next(1).value); // undefined
37 | ```
38 |
39 | ### Explanation
40 |
41 | On the first iteration (`generator.next().value`), the inner yield `yield 100` is resolved, so the console logs `100`.
42 |
43 | ---
44 |
45 | On the second iteration (`generator.next(1).value`), `generator.next(1)` passes `1` to the outer yield, so it is `yield 2 * 1` and logs `2`. When providing an argument to `.next()`, the argument becomes the result of `yield`. For example, in `variable = yield expression`, the value passed to `.next()` will be assigned to the `variable`. However, the value passed to `.next()` will be only assigned when resuming execution from a previous `yield`, the argument passed to the first call to `.next()` will be ignored. In other words, what you're passing in to `.next()` is being returned from the previous `yield`.
46 |
47 | Consider the following example:
48 |
49 | ```js
50 | function* gen() {
51 | const result = yield '2 + 2 = ?';
52 |
53 | console.log(result);
54 | }
55 |
56 | const generator = gen();
57 |
58 | const question = generator.next().value;
59 | console.log(question); // 2 + 2 = ?
60 |
61 | generator.next(4); // 4
62 | ```
63 |
64 | ```js
65 | function* gen() {
66 | const result = yield '2 + 2 = ?';
67 |
68 | console.log(result);
69 | }
70 |
71 | const generator = gen();
72 |
73 | const question = generator.next(1).value; // Argument `1` is ignored.
74 | console.log(question); // 2 + 2 = ?
75 |
76 | generator.next(4); // 4
77 | ```
78 |
79 | If we don't provide any argument to the second `.next()`, it will logs `undefined`:
80 |
81 | ```js
82 | function* gen() {
83 | const result = yield '2 + 2 = ?';
84 |
85 | // yield result;
86 | console.log(result);
87 | }
88 |
89 | const generator = gen();
90 |
91 | const question = generator.next().value;
92 | console.log(question); // 2 + 2 = ?
93 |
94 | // console.log(generator.next().value); // undefined
95 | generator.next(); // undefined
96 | ```
97 |
98 | So, we can rewrite the code snippet from the problem as following:
99 |
100 | ```js
101 | function* gen() {
102 | const returnedFromYield = yield 100;
103 | yield 2 * returnedFromYield;
104 | }
105 |
106 | const generator = gen();
107 | console.log(generator.next().value); // 100
108 | console.log(generator.next(1).value); // 2
109 | ```
110 |
111 | ---
112 |
113 | The third `generator.next(1).value` returns `undefined`, because the end of the generator is reached.
114 |
115 | ```js
116 | function* gen() {
117 | yield 2 * (yield 100);
118 | }
119 |
120 | const generator = gen();
121 | console.log(generator.next().value); // 100
122 | console.log(generator.next(1).value); // 2
123 | console.log(generator.next(1)); // { value: undefined, done: true }
124 | ```
125 |
126 | If we have a `return` statement at the end of the generator function `gen()`, then the third `.next()` will return whatever the `return` statement returns:
127 |
128 | ```js
129 | function* gen() {
130 | yield 2 * (yield 100);
131 | return 'Final result';
132 | }
133 |
134 | const generator = gen();
135 | console.log(generator.next().value); // 100
136 | console.log(generator.next(1).value); // 2
137 | console.log(generator.next(1)); // { value: 'Final result', done: true }
138 | ```
139 |
140 | If we assign `yield 2 * (yield 100)` to a variable and pass an argument to the third `.next()`, then it will return the argument:
141 |
142 | ```js
143 | function* gen() {
144 | const returnedFromYield = yield 2 * (yield 100);
145 | return `Final result: ${returnedFromYield}`;
146 | }
147 |
148 | const generator = gen();
149 | console.log(generator.next().value); // 100
150 | console.log(generator.next(1).value); // 2
151 | console.log(generator.next(4).value); // Final result: 4
152 | ```
153 |
154 | #
155 |
156 | ### Reference
157 |
158 | - [“yield” is a two-way street](https://javascript.info/generators#yield-is-a-two-way-street)
159 | - [How does Generator.next() processes its parameter?](https://stackoverflow.com/questions/37354461/how-does-generator-next-processes-its-parameter)
160 |
--------------------------------------------------------------------------------
/34.implement-Promise.any.md:
--------------------------------------------------------------------------------
1 | # 34. implement `Promise.any()`
2 | Promise.any is a method in JavaScript used to handle multiple promises concurrently, but with a different behavior compared to Promise.all. It returns a single promise that resolves as soon as any of the promises in the iterable fulfills, or rejects if all the promises reject.
3 |
4 | ### Key Features of `Promise.any`
5 |
6 | 1. **Concurrent Execution**:
7 |
8 | * `Promise.any` runs multiple promises in parallel.
9 | * It resolves as soon as one of the promises fulfills.
10 | 2. **Resolved Value**:
11 |
12 | * When the first promise fulfills, `Promise.any` resolves with the value of that promise.
13 | * Example: `Promise.any([promise1, promise2]).then((value) => { console.log(value); });`
14 | * If `promise1` fulfills with `value1`, the output will be `value1`.
15 | 3. **Rejection Handling**:
16 |
17 | * If all promises in the iterable reject, `Promise.any` rejects with an `AggregateError`, which is a new error type that groups multiple individual errors.
18 | * Example: `Promise.any([promise1, promise2]).catch((error) => { console.log(error); });`
19 | * If both `promise1` and `promise2` reject, `Promise.any` will reject with an `AggregateError` containing all the rejection reasons.
20 |
21 |
22 | ### Problem
23 |
24 | https://bigfrontend.dev/problem/implement-Promise-any
25 |
26 | #
27 |
28 | ### Problem Description
29 |
30 | > Promise.any() takes an iterable of Promise objects and, as soon as one of the promises in the iterable fulfils, returns a single promise that resolves with the value from that promise
31 |
32 | from [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any)
33 |
34 | Can you implement a `any()` to work the same as `Promise.any()`?
35 |
36 | **note**
37 |
38 | `AggregateError` is not supported in Chrome yet, but you can still use it in your code since we will add the Class into your code. Do something like following:
39 |
40 | ```js
41 | new AggregateError('No Promise in Promise.any was resolved', errors);
42 | ```
43 |
44 | #
45 |
46 | ### Solution
47 |
48 | ```js
49 | /**
50 | * @param {Array} promises
51 | * @return {Promise}
52 | */
53 | function any(promises) {
54 | if (promises.length === 0) {
55 | return Promise.resolve();
56 | }
57 |
58 | let isFulfilled = false;
59 | const errors = Array(promises.length);
60 | let numOfErrors = 0;
61 |
62 | return new Promise((resolve, reject) => {
63 | promises.forEach((promise, i) => {
64 | if (!(promise instanceof Promise)) {
65 | promise = Promise.resolve(promise);
66 | }
67 |
68 | promise.then(
69 | (value) => {
70 | if (isFulfilled) {
71 | return;
72 | }
73 |
74 | resolve(value);
75 | isFulfilled = true;
76 | },
77 | (reason) => {
78 | errors[i] = reason;
79 | numOfErrors++;
80 |
81 | if (numOfErrors === promises.length) {
82 | reject(
83 | new AggregateError(
84 | 'No Promise in Promise.any was resolved',
85 | errors
86 | )
87 | );
88 | }
89 | }
90 | );
91 | });
92 | });
93 | }
94 | ```
95 |
96 | ### Example Usage
97 |
98 | ```javascript
99 | const promise1 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Error1'));
100 | const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'Success!'));
101 | const promise3 = new Promise((resolve, reject) => setTimeout(reject, 300, 'Error3'));
102 |
103 | Promise.any([promise1, promise2, promise3])
104 | .then((value) => {
105 | console.log(value); // 'Success!'
106 | })
107 | .catch((error) => {
108 | console.error('All promises were rejected:', error);
109 | });
110 | ```
111 |
112 | ### Use Cases
113 |
114 | * **Race to Success**: When you want to proceed as soon as any one of several asynchronous operations succeeds, such as loading the fastest resource among several mirrors.
115 | * **Fault Tolerance**: When you want to use the first available data and ignore the rest, improving fault tolerance in case some promises fail.
116 |
117 | ### Important Notes
118 |
119 | * If the iterable passed to `Promise.any` is empty, it returns a promise that will be rejected with an `AggregateError`.
120 | * If all promises reject, the rejection reason is an `AggregateError` containing an array of all rejection reasons.
121 |
122 | By using `Promise.any`, developers can efficiently handle scenarios where the first successful completion of any among several asynchronous tasks is sufficient, thereby enhancing performance and reliability in cases where multiple potential sources or paths are available.
--------------------------------------------------------------------------------
/33.implement-Promise.allSettled.md:
--------------------------------------------------------------------------------
1 | # 33. implement `Promise.allSettled()`
2 | `Promise.allSettled` is a method in JavaScript that takes an iterable (such as an array) of promises and returns a single promise. This returned promise resolves when all the promises in the iterable have settled, meaning each promise has either resolved or rejected. It does not reject if any of the promises reject; instead, it provides the outcome of each promise.
3 |
4 | ### Key Features of `Promise.allSettled`
5 |
6 | 1. **Settling of All Promises**:
7 |
8 | * `Promise.allSettled` waits for all promises in the iterable to settle (either resolved or rejected).
9 | * It returns a promise that resolves with an array of objects describing the outcome of each promise.
10 | 2. **Resolved Value**:
11 |
12 | * The array contains objects with two properties: `status` and either `value` (if resolved) or `reason` (if rejected).
13 | * Example:
14 |
15 | ```javascript
16 | [
17 | { status: "fulfilled", value: result1 },
18 | { status: "rejected", reason: error }
19 | ]
20 | ```
21 |
22 | 3. **No Immediate Rejection**:
23 |
24 | * Unlike `Promise.all`, it does not reject immediately if any promise rejects. Instead, it waits for all promises to settle and provides their outcomes.
25 |
26 |
27 | ### Problem
28 |
29 | https://bigfrontend.dev/problem/implement-Promise-allSettled
30 |
31 | #
32 |
33 | ### Problem Description
34 |
35 | > The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
36 |
37 | from [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)
38 |
39 | Different from `Promise.all()` which rejects right away once an error occurs, `Promise.allSettled()` waits for all promises to settle.
40 |
41 | Now can you implement your own `allSettled()`?
42 |
43 | #
44 |
45 | ### Solution
46 |
47 | ```js
48 | /**
49 | * @param {Array} promises - notice that input might contains non-promises
50 | * @return {Promise>}
51 | */
52 | function allSettled(promises) {
53 | if (promises.length === 0) {
54 | return Promise.resolve([]);
55 | }
56 |
57 | const results = Array(promises.length);
58 | let numOfSettledPromise = 0;
59 |
60 | return new Promise((resolve, reject) => {
61 | promises.forEach((promise, i) => {
62 | if (!(promise instanceof Promise)) {
63 | promise = Promise.resolve(promise);
64 | }
65 |
66 | promise.then(
67 | (value) => {
68 | results[i] = {
69 | status: 'fulfilled',
70 | value,
71 | };
72 |
73 | numOfSettledPromise++;
74 | if (numOfSettledPromise === promises.length) {
75 | resolve(results);
76 | }
77 | },
78 | (reason) => {
79 | results[i] = {
80 | status: 'rejected',
81 | reason,
82 | };
83 |
84 | numOfSettledPromise++;
85 | if (numOfSettledPromise === promises.length) {
86 | resolve(results);
87 | }
88 | }
89 | );
90 | });
91 | });
92 | }
93 | ```
94 |
95 |
96 | ### Example Usage
97 |
98 | ```javascript
99 | const promise1 = new Promise((resolve, reject) => {
100 | setTimeout(() => {
101 | resolve("Promise 1 resolved");
102 | }, 1000);
103 | });
104 |
105 | const promise2 = new Promise((resolve, reject) => {
106 | setTimeout(() => {
107 | reject("Promise 2 rejected");
108 | }, 2000);
109 | });
110 |
111 | const promise3 = new Promise((resolve, reject) => {
112 | setTimeout(() => {
113 | resolve("Promise 3 resolved");
114 | }, 3000);
115 | });
116 |
117 | PromiseAllSettled([promise1, promise2, promise3]).then((results) => {
118 | console.log(results);
119 | })
120 |
121 | // output
122 | [
123 | { status: 'fulfilled', value: 'Promise 1 resolved' },
124 | { status: 'rejected', reason: 'Promise 2 rejected' },
125 | { status: 'fulfilled', value: 'Promise 3 resolved' }
126 | ]
127 | ```
128 |
129 | ### Use Cases
130 |
131 | 1. **Handling Multiple Outcomes**:
132 |
133 | * When you need to perform multiple asynchronous operations and handle each result individually, regardless of whether they succeed or fail.
134 | 2. **Ensuring All Promises Settle**:
135 |
136 | * When you want to ensure that all promises have settled before proceeding with further logic, useful in scenarios like cleaning up resources or aggregating results.
137 |
138 | By using `Promise.allSettled`, developers can manage multiple asynchronous operations and handle each outcome in a comprehensive manner, ensuring that all results are processed, whether the promises are fulfilled or rejected.
--------------------------------------------------------------------------------
/1.implement-curry.md:
--------------------------------------------------------------------------------
1 | # 1. implement curry()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/implement-curry
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Currying is a useful technique used in JavaScript applications.
12 |
13 | Please implement a curry() function, which accepts a function and return a curried one.
14 |
15 | Here is an example
16 |
17 | ```js
18 | # Example 1
19 |
20 | const join = (a, b, c) => {
21 | return `${a}_${b}_${c}`;
22 | };
23 |
24 | const curriedJoin = curry(join);
25 |
26 | curriedJoin(1, 2, 3); // '1_2_3'
27 |
28 | curriedJoin(1)(2, 3); // '1_2_3'
29 |
30 | curriedJoin(1, 2)(3); // '1_2_3'
31 |
32 |
33 |
34 | # Example 2
35 |
36 | function sum(a, b, c) {
37 | return a + b + c;
38 | }
39 |
40 | let curriedSum = curry(sum);
41 |
42 | console.log(curriedSum(1)(2)(3)); // Outputs: 6
43 | console.log(curriedSum(1, 2)(3)); // Outputs: 6
44 | console.log(curriedSum(1, 2, 3)); // Outputs: 6
45 |
46 | ```
47 |
48 | more to read
49 |
50 | https://javascript.info/currying-partials
51 | https://lodash.com/docs/4.17.15#curry
52 |
53 | #
54 |
55 | ### Solution
56 |
57 | ```js
58 | /**
59 | * @param { Function } func
60 | */
61 | function curry(func) {
62 | // Return a wrapper function to make it curry-able.
63 | return function curried(...args) {
64 | // If passed arguments count is greater than or equal to original function 'func'
65 | // parameters count, directly call 'func' with passed arguments.
66 | if (args.length >= func.length) {
67 | return func.apply(this, args);
68 | } else {
69 | // Otherwise return another wrapper function to gather new argument
70 | // and pass it to `curried` function. This will continue until
71 | // arguments count >= parameters count.
72 | return function (...args2) {
73 | return curried.apply(this, args.concat(args2));
74 | };
75 | }
76 | };
77 | }
78 | ```
79 |
80 | #
81 |
82 | ### Explanation
83 |
84 | the function curry in the provided JavaScript code is a higher-order function that transforms a given function into a curried function.
85 |
86 | A curried function is a function that takes multiple arguments one at a time. Given a function with 3 parameters, the curried version will take one argument and return a function that takes the next argument, which returns a function that takes the third argument. The last function returns the result of applying the function to all of its arguments.
87 |
88 | Here's a step-by-step explanation of the code:
89 |
90 | 1. The curry function takes a function func as an argument.
91 | 2. It returns a new function curried that can be called with some arguments (...args).
92 | 3. When curried is invoked, it checks if the number of arguments provided is enough to invoke the original function func. This is done by comparing args.length with func.length, where func.length is the number of expected parameters of func.
93 | 4. If enough arguments are provided, it calls func with the provided arguments using func.apply(this, args).
94 | 5. If not enough arguments are provided, it returns a new function that expects the remaining arguments. This function, when called, will call curried with the old and new argument.
95 |
96 | Here's an example:
97 |
98 | ```js
99 | function sum(a, b, c) {
100 | return a + b + c;
101 | }
102 |
103 | let curriedSum = curry(sum);
104 |
105 | console.log(curriedSum(1)(2)(3)); // Outputs: 6
106 | ```
107 | In this example, curriedSum is a curried version of sum. When we call curriedSum(1)(2)(3), it's equivalent to calling sum(1, 2, 3). Each call to curriedSum returns a new function expecting the next argument until all arguments are provided.
108 |
109 |
110 | let's break down how the curry function works with sum under the hood:
111 |
112 | ```js
113 | let curriedSum = curry(sum);
114 | ```
115 |
116 | Here, curry is called with sum as an argument. curry returns a new function curried that can be called with some arguments. This returned function is stored in curriedSum.
117 |
118 | ```js
119 | console.log(curriedSum(1)(2)(3))
120 | ```
121 |
122 | Here's what happens step by step:
123 |
124 | - curriedSum(1) is called. Inside curried, args is an array with one element [1]. Since args.length (1) is less than func.length (3, because sum takes 3 arguments), curried returns a new function that takes the remaining arguments.
125 |
126 | - This returned function is immediately called with (2). Now args is [1, 2]. Again, args.length (2) is less than func.length (3), so curried returns another function.
127 |
128 | - This returned function is immediately called with (3). Now args is [1, 2, 3]. This time, args.length (3) is equal to func.length (3), so curried calls sum with these arguments using func.apply(this, args), which is equivalent to sum(1, 2, 3). This returns 6, which is then logged to the console.
129 |
130 | The other calls
131 | ```js
132 | console.log(curriedSum(1, 2)(3))
133 | console.log(curriedSum(1, 2, 3))
134 | ```
135 | work in a similar way. The difference is that more arguments are provided at once, so the curried function returns a function that takes the remaining arguments fewer times before it has enough arguments to call sum.
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/19.find-corresponding-node-in-two-identical-DOM-tree.md:
--------------------------------------------------------------------------------
1 | # 19. find corresponding node in two identical DOM tree
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/find-corresponding-node-in-two-identical-DOM-tree
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Given two same DOM tree **A**, **B**, and an Element **a** in **A**, find the corresponding Element **b** in **B**.
12 |
13 | By **corresponding**, we mean **a** and **b** have the same relative position to their DOM tree root.
14 |
15 | _follow up_
16 |
17 | This could a problem on general Tree structure with only `children`.
18 |
19 | Could you solve it recursively and iteratively?
20 |
21 | Could you solve this problem both with special DOM api for better performance?
22 |
23 | What are the time cost for each solution?
24 |
25 | #
26 |
27 | ### Recursive Solution
28 |
29 | ```js
30 | /**
31 | * @param {HTMLElement} rootA
32 | * @param {HTMLElement} rootB - rootA and rootB are clone of each other
33 | * @param {HTMLElement} nodeA
34 | */
35 | const findCorrespondingNode = (rootA, rootB, targetNode) => {
36 | if (rootA === targetNode) {
37 | return rootB;
38 | }
39 |
40 | for (let i = 0; i < rootA.childNodes.length; i++) {
41 | const result = findCorrespondingNode(
42 | rootA.childNodes[i],
43 | rootB.childNodes[i],
44 | targetNode
45 | );
46 | if (result) {
47 | return result;
48 | }
49 | }
50 | return null;
51 | };
52 | ```
53 |
54 | #
55 |
56 | ### Iterative Solution
57 |
58 | ```js
59 | /**
60 | * @param {HTMLElement} rootA
61 | * @param {HTMLElement} rootB - rootA and rootB are clone of each other
62 | * @param {HTMLElement} nodeA
63 | */
64 | const findCorrespondingNode = (rootA, rootB, target) => {
65 | let stackA = [rootA];
66 | let stackB = [rootB];
67 |
68 | while (stackA.length > 0) {
69 | const nodeA = stackA.pop();
70 | const nodeB = stackB.pop();
71 |
72 | if (nodeA === target) {
73 | return nodeB;
74 | }
75 |
76 | stackA.push(...nodeA.childNodes);
77 | stackB.push(...nodeB.childNodes);
78 | }
79 |
80 | return;
81 | };
82 | ```
83 |
84 | #
85 |
86 | ### Solution with DOM API `parentNode`
87 |
88 | ```js
89 | /**
90 | * @param {HTMLElement} rootA
91 | * @param {HTMLElement} rootB - rootA and rootB are clone of each other
92 | * @param {HTMLElement} nodeA
93 | */
94 | const findCorrespondingNode = (rootA, rootB, target) => {
95 | const path = [];
96 | let node = target;
97 | while (node !== rootA) {
98 | const parentNode = node.parentNode;
99 | const childNodes = Array.from(parentNode.childNodes);
100 | path.push(childNodes.indexOf(node));
101 | node = parentNode;
102 | }
103 |
104 | return path.reduceRight((node, index) => node.childNodes[index], rootB);
105 | };
106 | ```
107 | #
108 |
109 | ### Usage
110 |
111 | ```js
112 | const rootA = {
113 | value: "A",
114 | childNodes: [
115 | {
116 | value: "B",
117 | childNodes: [
118 | {
119 | value: "C",
120 | childNodes: [],
121 | },
122 | ],
123 | },
124 | ],
125 | };
126 |
127 | const rootB = {
128 | value: "A",
129 | childNodes: [
130 | {
131 | value: "B",
132 | childNodes: [
133 | {
134 | value: "C",
135 | childNodes: [],
136 | },
137 | ],
138 | },
139 | ],
140 | };
141 |
142 | const targetNode = rootA.childNodes[0].childNodes[0];
143 |
144 | console.log(findCorrespondingNode(rootA, rootB, targetNode));
145 | ```
146 |
147 |
148 | #
149 |
150 | ### Explanation
151 |
152 | Let's break down the `findCorrespondingNode` function and its usage:
153 |
154 | 1. **Function Definition**: The `findCorrespondingNode` function takes three arguments: `nodeA`, `nodeB`, and `targetNode`. It's designed to find a node in `nodeB` that corresponds to `targetNode` in `nodeA`, assuming `nodeA` and `nodeB` have the same structure.
155 |
156 | 2. **Base Cases**: If `nodeA` is the `targetNode`, it returns `nodeB`. If `nodeA` has no child nodes, it returns `null` because there's no corresponding node in `nodeB`.
157 |
158 | 3. **Recursion**: If `nodeA` is not the `targetNode` and it has child nodes, it recursively calls `findCorrespondingNode` for each pair of child nodes in `nodeA` and `nodeB`. If a recursive call returns a non-null result, it returns that result.
159 |
160 | 4. **Usage**: The usage example creates two identical tree structures `nodeA` and `nodeB`, each with a root node and a single child node that also has a single child node. It then calls `findCorrespondingNode` with `nodeA`, `nodeB`, and the first child node of `nodeA` as the `targetNode`.
161 |
162 | 5. **Output**: The output of the function call is the first child node of `nodeB`, which corresponds to the `targetNode` in `nodeA`. This is logged to the console.
163 |
164 | This `findCorrespondingNode` function can be used to find corresponding nodes in two tree structures that have the same structure. It uses a depth-first search strategy, checking the current node before checking the child nodes.
165 |
166 | #
167 |
168 | ### Reference
169 |
170 | [Problem Discuss](https://bigfrontend.dev/problem/find-corresponding-node-in-two-identical-DOM-tree/discuss)
171 |
--------------------------------------------------------------------------------
/implement-String.prototype.slice.md:
--------------------------------------------------------------------------------
1 | # Implement String.prototype.slice
2 |
3 | ### Problem Description
4 |
5 | Implement your own version of the `String.prototype.slice` method in JavaScript.
6 |
7 | The `slice()` method extracts a section of a string and returns it as a new string, without modifying the original string.
8 |
9 | Your implementation should handle the following:
10 |
11 | - If `beginIndex` is greater than or equal to the length of the string, return an empty string.
12 | - If `endIndex` is omitted, extract until the end of the string.
13 | - Negative indices count back from the end of the string.
14 | - If `endIndex` is greater than the length of the string, extract until the end of the string.
15 | - Ensure the method works correctly for both positive and negative indices, and for edge cases where indices are out of bounds.
16 |
17 | ### Syntax
18 |
19 | ```js
20 | str.slice(beginIndex[, endIndex])
21 | ```
22 |
23 | #
24 |
25 | ### Solution
26 | ```js
27 | /**
28 | * Custom implementation of String.prototype.slice
29 | *
30 | * @param {string} str - The string to slice.
31 | * @param {number} beginIndex - The index at which to begin extraction.
32 | * @param {number} [endIndex] - The index before which to end extraction.
33 | * @return {string} - The extracted section of the string.
34 | */
35 | function customSlice(str, beginIndex, endIndex) {
36 | // Ensure the beginIndex is within the bounds of the string length
37 | if (beginIndex < 0) {
38 | beginIndex = Math.max(str.length + beginIndex, 0);
39 | } else {
40 | beginIndex = Math.min(beginIndex, str.length);
41 | }
42 |
43 | // If endIndex is not provided, use the length of the string
44 | if (endIndex === undefined || endIndex > str.length) {
45 | endIndex = str.length;
46 | } else if (endIndex < 0) {
47 | endIndex = Math.max(str.length + endIndex, 0);
48 | }
49 |
50 | // Calculate the length of the substring
51 | const length = Math.max(endIndex - beginIndex, 0);
52 |
53 | // Build the sliced string
54 | let result = '';
55 | for (let i = 0; i < length; i++) {
56 | result += str[beginIndex + i];
57 | }
58 |
59 | return result;
60 | }
61 |
62 | // Usage
63 |
64 | console.log(customSlice("Hello, world!", 7, 12)); // Expected: "world"
65 | console.log(customSlice("Hello, world!", 7)); // Expected: "world!"
66 | console.log(customSlice("Hello, world!", -5, -1)); // Expected: "orld"
67 | console.log(customSlice("Hello, world!", -5)); // Expected: "orld!"
68 | console.log(customSlice("Hello, world!", 7, 7)); // Expected: ""
69 | console.log(customSlice("Hello, world!", 7, 6)); // Expected: ""
70 | console.log(customSlice("Hello, world!", 7, 100)); // Expected: "world!"
71 | console.log(customSlice("Hello, world!", 7, -100)); // Expected: ""
72 | console.log(customSlice("Hello, world!", -100, 7)); // Expected: "Hello, "
73 | console.log(customSlice("Hello, world!", 100, 7)); // Expected: ""
74 | console.log(customSlice("Hello, world!", -100, -100)); // Expected: ""
75 | console.log(customSlice("Hello, world!", 100, 100)); // Expected: ""
76 | console.log(customSlice("Hello, world!", -100, -100)); // Expected: ""
77 | console.log(customSlice("Hello, world!", 0, 0)); // Expected: ""
78 | console.log(customSlice("Hello, world!", 0, 1)); // Expected: "H"
79 | console.log(customSlice("Hello, world!", 0, 100)); // Expected: "Hello, world!"
80 | console.log(customSlice("Hello, world!", 0, -100)); // Expected: ""
81 | console.log(customSlice("Hello, world!", -100, 0)); // Expected: ""
82 | console.log(customSlice("Hello, world!", 0, -1)); // Expected: "Hello, world"
83 | console.log(customSlice("Hello, world!", -1, 0)); // Expected: ""
84 | console.log(customSlice("Hello, world!", -1, -1)); // Expected: ""
85 | console.log(customSlice("Hello, world!", -1, -100)); // Expected: ""
86 | console.log(customSlice("Hello, world!", -1, 100)); // Expected: "!"
87 | console.log(customSlice("Hello, world!", -1)); // Expected: "!"
88 | console.log(customSlice("Hello, world!", 0)); // Expected: "Hello, world!"
89 | console.log(customSlice("Hello, world!", 1)); // Expected: "ello, world!"
90 | ```
91 |
92 | #
93 |
94 | ### Explanation
95 |
96 | The provided code is a custom implementation of the `String.prototype.slice` method in JavaScript. This method is used to extract a section of a string and return it as a new string, without modifying the original string.
97 |
98 |
99 | 1. The function takes three parameters: `str` the string to slice, `beginIndex` the index at which to begin extraction, and `endIndex` the index before which to end extraction.
100 |
101 | 2. The function first checks if `beginIndex` is less than 0. If it is, it adjusts `beginIndex` to count from the end of the string. If `beginIndex` is greater than the length of the string, it is set to the length of the string.
102 |
103 | 3. Next, the function checks if `endIndex` is provided. If it's not, or if it's greater than the length of the string, `endIndex` is set to the length of the string. If `endIndex` is less than 0, it is adjusted to count from the end of the string.
104 |
105 | 4. The function then calculates the length of the substring to be extracted. If `endIndex` is less than `beginIndex`, the length is set to 0.
106 |
107 | 5. Finally, the function builds the sliced string by iterating over the characters in the specified range and appending them to the result string.
108 |
--------------------------------------------------------------------------------
/151.implement-Array-prototype-map.md:
--------------------------------------------------------------------------------
1 | # 151. implement Array.prototype.map()
2 |
3 | ### Problem
4 |
5 | https://bigfrontend.dev/problem/implement-Array-prototype-map
6 |
7 | #
8 |
9 | ### Problem Description
10 |
11 | Please implement your own [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
12 |
13 | ```js
14 | [1, 2, 3].myMap((num) => num * 2);
15 | // [2,4,6]
16 | ```
17 |
18 | #
19 |
20 | ### Understanding the problem
21 |
22 | Write a prototype method for `Array` that returns a new array populated with the result of calling a provided function on every element in the calling array.
23 |
24 | #
25 |
26 | ### Approach
27 |
28 | Create an empty array to store the result. Iterate through every items in the calling array. At each item, call the provided callback and pass the item, its index and the calling array as arguments to the callback; store the return value in the result array.
29 |
30 | 🙋♀️🙋♂️ In the initial attempt, I didn't handle the following cases:
31 |
32 | 1. Besides the callback function, our function can also take in a second argument, the optional `thisArg`, when it is provided, the callback function should be executed with the provided context.
33 | 2. The callback might alter the length of the calling array, resulting in an infinite loop.
34 | 3. The calling array is a sparse array. A sparse array is an array in which indices are not contiguous. For instance, `[1, 3]` is a sparse array and the indices are `0, 2`, index `1` is not in the array. Our function should ignore indices that are not present in the calling array.
35 |
36 | ### Solution
37 |
38 | ```js
39 | Array.prototype.myMap = function (callback, thisArg) {
40 | // Store the length of the original array to avoid potential infinity loop
41 | // when the length of the calling array is altered on the fly.
42 | const length = this.length;
43 | // Initialize the resulting array to be the same size as the calling array.
44 | const result = new Array(length);
45 |
46 | for (let i = 0; i < length; i++) {
47 | // Ensure index is in the array. myMap should ignore
48 | // indices that are not in the array.
49 | if (i in this) {
50 | // Execute the callback with proper context and store its return value in the
51 | // result array.
52 | result[i] = callback.call(thisArg || this, this[i], i, this);
53 | }
54 | }
55 |
56 | return result;
57 | };
58 |
59 | // Usage
60 | const arr = [1, 2, 3, 4, 5];
61 | const mappedArr = arr.myMap((el) => el * 2);
62 | console.log(mappedArr); // [2, 4, 6, 8, 10]
63 | ```
64 |
65 | #
66 |
67 | ### Explanation
68 |
69 | This JavaScript code is a custom implementation of the `Array.prototype.map()`function, which is a built-in method in JavaScript that creates a new array with the results of calling a provided function on every element in the calling array.
70 |
71 | Here's a breakdown of the code:
72 |
73 | 1. `Array.prototype.myMap = function (callback, thisArg) {...}` This line is extending the Array prototype with a new method called `myMap`. This method takes two arguments: a `callback` function and an optional `thisArg` object that will be used as `this` when executing the callback.
74 |
75 | 2. `const length = this.length;`: This line is storing the length of the array on which `myMap` was called. This is done to avoid potential infinite loops if the array's length is changed during the execution of `myMap`.
76 |
77 | 3. `const result = new Array(length);`: This line is initializing a new array with the same length as the original array. This will be the array that gets returned at the end.
78 |
79 | 4. The `for` loop: This loop iterates over each element in the original array.
80 |
81 | 5. `if (i in this) {...}`: This line checks if the current index exists in the original array. This is done because in JavaScript, arrays can have "holes" (i.e., indices that have never been assigned a value).
82 |
83 | 6. `result[i] = callback.call(thisArg || this, this[i], i, this);`: This line is calling the `callback` function with the current element, its index, and the original array as arguments. The result is stored in the corresponding index of the `result`array.
84 |
85 | - `result[i]`: This is the assignment statement that assigns the return value of the callback function to the `i`-th index of the `result` array. It ensures that the return value is stored at the correct position in the resulting array.
86 |
87 | - `callback`: This is the function that is passed as an argument to the `myMap` function. It is the function that will be executed on each element of the array. `(el) => el * 2`
88 |
89 | - `call()`: `call()` is a method available on JavaScript functions. It allows you to invoke a function and specify the value of `this` inside that function. In this case, `callback.call(...)` is used to execute the `callback` function.
90 |
91 | - `thisArg || this`: This is the value that will be used as the `this` value inside the `callback` function. The `thisArg` argument is optional, so if it is not provided, `this` (referring to the calling array) will be used as the `this` value.
92 |
93 | - `this[i]`: This is the current element of the array that is being processed by the `callback` function. It is passed as the first argument to the `callback` function.
94 |
95 | - `i`: This is the index of the current element in the array. It is passed as the second argument to the `callback` function.
96 |
97 | - `this`: This refers to the calling array itself. It is passed as the third ar gument to the `callback` function.`[ 1, 2, 3, 4, 5 ]`
98 |
99 | 7. `return result;`: This line returns the new array after the `for` loop has finished executing. `[ 2, 4, 6, 8, 10 ]`
100 |
101 |
--------------------------------------------------------------------------------