https://github.com/lydiahallie/javascript-questions
https://github.com/lydiahallie/javascript-questions/blob/master/README-zh_CN.md
this
3.关于this在普通函数和箭头函数中的指向问题
What’s the output?
1 | const shape = { |
Note that the value of diameter
is a regular function, whereas the value of perimeter
is an arrow function.
With arrow functions, the this
keyword refers to its current surrounding scope, unlike regular functions! This means that when we call perimeter
, it doesn’t refer to the shape object, but to its surrounding scope (window for example).
There is no value radius
on that object, which returns undefined
.
class static
8.class中的静态方法static
What’s the output?
1 | class Chameleon { |
The colorChange
function is static. Static methods are designed to live only on the constructor in which they are created, and cannot be passed down to any children. Since freddie
is a child, the function is not passed down, and not available on the freddie
instance: a TypeError
is thrown.
new
12.new的性质
What’s the output?
1 | function Person(firstName, lastName) { |
For sarah
, we didn’t use the new
keyword. When using new
, it refers to the new empty object we create. However, if you don’t add new
it refers to the global object!
We said that this.firstName
equals "Sarah"
and this.lastName
equals "Smith"
. What we actually did, is defining global.firstName = 'Sarah'
and global.lastName = 'Smith'
. sarah
itself is left undefined
, since we don’t return a value from the Person
function.
事件传播
13.事件的执行顺序
Capturing > Target > Bubbling
在捕获(capturing)阶段中,事件从祖先元素向下传播到目标元素。当事件达到目标(target)元素后,冒泡(bubbling)才开始。
函数传参
17.传递字符串模板作为函数的参数
What’s the output?
1 | function getPersonInfo(one, two, three) { |
If you use tagged template literals, the value of the first argument is always an array of the string values. The remaining arguments get the values of the passed expressions!
sessionStorage
22.sessionStorage的访问时间
How long is cool_secret accessible?
1 | sessionStorage.setItem('cool_secret', 123) |
The data stored in sessionStorage
is removed after closing the tab.
If you used localStorage
, the data would’ve been there forever, unless for example localStorage.clear()
is invoked.
key type is string?
24.key在不同类型的数据中是什么类型?
What’s the output?
1 | const obj = { 1: "a", 2: "b", 3: "c" }; |
All object keys (excluding Symbols) are strings under the hood, even if you don’t type it yourself as a string. This is why obj.hasOwnProperty('1')
also returns true.
It doesn’t work that way for a set. There is no '1'
in our set: set.has('1')
returns false
. It has the numeric type 1
, set.has(1)
returns true
.
same key
25.key值相同会发生什么?
What’s the output?
1 | const obj = { a: "one", b: "two", a: "three" }; |
If you have two keys with the same name, the key will be replaced. It will still be in its first position, but with the last specified value.
key is object
29.对象的key是对象会怎样?
What’s the output?
1 | const a = {}; |
Object keys are automatically converted into strings. We are trying to set an object as a key to object a
, with the value of 123
.
However, when we stringify an object, it becomes "[Object object]"
. So what we are saying here, is that a["Object object"] = 123
. Then, we can try to do the same again. c
is another object that we are implicitly stringifying. So then, a["Object object"] = 456
.
Then, we log a[b]
, which is actually a["Object object"]
. We just set that to 456
, so it returns 456
.
event.target
31.事件对象
What is the event.target when clicking the button?
1 | <div onclick="console.log('first div')"> |
event.target 是 button
The deepest nested element that caused the event is the target of the event. You can stop bubbling by event.stopPropagation
打印顺序:button
second div
first div
在事件传播期间,有三个阶段:捕获、目标和冒泡。默认情况下,事件处理程序在冒泡阶段执行(除非将 useCapture
设置为 true
)。它从嵌套最深的元素向外传播。
try catch
38.try catch 的作用域块
What’s the output?
1 | (() => { |
The catch
block receives the argument x
. This is not the same x
as the variable when we pass arguments. This variable x
is block-scoped.
Later, we set this block-scoped variable equal to 1
, and set the value of the variable y
. Now, we log the block-scoped variable x
, which is equal to 1
.
Outside of the catch
block, x
is still undefined
, and y
is 2
. When we want to console.log(x)
outside of the catch
block, it returns undefined
, and y
returns 2
.
reduce
40.reduce的用法
What’s the output?
1 | [[0, 1], [2, 3]].reduce( |
[1, 2]
is our initial value. This is the value we start with, and the value of the very first acc
. During the first round, acc
is [1,2]
, and cur
is [0, 1]
. We concatenate them, which results in [1, 2, 0, 1]
.
Then, [1, 2, 0, 1]
is acc
and [2, 3]
is cur
. We concatenate them, and get [1, 2, 0, 1, 2, 3]
generator yield
44.generator 和 yield 的用法
What’s the output?
1 | function* generator(i) { |
Regular functions cannot be stopped mid-way after invocation. However, a generator function can be “stopped” midway, and later continue from where it stopped. Every time a generator function encounters a yield
keyword, the function yields the value specified after it. Note that the generator function in that case doesn’t return the value, it yields the value.
First, we initialize the generator function with i
equal to 10
. We invoke the generator function using the next()
method. The first time we invoke the generator function, i
is equal to 10
. It encounters the first yield
keyword: it yields the value of i
. The generator is now “paused”, and 10
gets logged.
Then, we invoke the function again with the next()
method. It starts to continue where it stopped previously, still with i
equal to 10
. Now, it encounters the next yield
keyword, and yields i * 2
. i
is equal to 10
, so it returns 10 * 2
, which is 20
. This results in 10, 20
.
Promise.race
45.Promise.race 的用法
What does this return?
1 | const firstPromise = new Promise((res, rej) => { |
When we pass multiple promises to the Promise.race
method, it resolves/rejects the first promise that resolves/rejects. To the setTimeout
method, we pass a timer: 500ms for the first promise (firstPromise
), and 100ms for the second promise (secondPromise
). This means that the secondPromise
resolves first with the value of 'two'
. res
now holds the value of 'two'
, which gets logged.
reference
46.又是一个关于引用的问题,挺有意思的
What’s the output?
1 | let person = { name: "Lydia" }; |
https://github.com/lydiahallie/javascript-questions#46-whats-the-output
parseInt
49.parseInt的用法
What’s the value of num
?
1 | const num = parseInt("7*6", 10); |
Only the first numbers in the string is returned. Based on the radix (the second argument in order to specify what type of number we want to parse it to: base 10, hexadecimal, octal, binary, etc.), the parseInt
checks whether the characters in the string are valid. Once it encounters a character that isn’t a valid number in the radix, it stops parsing and ignores the following characters.
*
is not a valid number. It only parses "7"
into the decimal 7
. num
now holds the value of 7
.
map
50.map的相关用法
What’s the output`?
1 | [1, 2, 3].map(num => { |
When mapping over the array, the value of num
is equal to the element it’s currently looping over. In this case, the elements are numbers, so the condition of the if statement typeof num === "number"
returns true
. The map function creates a new array and inserts the values returned from the function.
However, we don’t return a value. When we don’t return a value from the function, the function returns undefined
. For every element in the array, the function block gets called, so for each element we return undefined
.
try-catch throw
52.try catch 和 throw 的用法
What’s the output?
1 | function greeting() { |
With the throw
statement, we can create custom errors. With this statement, you can throw exceptions. An exception can be a string, a number, a boolean or an object. In this case, our exception is the string 'Hello world'
.
With the catch
statement, we can specify what to do if an exception is thrown in the try
block. An exception is thrown: the string 'Hello world'
. e
is now equal to that string, which we log. This results in 'Oh an error: Hello world'
.
new return
53.new 构造函数如果有显式的 return 呢
What’s the output?
1 | function Car() { |
When you return a property, the value of the property is equal to the returned value, not the value set in the constructor function. We return the string "Maserati"
, so myCar.make
is equal to "Maserati"
.
delete
55.delete相关
What’s the output?
1 | class Dog { |
We can delete properties from objects using the delete
keyword, also on the prototype. By deleting a property on the prototype, it is not available anymore in the prototype chain. In this case, the bark
function is not available anymore on the prototype after delete Dog.prototype.bark
, yet we still try to access it.
When we try to invoke something that is not a function, a TypeError
is thrown. In this case TypeError: pet.bark is not a function
, since pet.bark
is undefined
.
import export
57.修改导入的模块
What’s the output?
1 | // counter.js |
1 | // index.js |
An imported module is read-only: you cannot modify the imported module. Only the module that exports them can change its value.
When we try to increment the value of myCounter
, it throws an error: myCounter
is read-only and cannot be modified.
delete的返回值
58.delete成功删除或者删除失败的返回值
What’s the output?
1 | const name = "Lydia"; |
The delete
operator returns a boolean value: true
on a successful deletion, else it’ll return false
. However, variables declared with the var
, const
or let
keyword cannot be deleted using the delete
operator.
The name
variable was declared with a const
keyword, so its deletion is not successful: false
is returned. When we set age
equal to 21
, we actually added a property called age
to the global object. You can successfully delete properties from objects this way, also the global object, so delete age
returns true
.
… operator
60.运算符…的性质
What’s the output?
1 | const user = { name: "Lydia", age: 21 }; |
It’s possible to combine objects using the spread operator ...
. It lets you create copies of the key/value pairs of one object, and add them to another object. In this case, we create copies of the user
object, and add them to the admin
object. The admin
object now contains the copied key/value pairs, which results in { admin: true, name: "Lydia", age: 21 }
.
Object.defineProperty
61.关于Object.defineProperty
What’s the output?
1 | const person = { name: "Lydia" }; |
With the defineProperty
method, we can add new properties to an object, or modify existing ones. When we add a property to an object using the defineProperty
method, they are by default not enumerable. The Object.keys
method returns all enumerable property names from an object, in this case only "name"
.
Properties added using the defineProperty
method are immutable by default. You can override this behavior using the writable
, configurable
and enumerable
properties. This way, the defineProperty
method gives you a lot more control over the properties you’re adding to an object.
JSON.stringify
62.关于 json.stringify
What’s the output?
1 | const settings = { |
The second argument of JSON.stringify
is the replacer. The replacer can either be a function or an array, and lets you control what and how the values should be stringified.
If the replacer is an array, only the property names included in the array will be added to the JSON string. In this case, only the properties with the names "level"
and "health"
are included, "username"
is excluded. data
is now equal to "{"level":19, "health":90}"
.
If the replacer is a function, this function gets called on every property in the object you’re stringifying. The value returned from this function will be the value of the property when it’s added to the JSON string. If the value is undefined
, this property is excluded from the JSON string.
reduce
65.reduce 的相关性质
What’s the output?
1 | [1, 2, 3, 4].reduce((x, y) => console.log(x, y)); |
reducer
函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
reducer
函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
reducer
函数还有一个可选参数initialValue
, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供initialValue
,则将使用数组中的第一个元素。
在上述例子,reduce
方法接收的第一个参数(Accumulator)是x
, 第二个参数(Current Value)是y
。
在第一次调用时,累加器x
为1
,当前值“y”
为2
,打印出累加器和当前值:1
和2
。
例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回undefined
。 在下一次调用时,累加器为undefined
,当前值为“3”, 因此undefined
和3
被打印出。
在第四次调用时,回调函数依然没有返回值。 累加器再次为 undefined
,当前值为“4”。 undefined
和4
被打印出。
import require
67.模块引入机制
What’s the output?
1 | // index.js |
With the import
keyword, all imported modules are pre-parsed. This means that the imported modules get run first, the code in the file which imports the module gets executed after.
This is a difference between require()
in CommonJS and import
! With require()
, you can load dependencies on demand while the code is being run. If we would have used require
instead of import
, running index.js
, running sum.js
, 3
would have been logged to the console.
generator yield
71.generator和yield相关性质
How can we log the values that are commented out after the console.log statement?
1 | function* startGame() { |
A generator function “pauses” its execution when it sees the yield
keyword. First, we have to let the function yield the string “Do you love JavaScript?”, which can be done by calling game.next().value
.
Every line is executed, until it finds the first yield
keyword. There is a yield
keyword on the first line within the function: the execution stops with the first yield! This means that the variable answer is not defined yet!
When we call game.next("Yes").value
, the previous yield
is replaced with the value of the parameters passed to the next()
function, "Yes"
in this case. The value of the variable answer
is now equal to "Yes"
. The condition of the if-statement returns false
, and JavaScript loves you back ❤️
gets logged.
String.raw
72.String.raw 会忽略转义字符
What’s the output?
1 | console.log(String.raw`Hello\nworld`); |
String.raw
函数是用来获取一个模板字符串的原始字符串的,它返回一个字符串,其中忽略了转义符(\n
,\v
,\t
等)。但反斜杠可能造成问题,因为你可能会遇到下面这种类似情况:
1 | const path = `C:\Documents\Projects\table.html` |
这将导致:
1 | "C:DocumentsProjects able.html" |
直接使用String.raw
1 | String.raw`C:\Documents\Projects\table.html` |
它会忽略转义字符并打印:C:\Documents\Projects\table.html
上述情况,字符串是Hello\nworld
被打印出。
async await
73.async的返回值
What’s the output?
1 | async function getData() { |
An async function always returns a promise. The await
still has to wait for the promise to resolve: a pending promise gets returned when we call getData()
in order to set data
equal to it.
If we wanted to get access to the resolved value "I made it"
, we could have used the .then()
method on data
:
1 | data.then(res => console.log(res)) |
This would’ve logged "I made it!"
拓展:注意这里的打印顺序
1 | async function getData() { |
记忆函数
78.闭包和缓存
What is the output?
1 | const add = () => { |
The add
function is a memoized function. With memoization, we can cache the results of a function in order to speed up its execution. In this case, we create a cache
object that stores the previously returned values.
If we call the addFunction
function again with the same argument, it first checks whether it has already gotten that value in its cache. If that’s the case, the caches value will be returned, which saves on execution time. Else, if it’s not cached, it will calculate the value and store it afterwards.
We call the addFunction
function three times with the same value: on the first invocation, the value of the function when num
is equal to 10
isn’t cached yet. The condition of the if-statement num in cache
returns false
, and the else block gets executed: Calculated! 20
gets logged, and the value of the result gets added to the cache object. cache
now looks like { 10: 20 }
.
The second time, the cache
object contains the value that gets returned for 10
. The condition of the if-statement num in cache
returns true
, and 'From cache! 20'
gets logged.
The third time, we pass 5 * 2
to the function which gets evaluated to 10
. The cache
object contains the value that gets returned for 10
. The condition of the if-statement num in cache
returns true
, and 'From cache! 20'
gets logged.
import * export
89.import * 和 export 多个
What’s the output?
1 | // module.js |
With the import * as name
syntax, we import all exports from the module.js
file into the index.js
file as a new object called data
is created. In the module.js
file, there are two exports: the default export, and a named export. The default export is a function which returns the string "Hello World"
, and the named export is a variable called name
which has the value of the string "Lydia"
.
The data
object has a default
property for the default export, other properties have the names of the named exports and their corresponding values.
prototype
92.常规函数和箭头函数的prototype
What’s the output?
1 | function giveLydiaPizza() { |
Regular functions, such as the giveLydiaPizza
function, have a prototype
property, which is an object (prototype object) with a constructor
property. Arrow functions however, such as the giveLydiaChocolate
function, do not have this prototype
property. undefined
gets returned when trying to access the prototype
property using giveLydiaChocolate.prototype
.
Object.entries
93.Object.entries的性质
What’s the output?
1 | const person = { |
Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组,上述情况返回一个二维数组,数组每个元素是一个包含键和值的数组:
1 | [['name','Lydia'],['age',21]] |
使用for-of
循环,我们可以迭代数组中的每个元素,上述情况是子数组。 我们可以使用const [x,y]
在for-of
循环中解构子数组。 x
等于子数组中的第一个元素,y
等于子数组中的第二个元素。
第一个子阵列是[“name”,“Lydia”]
,其中x
等于name
,而y
等于Lydia
。 第二个子阵列是[“age”,21]
,其中x
等于age
,而y
等于21
。
扩展运算符 …
94.剩余参数应该放到最后
What’s the output?
1 | function getItems(fruitList, ...args, favoriteFruit) { |
... args
是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。
1 | function getItems(fruitList, favoriteFruit, ...args) { |
上述例子是有效的,将会返回数组:[ 'banana', 'apple', 'orange', 'pear' ]
Promise.resolve async await
102:Promise.resolve方式和 async await 方式的不同
What’s the value of output?
1 | const myPromise = () => Promise.resolve('I have resolved!') |
With a promise, we basically say I want to execute this function, but I’ll put it aside for now while it’s running since this might take a while. Only when a certain value is resolved (or rejected), and when the call stack is empty, I want to use this value.
We can get this value with both .then
and the await
keyword in an async
function. Although we can get a promise’s value with both .then
and await
, they work a bit differently.
In the firstFunction
, we (sort of) put the myPromise function aside while it was running, but continued running the other code, which is console.log('second')
in this case. Then, the function resolved with the string I have resolved
, which then got logged after it saw that the callstack was empty.
With the await keyword in secondFunction
, we literally pause the execution of an async function until the value has been resolved before moving to the next line.
This means that it waited for the myPromise
to resolve with the value I have resolved
, and only once that happened, we moved to the next line: second
got logged.
The + operator
103:加号运算符的机制
What’s the value of output?
1 | const set = new Set() |
The +
operator is not only used for adding numerical values, but we can also use it to concatenate strings. Whenever the JavaScript engine sees that one or more values are not a number, it coerces the number into a string.
The first one is 1
, which is a numerical value. 1 + 2
returns the number 3.
However, the second one is a string "Lydia"
. "Lydia"
is a string and 2
is a number: 2
gets coerced into a string. "Lydia"
and "2"
get concatenated, which results in the string "Lydia2"
.
{ name: "Lydia" }
is an object. Neither a number nor an object is a string, so it stringifies both. Whenever we stringify a regular object, it becomes "[Object object]"
. "[Object object]"
concatenated with "2"
becomes "[Object object]2"
.
access properties
106:访问属性 [] 和 . 的不同
What’s its value?
1 | const colorConfig = { |
In JavaScript, we have two ways to access properties on an object: bracket notation, or dot notation. In this example, we use dot notation (colorConfig.colors
) instead of bracket notation (colorConfig["colors"]
).
With dot notation, JavaScript tries to find the property on the object with that exact name. In this example, JavaScript tries to find a property called colors
on the colorConfig
object. There is no proprety called colors
, so this returns undefined
. Then, we try to access the value of the first element by using [1]
. We cannot do this on a value that’s undefined
, so it throws a TypeError
: Cannot read property '1' of undefined
.
JavaScript interprets (or unboxes) statements. When we use bracket notation, it sees the first opening bracket [
and keeps going until it finds the closing bracket ]
. Only then, it will evaluate the statement. If we would’ve used colorConfig[colors[1]]
, it would have returned the value of the red
property on the colorConfig
object.
modifies the original array
108:数组的这些方法哪个不改变原数组?
Which of these methods modifies the original array?
1 | const emojis = ['✨', '🥑', '😍'] |
With splice
method, we modify the original array by deleting, replacing or adding elements. In this case, we removed 2 items from index 1 (we removed '🥑'
and '😍'
) and added the ✨ emoji instead.
map
, filter
and slice
return a new array, find
returns an element, and reduce
returns a reduced value.
reference
109:复制数组的一个值然后改变会怎样呢?
What’s the output?
1 | const food = ['🍕', '🍫', '🥑', '🍔'] |
We set the value of the favoriteFood
property on the info
object equal to the string with the pizza emoji, '🍕'
. A string is a primitive data type. In JavaScript, primitive data types act by reference
In JavaScript, primitive data types (everything that’s not an object) interact by value. In this case, we set the value of the favoriteFood
property on the info
object equal to the value of the first element in the food
array, the string with the pizza emoji in this case ('🍕'
). A string is a primitive data type, and interact by value (see my blogpost if you’re interested in learning more)
Then, we change the value of the favoriteFood
property on the info
object. The food
array hasn’t changed, since the value of favoriteFood
was merely a copy of the value of the first element in the array, and doesn’t have a reference to the same spot in memory as the element on food[0]
. When we log food, it’s still the original array, ['🍕', '🍫', '🥑', '🍔']
.