https://github.com/lydiahallie/javascript-questions
https://github.com/lydiahallie/javascript-questions/blob/master/README-zh_CN.md
let
111:let的作用区块
What’s the output?
1 | let name = 'Lydia' |
Each function has its own execution context (or scope). The getName
function first looks within its own context (scope) to see if it contains the variable name
we’re trying to access. In this case, the getName
function contains its own name
variable: we declare the variable name
with the let
keyword, and with the value of 'Sarah'
.
Variables with the let
keyword (and const
) are hoisted, but unlike var
, don’t get initialized. They are not accessible before the line we declare (initialize) them. This is called the “temporal dead zone”. When we try to access the variables before they are declared, JavaScript throws a ReferenceError
.
If we wouldn’t have declared the name
variable within the getName
function, the javascript engine would’ve looked down the scope chain. The outer scope has a variable called name
with the value of Lydia
. In that case, it would’ve logged Lydia
.
yield
112:yield的相关性质,待学习
What’s the output?
1 | function* generatorOne() { |
With the yield
keyword, we yield
values in a generator function. With the yield*
keyword, we can yield values from another generator function, or iterable object (for example an array).
In generatorOne
, we yield the entire array ['a', 'b', 'c']
using the yield
keyword. The value of value
property on the object returned by the next
method on one
(one.next().value
) is equal to the entire array ['a', 'b', 'c']
.
1 | console.log(one.next().value) // ['a', 'b', 'c'] |
In generatorTwo
, we use the yield*
keyword. This means that the first yielded value of two
, is equal to the first yielded value in the iterator. The iterator is the array ['a', 'b', 'c']
. The first yielded value is a
, so the first time we call two.next().value
, a
is returned.
1 | console.log(two.next().value) // 'a' |
operator
113:中的立即执行函数
What’s the output?
1 | console.log(`${(x => x)('I love')} to program`) |
Expressions within template literals are evaluated first. This means that the string will contain the returned value of the expression, the immediately invoked function (x => x)('I love')
in this case. We pass the value 'I love'
as an argument to the x => x
arrow function. x
is equal to 'I love'
, which gets returned. This results in I love to program
.
objects to null setInterval
114:垃圾回收机制,和setInterval的性质
What will happen?
1 | let config = { |
Normally when we set objects equal to null
, those objects get garbage collected as there is no reference anymore to that object. However, since the callback function within setInterval
is an arrow function (thus bound to the config
object), the callback function still holds a reference to the config
object. As long as there is a reference, the object won’t get garbage collected. Since it’s not garbage collected, the setInterval
callback function will still get invoked every 1000ms (1s).
Map
115:Map中key的特性
Which method(s) will return the value 'Hello world!'
?
1 | const myMap = new Map() |
When adding a key/value pair using the set
method, the key will be the value of the first argument passed to the set
function, and the value will be the second argument passed to the set
function. The key is the function () => 'greeting'
in this case, and the value 'Hello world'
. myMap
is now { () => 'greeting' => 'Hello world!' }
.
1 is wrong, since the key is not 'greeting'
but () => 'greeting'
. 3 is wrong, since we’re creating a new function by passing it as a parameter to the get
method. Object interact by reference. Functions are objects, which is why two functions are never strictly equal, even if they are identical: they have a reference to a different spot in memory.
reference
116:默认参数和引用,这里可以给person添加一个 food:[1,2,3]
What’s the output?
1 | const person = { |
Both the changeAge
and changeAgeAndName
functions have a default parameter, namely a newly created object { ...person }
. This object has copies of all the key/values in the person
object.
First, we invoke the changeAge
function and pass the person
object as its argument. This function increases the value of the age
property by 1. person
is now { name: "Lydia", age: 22 }
.
Then, we invoke the changeAgeAndName
function, however we don’t pass a parameter. Instead, the value of x
is equal to a new object: { ...person }
. Since it’s a new object, it doesn’t affect the values of the properties on the person
object. person
is still equal to { name: "Lydia", age: 22 }
.
setter
121.setter的返回值
What’s the output?
1 | const config = { |
The language
method is a setter
. Setters don’t hold an actual value, their purpose is to modify properties. When calling a setter
method, undefined
gets returned.
async await
124.async 和 await 相关
What’s the output?
1 | async function* range(start, end) { |
The generator function range
returns an async object with promises for each item in the range we pass: Promise{1}
, Promise{2}
, Promise{3}
. We set the variable gen
equal to the async object, after which we loop over it using a for await ... of
loop. We set the variable item
equal to the returned Promise values: first Promise{1}
, then Promise{2}
, then Promise{3}
. Since we’re awaiting the value of item
, the resolved promsie, the resolved values of the promises get returned: 1
, 2
, then 3
.
reference
127.reference destructuring 相关
What’s the output?
1 | const spookyItems = ["👻", "🎃", "🕸"]; |
By destructuring objects, we can unpack values from the right-hand object, and assign the unpacked value to the value of the same property name on the left-hand object. In this case, we’re assigning the value “💀” to spookyItems[3]
. This means that we’re modifying the spookyItems
array, we’re adding the “💀” to it. When logging spookyItems
, ["👻", "🎃", "🕸", "💀"]
gets logged.
const
129.const 的暂时性死区
What’s the output?
1 | const randomValue = 21; |
Variables declared with the const
keyword are not referencable before their initialization: this is called the temporal dead zone. In the getInfo
function, the variable randomValue
is scoped in the functional scope of getInfo
. On the line where we want to log the value of typeof randomValue
, the variable randomValue
isn’t initialized yet: a ReferenceError
gets thrown! The engine didn’t go down the scope chain since we declared the variable randomValue
in the getInfo
function.
async await
130.async await 加上 try catch finally
What’s the output?
1 | const myPromise = Promise.resolve("Woah some cool data"); |
In the try
block, we’re logging the awaited value of the myPromise
variable: "Woah some cool data"
. Since no errors were thrown in the try
block, the code in the catch
block doesn’t run. The code in the finally
block always runs, "Oh finally!"
gets logged.
call stack
133.考察的是代码的执行顺序
What’s the output?
1 | const myPromise = Promise.resolve(Promise.resolve("Promise!")); |
First, we invoke funcOne
. On the first line of funcOne
, we call the myPromise
promise, which is an asynchronous operation. While the engine is busy completing the promise, it keeps on running the function funcOne
. The next line is the asynchronous setTimeout
function, from which the callback is sent to the Web API. (see my article on the event loop here.)
Both the promise and the timeout are asynchronous operations, the function keeps on running while it’s busy completing the promise and handling the setTimeout
callback. This means that Last line!
gets logged first, since this is not an asynchonous operation. This is the last line of funcOne
, the promise resolved, and Promise!
gets logged. However, since we’re invoking funcTwo()
, the call stack isn’t empty, and the callback of the setTimeout
function cannot get added to the callstack yet.
In funcTwo
we’re, first awaiting the myPromise promise. With the await
keyword, we pause the execution of the function until the promise has resolved (or rejected). Then, we log the awaited value of res
(since the promise itself returns a promise). This logs Promise!
.
The next line is the asynchronous setTimeout
function, from which the callback is sent to the Web API.
We get to the last line of funcTwo
, which logs Last line!
to the console. Now, since funcTwo
popped off the call stack, the call stack is empty. The callbacks waiting in the queue (() => console.log("Timeout!")
from funcOne
, and () => console.log("Timeout!")
from funcTwo
) get added to the call stack one by one. The first callback logs Timeout!
, and gets popped off the stack. Then, the second callback logs Timeout!
, and gets popped off the stack. This logs Last line! Promise! Promise! Last line! Timeout! Timeout!
export default
134.考察 export default 和 import * as
How can we invoke sum
in index.js
from sum.js?
1 | // sum.js |
For the sum
example, it means that the imported value sum
looks like this:
1 | { default: function sum(x) { return x + x } } |
We can invoke this function, by calling sum.default
class
139.考察 class 中的变量
What’s the output?
1 | class Counter { |
In ES2020, we can add private variables in classes by using the #
. We cannot access these variables outside of the class. When we try to log counter.#number
, a SyntaxError gets thrown: we cannot acccess it outside the Counter
class!
yield
140.考察 yield
✨ SOMETHING IS MISSING HERE ✨ 这里应该填什么
1 | const teams = [ |
In order to iterate over the members
in each element in the teams
array, we need to pass teams[i].members
to the getMembers
generator function. The generator function returns a generator object. In order to iterate over each element in this generator object, we need to use yield*
.
If we would’ve written yield
, return yield
, or return
, the entire generator function would’ve gotten returned the first time we called the next
method.
博主更新到了 144 题。