国外前端会考察哪些问题?js函数、系统设计、TS类型体操的用法,大大拓宽了我的眼界,去思考和实战更加相关的问题吧,输入输出考验了对js语法的掌握程度,值得多次学习。虽然有的实际使用中不会碰到,但至少可以养成遇到问题查文档的好习惯。
输入输出
该部分综合考察对js的熟悉程度
1. Promise order
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
实践循环宏任务微任务
1 | 1 |
详细解析
Part 1 - Sync Code
1 | console.log(1) // sync JS |
Until now the result output is Sequential & Sync:
1 | 1 |
Part 2 - Async & Sync
1 | // Async: Job Queue for promise |
For the 2nd part, it has both Sync & Async, then the priority and order becomes:
- 1st: Sync code
- 2nd: Async for Job Queue -> process promise
- 3rd: Async for Callback Queue -> process Web API call
Output for second part should be:
1 | 7 // sync code |
2. Promise executor
题目
What does the code snippet to the right output by console.log?
1 | new Promise((resolve, reject) => { |
答案
Promise链式调用返回的值和值的穿透
1 | 1 |
3. Promise then callbacks
题目
What does the code snippet to the right output by console.log?
1 | Promise.resolve(1) |
答案
Promise链式调用返回的值和值的穿透
1 | 6 |
详细解析
1 | > Promise.resolve(1) |
Is the same as
1 | > new Promise((resolve) => resolve(1)) |
Therefore,
1 | Promise.then(() => 2) |
But don’t forget that if you don’t use the new value you are chaining, it won’t be used, e.g.
1 | Promise.resolve(1) |
And you can’t have numbers in .then calls, they need to be functions, ts throws this error, therefore that goes onto the reject pile and we skip over it
1 | Promise.resolve(1) |
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the resolution/rejection of the promise returned by the handler. Also, the resolved value of the promise returned by then will be the same as the resolved value of the promise returned by the handler.
1 | Promise.resolve(1) // 1 |
4. Promise then callbacks II
题目
What does the code snippet to the right output by console.log?
1 | Promise.resolve(1) |
答案
Promise链式调用返回的值和值的穿透
1 | 1 |
详细解析
I was told that in other classical languages eg: C++, or java, the template order of these 3 keyword is impressive in my memory:
1 | try{} |
But let’s think in JavaScript .
1. finally() never receive an argument
docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
2. Normal return value in finally won’t make effect on promise object
3. throw Error in finally()
Note: A throw (or returning a rejected promise) in the finally callback will reject the new promise with the rejection reason specified when calling throw.
1 | Promise.reject(1) |
The return promise object rejected value will be affected with 2.
4. Order of then() & catch()
Remember then() & catch() can be called to handle the promise at any time and at any order. It will use the latest final state of the promise object, and affects the new value of the promise object.
1 | Promise.reject(1) |
5. Full solution of original problem
1 | Promise.resolve(1) |
Final Output:
1 | 1 |
5. block scope
题目
What does the code snippet to the right output by console.log?
1 | for (var i = 0; i < 5; i++) { |
答案
考的是对于声明变量和作用域的理解
1 | 5 |
详细解析
Using let means program work as you expect. e.g. this below snippet prints
0 1 2 3 4
1 | for (let i = 0; i < 5; i++) { |
However, with
1 | for (var i = 0; i < 5; i++) { |
If we use var, then var gets hoisted outside of the block scope into the outer function scope, as var makes it function scoped instead of block scoped.
And, if we have any closures created in the loop, let variables will be bound to the value from only that iteration of the loop, whereas var variables will be the current value of the variable, which at that point of the settimeout it is 5, hence it prints.
5 5 5 5 5
6. Arrow Function
题目
What does the code snippet to the right output by console.log?
1 | const obj = { |
答案
考察this指向,主要是箭头函数和字面量函数
1 | "bfe" |
详细解析
Equivalent
1 | const obj = { |
Arrow function ‘this’
1 | const obj = { |
IFFE and arrow functions
1 | const obj = { |
Finally nested arrow functions
1 | const obj = { |
7. Increment Operator
题目
What does the code snippet to the right output by console.log?
1 | let a = 1 |
答案
考察++的执行顺序,输出
1 | 3 |
详细解析
1 | let a = 1; |
8. Implicit Coercion I
题目
What does the code snippet to the right output by console.log?
1 | console.log(Boolean('false')) |
答案
类型转换和隐式转换
1 | true |
详细解析
1 | console.log(Boolean('false')) // ONLY empty string will be false |
Final solution:
1 | true |
9. null and undefined
题目
What does the code snippet to the right output by console.log?
1 | console.log(JSON.stringify([1,2,null,3])) |
答案
一定要注意null == 0是错误的,只和null或undefined相等,undefined转为数会变为NaN
1 | "[1,2,null,3]" |
详细解析
1 | console.log(JSON.stringify([1,2,null,3])) // happy path: "[1,2,null,3]" |
Notes:
- When converting to Number, null and undefined are handled differently: null becomes 0, whereas undefined becomes NaN.
- When applying
==tonullorundefined, numeric conversion does not happen. null equals only to null or undefined, and does not equal to anything else.
1 | null == 0 // false, null is not converted to 0 |
Final Solution:
1 | "[1,2,null,3]" |
10. Equal
题目
1 | console.log(0 == false) |
答案
==一般会转为数类型进行对比
1 | true |
详细解析
I read the ECMA2020-$7.2.14 here to finish this quiz
Rules summary:
- Number & String: convert to Number in priority
- Boolean & other: convert boolean to Number, then compare
-String & other Object: convert Object ToPrimitive(), then compare
- undefined, null is special, just remember rules
1 | console.log(0 == false) // ToNumber(false) = 0 -> 0 == 0 -> true |
Helpful Notes:
ToNumber()you can explicitly try to call withNumber(.....)or use Unary Plus Sign expression:+[], +1n ......String to Number: it first trims the white space and starting zeros, eg:
1
"01 " -> "1" -> 1
Final solution:
1 | true |
11. Implicit Coercion II
题目
What does the code snippet to the right output by console.log?
1 | console.log([] + []) |
答案
+转化为字符串,-转化为数
1 | "" |
详细解析
Hey, this puzzle really need to fully and clearly understand some weird and bad parts of Javascript Type coercion for Objects!
I. How object converts to different types?
Strongly Suggest Read this article before start this quiz. [What is {} + ] in JavaScript?
II. A Call Out: one of the answer maybe WRONG here!
{} + {} - Firefox & Chrome handle it differently !!!
Answer is different in two browsers:
1 | // firefox |
III. Explain parts of the problem
I parse all the expression as:
1 | Expression: value1 +/- value2 |
Case1: addition + operator
1 | console.log([[]] + 1) |
Case2: subtraction - operator
Subtract Op (-) ONLY triggers ToNumber() conversion, NO string operation.
1 | console.log([[]] - 1) |
Case3: object conversion
1 | console.log([] + {}) |
IV. Final Output
1 | "" |
12. arguments
题目
What does the code snippet to the right output by console.log?
1 | function log(a,b,c,d) { |
答案
要注意d没有被重新赋值,只有arguments[3]被重新赋值
1 | 1,2,3,undefined |
详细解析
arguments is the actual value passed when function is invoked.
1 | log(1,2,3) |
Here 1,2 and 3 are arguments.
parameters are local variables in the function.
1 | function log(a,b,c,d){} |
Here a,b,c, and d are parameters.
On execution of
1 | arguments[0] = 'bfe' |
the first argument i.e 1 is replaced with ‘bfe’.
However, when
1 | arguments[3] ='dev' |
is executed it results in
1 | { |
so while accessing d parameter, we get undefined since no only 1,2, and 3 are initially passed as arguments and not d.
13. Operator precedence
题目
What does the code snippet to the right output by console.log?
1 | console.log(0 == 1 == 2) |
答案
从左到右判断,bool要转成number
1 | false |
详细解析
When we use any comparison operator like ==, < and >, if one of the operands is boolean and another is a number it’ll convert the boolean into a number and then compare i.e. false becomes 0 and true becomes 1
Also, these operators work from left to right See this
1 | console.log(0 == 1 == 2) // false == 2 👉🏻 0 == 2 👉🏻 false |
14. Addition vs Unary Plus
题目
What does the code snippet to the right output by console.log?
There is a difference between Addition Operator(+) and Unary plus operator(+), even though they use the same ‘+’.
1 | console.log(1 + 2) |
答案
三个加号会无视掉中间的,准确来说是先执行最后一个转化为number,再执行倒数第二个依然是number。
1 | 3 |
详细解析
Addition operator + works on both numbers and strings (used in string concatenation). Hence, if any of the operands is not a number, using + converts all operand/s to string and concatenates.
The unary plus operator (+) precedes its operand and attempts to convert it into a number if it isn’t already.
Also, few things to know
1 | +1 // 1 |
Remember that the unary operator has higher precedence over the addition operator
1 | console.log(1 + 2) // 3 |
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
15. instanceOf
题目
What does the code snippet to the right output by console.log?
1 | console.log(typeof null) |
答案
对象才有原型链
1 | "object" |
详细解析
1 | console.log(typeof null); // "object" - 'null' has "object" type in js (backward compatibility) |
16. parseInt
题目
What does the code snippet to the right output by console.log?
1 | console.log(['0'].map(parseInt)) |
答案
注意parseInt第二个参数,没有默认是0
1 | [0] |
详细解析
1 | /** |
17. reduce
题目
What does the code snippet to the right output by console.log?
1 | [1,2,3].reduce((a,b) => { |
答案
reduce根据初始值决定后续的值
1 | 1,2 |
详细解析
Modify the example and add semi colons on lines 3 and the last line:
1 | [1,2,3].reduce((a,b) => { |
The output is tricky as it’s worth knowing the exact API of reduce:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
If you don’t supply an initial value then the FIRST value is used (and skipped!)
First:
1 | 1,2 |
Second
1 | 0,1 |
18. Promise executor II
题目
What does the code snippet to the right output by console.log?
1 | const p1 = Promise.resolve(1) |
答案
p1、p3
1 | false |
详细解析
1 | const p1 = Promise.resolve(1) // p1 is 1 Promise, Object=> `Promise { 1 }` |
19. this
题目
What does the code snippet to the right output by console.log?
1 | const obj = { |
答案
注意箭头函数只与所在位置外层函数有关,call、appy无效
1 | 1 |
详细解析
1 | /* |
20. name for Function expression
题目
What does the code snippet to the right output by console.log?
1 | function a(){ |
答案
主要是考察匿名函数的重新赋值无效
1 | "function" |
详细解析
ais a Function Declaration and has data typefunctionbandcare Function Expression and have data typefunctiondis a Named Function Expression This namedis then local only to the function body (scope) hence outside the function bodytypeof dreturnsundefined
1 | /* |
The special case is inside the named function d. The function name is un-reassignable inside the function. You can easily see the difference if you run this in "use strict" mode where it gives an error Uncaught TypeError: Assignment to constant variable. Thus, d will still point to the named function d despite being reassigned to "e"
Note that, the result would have been different if we had redeclared d as var d = "e" in which case the next console.log would have printed string See the Diff
21. Array I
题目
What does the code snippet to the right output by console.log?
1 | const a = [0] |
答案
注意length不会因为delete减少,重新设置后,后面的就算有值也是undefined,ES5方法跳过空位
1 | 1 |
详细解析
In JavaScript, arrays aren’t primitives but are instead Array objects
The length property of an Array sets or returns the number of elements in that array. You can set the length property to truncate an array at any time. The thing to remember is that the length property does not necessarily indicate the number of defined values in the array
In Array.map() callback is invoked only for indexes of the array which have assigned values (including undefined). It is not called for missing elements of the array
Similarly, Array.forEach() is not invoked for index properties that have been deleted or are uninitialized
Arrays being very similar to Objects allow Object.keys to be called on it returning the indexes as an array. However, for sparse arrays, only the defined indexes are returned
Deleting array element using delete just unassigns the value (making it empty) at that index
1 | const a = [0] |
22. min max
题目
What does the code snippet to the right output by console.log?
1 | console.log(Math.min()) |
答案
最大的数和最小的数默认是-Infinity和Infinity,一旦输入有NaN,输出必然是NaN
1 | Infinity |
详细解析
1 | console.log(Math.min()) // Infinity |
Math.min() function returns the smallest numbers given as input parameters
Math.max() function returns the largest numbers given as input parameters
1. If no parameters are passed, Math.min() returns Infinity. This can be understood if you think of implementing this logic yourself, we’ll set the min value as the largest possible value i.e. Infinity, and will loop over the parameters and compare the current value with this min value and update if current < min. In the end, we return min. Now, since no parameters are passed, we return Infinity.
2. The inverse of this logic applies to Math.max() that returns -Infinity when no parameters are passed.
3. & 4. Usual behavior
5. If any one or more of the parameters cannot be converted into a number, NaN is returned by both methods.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
23. Promise.all()
题目
What does the code snippet to the right output by console.log?
1 | (async () => { |
答案
一旦有reject,进入catch
1 | [] |
详细解析
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
It rejects immediately upon any of the input promises rejecting or non-promises throwing an error and will reject with this first rejection message/error.
1 | (async () => { |
24. Equality & Sameness
题目
What does the code snippet to the right output by console.log?
1 | console.log(0 == '0') |
答案
==会有隐式转换,但关于NaN,0与-0判断有部分问题,Object.is完全相等
1 | true |
详细解析
In a nutshell,
The equality operator (==) checks whether its two operands are equal, it attempts to convert and compare operands that are of different types
The strict equality operator (===) checks whether its two operands are equal without any implicit conversion
The Object.is() method determines whether two values are the same value. Note that this is not the same as being equal according to the == or === operator
Also, note that in Javascript, 0 is represented as both -0 and +0 (where 0 is an alias for +0). In practice, there is almost no difference between the different representations; for example, +0 === -0 is true. This difference can be noticed when using Object.is for comparison
NaN compares unequal (via ==, !=, ===, and !==) to any other value – including to another NaN value. However, Object.is gives a true result.
1 | console.log(0 == '0') // true (after type conversion '0' = 0) |
25. zero
题目
What does the code snippet to the right output by console.log?
1 | console.log(1 / 0) |
答案
Object.is完全相等,区分0与-0,Math.round(-0.5)=-0, Math.sign(-0)=-0,bigInt除法会报错
1 | Infinity |
详细解析
In Javascript, number data type has only one integer with multiple representations: 0 is represented as both -0 and +0 (where 0 is an alias for +0). In practice, there is almost no difference between the different representations; for example, +0 === -0 is true. However, you are able to notice this when you divide by zero
1 | console.log(42 / +0) // Infinity |
Object.is() method determines whether two values are the same value. This is not the same as being equal according to the == operator. The == operator applies various coercions to both sides (if they are not the same Type)
Math.sign() function returns either a positive or negative +/- 1, indicating the sign of a number passed into the argument. If the number passed into Math.sign() is 0, it will return a +/- 0.
1 | console.log(1 / 0) // Infinity |
26. true or false
题目
What does the code snippet to the right output by console.log?
1 | console.log([] == 0) |
答案
==隐式转换
1 | true |
详细解析
== follows certain rules when comparing operands. If the operands are of different types, try to convert them to the same type before comparing
In Boolean(), if the value is omitted or is 0, -0, null, false, NaN, undefined, empty string ("") it is falsy. All other values are truthy. Any object of which the value is not undefined or null, including a Boolean object whose value is false, evaluates to true
1 | // Number([]) // 0 |
27. Hoisting I
题目
What does the code snippet to the right output by console.log?
1 | const a = 1 |
答案
变量提升
1 | 1 |
详细解析
1 | const a = 1 |
28. Hoisting II
题目
What does the code snippet to the right output by console.log?
1 | const func1 = () => console.log(1) |
答案
变量提升
1 | 1 |
详细解析
1 | const func1 = () => console.log(1) |
Hoisting is the process whereby the interpreter moves the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code.
func1 is normal execution
We are able to execute func2 before declaring it because of hoisting.
1 | func3` is a Function Expression. Only declarations are hoisted and not initialization, hence `func3` is available but it is `undefined` before initialization. When we try to run `func3()` it throws an error `Uncaught TypeError: func3 is not a function |
29. Hoisting III
题目
What does the code snippet to the right output by console.log?
1 | var a = 1 |
答案
变量提升,b提前声明
1 | 2 |
详细解析
Hoisting is JavaScript’s default behavior of moving all declarations to the top of the current scope (to the top of the current script or the current function).
Here, in this example inside function func the var a gets hoisted to top and hence a=2 in that function. More so, since a is declared in that function only it is having a function scope i.e. it doesn’t leak outside. That’s why outside func, a is still 1
The variable b on the other hand is just declared inside a block so it gets hoisted to the top of the script. That’s why 'b' in window evaluates to true and its not going into if block and prints undefined instead
1 | var a = 1 |
30. Equal II
题目
What does the code snippet to the right output by console.log?
ref to the The Abstract Equality Comparison Algorithm
1 | console.log([1] == 1) |
答案
2个引用类型比较看是不是同一个地址
1 | true |
详细解析
The equality operator (==) checks whether its two operands are equal, returning a Boolean result.
If one of the operands is a boolean, it’ll convert the boolean operand to 1 if it is true and +0 if it is false. Other operands are also converted to Number and then compared. This also happens if operands are of different data types.
Also, remember the following -
1 | Number([1]) // 1 |
Also, If the operands are both objects, return true only if both operands reference the same object (remember Arrays are also objects)
1 | /* |
31. Math
题目
What does the code snippet to the right output by console.log?
1 | console.log(1 / 0) |
答案
0/0 = NaN,正数/0=Infinity,负数/0=-Infinity,0Infinity=NaN,正数\Infinity=Infinity,负数*Infinity=-Infinity
1 | Infinity |
详细解析
If we know how Operator Precedence works this becomes much easy to understand.
Note that, if two operators have same precedence (ex. * and /) its evaluated from left to right
1 | /* |
32. Hoisting IIII
题目
What does the code snippet to the right output by console.log?
1 | var a = 1 |
答案
先提升var声明,再提升函数声明
1 | "number" |
详细解析
Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code.
In this example, imagine all the function declarations moved to the top prior to the execution of code. So, all the initializations of a, b, and c to 1 happen after they are declared(or functions are declared). Hence, typeof returns "number"
Then we have an IIFE within which d='2' hence typeof inside the function prints "string". However, after IIFE is executed the scope gets destroyed and hence after that it logs "number" as d is 1
Lastly, e is a named function expression but this name is then local only to the function body (scope). Thus, outside the expression body e is still 1 i.e. "number"
1 | var a = 1 |
33. this II
题目
What does the code snippet to the right output by console.log?
ref: https://javascript.info/reference-type
1 | const obj = { |
答案
只有直接调用或IIFE保存this.a才能为1
1 | 1 |
详细解析
Things to know-
- In Normal Functions, the value of this is determined by how a function is called i.e. runtime binding.
- The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand
- The ternary operator takes three operands: a condition followed by a question mark (?), then an expression to execute if the condition is truthy followed by a colon (:), and finally the expression to execute if the condition is falsy.
- The assignment operator returns the value of the object specified by the left operand after the assignment
1 | /* |
1. When b is called as a method of obj, it’s this is set to the object the method is called on (i.e. obj object). Hence , this.a is 1
1 | /** |
34. precedence
题目
What does the code snippet to the right output by console.log?
1 | let a = 1 |
答案
优先执行单目运算符,执行结束后a=2, b=1, c=0, d=1
1 | 3 |
详细解析
Whenever three unary operators are placed one after another it actually becomes a post-increment/decrement operator followed by another operator See Reason
But, if there are spaces between the operators, it actually means multiple unary operators one after another
Basically,
1 | a +++ a // is same as (a++ + a) |
Also, remember If used postfix, with operator after operand (for example, x++), the increment operator increments and returns the value before incrementing.
1 | let a = 1 |
35. Implicit Coercion III
题目
What does the code snippet to the right output by console.log?
1 | console.log( [] + {} ) |
答案
优先执行单目运算符,执行结束后a=2, b=1, c=0, d=1
1 | "[object Object]" |
详细解析
In mathematical operators, + works on both numbers and strings (used in string concatenation). Hence, if any of the operands is not a number, using + converts all operand/s to string and concatenates.
The unary plus operator (+) precedes its operand and attempts to convert it into a number if it isn’t already.
Also, unary operators have higher precedence over addition/concatenation
Let’s know a few things first-
1 | + "1" // converts into number 1 |
Breaking it down line by line—
1 | /* |
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_plus
36. Promise.prototype.finally()
题目
What does the code snippet to the right output by console.log?
1 | Promise.resolve(1) |
答案
finally不接收参数
1 | undefined |
详细解析
Here, Promise resolves with the value 1 and control goes to the first finally block. A finally callback will not receive any argument hence it logs undefined and it returns a rejected promise.
Next control goes to the first catch block and “error” parameter is passed to it. It throws another error “error2” and control goes to the chained finally. Again, no argument is passed to finally so it logs undefined
This finally returns a resolved promise that logs 2.
Lastly, the previously thrown error2 is catched and logs error2
1 | /* Promise is resolved immeditely, then |
37. push unshift
题目
What does the code snippet to the right output by console.log?
1 | const arr = [1,2] |
答案
多个数/数组push/unshift按照顺序插入
1 | [5,6,1,2,3,4] |
详细解析
The
push()method adds one or more elements to the end of an array
The
unshift()method inserts one or more values to the beginning of an array.
The important thing to note is that, if multiple elements are passed as parameters, they’re inserted as a chunk at the beginning, in the exact same order they were passed as parameters. This is different than adding it one by one.
1 | /* |
38. Hoisting IV
题目
What does the code snippet to the right output by console.log?
1 | let foo = 10 |
答案
var提前声明,let块状作用域
1 | undefined |
详细解析
Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code.
The default initialization of the var is undefined. A variable declared with let is also hoisted but, unlike var, its not initialized with a default value. An exception will be thrown if we try to read before it is initialized.
1 | /* |
39. var
题目
What does the code snippet to the right output by console.log?
1 | function foo() { |
答案
var提前声明
1 | undefined |
详细解析
1 | /* |
40. RegExp.prototype.test
题目
What does the code snippet to the right output by console.log?
1 | console.log(/^4\d\d$/.test('404')) |
答案
测试数据会转化为 字符串加入判断
1 | true |
详细解析
1 | console.log(/^4\d\d$/.test('404')) // true |
The test() method expects a string as input, against which to match the regular expression.
So, if the input is not a string it simply converts the input to a string and then matches with regex.
^4\d\d$ 👉🏻 Starts with 4 followed by exactly one digit and ending with another digit
1 | "404" // true |
41. this III
题目
What does the code snippet to the right output by console.log?
1 | const obj = { |
答案
obj.b不是函数相当于window.a + 1,箭头函数this指向非箭头函数的外层作用域
1 | NaN |
详细解析
Normal Function— the value of this is determined by how a function is called i.e. runtime binding.
Arrow Function— don’t provide their own this binding; it retains thethisvalue of the enclosing lexical context i.e. static binding.
Also, In Javascript, only function calls establish a new this context. An object literal constructor is not a method call, so it does not affect this in any way; it will still refer to whatever it was referring to outside of the object literal.
1 | /* |
42. Hoisting V
题目
What does the code snippet to the right output by console.log?
1 | (() => { |
答案
有判断函数声明内部判断逻辑会提升,将函数定义为undefined
1 | "2" |
详细解析
We are using IIFE here to execute functions.
Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code. The default initialization of the var is undefined.
fn is a Conditionally Created Function whose behavior differs across browsers. In chrome, fn gets hoisted but since its undefined the if evaluates to true adn fn() gets defined printing "2" later
fn1 is declared twice (both outside and inside IIFE). However, inside the IIFE because of Hoisting fn1 is actually undefined hence if evaluates to true and fn1 gets redeclared 👉🏻 prints "4" fn3 gets hoisted too, however the difference is this time if is using false so it never gets inside it and fn3 remains undefined. Executing fn3() thus throws `Uncaught TypeError: fn3 is not a function
1 | (() => { |
for more details: https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6
43. JSON.stringify()
题目
What does the code snippet to the right output by console.log?
Please refer to the format guide for cases like quotes in quotes
1 | console.log(JSON.stringify(['false', false])) |
答案
JSON.stringify()能区分string、bool和number类型,但无法区分NaN, null, Infinity和undefined,并且undefined用作对象的值会消失
1 | "["false",false]" |
详细解析
JSON.stringify() method converts a JavaScript object or value to a JSON string following these rules-
Boolean,Number, andStringare converted to the corresponding primitive values during stringificationundefinedis not a valid JSON value. Such values encountered during conversion are either omitted (in an object) or changed tonull( in an array)Infinity,NaN, as well as the valuenull, are all considerednull
1 | /* In Array & Objects, stringify make `NaN, Infinity, undefined` => 'null'. Then wrap |
44. Function call
题目
What does the code snippet to the right output by console.log?
1 | function a() { |
答案
先执行外面的函数a(),打印1,再执行返回的函数,打印2,最后再次执行外面的函数a(),打印1
1 | 1 |
详细解析
In this example, when we call the outer function, it prints 1 and then returns an object with another function a. By calling it like a().a() we invoke the a method of this returned object too. That’s why it prints 2.
On the last line, when we write return a(), its actually a function invocation of outer function a. Hence, it prints 1 again
1 | /* |
45. Hoisting VI
题目
What does the code snippet to the right output by console.log?
1 | var foo = 1; |
答案
注意IFEE内部变量和全局变量
1 | undefined |
详细解析
Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code. JavaScript only hoists declarations, not initializations! The default initialization of the var is undefined
Here, we have created a global variable foo that will also be available on window. Then we are executing an IIFE and in this function, a local foo is created.
Note that, this foo is different from window.foo as, foo will refer to the local variable while window.foo points to the outer variable foo = 1
1 | /* |
46. Implicit Coercion IV
题目
What does the code snippet to the right output by console.log?
1 | const foo = [0] |
答案
[0]转化为bool为true,转化为字符为”0”
1 | false |
详细解析
In this example, if(foo) is trying to check if [0] is truthy or falsy.
Basically, if the value is or is 0, -0, null, false, NaN, undefined, or "" its falsy. All other values, including any object, an empty array ([]), or the string “false” are truthy
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
That’s why it goes into the if block.
The way == works, If the operands are of different types, it’ll try to convert them to the same type before comparing. For example, here one of the operands is a boolean, and the other is a number so it’ll convert them to numbers and then compare.
1 | Number(true) // 1 |
so
1 | /* |
47. Promise Order II
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
先同步再异步,异步中先微任务再宏任务
1 | 1 |
详细解析
Key points to remember are-
- All synchronous code gets executed first
- The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Basically, it’s a nonblocking block of code that executes when the call stack is empty
- The executor is called synchronously by the Promise constructor before even the Promise constructor returns the created object
PromisescreateMicrotaskwhich has priority over theMacrotaskcreated bysetTimeout.
So all code execution can be explained in these steps-
1gets printed3and2get pushed to the Macrotask queue (remember 3 gets pushed before 2 because of less delay)4and6get printed next and the promise is queued to the Microtask queue13also gets printed being a synchronous code- Now since, the call stack is empty. Microtask queue is checked first and promise gets executed. Since it is rejected, it goes into catch block and
8gets printed - Because of promise chaining as long as no error happens, all subsequent
thenget executed i.e.9,11,undefinedand thenfinallygets invoked so12gets printed - Lastly, queued setTimeouts get executed i.e. 3 and 2 will get printed
1 | console.log(1); // 1st => 1 |
48. Prototype
题目
What does the code snippet to the right output by console.log?
1 | function Foo() { } |
答案
Foo.prototype是改变原型链上的数,Foo.prototype = {bar: 3}是重新改变原型链,原来已声明的实例
1 | 1 |
详细解析
Prototypes are the mechanism by which JavaScript objects inherit features from one another. Basically, when you try to access a property of an object: if the property can’t be found in the object itself, the prototype is searched for the property. If the property still can’t be found, then the prototype’s prototype is searched, and so on until either the property is found.
Here, we have added bar property to the prototype of Foo. However, in the first two instances viz. Foo.prototype.bar = 1 and Foo.prototype.bar = 2 we are just updating this property to a value thus this change affects both previously created objects and newly created objects.
In the last instance, we are actually replacing the the Foo.prototype to a new object which will break the prototype chain. Hence, only newly created objects beyond this will have bar as 3
1 | /* |
49. this IV
题目
What does the code snippet to the right output by console.log?
1 | var bar = 1 |
答案
只有a.foo1()调用的是对象里的bar,其余this都指向window
1 | 1 |
详细解析
The call() method calls a function with a given this value and arguments provided individually.
When we don’t specify this, it’ll refer to the globalThis aka window in the browser’s context.
a.foot.call()makesthispoint to the window.bar and hence 1 is returned (it also increments bar because of post-increment)a.foo1()this time its a normal function call sothiswill refer toaand returns 10 (again increments it too)a.foo2.callis similar to the first case, no argument is passed hencethisis the window and it prints 2 (again incremented after returning)- In
a.foo2(), inside the function bodyfoois executed independently losing context ofa. Hence, it again prints globalbari.e. 3
1 | /* |
50. async await
题目
What does the code snippet to the right output by console.log?
1 | async function async1(){ |
答案
1 | 4 |
详细解析
An async function is a function declared with the async keyword, and the await keyword is permitted within it. The async and await keywords enable asynchronous, promise-based behavior.
Key points to remember are-
- All synchronous code gets executed first
- The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Basically, it’s a nonblocking block of code that executes when the call stack is empty
- The executor is called synchronously by the Promise constructor before even the Promise constructor returns the created object
PromisescreateMicrotaskwhich has priority over theMacrotaskcreated bysetTimeout.- If the value of the expression following the await operator is not a Promise, it’s converted to a resolved Promise i.e. await will also trigger an item into microtask queue
1 | async function async1(){ |
So all code execution can be explained in these steps-
4gets printed as its synchronous statement5is a setTimeout get pushed to the Macrotask queueasync1gets called and1gets printed, thenasync2is invoked and3gets printed.2gets queued to Microtask queue6gets printed as it’s synchronous inside executer function of the promise while7gets queued to Microtask queue8gets printed being synchronous- Now since, the call stack is empty. Microtask queue is checked first and
2and7gets printed - Lastly, queued setTimeout get executed i.e.
5will get printed
Note that, this order may vary in browsers because of underlying implementations. For example, the same code in Safari prints 4,1,3,6,8,7,2,5
51. method
题目
What does the code snippet to the right output by console.log?
1 | // This is a trick question |
答案
编译时报错,super只在方法中有效
1 | Error |
详细解析
In obj1 we have defined an object method foo. However, in case of obj2 we have defined a property foo which is a function. There’s a subtle difference.
This code throws Uncaught SyntaxError: 'super' keyword unexpected here error because super is valid only inside methods and not normal property/function.
1 | // case 1 |
52. requestAnimationFrame
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
requestAnimationFrame里的 setTimeout在执行别的 setTimeout事件后重新执行
1 | 1 |
详细解析
setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires. These tasks are queued to the Macrotask Queue
requestAnimationFrame() method tells the browser that you wish to call a specified function to update an animation before the next repaint. These tasks are queued to the Animation Queue
Since, JS is single-threaded, the main thread can only execute one statement at a time. The order in which this execution happens is—
- First all synchronous tasks are queued
- The micro task queue (promises) will execute immediately when the JS run stack is empty
- The animation queue is executed
- The priority of macro task queue is lower than that of micro task queue and animation queue
So here, first the synchronous statement 1 gets logged. Then setTimeout of 2 is added to macrotask queue. Then, RAF 3 is queued in the animation queue. Next, RAF 4 is queued to the animation queue and 5 is queued to the macrotask queue. Lastly, 6 is a synchronous statement so it gets printed second.
Now, since the main call stack is empty, animation queue tasks are executed (as they have higher priority over macrotask queue). Hence, order in which they get printed is 1 -> 6 -> 3 -> 4 -> 2 -> 5
1 | /* |
53. Prototype 2
题目
What does the code snippet to the right output by console.log?
1 | function F() { |
答案
实例无prototype
1 | undefined |
详细解析
Functions like F have prototypes. Object instances, like f, don’t.
The instance object, in your case f, has an internal reference to the prototype object. This reference is used internally to resolve properties and methods in the prototype chain. However it is not accessible directly, and thus we can’t access prototype property of f.
1 | // Only constructor functions have ".prototype". |
54. setTimeout(0ms)
题目
What does the code snippet to the right output by console.log?
1 | setTimeout(() => { |
答案
实例无prototype
1 | 1 |
详细解析
1 | setTimeout(() => { |
setTimeout() method sets a timer that executes a function or specified piece of code once the timer expires. When this happens the handler gets pushed to a queue where they are executed one by one (if the call stack is empty)
Here, 2ms is long enough for other timeouts to be pushed to the queue before it. However, 1ms is not enough and is pushed earlier than even the following 0ms.
Hence, 1 gets pushed first, then 0 and lastly 2
This order is different in Firefox as the underlying implementation might be different. 0 -> 1 -> 2
55. sparse array
题目
What does the code snippet to the right output by console.log?
1 | const arr = [1,,,2] |
答案
ES5的方法跳过空格,ES6保持空格,别的方法将空格转为undefined
1 | 1 |
详细解析
Array.forEach() is not invoked for index properties that have been deleted or are uninitialized
Similarly, In Array.map() callback is invoked only for indexes of the array which have assigned values (including undefined). It is not called for missing elements of the array. But output arrays does contain the holes.
The for…of statement creates a loop iterating over iterable objects like an Array. For a sparse array, the holes are treated as undefined
The Spread Operator (…) follows a similar behavior and expands an array such that holes are undefined
1 | const arr = [1, , , 2]; |
56. to primitive
题目
What does the code snippet to the right output by console.log?
1 | // case 1 |
答案
优先[Symbol.toPrimitive],其次toString(),再其次valueOf
1 | 2 |
详细解析
valueOf() method returns the primitive value of the specified object.
Symbol.toPrimitive is a symbol that specifies a function valued property that is called to convert an object to a corresponding primitive value. The function is called with a string argument hint, which specifies the preferred type of the result primitive value. The hint argument can be one of “number”, “string”, and “default”
Let’s break it down case by case
Case 1
For obj1, we have defined valueOf and toString. While adding we use valueOf to get the primitive value and add 1 to it thus logging 2. parseInt will convert the input using toString method that gives ‘100’ converting it to number gives 100
Case 2
For obj2, we have defined Symbol.toPrimitive, valueOf and toString methods. In this example, both operations viz. addition and parseInt make use of toPrimitive method i.e. using 200
Case 3
For obj3, we have only defined toString. All three operations here, Unary operator, Addition and parseInt will use toString method i.e. “100”
Case 4
For obj4, we have only defined valueOf. So addition uses this value 1. But in parseInt, since we have not defined toString method, the string value of obj4 will be [object Object] hence parseInt returns NaN
Case 5
For obj5, we have only defined Symbol.toPrimitive with custom handling for hints. For Addition(+), the hint is implicitly number, which means toPrimitive returns 1 and 1+1=2. parseInt implicitly uses toPrimitive method with the hint passed as string and thus it uses “100” returning 100
1 | // case 1 |
57. non-writable
题目
What does the code snippet to the right output by console.log?
1 | const a = {} |
答案
Object.defineProperty劫持属性,不会被修改
1 | 1 |
详细解析
Object.defineProperty() defines a new property directly on an object, or modifies an existing property on an object, and returns the object.
This takes in 3 parameters; the target object, nameof the property and descriptor. The writable descriptor describes if the value associated with the property can be changed with an assignment operator. Defaults to false
Here, when defining foo1, since the writable property is not defined explicitly it’ll default to false. Meaning, that the value cannot be changed later on hence b.foo1 is locked at 1 and is immutable.
Object.create() method creates a new object, using an existing object as the prototype of the newly created object. Here, we have created a new object b using a as prototype
1 | /* |
58. inherit getter setter
题目
What does the code snippet to the right output by console.log?
1 | let val = 0 |
答案
B没有重写getter/setter,C重写getter/setter
1 | 0 |
详细解析
Here we have a global val and then three classes. A has both setter and getter defined. B extends A without any overriding. C extends A with get foo() overridden.
Note that since we have not used this keyword while setting, it’ll actually update the outer variable val
So, when we create a new object b, the value of val is 0 and hence b.foo returns 0. Then we do b.foo = 1 which means val also gets updated to 1 and hence it prints 1 next.
Next we create a new object c, c.foo returns 1 (from above). However, c.foo = 2 will not update val as there is no setter defined hence val remains 1 thus printing 1 for both c.foo and b.foo Apparently, when we override the get method, it appears that the set method must also be overridden, otherwise undefined is returned See this
1 | let val = 0 |
59. override setter
题目
What does the code snippet to the right output by console.log?
1 | class A { |
答案
B重新修改val
1 | 1 |
详细解析
Here, we have two classes defined. A has a property val initialized to 1 and a getter foo defined. B also has a property val and a setter foo
Remember that, when we override the get method, it appears that the set method must also be overridden, otherwise undefined is returned and vice-versa See this
So, when we create two objects a and b; a.foo will return the value 1 as getter is defined but b.foo returns undefined.
Also, when we do b.foo=3, the setter works and val gets updated to 3. However, as there is no getter defined b.foo returns undefined. Note that, accessing b.val directly returns the new value 3 though
1 | class A { |
60. postMessage
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
window.onmessage是微任务,优先级在promise之后
1 | 1 |
详细解析
MessageChannel interface of the Channel Messaging API allows us to create a new message channel and send data through it
Promise.resolve() method returns a Promise object that is resolved with a given value.
setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires. These tasks are queued to the Macrotask Queue
Since JS is single-threaded, the main thread can only execute one statement at a time. The order in which this execution happens is—
- First all synchronous tasks are queued
- The micro task queue (promises, worker threads, messaagechannel) will execute immediately when the JS run stack is empty
- The priority of macro task queue is lower than that of micro task queue
So here, first the synchronous statement 1 gets logged. Then onmessage handler gets associated with messagechannel. 3 is a resolved promise so it’s added to the microtask queue. setTimeout of 4 is added to macrotask queue. 5 is a synchronous statement so it gets printed second. Using postMessage we trigger the handler and 2 gets added to micro task queue. Lastly, 6 is a synchronous statement so it gets printed third.
Now, since the main call stack is empty, microtask queue tasks are executed (as they have higher priority over macrotask queue). Hence, order in which they get printed is 1 -> 5 -> 6 -> 3 -> 2 -> 4
1 | /* |
61. onClick
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
onClick是一个同步任务
1 | 1 |
详细解析
Key points to remember are-
- Synchronous code (including event listeners) gets executed first in order.
- The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Basically, it’s a non-blocking block of code that executes when the call stack is empty
PromisescreateMicrotaskwhich has priority over theMacrotaskcreated bysetTimeout.
In this example, 1 gets printed, Promise 3 gets queued to Micro Stack, setTimeout 4 gets queued as Macro task. 5, 2, and 6 then get printed synchronously
Once the call stack is empty,3 is printed. Once the Microtask queue is empty then 4 gets printed
1 | console.log(1) // 1️⃣ |
62. MessageChannel
题目
What does the code snippet to the right output by console.log?
1 | console.log(1) |
答案
MessageChannel是微任务,优先级在promise之后
1 | 1 |
详细解析
setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires. These tasks are queued to the Macrotask Queue
Promise.resolve() method returns a Promise object that is resolved with a given value.
MessageChannel interface of the Channel Messaging API allows us to create a new message channel and send data through it
Since JS is single-threaded, the main thread can only execute one statement at a time. The order in which this execution happens is—
- First all synchronous tasks are queued
- The micro task queue (promises, worker threads, messaagechannel) will execute immediately when the JS run stack is empty
- The priority of macro task queue is lower than that of micro task queue
So here, first the synchronous statement 1 gets logged. Then onmessage handler gets associated with messagechannel. 3 is a resolved promise so it’s added to the microtask queue. setTimeout of 4 is added to macrotask queue. 5 is a synchronous statement so it gets printed second. Using postMessage we trigger the handler and 2 gets added to micro task queue. Lastly, 6 is a synchronous statement so it gets printed third.
Now, since the main call stack is empty, microtask queue tasks are executed (as they have higher priority over macrotask queue). Hence, order in which they get printed is 1 -> 5 -> 6 -> 3 -> 2 -> 4
This requires understaning of the event loop.
console.log is immediately queued in the stack. promise callback is queued in a microtask queue. others like setTimeout and onmessage callback are queued in the task queue.
The stack determines execution order. The microtasks are pushed to the stack before the task queue.
So, in this example
1 | /* |
63. in
题目
What does the code snippet to the right output by console.log?
1 | const obj = { |
答案
键转化为字符串
1 | true |
详细解析
The in operator returns true if the specified property is in the specified object or its prototype chain
The property could be a string or symbol representing a property name or array index (non-symbols will be coerced to strings). So, ['foo'] gets coerced to "foo" which is present in obj
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
1 | const obj = { |
64. reference type
题目
What does the code snippet to the right output by console.log?
1 | const obj = { |
答案
第一二个调用了obj.msg,第三个obj.foo执行,this指向window
1 | "BFE" |
详细解析
In normal javascript functions, this keyword points to who is calling the function (dynamic binding). Thus, when we invoke a function as obj.foo() the this keyword points to obj.
Note that, (obj.foo)() is the same as obj.foo() and this again points to obj
() is a grouping operator and is evaluated before execution. Here the logical OR expression || returns the first truthy value i.e. obj.foo which is a plain function foo({console.log(this.msg)}. Later on, when we execute this function, it’s just a function without any connection to obj and hence it points to window and window.msg is undefined
1 | const obj = { |
65. Function name
题目
What does the code snippet to the right output by console.log?
1 | var foo = function bar(){ |
答案
明明函数可以直接调用,函数名无法声明
1 | "BFE" |
详细解析
Here, because the name bar is used within a function expression, it doesn’t get declared in the outer scope. With named function expressions, the name of the function expression is enclosed within its own scope.
That’s why foo() return “BFE”, while bar() throws a ReferenceError
1 | var foo = function bar() { |
66. comma
题目
What does the code snippet to the right output by console.log?
1 | var obj = { |
答案
common operator evaluates from left to right and returns the last(right most) operand, so func is assined with bar()
1 | "dev" |
详细解析
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand.
This means, func is actually assigned the function bar as it is the last operand; and when invoked it prints this.b i.e. "dev"
1 | var obj = { |
67. if
题目
What does the code snippet to the right output by console.log?
1 | if (true) { |
答案
foo是函数,bar是undefined
1 | "BFE" |
详细解析
Function declarations are not block scoped. This means a function declared in a block will leak outside it. If and when Javascript actually executes the block and creates the function for you.
In the first if, JS will actually create the function foo that is available outside the block too. When we are declaring the function inside the if block, with a false condition, JS won’t execute the if block hence won’t create the function bar.
So, you get an error while calling the function bar.
However, this behavior may be inconsistent
1 | /* |
68. if II
题目
What does the code snippet to the right output by console.log?
1 | if (function foo(){ console.log('BFE') }) { |
答案
foo是undefined,函数转化为bool进行判断
1 | "dev" |
详细解析
Here, because we are using a function inside an if it’s supposed to be evaluated as a boolean. Boolean(any function) is always true. So, if block gets through and "dev" gets printed.
In simple terms, foo gets evaluated and it exists for that evaluation step only. Unlike other normal function declarations foo will not get hoisted and will not be available to execute anywhere (not even inside that if block)
Trying to run foo after will give ReferenceError: foo is not defined
1 | if (function foo(){ console.log('BFE') }) { |
69. undefined
题目
What does the code snippet to the right output by console.log?
1 | function foo(a, b, undefined, undefined) { |
答案
函数参数的个数
1 | 4 |
详细解析
length is a property of a function object, and indicates how many arguments the function expects, i.e. the number of formal parameters. In this case, foo has 4 parameters hence it logs 4
Note that we are just logging the foo.length and not invoking foo
1 | function foo(a, b, undefined, undefined) { |
Reason:
length method when used will function returns the number of parameters - (a, b, undefined, undefined)
Total parameter = 4
1 | console.log('BFE.dev') |
70. function
题目
What does the code snippet to the right output by console.log?
1 | function foo(){ console.log(1) } |
答案
最后foo=2
1 | Error |
详细解析
Hoisting is the process whereby the interpreter appears to move the declaration of functions, and variables to the top of their scope, prior to execution of the code.
JavaScript only hoists declarations, not initializations! However, with function declarations, the entire function gets moved to the top.
So, effectively the above code becomes —
1 | function foo(){ console.log(1) } |
Reason:
Line 1: Hoisting will take place, Function declaration will be hoisted and in the global object foo will be equal to a function.
Line 2: foo variable will be hoisted. Now since foo keyword is already present in the global object as a function it will get replaced by foo variable (foo=2).
Why did this happen?
Because variable assignment has more importance than function declaration (when the name of variable and function are the same)
Order of Precedence: variable Assigment > function declaration > variable declaration
Line 3: It will have no effect due to the above-mentioned rule.
Line 4: Since foo is not a function it will throw an error.
71. two-way generator
题目
What does the code snippet to the right output by console.log?
1 | function* gen() { |
答案
第一次返回的结果为1,打印100,第二次打印2*1=2
1 | 100 |
详细解析
With a generator function, values are not evaluated until they are needed. Therefore a generator allows us to define a potentially infinite data structure.
The next() method returns an object with two properties done and value. You can also provide a parameter to the next method to send a value to the generator. The value will be assigned as a result of a yield expression.
- Because
Grouping Operatorgets evaluated first, the generator functiongenyields/returns 100 - Since we have passed
1to the next() method.yield 100is basically replaced with 1 and it returns 2*1 = 2 - Since generator is completed it returns
undefined
The equivalent syntax of the gen() method is this:
1 | function* gen() { |
to resolve the outer yield, the inner yield need to resolve first in order to calculate the entire express similar to when we say
1 | return 3 * getValue(); |
Where we need to call and resolve getValue before we get the result.
1 | function* gen() { |
类似的题目
1 | function *foo(x) { |
72. Array length
题目
What does the code snippet to the right output by console.log?
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 10 |
详细解析
Subclasses cannot override parentClass properties. This is by design.
The parent’s length is an instance property, and it overshadows the child class’s length property, which is part of the instance’s prototype. Basically, you will not be able to override the parent class’s properties.
I think this behavior might be because variable overriding might break methods inherited from the Parent if we change its type in the Child class.
1 | class MyArray extends Array { |
73. window name
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | NaN |
详细解析
The Window.name property gets/sets the name of the window’s browsing context.
name is a special case such that when you are using the variable named name in the global scope it is treated as window.name In Google Chrome, window.name gets the value as a string.
1 | var name = {}; // this is converted using toString() |
In each of these functions, the inner variable gets hoisted inside the function and hence its value is undefined. However, both this.a and this.name refer to global variables.
1 | var a = 1; |
74. Typed Array length
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 3 |
详细解析
This is quite similar to this previous problem but the difference being we are extending from Uint8Array typed array that uses ArrayBuffer and in this case Subclass constructors may over-ride it to change the constructor assignment [See this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/@@species#:~:text=Subclass constructors may over-ride it to change the constructor assignment.)
1 | class MyArray extends Uint8Array { |
75. meaningless calculation
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 21 |
详细解析
Few things to keep in mind,
The unary plus operator (+) precedes its operand attempts to convert it into a number, if it isn’t already.
The bitwise NOT operator (~) inverts the bits of its operand.
So basically,
1 | +[] // 0 since Number([]) = 0 |
In the case of unary operators, evaluation happens from Right to Left. Of course, the Grouping operator () is evaluated first. But the overall evaluation will happen from Left to Right. See Operator Precedence
Let’s break this down,
1 | const num = +((~~!+[])+(~~!+[])+[]+(~~!+[])) |
76. const
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | "undefined","number","number" |
详细解析
In javascript, the assignment happens from right to left. So, const statement is only applicable to a, not b and c. All the other variables are considered Global without the var/let/const identifier, hence b and c will be globally scoped.
To visualize, think of it like
1 | const a = (b = (c = 1)); |
that’s why a is unavailable outside the function block, while b and c are 1; Hence, the output undefined, number, number
See https://stackoverflow.com/questions/41551270/is-there-a-way-to-do-multiple-assignment-and-initialization-with-block-scope for possible workarounds
1 | /* |
77. parseInt 2
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 0 |
详细解析
Before converting a string to a number parseInt first converts a number to a string. If the number >= 1e+21 or number <= 1e-7 , it’s represented in its scientific notation.
1. & 2. are straightforward, the integer part is 0
3. Since the number is equal to 1e-7, 0.0000001 gets converted to its scientific notation 1e-7. Now, as soon as parseInt encounters a character that is not a numeral in the specified radix, it ignores it and all succeeding characters and returns the integer value parsed up to that point
1 | // 0.0000001.toString() becomes 1e-7 |
4. If the input string begins with “0x” or “0X” (a zero, followed by lowercase or uppercase X), radix is assumed to be 16 and the rest of the string is parsed as a hexadecimal number. 0x12 in HEX is 18 in decimal
5. Similar to 3. it ignores e2 and returns 1
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
1 | /* `parseInt` operate on string. Thus, it call casting function `String` |
78. RegExp
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | ["a","c"] |
详细解析
Intuitively, it seems that output should be ["a","b","c"] since these items match the regular expression.
But when a regex has the global flag set, test() will advance the lastIndex of the regex. lastIndex is a property of RegExp that specifies the index at which to start the next match.
Basically,
- As long as
test()returnstrue, lastIndex will not reset—even when testing a different string! - When
test()returnsfalse, the calling regex’s lastIndex property will reset to 0.
/^[a-z]$/gi👉🏻 Any character from character set [a-z],gmeans global flag for multiple matches,imeans character insensitive same as [a-zA-Z]
So the loop effectively becomes,
1 | regExp.test('a') // true and it sets lastIndex = 1 |
So
1 | /* When a regex has the global flag set, test() will advance the lastIndex of |
79. Equal III
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | true |
详细解析
Here, it’ll convert the string and boolean values to number and then compare
1 | Number(2.0) // 2 |
As per Operator Precedence, equality operator == attempts to convert and compare operands that are of different types and comparisons happen from Left to Right.
1 | console.log(2.0 == "2" == new Boolean(true) == "1") // true |
80. Proxy I
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 1 |
详细解析
The Proxy object allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties.
If a handler has not been defined, the default behavior is to forward the operation to the target, however that only applies to standard behaviors like property access, and not the internal slots of exotic objects.
1 | const obj = new Map(); |
⚠️ Details are pretty technical 🤯
If the target does not have a [[MapData]] internal slot, throw a TypeError exception. This is also explained here
Similar behavior in ES6 Set is explained at https://stackoverflow.com/questions/43927933/why-is-set-incompatible-with-proxy
81. setTimeout II
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 4 |
详细解析
Here, num behaves like var The variable num is actually declared within the for loop and the setTimeout’s inner function accesses it. So when the for loop is done running, each of the inner functions refers to the same variable num, which at the end of the loop is equal to 4
1 | let num |
82. Proxy II
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | "BFE" |
详细解析
Private class members can be created by using a hash # prefix. Private fields are accessible on the class constructor from inside the class declaration itself and is not accessible from the derived Subclass.
A proxy cannot read private member #name from the dev object.
1 | class Dev { |
83. Plus Plus
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 2 |
详细解析
The unary plus operator (+) precedes its operand and attempts to convert it into a
numberif it isn’t already. If it’s not possible it returnsNaN
In mathematical operators, + works on both numbers and strings (used in string concatenation). Hence, if any of the operands is not a number, using + converts all operand/s to string and concatenates.
The unary operator has higher precedence over the addition operator.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
1 | console.log(1 + 1) // 2 |
84. Array.prototype.sort()
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 2 |
详细解析
Array.sort() method sorts the elements of an array in place and returns the sorted array
Note that the array is sorted in place, and no copy is made.
This explains why both a and b get sorted. a is sorted in place while this sorted array is then assigned to b.
As to why numbers don’t appear in order, remember that sort expects a compare function that defines the sort order. If omitted, the array elements are converted to strings, then sorted according to each character’s Unicode code point value.
When compared in strings, "111" < "2", "1111" < "999", etc.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
1 | // In global excution environment `a` variable created and it points to an array object |
85. String.raw()
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | "BFE\n.dev" |
详细解析
1 | /* static `String.raw()` method is a tag function of template literals. Two |
The static String.raw() method is a tag function of template literals. It’s used to get the raw string form of template literals, that is, substitutions (e.g. ${foo}) are processed, but escapes (e.g. \n) are not. Note that it works just like the default template function and performs string concatenation. The important difference is that it’ll not escape characters.
Example,
1 | console.log("BFE\ndev") // \n gets escaped |
String.raw() works like an interweaving function. The first argument is an object with a raw property whose value is an iterable(could be a string or an array) representing the separated strings in the template literal. The rest of the arguments are the substitutions. Extra substitutions are ignored
Example,
1 | // using array |
86. setTimeout III
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 1 |
详细解析
setTimeout() method sets a timer that executes a function or specified piece of code once the timer expires.
Here, the func is changed inside the first setimeout but since this is part of the callback it will not execute until the first setTimeout is invoked. That is why, the original func is passed as a reference to the second timeout which eventually gets invoked after 100ms logging 1
1 | let func = () => { |
87. instanceOf 2
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | true |
详细解析
The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.
Simply put,
1 | console.log(Function instanceof Object) // true |
See more insightful answers on this thread https://stackoverflow.com/questions/23622695/why-in-javascript-both-object-instanceof-function-and-function-instanceof-obj
88. try…catch
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | "a1" |
详细解析
The try…catch statement marks a try block and a catch block. If the code in the try block throws an exception then the code in the catch block will be executed. In all these examples, we are throwing an exception in try which is why the control immediately goes to the catch block.
The important thing to know is that the catch-block specifies an identifier that holds the value of the exception; this value is only available in the scope of the catch-block
1 | var a = 'a' |
- We have just written
catchand are not even capturing the error parameter sovar adeclared inside actually becomes global and overwrites outerahence printinga1later - We have written
catch(b)which means we are usingbto hold the exception value which is only available locally inside this catch block. Hence, even after declaringbinside, the global value remains unchanged. Thus, printingb - We have written
catch(error)and are usingerrorvariable to capture the error value , soerroris a locally scoped variable butcis not. Hence, similar to 1.var cdeclared inside actually becomes global and overwrites outerchence printingc1later.
89. let
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 2 |
详细解析
The answer is 2 I think that we can image function foo’s scope chain as below:
1 | [foo.AO , IIFE.AO , globalContext.VO] |
when foo return a , it will find a in foo.AO where it is 2
1 | let a = 1; |
Here, inside the IIFE the function foo simply returns a. Whenever we execute it, it’ll return the value of a defined at that point.
In this example, even though a is assigned a value 2 after foo is declared it is still before foo is invoked. Thus, at the time of execution it prints 2
90. array keys
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | ["length"] |
详细解析
The static Reflect.ownKeys() method returns an array of the target object’s own property keys.
Arrays under the hood in JavaScript are Objects. Their keys are numbers, and their values are the elements. By default, all arrays also have a property length that reflects the number of elements in that array. In the case of a sparse array, holes do not add the corresponding key to the array.
Also, the spread operator converts these holes to undefined
1. Its an empty array so answer only contains `length
2. This array [,] is also an empty sparse array hence holes are ignored
3. [1,,2] has values defined at index 0 and 2, hence it prints [“0”,”2”,”length”]
4. Using Spread Operator changes the input array to [1,undefined,2] thus all the keys give following output [“0”,”1”,”2”,”length”]
1 | console.log(Reflect.ownKeys([])) // ["length"] |
91. largest Array index
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 1 |
详细解析
The at() method takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array. For the last item array[array.length-1], you can also call array.at(-1)
JavaScript arrays are zero-based and use 32-bit indexes: the index of the first element is 0, and the highest possible index is 4294967294 (2^32 − 2) which we have assigned as 1
Accessing, arr.at(-1) thus returns the last element 1.
Also, note that arr[2^32 - 1] = 2 does store 2 at that index however since its more than the max possible length it cannot be accessed by at
1 | const arr = [] |
92. NaN
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | false |
详细解析
The global NaN property is a value representing Not-A-Number.
1. and 2. NaN compares unequal (via both == and ===) to any other value including to another NaN value.
3. Object.is() determines whether two values are the same value and returns true when we compare NaN
4. and 5. indexOf uses Strict Equality Comparison and thus [NaN].indexOf(NaN) === -1 , array.includes uses SameValueZero comparison algorithm , thus making [NaN].includes(NaN) true.
6. , 7. and 8. The Math.max() and Math.min() functions return NaN if any parameter isn’t a number and can’t be converted into one (of course NaN cannot be converted into a number).
1 | console.log(NaN == NaN) // false |
93. string
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | "bfe.dev" |
详细解析
Starting from ECMAScript 5, a string can be treated as an array-like object, where individual characters correspond to a numerical index.
However, when using bracket notation for character access, attempting to delete or assign a value to these properties will not succeed. The properties involved are neither writable nor configurable.
1 | let a = 'bfe.dev' |
94. emoji
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 2 |
详细解析
JavaScript strings are represented using UTF-16 code units. Each code unit can be used to represent a code point in the [U+0000, U+FFFF] range – also known as the “basic multilingual plane” (BMP).
For code points beyond U+FFFF, you’d represent them as a surrogate pair. That is to say, two contiguous code units. For instance, the emoji ‘👍’ code point is represented with the ‘\uD83D\uDC4D’ contiguous code units.
String.length property returns the number of code units in the string. While this is usually the same as character length, using characters that are outside the BMP([U+0000, U+FFFF] range) can return a different length
If you’re curious, Read this
1 | console.log('👍'.length) // 2 |
95. number format
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript Quiz from BFE.dev |
答案
1 | 6 |
详细解析
A leading zero represents an octal (base 8) number unless any digit is 8 or 9, in which case, the leading 0 is ignored
Here, 017 and 011 are valid octal numbers while 018 and 019 are treated as decimal. We have, 017 = 15 and 011 = 9 in decimal system
1 | console.log(017 - 011) // 15 - 9 = 6 |
96. comparison
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript quiz from BFE.dev |
答案
1 | true |
详细解析
Reference to https://zh.javascript.info/comparison The number is compared directly. Otherwise, convert the other to number if possible. The last one, compare by dictionary order.
When using > to compare two operands, if either operand is a number, Javascript will first convert the string to its equivalent number and then numerically compare.
Only when both operands are string, they are compared lexicographically. i.e. character by character until they are not equal or there aren’t any characters left. The first character of ‘10’ is less than the first character of ‘9’ hence ‘10’ is < ‘9’
1 | console.log(10 > 9) // true |
97. this V
题目
What does the code snippet to the right output by console.log
1 | // This is a JavaScript quiz from BFE.dev |
答案
1 | "undefined1" |
详细解析
Please pay attention to the callback of forEach, it isn’t an arrow function, so the this inside of callback point to the window, so window.prefix is equal to undefined.
Here, this inside the log function will point to obj as it’s a normal function. Thus, this.list.forEach loops over the array [‘1’,’2’,’3’].
However, inside the forEach callback function, since it’s not the obj invoking callback, this will not point to obj. Rather, it will refer to window hence this.prefix will be undefined
1 | const obj = { |