https://github.com/lydiahallie/javascript-questions/blob/master/zh-CN/README-zh_CN.md
fork了一篇js選擇題,很多題都感覺醍醐灌頂,覺得講的特別好,上邊是原文鏈接
JavaScript 進階問題列表
我在我的 Instagram 上每天都會發布 JavaScript 的多選問題,並且同時也會在這個倉庫中發佈。
從基礎到進階,測試你有多瞭解 JavaScript,刷新你的知識,或者幫助你的 coding 面試!
💪 🚀 我每週都會在這個倉庫下更新新的問題。
答案在問題下方的摺疊部分,點擊即可展開問題。祝你好運 ❤️
1. 輸出是什麼?
function sayHi() {
console.log(name)
console.log(age)
var name = 'Lydia'
let age = 21
}
sayHi()
- A:
Lydia
和undefined
- B:
Lydia
和ReferenceError
- C:
ReferenceError
和21
- D:
undefined
和ReferenceError
#### 答案: D 在函數內部,我們首先通過 `var` 關鍵字聲明瞭 `name` 變量。這意味着變量被提升了(內存空間在創建階段就被設置好了),直到程序運行到定義變量位置之前默認值都是 `undefined`。因爲當我們打印 `name` 變量時還沒有執行到定義變量的位置,因此變量的值保持爲 `undefined`。
通過 let
和 const
關鍵字聲明的變量也會提升,但是和 var
不同,它們不會被初始化。在我們聲明(初始化)之前是不能訪問它們的。這個行爲被稱之爲暫時性死區。當我們試圖在聲明之前訪問它們時,JavaScript 將會拋出一個 ReferenceError
錯誤。
2. 輸出是什麼?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
- A:
0 1 2
和0 1 2
- B:
0 1 2
和3 3 3
- C:
3 3 3
和0 1 2
答案: C
由於 JavaScript 的事件循環,setTimeout
回調會在遍歷結束後才執行。因爲在第一個遍歷中遍歷 i
是通過 var
關鍵字聲明的,所以這個值是全局作用域下的。在遍歷過程中,我們通過一元操作符 ++
來每次遞增 i
的值。當 setTimeout
回調執行的時候,i
的值等於 3。
在第二個遍歷中,遍歷 i
是通過 let
關鍵字聲明的:通過 let
和 const
關鍵字聲明的變量是擁有塊級作用域(指的是任何在 {} 中的內容)。在每次的遍歷過程中,i
都有一個新值,並且每個值都在循環內的作用域中。
3. 輸出是什麼?
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
- A:
20
and62.83185307179586
- B:
20
andNaN
- C:
20
and63
- D:
NaN
and63
答案: B
注意 diameter
的值是一個常規函數,但是 perimeter
的值是一個箭頭函數。
對於箭頭函數,this
關鍵字指向的是它當前周圍作用域(簡單來說是包含箭頭函數的常規函數,如果沒有常規函數的話就是全局對象),這個行爲和常規函數不同。這意味着當我們調用 perimeter
時,this
不是指向 shape
對象,而是它的周圍作用域(在例子中是 window
)。
在 window
中沒有 radius
這個屬性,因此返回 undefined
。
4. 輸出是什麼?
+true;
!"Lydia";
- A:
1
andfalse
- B:
false
andNaN
- C:
false
andfalse
答案: A
一元操作符加號嘗試將 bool 轉爲 number。true
轉換爲 number 的話爲 1
,false
爲 0
。
字符串 'Lydia'
是一個真值,真值取反那麼就返回 false
。
5. 哪一個是正確的?
const bird = {
size: 'small'
}
const mouse = {
name: 'Mickey',
small: true
}
- A:
mouse.bird.size
是無效的 - B:
mouse[bird.size]
是無效的 - C:
mouse[bird["size"]]
是無效的 - D: 以上三個選項都是有效的
答案: A
在 JavaScript 中,所有對象的 keys 都是字符串(除非對象是 Symbol)。儘管我們可能不會定義它們爲字符串,但它們在底層總會被轉換爲字符串。
當我們使用括號語法時([]),JavaScript 會解釋(或者 unboxes)語句。它首先看到第一個開始括號 [
並繼續前進直到找到結束括號 ]
。只有這樣,它纔會計算語句的值。
mouse[bird.size]
:首先計算 bird.size
,這會得到 small
。mouse["small"]
返回 true
。
然後使用點語法的話,上面這一切都不會發生。mouse
沒有 bird
這個 key,這也就意味着 mouse.bird
是 undefined
。然後當我們使用點語法 mouse.bird.size
時,因爲 mouse.bird
是 undefined
,這也就變成了 undefined.size
。這個行爲是無效的,並且會拋出一個錯誤類似 Cannot read property "size" of undefined
。
6. 輸出是什麼?
let c = { greeting: 'Hey!' }
let d
d = c
c.greeting = 'Hello'
console.log(d.greeting)
- A:
Hello
- B:
undefined
- C:
ReferenceError
- D:
TypeError
答案: A
在 JavaScript 中,當設置兩個對象彼此相等時,它們會通過引用進行交互。
首先,變量 c
的值是一個對象。接下來,我們給 d
分配了一個和 c
對象相同的引用。
因此當我們改變其中一個對象時,其實是改變了所有的對象。
7. 輸出是什麼?
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
- A:
true
false
true
- B:
false
false
true
- C:
true
false
false
- D:
false
true
true
答案: C
new Number()
是一個內建的函數構造器。雖然它看着像是一個 number,但它實際上並不是一個真實的 number:它有一堆額外的功能並且它是一個對象。
當我們使用 ==
操作符時,它只會檢查兩者是否擁有相同的值。因爲它們的值都是 3
,因此返回 true
。
然後,當我們使用 ===
操作符時,兩者的值以及類型都應該是相同的。new Number()
是一個對象而不是 number,因此返回 false
。
8. 輸出是什麼?
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = 'green' } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
- A:
orange
- B:
purple
- C:
green
- D:
TypeError
答案: D
colorChange
是一個靜態方法。靜態方法被設計爲只能被創建它們的構造器使用(也就是 Chameleon
),並且不能傳遞給實例。因爲 freddie
是一個實例,靜態方法不能被實例使用,因此拋出了 TypeError
錯誤。
9. 輸出是什麼?
let greeting
greetign = {} // Typo!
console.log(greetign)
- A:
{}
- B:
ReferenceError: greetign is not defined
- C:
undefined
答案: A
代碼打印出了一個對象,這是因爲我們在全局對象上創建了一個空對象!當我們將 greeting
寫錯成 greetign
時,JS 解釋器實際在上瀏覽器中將它視爲 global.greetign = {}
(或者 window.greetign = {}
)。
爲了避免這個爲題,我們可以使用 `“use strict”。這能確保當你聲明變量時必須賦值。
10. 當我們這麼做時,會發生什麼?
function bark() {
console.log('Woof!')
}
bark.animal = 'dog'
- A: 正常運行!
- B:
SyntaxError
. 你不能通過這種方式給函數增加屬性。 - C:
undefined
- D:
ReferenceError
答案: A
這在 JavaScript 中是可以的,因爲函數是對象!(除了基本類型之外其他都是對象)
函數是一個特殊的對象。你寫的這個代碼其實不是一個實際的函數。函數是一個擁有屬性的對象,並且屬性也可被調用。
11. 輸出是什麼?
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
console.log(member.getFullName());
- A:
TypeError
- B:
SyntaxError
- C:
Lydia Hallie
- D:
undefined
undefined
答案: A
你不能像常規對象那樣,給構造函數添加屬性。如果你想一次性給所有實例添加特性,你應該使用原型。因此本例中,使用如下方式:
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
這纔會使 member.getFullName()
起作用。爲什麼這麼做有益的?假設我們將這個方法添加到構造函數本身裏。也許不是每個 Person
實例都需要這個方法。這將浪費大量內存空間,因爲它們仍然具有該屬性,這將佔用每個實例的內存空間。相反,如果我們只將它添加到原型中,那麼它只存在於內存中的一個位置,但是所有實例都可以訪問它!
12. 輸出是什麼?
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const lydia = new Person('Lydia', 'Hallie')
const sarah = Person('Sarah', 'Smith')
console.log(lydia)
console.log(sarah)
- A:
Person {firstName: "Lydia", lastName: "Hallie"}
andundefined
- B:
Person {firstName: "Lydia", lastName: "Hallie"}
andPerson {firstName: "Sarah", lastName: "Smith"}
- C:
Person {firstName: "Lydia", lastName: "Hallie"}
and{}
- D:
Person {firstName: "Lydia", lastName: "Hallie"}
andReferenceError
答案: A
對於 sarah
,我們沒有使用 new
關鍵字。當使用 new
時,this
引用我們創建的空對象。當未使用 new
時,this
引用的是全局對象(global object)。
我們說 this.firstName
等於 "Sarah"
,並且 this.lastName
等於 "Smith"
。實際上我們做的是,定義了 global.firstName = 'Sarah'
和 global.lastName = 'Smith'
。而 sarah
本身是 undefined
。
13. 事件傳播的三個階段是什麼?
- A: Target > Capturing > Bubbling
- B: Bubbling > Target > Capturing
- C: Target > Bubbling > Capturing
- D: Capturing > Target > Bubbling
答案: D
在捕獲(capturing)階段中,事件從祖先元素向下傳播到目標元素。當事件達到目標(target)元素後,冒泡(bubbling)纔開始。
14. 所有對象都有原型。
- A: 對
- B: 錯
答案: B
除了基本對象(base object),所有對象都有原型。基本對象可以訪問一些方法和屬性,比如 .toString
。這就是爲什麼你可以使用內置的 JavaScript 方法!所有這些方法在原型上都是可用的。雖然 JavaScript 不能直接在對象上找到這些方法,但 JavaScript 會沿着原型鏈找到它們,以便於你使用。
15. 輸出是什麼?
function sum(a, b) {
return a + b
}
sum(1, '2')
- A:
NaN
- B:
TypeError
- C:
"12"
- D:
3
答案: C
JavaScript 是一種動態類型語言:我們不指定某些變量的類型。值可以在你不知道的情況下自動轉換成另一種類型,這種類型稱爲隱式類型轉換(implicit type coercion)。Coercion 是指將一種類型轉換爲另一種類型。
在本例中,JavaScript 將數字 1
轉換爲字符串,以便函數有意義並返回一個值。在數字類型(1
)和字符串類型('2'
)相加時,該數字被視爲字符串。我們可以連接字符串,比如 "Hello" + "World"
,這裏發生的是 "1" + "2"
,它返回 "12"
。
16. 輸出是什麼?
let number = 0
console.log(number++)
console.log(++number)
console.log(number)
- A:
1
1
2
- B:
1
2
2
- C:
0
2
2
- D:
0
1
2
答案: C
一元後自增運算符 ++
:
- 返回值(返回
0
) - 值自增(number 現在是
1
)
一元前自增運算符 ++
:
- 值自增(number 現在是
2
) - 返回值(返回
2
)
結果是 0 2 2
.
17. 輸出是什麼?
function getPersonInfo(one, two, three) {
console.log(one)
console.log(two)
console.log(three)
}
const person = 'Lydia'
const age = 21
getPersonInfo`${person} is ${age} years old`
- A:
"Lydia"
21
["", " is ", " years old"]
- B:
["", " is ", " years old"]
"Lydia"
21
- C:
"Lydia"
["", " is ", " years old"]
21
答案: B
如果使用標記模板字面量,第一個參數的值總是包含字符串的數組。其餘的參數獲取的是傳遞的表達式的值!
18. 輸出是什麼?
function checkAge(data) {
if (data === { age: 18 }) {
console.log('You are an adult!')
} else if (data == { age: 18 }) {
console.log('You are still an adult.')
} else {
console.log(`Hmm.. You don't have an age I guess`)
}
}
checkAge({ age: 18 })
- A:
You are an adult!
- B:
You are still an adult.
- C:
Hmm.. You don't have an age I guess
答案: C
在測試相等性時,基本類型通過它們的值(value)進行比較,而對象通過它們的引用(reference)進行比較。JavaScript 檢查對象是否具有對內存中相同位置的引用。
題目中我們正在比較的兩個對象不是同一個引用:作爲參數傳遞的對象引用的內存位置,與用於判斷相等的對象所引用的內存位置並不同。
這也是 { age: 18 } === { age: 18 }
和 { age: 18 } == { age: 18 }
都返回 false
的原因。
19. 輸出是什麼?
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
- A:
"number"
- B:
"array"
- C:
"object"
- D:
"NaN"
答案: C
擴展運算符(...args
)會返回實參組成的數組。而數組是對象,因此 typeof args
返回 "object"
。
20. 輸出是什麼?
function getAge() {
'use strict'
age = 21
console.log(age)
}
getAge()
- A:
21
- B:
undefined
- C:
ReferenceError
- D:
TypeError
答案: C
使用 "use strict"
,你可以確保不會意外地聲明全局變量。我們從來沒有聲明變量 age
,因爲我們使用 "use strict"
,它將拋出一個引用錯誤。如果我們不使用 "use strict"
,它就會工作,因爲屬性 age
會被添加到全局對象中了。
21. 輸出是什麼?
const sum = eval('10*10+5')
- A:
105
- B:
"105"
- C:
TypeError
- D:
"10*10+5"
答案: A
代碼以字符串形式傳遞進來,eval
對其求值。如果它是一個表達式,就像本例中那樣,它對表達式求值。表達式是 10 * 10 + 5
。這將返回數字 105
。
22. cool_secret 可訪問多長時間?
sessionStorage.setItem('cool_secret', 123)
- A: 永遠,數據不會丟失。
- B: 當用戶關掉標籤頁時。
- C: 當用戶關掉整個瀏覽器,而不只是關掉標籤頁。
- D: 當用戶關閉電腦時。
答案: B
關閉 tab 標籤頁 後,sessionStorage
存儲的數據纔會刪除。
如果使用 localStorage
,那麼數據將永遠在那裏,除非調用了 localStorage.clear()
。
23. 輸出是什麼?
var num = 8
var num = 10
console.log(num)
- A:
8
- B:
10
- C:
SyntaxError
- D:
ReferenceError
答案: B
使用 var
關鍵字,你可以用相同的名稱聲明多個變量。然後變量將保存最新的值。
你不能使用 let
或 const
來實現這一點,因爲它們是塊作用域的。
24. 輸出是什麼?
const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])
obj.hasOwnProperty('1')
obj.hasOwnProperty(1)
set.has('1')
set.has(1)
- A:
false
true
false
true
- B:
false
true
true
true
- C:
true
true
false
true
- D:
true
true
true
true
答案: C
所有對象的鍵(不包括 Symbol)在底層都是字符串,即使你自己沒有將其作爲字符串輸入。這就是爲什麼 obj.hasOwnProperty('1')
也返回 true
。
對於集合,它不是這樣工作的。在我們的集合中沒有 '1'
:set.has('1')
返回 false
。它有數字類型爲 1
,set.has(1)
返回 true
。
25. 輸出是什麼?
const obj = { a: 'one', b: 'two', a: 'three' }
console.log(obj)
- A:
{ a: "one", b: "two" }
- B:
{ b: "two", a: "three" }
- C:
{ a: "three", b: "two" }
- D:
SyntaxError
答案: C
如果你有兩個名稱相同的鍵,則鍵會被替換掉。它仍然位於第一個鍵出現的位置,但是值是最後出現那個鍵的值。
26. JavaScript 全局執行上下文爲你做了兩件事:全局對象和 this 關鍵字。
- A: 對
- B: 錯
- C: 看情況
答案: A
基本執行上下文是全局執行上下文:它是代碼中隨處可訪問的內容。
27. 輸出是什麼?
for (let i = 1; i < 5; i++) {
if (i === 3) continue
console.log(i)
}
- A:
1
2
- B:
1
2
3
- C:
1
2
4
- D:
1
3
4
答案: C
如果某個條件返回 true
,則 continue
語句跳過本次迭代。
28. 輸出是什麼?
String.prototype.giveLydiaPizza = () => {
return 'Just give Lydia pizza already!'
}
const name = 'Lydia'
name.giveLydiaPizza()
- A:
"Just give Lydia pizza already!"
- B:
TypeError: not a function
- C:
SyntaxError
- D:
undefined
答案: A
String
是內置的構造函數,我們可以向它添加屬性。我只是在它的原型中添加了一個方法。基本類型字符串被自動轉換爲字符串對象,由字符串原型函數生成。因此,所有 string(string 對象)都可以訪問該方法!
29. 輸出是什麼?
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }
a[b] = 123
a[c] = 456
console.log(a[b])
- A:
123
- B:
456
- C:
undefined
- D:
ReferenceError
答案: B
對象的鍵被自動轉換爲字符串。我們試圖將一個對象 b
設置爲對象 a
的鍵,且相應的值爲 123
。
然而,當字符串化一個對象時,它會變成 "[object Object]"
。因此這裏說的是,a["[object Object]"] = 123
。然後,我們再一次做了同樣的事情,c
是另外一個對象,這裏也有隱式字符串化,於是,a["[object Object]"] = 456
。
然後,我們打印 a[b]
,也就是 a["[object Object]"]
。之前剛設置爲 456
,因此返回的是 456
。
30. 輸出是什麼?
const foo = () => console.log('First')
const bar = () => setTimeout(() => console.log('Second'))
const baz = () => console.log('Third')
bar()
foo()
baz()
- A:
First
Second
Third
- B:
First
Third
Second
- C:
Second
First
Third
- D:
Second
Third
First
答案: B
我們有一個 setTimeout
函數,並首先調用它。然而,它是最後打印日誌的。
這是因爲在瀏覽器中,我們不僅有運行時引擎,還有一個叫做 WebAPI
的東西。WebAPI
提供了 setTimeout
函數,也包含其他的,例如 DOM。
將 callback 推送到 WebAPI 後,setTimeout
函數本身(但不是回調!)將從棧中彈出。
現在,foo
被調用,打印 "First"
。
foo
從棧中彈出,baz
被調用. 打印 "Third"
。
WebAPI 不能隨時向棧內添加內容。相反,它將回調函數推到名爲 queue 的地方。
這就是事件循環開始工作的地方。一個事件循環查看棧和任務隊列。如果棧是空的,它接受隊列上的第一個元素並將其推入棧。
bar
被調用,打印 "Second"
,然後它被棧彈出。
31. 當點擊按鈕時,event.target是什麼?
<div onclick="console.log('first div')">
<div onclick="console.log('second div')">
<button onclick="console.log('button')">
Click!
</button>
</div>
</div>
- A: Outer
div
- B: Inner
div
- C:
button
- D: 一個包含所有嵌套元素的數組。
答案: C
導致事件的最深嵌套的元素是事件的 target。你可以通過 event.stopPropagation
來停止冒泡。
32. 當您單擊該段落時,日誌輸出是什麼?
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
- A:
p
div
- B:
div
p
- C:
p
- D:
div
答案: A
如果我們點擊 p
,我們會看到兩個日誌:p
和 div
。在事件傳播期間,有三個階段:捕獲、目標和冒泡。默認情況下,事件處理程序在冒泡階段執行(除非將 useCapture
設置爲 true
)。它從嵌套最深的元素向外傳播。
33. 輸出是什麼?
const person = { name: 'Lydia' }
function sayHi(age) {
console.log(`${this.name} is ${age}`)
}
sayHi.call(person, 21)
sayHi.bind(person, 21)
- A:
undefined is 21
Lydia is 21
- B:
function
function
- C:
Lydia is 21
Lydia is 21
- D:
Lydia is 21
function
答案: D
使用這兩種方法,我們都可以傳遞我們希望 this
關鍵字引用的對象。但是,.call
是立即執行的。
.bind
返回函數的副本,但帶有綁定上下文!它不是立即執行的。
34. 輸出是什麼?
function sayHi() {
return (() => 0)()
}
typeof sayHi()
- A:
"object"
- B:
"number"
- C:
"function"
- D:
"undefined"
答案: B
sayHi
方法返回的是立即執行函數(IIFE)的返回值.此立即執行函數的返回值是 0
, 類型是 number
參考:只有7種內置類型:null
,undefined
,boolean
,number
,string
,object
和 symbol
。 function
不是一種類型,函數是對象,它的類型是object
。
35. 下面哪些值是 falsy?
0
new Number(0)
('')
(' ')
new Boolean(false)
undefined
- A:
0
,''
,undefined
- B:
0
,new Number(0)
,''
,new Boolean(false)
,undefined
- C:
0
,''
,new Boolean(false)
,undefined
- D: All of them are falsy
答案: A
只有 6 種 falsy 值:
undefined
null
NaN
0
''
(empty string)false
Function
構造函數, 比如 new Number
和 new Boolean
,是 truthy。
36. 輸出是什麼?
console.log(typeof typeof 1)
- A:
"number"
- B:
"string"
- C:
"object"
- D:
"undefined"
答案: B
typeof 1
返回 "number"
。
typeof "number"
返回 "string"
。
37. 輸出是什麼?
const numbers = [1, 2, 3]
numbers[10] = 11
console.log(numbers)
- A:
[1, 2, 3, 7 x null, 11]
- B:
[1, 2, 3, 11]
- C:
[1, 2, 3, 7 x empty, 11]
- D:
SyntaxError
答案: C
當你爲數組設置超過數組長度的值的時候, JavaScript 會創建名爲 “empty slots” 的東西。它們的值實際上是 undefined
。你會看到以下場景:
[1, 2, 3, 7 x empty, 11]
這取決於你的運行環境(每個瀏覽器,以及 node 環境,都有可能不同)
38. 輸出是什麼?
(() => {
let x, y
try {
throw new Error()
} catch (x) {
(x = 1), (y = 2)
console.log(x)
}
console.log(x)
console.log(y)
})()
- A:
1
undefined
2
- B:
undefined
undefined
undefined
- C:
1
1
2
- D:
1
undefined
undefined
答案: A
catch
代碼塊接收參數 x
。當我們傳遞參數時,這與之前定義的變量 x
不同 。這個 x
是屬於 catch
塊級作用域的。
然後,我們將塊級作用域中的變量賦值爲 1
,同時也設置了變量 y
的值。現在,我們打印塊級作用域中的變量 x
,值爲 1
。
catch
塊之外的變量 x
的值仍爲 undefined
, y
的值爲 2
。當我們在 catch
塊之外執行 console.log(x)
時,返回 undefined
,y
返回 2
。
39. JavaScript 中的一切都是?
答案
答案: A
JavaScript 只有基本類型和對象。
基本類型包括 boolean
, null
, undefined
, bigint
, number
, string
, symbol
。
40. 輸出是什麼?
[[0, 1], [2, 3]].reduce(
(acc, cur) => {
return acc.concat(cur)
},
[1, 2]
)
- A:
[0, 1, 2, 3, 1, 2]
- B:
[6, 1, 2]
- C:
[1, 2, 0, 1, 2, 3]
- D:
[1, 2, 6]
答案: C
[1, 2]
是初始值。初始值將會作爲首次調用時第一個參數 acc
的值。在第一次執行時, acc
的值是 [1, 2]
, cur
的值是 [0, 1]
。合併它們,結果爲 [1, 2, 0, 1]
。
第二次執行, acc
的值是 [1, 2, 0, 1]
, cur
的值是 [2, 3]
。合併它們,最終結果爲 [1, 2, 0, 1, 2, 3]
41. 輸出是什麼?
!!null
!!''
!!1
- A:
false
true
false
- B:
false
false
true
- C:
false
true
true
- D:
true
true
false
答案: B
null
是 falsy。 !null
的值是 true
。 !true
的值是 false
。
""
是 falsy。 !""
的值是 true
。 !true
的值是 false
。
1
是 truthy。 !1
的值是 false
。 !false
的值是 true
。
42. setInterval
方法的返回值是什麼?
setInterval(() => console.log('Hi'), 1000)
- A: 一個唯一的id
- B: 該方法指定的毫秒數
- C: 傳遞的函數
- D:
undefined
答案: A
setInterval
返回一個唯一的 id。此 id 可被用於 clearInterval
函數來取消定時。
43. 輸出是什麼?
[...'Lydia']
- A:
["L", "y", "d", "i", "a"]
- B:
["Lydia"]
- C:
[[], "Lydia"]
- D:
[["L", "y", "d", "i", "a"]]
答案: A
string 類型是可迭代的。擴展運算符將迭代的每個字符映射成一個元素。
44. 輸出是什麼?
function* generator(i) {
yield i;
yield i * 2;
}
const gen = generator(10);
console.log(gen.next().value);
console.log(gen.next().value);
- A:
[0, 10], [10, 20]
- B:
20, 20
- C:
10, 20
- D:
0, 10 and 10, 20
答案: C
一般的函數在執行之後是不能中途停下的。但是,生成器函數卻可以中途“停下”,之後可以再從停下的地方繼續。當生成器遇到yield
關鍵字的時候,會生成yield
後面的值。注意,生成器在這種情況下不 返回 (return )值,而是 生成 (yield)值。
首先,我們用10
作爲參數i
來初始化生成器函數。然後使用next()
方法一步步執行生成器。第一次執行生成器的時候,i
的值爲10
,遇到第一個yield
關鍵字,它要生成i
的值。此時,生成器“暫停”,生成了10
。
然後,我們再執行next()
方法。生成器會從剛纔暫停的地方繼續,這個時候i
還是10
。於是我們走到了第二個yield
關鍵字處,這時候需要生成的值是i*2
,i
爲10
,那麼此時生成的值便是20
。所以這道題的最終結果是10,20
。
45. 返回值是什麼?
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
- A:
"one"
- B:
"two"
- C:
"two" "one"
- D:
"one" "two"
答案: B
當我們向Promise.race
方法中傳入多個Promise
時,會進行 優先 解析。在這個例子中,我們用setTimeout
給firstPromise
和secondPromise
分別設定了500ms和100ms的定時器。這意味着secondPromise
會首先解析出字符串two
。那麼此時res
參數即爲two
,是爲輸出結果。
46. 輸出是什麼?
let person = { name: "Lydia" };
const members = [person];
person = null;
console.log(members);
- A:
null
- B:
[null]
- C:
[{}]
- D:
[{ name: "Lydia" }]
答案: D
首先我們聲明瞭一個擁有name
屬性的對象 person
。
然後我們又聲明瞭一個變量members
. 將首個元素賦值爲變量person
。 當設置兩個對象彼此相等時,它們會通過 引用 進行交互。但是當你將引用從一個變量分配至另一個變量時,其實只是執行了一個 複製 操作。(注意一點,他們的引用 並不相同!)
接下來我們讓person
等於null
。
我們沒有修改數組第一個元素的值,而只是修改了變量person
的值,因爲元素(複製而來)的引用與person
不同。members
的第一個元素仍然保持着對原始對象的引用。當我們輸出members
數組時,第一個元素會將引用的對象打印出來。
47. 輸出是什麼?
const person = {
name: "Lydia",
age: 21
};
for (const item in person) {
console.log(item);
}
- A:
{ name: "Lydia" }, { age: 21 }
- B:
"name", "age"
- C:
"Lydia", 21
- D:
["name", "Lydia"], ["age", 21]
答案: B
在for-in
循環中,我們可以通過對象的key來進行迭代,也就是這裏的name
和age
。在底層,對象的key都是字符串(如果他們不是Symbol的話)。在每次循環中,我們將item
設定爲當前遍歷到的key.所以一開始,item
是name
,之後 item
輸出的則是age
。
48. 輸出是什麼?
console.log(3 + 4 + "5");
- A:
"345"
- B:
"75"
- C:
12
- D:
"12"
答案: B
當所有運算符的 優先級 相同時,計算表達式需要確定運算符的結合順序,即從右到左還是從左往右。在這個例子中,我們只有一類運算符+
,對於加法來說,結合順序就是從左到右。
3 + 4
首先計算,得到數字7
.
由於類型的強制轉換,7 + '5'
的結果是"75"
. JavaScript將7
轉換成了字符串,可以參考問題15.我們可以用+
號把兩個字符串連接起來。 "7" + "5"
就得到了"75"
.
49. num
的值是什麼?
const num = parseInt("7*6", 10);
- A:
42
- B:
"42"
- C:
7
- D:
NaN
答案: C
只返回了字符串中第一個字母. 設定了 進制 後 (也就是第二個參數,指定需要解析的數字是什麼進制: 十進制、十六機制、八進制、二進制等等……),parseInt
檢查字符串中的字符是否合法. 一旦遇到一個在指定進制中不合法的字符後,立即停止解析並且忽略後面所有的字符。
*
就是不合法的數字字符。所以只解析到"7"
,並將其解析爲十進制的7
. num
的值即爲7
.
50. 輸出是什麼?
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});
- A:
[]
- B:
[null, null, null]
- C:
[undefined, undefined, undefined]
- D:
[ 3 x empty ]
答案: C
對數組進行映射的時候,num
就是當前循環到的元素. 在這個例子中,所有的映射都是number類型,所以if中的判斷typeof num === "number"
結果都是true
.map函數創建了新數組並且將函數的返回值插入數組。
但是,沒有任何值返回。當函數沒有返回任何值時,即默認返回undefined
.對數組中的每一個元素來說,函數塊都得到了這個返回值,所以結果中每一個元素都是undefined
.
51. 輸出的是什麼?
function getInfo(member, year) {
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
- A:
{ name: "Lydia" }, "1997"
- B:
{ name: "Sarah" }, "1998"
- C:
{ name: "Lydia" }, "1998"
- D:
{ name: "Sarah" }, "1997"
答案: A
普通參數都是 值 傳遞的,而對象則不同,是 引用 傳遞。所以說,birthYear
是值傳遞,因爲他是個字符串而不是對象。當我們對參數進行值傳遞時,會創建一份該值的 複製 。(可以參考問題46)
變量birthYear
有一個對"1997"
的引用,而傳入的參數也有一個對"1997"
的引用,但二者的引用並不相同。當我們通過給 year
賦值"1998"
來更新year
的值的時候我們只是更新了year
(的引用)。此時birthYear
仍然是"1997"
.
而person
是個對象。參數member
引用與之 相同的 對象。當我們修改member
所引用對象的屬性時,person
的相應屬性也被修改了,因爲他們引用了相同的對象. person
的 name
屬性也變成了 "Lydia"
.
52. 輸出是什麼?
function greeting() {
throw "Hello world!";
}
function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error:", e);
}
}
sayHi();
- A:
"It worked! Hello world!"
- B:
"Oh no an error: undefined
- C:
SyntaxError: can only throw Error objects
- D:
"Oh no an error: Hello world!
答案: D
通過throw
語句,我麼可以創建自定義錯誤。 而通過它,我們可以拋出異常。異常可以是一個字符串, 一個 數字, 一個 布爾類型 或者是一個 對象。在本例中,我們的異常是字符串'Hello world'
.
通過 catch
語句,我們可以設定當try
語句塊中拋出異常後應該做什麼處理。在本例中拋出的異常是字符串'Hello world'
. e
就是這個字符串,因此被輸出。最終結果就是'Oh an error: Hello world'
.
53. 輸出是什麼?
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}
const myCar = new Car();
console.log(myCar.make);
- A:
"Lamborghini"
- B:
"Maserati"
- C:
ReferenceError
- D:
TypeError
答案: B
返回屬性的時候,屬性的值等於 返回的 值,而不是構造函數中設定的值。我們返回了字符串 "Maserati"
,所以 myCar.make
等於"Maserati"
.
54. 輸出是什麼?
(() => {
let x = (y = 10);
})();
console.log(typeof x);
console.log(typeof y);
- A:
"undefined", "number"
- B:
"number", "number"
- C:
"object", "number"
- D:
"number", "undefined"
答案: A
let x = y = 10;
是下面這個表達式的縮寫:
y = 10;
let x = y;
我們設定y
等於10
時,我們實際上增加了一個屬性y
給全局對象(瀏覽器裏的window
, Nodejs裏的global
)。在瀏覽器中, window.y
等於10
.
然後我們聲明瞭變量x
等於y
,也是10
.但變量是使用 let
聲明的,它只作用於 塊級作用域, 僅在聲明它的塊中有效;就是案例中的立即調用表達式(IIFE)。使用typeof
操作符時, 操作值 x
沒有被定義:因爲我們在x
聲明塊的外部,無法調用它。這就意味着x
未定義。未分配或是未聲明的變量類型爲"undefined"
. console.log(typeof x)
返回"undefined"
.
而我們創建了全局變量y
,並且設定y
等於10
.這個值在我們的代碼各處都訪問的到。 y
已經被定義了,而且有一個"number"
類型的值。 console.log(typeof y)
返回"number"
.
55. 輸出是什麼?
class Dog {
constructor(name) {
this.name = name;
}
}
Dog.prototype.bark = function() {
console.log(`Woof I am ${this.name}`);
};
const pet = new Dog("Mara");
pet.bark();
delete Dog.prototype.bark;
pet.bark();
- A:
"Woof I am Mara"
,TypeError
- B:
"Woof I am Mara"
,"Woof I am Mara"
- C:
"Woof I am Mara"
,undefined
- D:
TypeError
,TypeError
答案: A
我們可以用delete
關鍵字刪除對象的屬性,對原型也是適用的。刪除了原型的屬性後,該屬性在原型鏈上就不可用了。在本例中,函數bark
在執行了delete Dog.prototype.bark
後不可用, 然而後面的代碼還在調用它。
當我們嘗試調用一個不存在的函數時TypeError
異常會被拋出。在本例中就是 TypeError: pet.bark is not a function
,因爲pet.bark
是undefined
.
56. 輸出是什麼?
const set = new Set([1, 1, 2, 3, 4]);
console.log(set);
- A:
[1, 1, 2, 3, 4]
- B:
[1, 2, 3, 4]
- C:
{1, 1, 2, 3, 4}
- D:
{1, 2, 3, 4}
答案: D
Set
對象是獨一無二的值的集合:也就是說同一個值在其中僅出現一次。
我們傳入了數組[1, 1, 2, 3, 4]
,他有一個重複值1
.以爲一個集合裏不能有兩個重複的值,其中一個就被移除了。所以結果是 {1, 2, 3, 4}
.
57. 輸出是什麼?
// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";
myCounter += 1;
console.log(myCounter);
- A:
10
- B:
11
- C:
Error
- D:
NaN
答案: C
引入的模塊是 只讀 的: 你不能修改引入的模塊。只有導出他們的模塊才能修改其值。
當我們給myCounter
增加一個值的時候會拋出一個異常: myCounter
是隻讀的,不能被修改。
58. 輸出是什麼?
const name = "Lydia";
age = 21;
console.log(delete name);
console.log(delete age);
- A:
false
,true
- B:
"Lydia"
,21
- C:
true
,true
- D:
undefined
,undefined
答案: A
delete
操作符返回一個布爾值: true
指刪除成功,否則返回false
. 但是通過 var
, const
或 let
關鍵字聲明的變量無法用 delete
操作符來刪除。
name
變量由const
關鍵字聲明,所以刪除不成功:返回 false
. 而我們設定age
等於21
時,我們實際上添加了一個名爲age
的屬性給全局對象。對象中的屬性是可以刪除的,全局對象也是如此,所以delete age
返回true
.
59. 輸出是什麼?
const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;
console.log(y);
- A:
[[1, 2, 3, 4, 5]]
- B:
[1, 2, 3, 4, 5]
- C:
1
- D:
[1]
答案: C
我們可以通過解構賦值來解析來自對象的數組或屬性的值,比如說:
[a, b] = [1, 2];
a
的值現在是1
,b
的值現在是2
.而在題目中,我們是這麼做的:
[y] = [1, 2, 3, 4, 5];
也就是說,y
等於數組的第一個值就是數字1
.我們輸出y
, 返回1
.
60. 輸出是什麼?
const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };
console.log(admin);
- A:
{ admin: true, user: { name: "Lydia", age: 21 } }
- B:
{ admin: true, name: "Lydia", age: 21 }
- C:
{ admin: true, user: ["Lydia", 21] }
- D:
{ admin: true }
答案: B
擴展運算符...
爲對象的組合提供了可能。你可以複製對象中的鍵值對,然後把它們加到另一個對象裏去。在本例中,我們複製了user
對象鍵值對,然後把它們加入到admin
對象中。admin
對象就擁有了這些鍵值對,所以結果爲{ admin: true, name: "Lydia", age: 21 }
.
61. 輸出是什麼?
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
- A:
{ name: "Lydia", age: 21 }
,["name", "age"]
- B:
{ name: "Lydia", age: 21 }
,["name"]
- C:
{ name: "Lydia"}
,["name", "age"]
- D:
{ name: "Lydia"}
,["age"]
答案: B
通過defineProperty
方法,我們可以給對象添加一個新屬性,或者修改已經存在的屬性。而我們使用defineProperty
方法給對象添加了一個屬性之後,屬性默認爲 不可枚舉(not enumerable). Object.keys
方法僅返回對象中 可枚舉(enumerable) 的屬性,因此只剩下了"name"
.
用defineProperty
方法添加的屬性默認不可變。你可以通過writable
, configurable
和 enumerable
屬性來改變這一行爲。這樣的話, 相比於自己添加的屬性,defineProperty
方法添加的屬性有了更多的控制權。
62. 輸出是什麼?
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};
const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);
- A:
"{"level":19, "health":90}"
- B:
"{"username": "lydiahallie"}"
- C:
"["level", "health"]"
- D:
"{"username": "lydiahallie", "level":19, "health":90}"
答案: A
JSON.stringify
的第二個參數是 替代者(replacer). 替代者(replacer)可以是個函數或數組,用以控制哪些值如何被轉換爲字符串。
如果替代者(replacer)是個 數組 ,那麼就只有包含在數組中的屬性將會被轉化爲字符串。在本例中,只有名爲"level"
和 "health"
的屬性被包括進來, "username"
則被排除在外。 data
就等於 "{"level":19, "health":90}"
.
而如果替代者(replacer)是個 函數,這個函數將被對象的每個屬性都調用一遍。
函數返回的值會成爲這個屬性的值,最終體現在轉化後的JSON字符串中(譯者注:Chrome下,經過實驗,如果所有屬性均返回同一個值的時候有異常,會直接將返回值作爲結果輸出而不會輸出JSON字符串),而如果返回值爲undefined
,則該屬性會被排除在外。
63. 輸出是什麼?
let num = 10;
const increaseNumber = () => num++;
const increasePassedNumber = number => number++;
const num1 = increaseNumber();
const num2 = increasePassedNumber(num1);
console.log(num1);
console.log(num2);
- A:
10
,10
- B:
10
,11
- C:
11
,11
- D:
11
,12
答案: A
一元操作符 ++
先返回 操作值, 再累加 操作值。num1
的值是10
, 因爲increaseNumber
函數首先返回num
的值,也就是10
,隨後再進行 num
的累加。
num2
是10
因爲我們將 num1
傳入increasePassedNumber
. number
等於10
(num1
的值。同樣道理,++
先返回 操作值, 再累加 操作值。) number
是10
,所以num2
也是10
.
64. 輸出什麼?
const value = { number: 10 };
const multiply = (x = { ...value }) => {
console.log(x.number *= 2);
};
multiply();
multiply();
multiply(value);
multiply(value);
- A:
20
,40
,80
,160
- B:
20
,40
,20
,40
- C:
20
,20
,20
,40
- D:
NaN
,NaN
,20
,40
答案: C
在ES6中,我們可以使用默認值初始化參數。如果沒有給函數傳參,或者傳的參值爲 "undefined"
,那麼參數的值將是默認值。上述例子中,我們將 value
對象進行了解構並傳到一個新對象中,因此 x
的默認值爲 {number:10}
。
默認參數在調用時纔會進行計算,每次調用函數時,都會創建一個新的對象。我們前兩次調用 multiply
函數且不傳遞值,那麼每一次 x
的默認值都爲 {number:10}
,因此打印出該數字的乘積值爲20
。
第三次調用 multiply
時,我們傳遞了一個參數,即對象value
。 *=
運算符實際上是x.number = x.number * 2
的簡寫,我們修改了x.number
的值,並打印出值20
。
第四次,我們再次傳遞value
對象。 x.number
之前被修改爲20
,所以x.number * = 2
打印爲40
。
65. 輸出什麼?
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
- A:
1
2
and3
3
and6
4
- B:
1
2
and2
3
and3
4
- C:
1
undefined
and2
undefined
and3
undefined
and4
undefined
- D:
1
2
andundefined
3
andundefined
4
答案: D
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
被打印出。
66. 使用哪個構造函數可以成功繼承Dog
類?
class Dog {
constructor(name) {
this.name = name;
}
};
class Labrador extends Dog {
// 1
constructor(name, size) {
this.size = size;
}
// 2
constructor(name, size) {
super(name);
this.size = size;
}
// 3
constructor(size) {
super(name);
this.size = size;
}
// 4
constructor(name, size) {
this.name = name;
this.size = size;
}
};
- A: 1
- B: 2
- C: 3
- D: 4
答案: B
在子類中,在調用super
之前不能訪問到this
關鍵字。 如果這樣做,它將拋出一個ReferenceError
:1和4將引發一個引用錯誤。
使用super
關鍵字,需要用給定的參數來調用父類的構造函數。 父類的構造函數接收name
參數,因此我們需要將name
傳遞給super
。
Labrador
類接收兩個參數,name
參數是由於它繼承了Dog
,size
作爲Labrador
類的額外屬性,它們都需要傳遞給Labrador
的構造函數,因此使用構造函數2正確完成。
67. 輸出什麼?
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
- A:
running index.js
,running sum.js
,3
- B:
running sum.js
,running index.js
,3
- C:
running sum.js
,3
,running index.js
- D:
running index.js
,undefined
,running sum.js
答案: B
import
命令是編譯階段執行的,在代碼運行之前。因此這意味着被導入的模塊會先運行,而導入模塊的文件會後執行。
這是CommonJS中require()
和import
之間的區別。使用require()
,您可以在運行代碼時根據需要加載依賴項。 如果我們使用require
而不是import
,running index.js
,running sum.js
,3
會被依次打印。
68. 輸出什麼?
console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
- A:
true
,true
,false
- B:
false
,true
,false
- C:
true
,false
,true
- D:
true
,true
,true
答案: A
每個Symbol
都是完全唯一的。傳遞給Symbol
的參數只是給Symbol
的一個描述。 Symbol
的值不依賴於傳遞的參數。 當我們測試相等時,我們創建了兩個全新的符號:第一個Symbol('foo')
,第二個Symbol('foo')
, 這兩個值是唯一的,彼此不相等,因此返回false
。
69. 輸出什麼?
const name = "Lydia Hallie"
console.log(name.padStart(13))
console.log(name.padStart(2))
- A:
"Lydia Hallie"
,"Lydia Hallie"
- B:
" Lydia Hallie"
," Lydia Hallie"
("[13x whitespace]Lydia Hallie"
,"[2x whitespace]Lydia Hallie"
) - C:
" Lydia Hallie"
,"Lydia Hallie"
("[1x whitespace]Lydia Hallie"
,"Lydia Hallie"
) - D:
"Lydia Hallie"
,"Lyd"
答案: C
使用padStart
方法,我們可以在字符串的開頭添加填充。傳遞給此方法的參數是字符串的總長度(包含填充)。字符串Lydia Hallie
的長度爲12
, 因此name.padStart(13)
在字符串的開頭只會插入1(13 - 12 = 1
)個空格。
如果傳遞給padStart
方法的參數小於字符串的長度,則不會添加填充。
70. 輸出什麼?
console.log("🥑" + "💻");
- A:
"🥑💻"
- B:
257548
- C: A string containing their code points
- D: Error
答案: A
使用+
運算符,您可以連接字符串。 上述情況,我們將字符串“🥑”
與字符串”💻“
連接起來,產生”🥑💻“
。
71. 如何能打印出console.log
語句後註釋掉的值?
function* startGame() {
const 答案 = yield "Do you love JavaScript?";
if (答案 !== "Yes") {
return "Oh wow... Guess we're gone here";
}
return "JavaScript loves you back ❤️";
}
const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 2 */); // JavaScript loves you back ❤️
- A:
game.next("Yes").value
andgame.next().value
- B:
game.next.value("Yes")
andgame.next.value()
- C:
game.next().value
andgame.next("Yes").value
- D:
game.next.value()
andgame.next.value("Yes")
答案: C
generator
函數在遇到yield
關鍵字時會“暫停”其執行。 首先,我們需要讓函數產生字符串Do you love JavaScript?
,這可以通過調用game.next().value
來完成。上述函數的第一行就有一個yield
關鍵字,那麼運行立即停止了,yield
表達式本身沒有返回值,或者說總是返回undefined
, 這意味着此時變量 答案
爲undefined
next
方法可以帶一個參數,該參數會被當作上一個 yield
表達式的返回值。當我們調用game.next("Yes").value
時,先前的 yield
的返回值將被替換爲傳遞給next()
函數的參數"Yes"
。此時變量 答案
被賦值爲 "Yes"
,if
語句返回false
,所以JavaScript loves you back ❤️
被打印。
72. 輸出什麼?
console.log(String.raw`Hello\nworld`);
- A:
Hello world!
- B:
Hello
world
- C:
Hello\nworld
- D:
Hello\n
world
答案: C
String.raw
函數是用來獲取一個模板字符串的原始字符串的,它返回一個字符串,其中忽略了轉義符(\n
,\v
,\t
等)。但反斜槓可能造成問題,因爲你可能會遇到下面這種類似情況:
const path = `C:\Documents\Projects\table.html`
String.raw`${path}`
這將導致:
"C:DocumentsProjects able.html"
直接使用String.raw
String.raw`C:\Documents\Projects\table.html`
它會忽略轉義字符並打印:C:\Documents\Projects\table.html
上述情況,字符串是Hello\nworld
被打印出。
73. 輸出什麼?
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
- A:
"I made it!"
- B:
Promise {<resolved>: "I made it!"}
- C:
Promise {<pending>}
- D:
undefined
答案: C
異步函數始終返回一個promise。await
仍然需要等待promise的解決:當我們調用getData()
並將其賦值給data
,此時data
爲getData
方法返回的一個掛起的promise,該promise並沒有解決。
如果我們想要訪問已解決的值"I made it!"
,可以在data
上使用.then()
方法:
data.then(res => console.log(res))
這樣將打印 "I made it!"
74. 輸出什麼?
function addToList(item, list) {
return list.push(item);
}
const result = addToList("apple", ["banana"]);
console.log(result);
- A:
['apple', 'banana']
- B:
2
- C:
true
- D:
undefined
答案: B
push()
方法返回新數組的長度。一開始,數組包含一個元素(字符串"banana"
),長度爲1。 在數組中添加字符串"apple"
後,長度變爲2,並將從addToList
函數返回。
push
方法修改原始數組,如果你想從函數返回數組而不是數組長度,那麼應該在push item
之後返回list
。
75. 輸出什麼?
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape)
- A:
{ x: 100, y: 20 }
- B:
{ x: 10, y: 20 }
- C:
{ x: 100 }
- D:
ReferenceError
答案: B
Object.freeze
使得無法添加、刪除或修改對象的屬性(除非屬性的值是另一個對象)。
當我們創建變量shape
並將其設置爲等於凍結對象box
時,shape
指向的也是凍結對象。你可以使用Object.isFrozen
檢查一個對象是否被凍結,上述情況,Object.isFrozen(shape)
將返回true
。
由於shape
被凍結,並且x
的值不是對象,所以我們不能修改屬性x
。 x
仍然等於10
,{x:10,y:20}
被打印。
注意,上述例子我們對屬性x
進行修改,可能會導致拋出TypeError異常(最常見但不僅限於嚴格模式下時)。
76. 輸出什麼?
const { name: myName } = { name: "Lydia" };
console.log(name);
- A:
"Lydia"
- B:
"myName"
- C:
undefined
- D:
ReferenceError
答案: D
當我們從右側的對象解構屬性name
時,我們將其值Lydia
分配給名爲myName
的變量。
使用{name:myName}
,我們是在告訴JavaScript我們要創建一個名爲myName
的新變量,並且其值是右側對象的name
屬性的值。
當我們嘗試打印name
,一個未定義的變量時,就會引發ReferenceError
。
77. 以下是個純函數麼?
function sum(a, b) {
return a + b;
}
- A: Yes
- B: No
答案: A
純函數一種若輸入參數相同,則永遠會得到相同輸出的函數。
sum
函數總是返回相同的結果。 如果我們傳遞1
和2
,它將總是返回3
而沒有副作用。 如果我們傳遞5
和10
,它將總是返回15
,依此類推,這是純函數的定義。
78. 輸出什麼?
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};
const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));
- A:
Calculated! 20
Calculated! 20
Calculated! 20
- B:
Calculated! 20
From cache! 20
Calculated! 20
- C:
Calculated! 20
From cache! 20
From cache! 20
- D:
Calculated! 20
From cache! 20
Error
答案: C
add
函數是一個記憶函數。 通過記憶化,我們可以緩存函數的結果,以加快其執行速度。上述情況,我們創建一個cache
對象,用於存儲先前返回過的值。
如果我們使用相同的參數多次調用addFunction
函數,它首先檢查緩存中是否已有該值,如果有,則返回緩存值,這將節省執行時間。如果沒有,那麼它將計算該值,並存儲在緩存中。
我們用相同的值三次調用了addFunction
函數:
在第一次調用,num
等於10
時函數的值尚未緩存,if語句num in cache
返回false
,else塊的代碼被執行:Calculated! 20
,並且其結果被添加到緩存對象,cache
現在看起來像{10:20}
。
第二次,cache
對象包含10
的返回值。 if語句 num in cache
返回true
,From cache! 20
被打印。
第三次,我們將5 * 2
(值爲10)傳遞給函數。 cache
對象包含10
的返回值。 if語句 num in cache
返回true
,From cache! 20
被打印。
79. 輸出什麼?
const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]
for (let item in myLifeSummedUp) {
console.log(item)
}
for (let item of myLifeSummedUp) {
console.log(item)
}
- A:
0
1
2
3
and"☕"
"💻"
"🍷"
"🍫"
- B:
"☕"
"💻"
"🍷"
"🍫"
and"☕"
"💻"
"🍷"
"🍫"
- C:
"☕"
"💻"
"🍷"
"🍫"
and0
1
2
3
- D:
0
1
2
3
and{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
答案: A
通過for-in
循環,我們可以遍歷一個對象自有的、繼承的、可枚舉的、非Symbol的屬性。 在數組中,可枚舉屬性是數組元素的“鍵”, 即它們的索引。 類似於下面這個對象:
{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
其中鍵則是可枚舉屬性,因此 0
,1
,2
,3
被記錄。
通過for-of
循環,我們可以迭代可迭代對象(包括 Array
,Map
,Set
,String
,arguments
等)。當我們迭代數組時,在每次迭代中,不同屬性的值將被分配給變量item
, 因此“☕”
,“💻”
,“🍷”
,“🍫”
被打印。
80. 輸出什麼?
const list = [1 + 2, 1 * 2, 1 / 2]
console.log(list)
- A:
["1 + 2", "1 * 2", "1 / 2"]
- B:
["12", 2, 0.5]
- C:
[3, 2, 0.5]
- D:
[1, 1, 1]
答案: C
數組元素可以包含任何值。 數字,字符串,布爾值,對象,數組,null
,undeifned
, 以及其他表達式,如日期,函數和計算。
元素將等於返回的值。 1 + 2
返回3
,1 * 2
返回’2,'1 / 2
返回0.5
。
81. 輸出什麼?
function sayHi(name) {
return `Hi there, ${name}`
}
console.log(sayHi())
- A:
Hi there,
- B:
Hi there, undefined
- C:
Hi there, null
- D:
ReferenceError
答案: B
默認情況下,如果不給函數傳參,參數的值將爲undefined
。 上述情況,我們沒有給參數name
傳值。 name
等於undefined
,並被打印。
在ES6中,我們可以使用默認參數覆蓋此默認的undefined
值。 例如:
function sayHi(name =“Lydia”){...}
在這種情況下,如果我們沒有傳遞值或者如果我們傳遞undefined
,name
總是等於字符串Lydia
82. 輸出什麼?
var status = "😎"
setTimeout(() => {
const status = "😍"
const data = {
status: "🥑",
getStatus() {
return this.status
}
}
console.log(data.getStatus())
console.log(data.getStatus.call(this))
}, 0)
- A:
"🥑"
and"😍"
- B:
"🥑"
and"😎"
- C:
"😍"
and"😎"
- D:
"😎"
and"😎"
答案: B
this
關鍵字的指向取決於使用它的位置。 在函數中,比如getStatus
,this
指向的是調用它的對象,上述例子中data
對象調用了getStatus
,因此this
指向的就是data
對象。 當我們打印this.status
時,data
對象的status
屬性被打印,即"🥑"
。
使用call
方法,可以更改this
指向的對象。data.getStatus.call(this)
是將this
的指向由data
對象更改爲全局對象。在全局對象上,有一個名爲status
的變量,其值爲”😎“
。 因此打印this.status
時,會打印“😎”
。
83. 輸出什麼?
const person = {
name: "Lydia",
age: 21
}
let city = person.city
city = "Amsterdam"
console.log(person)
- A:
{ name: "Lydia", age: 21 }
- B:
{ name: "Lydia", age: 21, city: "Amsterdam" }
- C:
{ name: "Lydia", age: 21, city: undefined }
- D:
"Amsterdam"
答案: A
我們將變量city
設置爲等於person
對象上名爲city
的屬性的值。 這個對象上沒有名爲city
的屬性,因此變量city
的值爲undefined
。
請注意,我們沒有引用person
對象本身,只是將變量city
設置爲等於person
對象上city
屬性的當前值。
然後,我們將city
設置爲等於字符串“Amsterdam”
。 這不會更改person對象:沒有對該對象的引用。
因此打印person
對象時,會返回未修改的對象。
84. 輸出什麼?
function checkAge(age) {
if (age < 18) {
const message = "Sorry, you're too young."
} else {
const message = "Yay! You're old enough!"
}
return message
}
console.log(checkAge(21))
- A:
"Sorry, you're too young."
- B:
"Yay! You're old enough!"
- C:
ReferenceError
- D:
undefined
答案: C
const
和let
聲明的變量是具有塊級作用域的,塊是大括號({}
)之間的任何東西, 即上述情況if / else
語句的花括號。 由於塊級作用域,我們無法在聲明的塊之外引用變量,因此拋出ReferenceError
。
85. 什麼樣的信息將被打印?
fetch('https://www.website.com/api/user/1')
.then(res => res.json())
.then(res => console.log(res))
- A:
fetch
方法的結果 - B: 第二次調用
fetch
方法的結果 - C: 前一個
.then()
中回調方法返回的結果 - D: 總是
undefined
答案: C
第二個.then
中res
的值等於前一個.then
中的回調函數返回的值。 你可以像這樣繼續鏈接.then
,將值傳遞給下一個處理程序。
86. 哪個選項是將hasName
設置爲true
的方法,前提是不能將true
作爲參數傳遞?
function getName(name) {
const hasName = //
}
- A:
!!name
- B:
name
- C:
new Boolean(name)
- D:
name.length
答案: A
使用邏輯非運算符!
,將返回一個布爾值,使用!! name
,我們可以確定name
的值是真的還是假的。 如果name
是真實的,那麼!name
返回false
。 !false
返回true
。
通過將hasName
設置爲name
,可以將hasName
設置爲等於傳遞給getName
函數的值,而不是布爾值true
。
new Boolean(true)
返回一個對象包裝器,而不是布爾值本身。
name.length
返回傳遞的參數的長度,而不是布爾值true
。
87. 輸出什麼?
console.log("I want pizza"[0])
- A:
"""
- B:
"I"
- C:
SyntaxError
- D:
undefined
答案: B
可以使用方括號表示法獲取字符串中特定索引的字符,字符串中的第一個字符具有索引0,依此類推。 在這種情況下,我們想要得到索引爲0的元素,字符'I'
被記錄。
請注意,IE7及更低版本不支持此方法。 在這種情況下,應該使用.charAt()
88. 輸出什麼?
function sum(num1, num2 = num1) {
console.log(num1 + num2)
}
sum(10)
- A:
NaN
- B:
20
- C:
ReferenceError
- D:
undefined
答案: B
您可以將默認參數的值設置爲函數的另一個參數,只要另一個參數定義在其之前即可。 我們將值10
傳遞給sum
函數。 如果sum
函數只接收1個參數,則意味着沒有傳遞num2
的值,這種情況下,num1
的值等於傳遞的值10
。 num2
的默認值是num1
的值,即10
。 num1 + num2
返回20
。
如果您嘗試將默認參數的值設置爲後面定義的參數,則可能導致參數的值尚未初始化,從而引發錯誤。比如:
function test(m = n, n = 2) {
console.log(m, n)
}
test() // Uncaught ReferenceError: Cannot access 'n' before initialization
test(3) // 3 2
test(3, 4) // 3 4
89. 輸出什麼?
// module.js
export default () => "Hello world"
export const name = "Lydia"
// index.js
import * as data from "./module"
console.log(data)
- A:
{ default: function default(), name: "Lydia" }
- B:
{ default: function default() }
- C:
{ default: "Hello world", name: "Lydia" }
- D: Global object of
module.js
答案: A
使用import * as name
語法,我們將module.js
文件中所有export
導入到index.js
文件中,並且創建了一個名爲data
的新對象。 在module.js
文件中,有兩個導出:默認導出和命名導出。 默認導出是一個返回字符串“Hello World”的函數,命名導出是一個名爲name
的變量,其值爲字符串“Lydia”
。
data
對象具有默認導出的default
屬性,其他屬性具有指定exports的名稱及其對應的值。
90. 輸出什麼?
class Person {
constructor(name) {
this.name = name
}
}
const member = new Person("John")
console.log(typeof member)
- A:
"class"
- B:
"function"
- C:
"object"
- D:
"string"
答案: C
類是構造函數的語法糖,如果用構造函數的方式來重寫Person
類則將是:
function Person() {
this.name = name
}
通過new
來調用構造函數,將會生成構造函數Person
的實例,對實例執行typeof
關鍵字將返回"object"
,上述情況打印出"object"
。
91. 輸出什麼?
let newList = [1, 2, 3].push(4)
console.log(newList.push(5))
- A:
[1, 2, 3, 4, 5]
- B:
[1, 2, 3, 5]
- C:
[1, 2, 3, 4]
- D:
Error
答案: D
.push
方法返回數組的長度,而不是數組本身! 通過將newList
設置爲[1,2,3].push(4)
,實際上newList
等於數組的新長度:4
。
然後,嘗試在newList
上使用.push
方法。 由於newList
是數值4
,拋出TypeError。
92. 輸出什麼?
function giveLydiaPizza() {
return "Here is pizza!"
}
const giveLydiaChocolate = () => "Here's chocolate... now go hit the gym already."
console.log(giveLydiaPizza.prototype)
console.log(giveLydiaChocolate.prototype)
- A:
{ constructor: ...}
{ constructor: ...}
- B:
{}
{ constructor: ...}
- C:
{ constructor: ...}
{}
- D:
{ constructor: ...}
undefined
答案: D
常規函數,例如giveLydiaPizza
函數,有一個prototype
屬性,它是一個帶有constructor
屬性的對象(原型對象)。 然而,箭頭函數,例如giveLydiaChocolate
函數,沒有這個prototype
屬性。 嘗試使用giveLydiaChocolate.prototype
訪問prototype
屬性時會返回undefined
。
93. 輸出什麼?
const person = {
name: "Lydia",
age: 21
}
for (const [x, y] of Object.entries(person)) {
console.log(x, y)
}
- A:
name
Lydia
andage
21
- B:
["name", "Lydia"]
and["age", 21]
- C:
["name", "age"]
andundefined
- D:
Error
答案: A
Object.entries()
方法返回一個給定對象自身可枚舉屬性的鍵值對數組,上述情況返回一個二維數組,數組每個元素是一個包含鍵和值的數組:
[['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. 輸出什麼?
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
- A:
["banana", "apple", "pear", "orange"]
- B:
[["banana", "apple"], "pear", "orange"]
- C:
["banana", "apple", ["pear"], "orange"]
- D:
SyntaxError
答案: D
... args
是剩餘參數,剩餘參數的值是一個包含所有剩餘參數的數組,並且只能作爲最後一個參數。上述示例中,剩餘參數是第二個參數,這是不可能的,並會拋出語法錯誤。
function getItems(fruitList, favoriteFruit, ...args) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
上述例子是有效的,將會返回數組:[ 'banana', 'apple', 'orange', 'pear' ]
95. 輸出什麼?
function nums(a, b) {
if
(a > b)
console.log('a is bigger')
else
console.log('b is bigger')
return
a + b
}
console.log(nums(4, 2))
console.log(nums(1, 2))
- A:
a is bigger
,6
andb is bigger
,3
- B:
a is bigger
,undefined
andb is bigger
,undefined
- C:
undefined
andundefined
- D:
SyntaxError
答案: B
在JavaScript中,我們不必顯式地編寫分號(;
),但是JavaScript引擎仍然在語句之後自動添加分號。這稱爲自動分號插入。例如,一個語句可以是變量,或者像throw
、return
、break
這樣的關鍵字。
在這裏,我們在新的一行上寫了一個return
語句和另一個值a + b
。然而,由於它是一個新行,引擎並不知道它實際上是我們想要返回的值。相反,它會在return
後面自動添加分號。你可以這樣看:
return;
a + b
這意味着永遠不會到達a + b
,因爲函數在return
關鍵字之後停止運行。如果沒有返回值,就像這裏,函數返回undefined
。注意,在if/else
語句之後沒有自動插入!
96. 輸出什麼?
class Person {
constructor() {
this.name = "Lydia"
}
}
Person = class AnotherPerson {
constructor() {
this.name = "Sarah"
}
}
const member = new Person()
console.log(member.name)
- A:
"Lydia"
- B:
"Sarah"
- C:
Error: cannot redeclare Person
- D:
SyntaxError
答案: B
我們可以將類設置爲等於其他類/函數構造函數。 在這種情況下,我們將Person
設置爲AnotherPerson
。 這個構造函數的名字是Sarah
,所以新的Person
實例member
上的name屬性是Sarah
。
97. 輸出什麼?
const info = {
[Symbol('a')]: 'b'
}
console.log(info)
console.log(Object.keys(info))
- A:
{Symbol('a'): 'b'}
and["{Symbol('a')"]
- B:
{}
and[]
- C:
{ a: "b" }
and["a"]
- D:
{Symbol('a'): 'b'}
and[]
答案: D
Symbol
類型是不可枚舉的。Object.keys
方法返回對象上的所有可枚舉的鍵屬性。Symbol
類型是不可見的,並返回一個空數組。 記錄整個對象時,所有屬性都是可見的,甚至是不可枚舉的屬性。
這是Symbol
的衆多特性之一:除了表示完全唯一的值(防止對象意外名稱衝突,例如當使用2個想要向同一對象添加屬性的庫時),您還可以隱藏
這種方式對象的屬性(儘管不完全。你仍然可以使用Object.getOwnPropertySymbols()
方法訪問 Symbol
。
98. 輸出什麼?
const getList = ([x, ...y]) => [x, y]
const getUser = user => { name: user.name, age: user.age }
const list = [1, 2, 3, 4]
const user = { name: "Lydia", age: 21 }
console.log(getList(list))
console.log(getUser(user))
- A:
[1, [2, 3, 4]]
andundefined
- B:
[1, [2, 3, 4]]
and{ name: "Lydia", age: 21 }
- C:
[1, 2, 3, 4]
and{ name: "Lydia", age: 21 }
- D:
Error
and{ name: "Lydia", age: 21 }
答案: A
getList
函數接收一個數組作爲其參數。 在getList
函數的括號之間,我們立即解構這個數組。 您可以將其視爲:
[x, ...y] = [1, 2, 3, 4]
使用剩餘的參數... y
,我們將所有剩餘參數放在一個數組中。 在這種情況下,其餘的參數是2
,3
和4
。 y
的值是一個數組,包含所有其餘參數。 在這種情況下,x
的值等於1
,所以當我們打印[x,y]
時,會打印[1,[2,3,4]]
。
getUser
函數接收一個對象。對於箭頭函數,如果只返回一個值,我們不必編寫花括號。但是,如果您想從一個箭頭函數返回一個對象,您必須在圓括號之間編寫它,否則不會返回任何值!下面的函數將返回一個對象:
const getUser = user => ({ name: user.name, age: user.age })
由於在這種情況下不返回任何值,因此該函數返回undefined
。
99. 輸出什麼?
const name = "Lydia"
console.log(name())
- A:
SyntaxError
- B:
ReferenceError
- C:
TypeError
- D:
undefined
答案: C
變量name
保存字符串的值,該字符串不是函數,因此無法調用。
當值不是預期類型時,會拋出TypeErrors
。 JavaScript期望name
是一個函數,因爲我們試圖調用它。 但它是一個字符串,因此拋出TypeError
:name is not a function
當你編寫了一些非有效的JavaScript時,會拋出語法錯誤,例如當你把return
這個詞寫成retrun
時。
當JavaScript無法找到您嘗試訪問的值的引用時,拋出ReferenceErrors
。
100. 輸出什麼?
// 🎉✨ This is my 100th question! ✨🎉
const output = `${[] && 'Im'}possible!
You should${'' && `n't`} see a therapist after so much JavaScript lol`
- A:
possible! You should see a therapist after so much JavaScript lol
- B:
Impossible! You should see a therapist after so much JavaScript lol
- C:
possible! You shouldn't see a therapist after so much JavaScript lol
- D:
Impossible! You shouldn't see a therapist after so much JavaScript lol
答案: B
[]
是一個真值。 使用&&
運算符,如果左側值是真值,則返回右側值。 在這種情況下,左側值[]
是一個真值,所以返回Im
。
""
是一個假值。 如果左側值是假的,則不返回任何內容。 n't
不會被退回。
101.輸出什麼?
const one = (false || {} || null)
const two = (null || false || "")
const three = ([] || 0 || true)
console.log(one, two, three)
- A:
false
null
[]
- B:
null
""
true
- C:
{}
""
[]
- D:
null
null
true
答案: C
使用||
運算符,我們可以返回第一個真值。 如果所有值都是假值,則返回最後一個值。
(false || {} || null)
:空對象{}
是一個真值。 這是第一個(也是唯一的)真值,它將被返回。one
等於{}
。
(null || false ||“”)
:所有值都是假值。 這意味着返回傳遞的值""
。 two
等於""
。
([] || 0 ||“”)
:空數組[]
是一個真值。 這是第一個返回的真值。 three
等於[]
。
102. 依次輸出什麼?
const myPromise = () => Promise.resolve('I have resolved!')
function firstFunction() {
myPromise().then(res => console.log(res))
console.log('second')
}
async function secondFunction() {
console.log(await myPromise())
console.log('second')
}
firstFunction()
secondFunction()
- A:
I have resolved!
,second
andI have resolved!
,second
- B:
second
,I have resolved!
andsecond
,I have resolved!
- C:
I have resolved!
,second
andsecond
,I have resolved!
- D:
second
,I have resolved!
andI have resolved!
,second
答案: D
有了promise,我們通常會說:當我想要調用某個方法,但是由於它可能需要一段時間,因此暫時將它放在一邊。只有當某個值被resolved/rejected,並且執行棧爲空時才使用這個值。
我們可以在async
函數中通過.then
和await
關鍵字獲得該值。 儘管我們可以通過.then
和await
獲得promise的價值,但是它們的工作方式有所不同。
在 firstFunction
中,當運行到myPromise
方法時我們將其放在一邊,即promise進入微任務隊列,其他後面的代碼(console.log('second')
)照常運行,因此second
被打印出,firstFunction
方法到此執行完畢,執行棧中宏任務隊列被清空,此時開始執行微任務隊列中的任務,I have resolved
被打印出。
在secondFunction
方法中,我們通過await
關鍵字,暫停了後面代碼的執行,直到異步函數的值被解析纔開始後面代碼的執行。這意味着,它會等着直到 myPromise
以值I have resolved
被解決之後,下一行second
纔開始執行。
103. 輸出什麼?
const set = new Set()
set.add(1)
set.add("Lydia")
set.add({ name: "Lydia" })
for (let item of set) {
console.log(item + 2)
}
- A:
3
,NaN
,NaN
- B:
3
,7
,NaN
- C:
3
,Lydia2
,[Object object]2
- D:
"12"
,Lydia2
,[Object object]2
答案: C
“+”運算符不僅用於添加數值,還可以使用它來連接字符串。 每當JavaScript引擎發現一個或多個值不是數字時,就會將數字強制爲字符串。
第一個是數字1。 1 + 2返回數字3。
但是,第二個是字符串“Lydia”。 “Lydia”是一個字符串,2是一個數字:2被強制轉換爲字符串。 “Lydia”和“2”被連接起來,產生字符串“Lydia2”。
{name:“ Lydia”}
是一個對象。 數字和對象都不是字符串,因此將二者都字符串化。 每當我們對常規對象進行字符串化時,它就會變成[Object object]
。 與“2”串聯的“ [Object object]”成爲“[Object object]2”。
104. 結果是什麼?
Promise.resolve(5)
- A:
5
- B:
Promise {<pending>: 5}
- C:
Promise {<resolved>: 5}
- D:
Error
答案: C
我們可以將我們想要的任何類型的值傳遞Promise.resolve
,無論是否promise
。 該方法本身返回帶有已解析值的Promise
。 如果您傳遞常規函數,它將是具有常規值的已解決promise
。 如果你通過了promise,它將是一個已經resolved的且帶有傳的值的promise。
上述情況,我們傳了數字5,因此返回一個resolved狀態的promise,resolve值爲5
105. 輸出什麼?
function compareMembers(person1, person2 = person) {
if (person1 !== person2) {
console.log("Not the same!")
} else {
console.log("They are the same!")
}
}
const person = { name: "Lydia" }
compareMembers(person)
- A:
Not the same!
- B:
They are the same!
- C:
ReferenceError
- D:
SyntaxError
答案: B
對象通過引用傳遞。 當我們檢查對象的嚴格相等性(===)時,我們正在比較它們的引用。
我們將“person2”的默認值設置爲“person”對象,並將“person”對象作爲“person1”的值傳遞。
這意味着兩個值都引用內存中的同一位置,因此它們是相等的。
運行“ else”語句中的代碼塊,並記錄They are the same!
。
106. 輸出什麼?
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}
const colors = ["pink", "red", "blue"]
console.log(colorConfig.colors[1])
- A:
true
- B:
false
- C:
undefined
- D:
TypeError
答案: D
在JavaScript中,我們有兩種訪問對象屬性的方法:括號表示法或點表示法。 在此示例中,我們使用點表示法(colorConfig.colors
)代替括號表示法(colorConfig [“ colors”]
)。
使用點表示法,JavaScript會嘗試使用該確切名稱在對象上查找屬性。 在此示例中,JavaScript嘗試在colorconfig對象上找到名爲colors的屬性。 沒有名爲“colors”的屬性,因此返回“undefined”。
然後,我們嘗試使用[1]
訪問第一個元素的值。 我們無法對未定義的值執行此操作,因此會拋出Cannot read property '1' of undefined
。
JavaScript解釋(或取消裝箱)語句。 當我們使用方括號表示法時,它會看到第一個左方括號[
並一直進行下去,直到找到右方括號]
。 只有這樣,它纔會評估該語句。 如果我們使用了colorConfig [colors [1]],它將返回colorConfig對象上red屬性的值。
107. 輸出什麼?
console.log('❤️' === '❤️')
- A:
true
- B:
false
答案: A
在內部,表情符號是unicode。 heat表情符號的unicode是“ U + 2764 U + FE0F”
。 對於相同的表情符號,它們總是相同的,因此我們將兩個相等的字符串相互比較,這將返回true。
108. 哪些方法修改了原數組?
const emojis = ['✨', '🥑', '😍']
emojis.map(x => x + '✨')
emojis.filter(x => x !== '🥑')
emojis.find(x => x !== '🥑')
emojis.reduce((acc, cur) => acc + '✨')
emojis.slice(1, 2, '✨')
emojis.splice(1, 2, '✨')
- A:
All of them
- B:
map
reduce
slice
splice
- C:
map
slice
splice
- D:
splice
答案: D
使用splice
方法,我們通過刪除,替換或添加元素來修改原始數組。 在這種情況下,我們從索引1中刪除了2個元素(我們刪除了'🥑'
和'😍'
),同時添加了✨emoji表情。
map
,filter
和slice
返回一個新數組,find
返回一個元素,而reduce
返回一個減小的值。
109. 輸出什麼?
const food = ['🍕', '🍫', '🥑', '🍔']
const info = { favoriteFood: food[0] }
info.favoriteFood = '🍝'
console.log(food)
- A:
['🍕', '🍫', '🥑', '🍔']
- B:
['🍝', '🍫', '🥑', '🍔']
- C:
['🍝', '🍕', '🍫', '🥑', '🍔']
- D:
ReferenceError
答案: A
我們將info
對象上的favoriteFood
屬性的值設置爲披薩表情符號“🍕”的字符串。字符串是原始數據類型。在JavaScript中,原始數據類型通過值起作用
在這種情況下,我們將info
對象上的favoriteFood
屬性的值設置爲等於food
數組中的第一個元素的值,字符串爲披薩表情符號('🍕'
)。字符串是原始數據類型,並且通過值進行交互,我們更改info
對象上favoriteFood
屬性的值。 food數組沒有改變,因爲favoriteFood的值只是該數組中第一個元素的值的複製,並且與該元素上的元素沒有相同的內存引用食物[0]
。當我們記錄食物時,它仍然是原始數組['🍕','🍫','🥑','🍔']
。
110. 這個函數幹了什麼?
JSON.parse()
- A: Parses JSON to a JavaScript value
- B: Parses a JavaScript object to JSON
- C: Parses any JavaScript value to JSON
- D: Parses JSON to a JavaScript object only
答案: A
使用JSON.parse()
方法,我們可以將JSON字符串解析爲JavaScript值。
// 將數字字符串化爲有效的JSON,然後將JSON字符串解析爲JavaScript值:
const jsonNumber = JSON.stringify(4) // '4'
JSON.parse(jsonNumber) // 4
// 將數組值字符串化爲有效的JSON,然後將JSON字符串解析爲JavaScript值:
const jsonArray = JSON.stringify([1, 2, 3]) // '[1, 2, 3]'
JSON.parse(jsonArray) // [1, 2, 3]
// 將對象字符串化爲有效的JSON,然後將JSON字符串解析爲JavaScript值:
const jsonArray = JSON.stringify({ name: "Lydia" }) // '{"name":"Lydia"}'
JSON.parse(jsonArray) // { name: 'Lydia' }
111. 輸出什麼?
let name = 'Lydia'
function getName() {
console.log(name)
let name = 'Sarah'
}
getName()
- A: Lydia
- B: Sarah
- C:
undefined
- D:
ReferenceError
答案: D
每個函數都有其自己的執行上下文。 getName
函數首先在其自身的上下文(範圍)內查找,以查看其是否包含我們嘗試訪問的變量name
。 上述情況,getName
函數包含其自己的name
變量:我們用let
關鍵字和Sarah
的值聲明變量name
。
帶有let
關鍵字(和const
)的變量被提升,但是與var
不同,它不會被***初始化***。 在我們聲明(初始化)它們之前,無法訪問它們。 這稱爲“暫時性死區”。 當我們嘗試在聲明變量之前訪問變量時,JavaScript會拋出ReferenceError: Cannot access 'name' before initialization
。
如果我們不在getName
函數中聲明name
變量,則javascript引擎會查看原型練。會找到其外部作用域有一個名爲name
的變量,其值爲Lydia
。 在這種情況下,它將打印Lydia
:
let name = 'Lydia'
function getName() {
console.log(name)
}
getName() // Lydia
112. 輸出什麼?
function* generatorOne() {
yield ['a', 'b', 'c'];
}
function* generatorTwo() {
yield* ['a', 'b', 'c'];
}
const one = generatorOne()
const two = generatorTwo()
console.log(one.next().value)
console.log(two.next().value)
- A:
a
anda
- B:
a
andundefined
- C:
['a', 'b', 'c']
anda
- D:
a
and['a', 'b', 'c']
答案: C
通過 yield
關鍵字, 我們在 Generator
函數裏執行yield
表達式. 通過 yield*
關鍵字, 我們可以在一個Generator
函數裏面執行(yield
表達式)另一個 Generator
函數, 或可遍歷的對象 (如數組).
在函數 generatorOne
中, 我們通過 yield
關鍵字 yield 了一個完整的數組 ['a', 'b', 'c']
。函數one
通過next
方法返回的對象的value
屬性的值 (one.next().value
) 等價於數組 ['a', 'b', 'c']
.
console.log(one.next().value) // ['a', 'b', 'c']
console.log(one.next().value) // undefined
在函數 generatorTwo
中, 我們使用 yield*
關鍵字。就相當於函數two
第一個yield
的值, 等價於在迭代器中第一個 yield
的值。數組['a', 'b', 'c']
就是這個迭代器. 第一個 yield
的值就是 a
, 所以我們第一次調用 two.next().value
時, 就返回a
。
console.log(two.next().value) // 'a'
console.log(two.next().value) // 'b'
console.log(two.next().value) // 'c'
console.log(two.next().value) // undefined
113. 輸出什麼?
console.log(`${(x => x)('I love')} to program`)
- A:
I love to program
- B:
undefined to program
- C:
${(x => x)('I love') to program
- D:
TypeError
答案: A
帶有模板字面量的表達式首先被執行。相當於字符串會包含表達式,這個立即執行函數 (x => x)('I love')
返回的值. 我們向箭頭函數 x => x
傳遞 'I love'
作爲參數。x
等價於返回的 'I love'
。這就是結果 I love to program
。
114. 將會發生什麼?
let config = {
alert: setInterval(() => {
console.log('Alert!')
}, 1000)
}
config = null
- A:
setInterval
的回調不會被調用 - B:
setInterval
的回調被調用一次 - C:
setInterval
的回調仍然會被每秒鐘調用 - D: 我們從沒調用過
config.alert()
, config 爲null
答案: C
一般情況下當我們將對象賦值爲 null
, 那些對象會被進行 垃圾回收(garbage collected) 因爲已經沒有對這些對象的引用了。然而,setInterval
的參數是一個箭頭函數(所以上下文綁定到對象 config
了),回調函數仍然保留着對 config
的引用。只要存在引用,對象就不會被垃圾回收。因爲沒有被垃圾回收,setInterval
的回調每1000ms (1s)會被調用一次。
115. 哪一個方法會返回 'Hello world!'
?
const myMap = new Map()
const myFunc = () => 'greeting'
myMap.set(myFunc, 'Hello world!')
//1
myMap.get('greeting')
//2
myMap.get(myFunc)
//3
myMap.get(() => 'greeting')
- A: 1
- B: 2
- C: 2 and 3
- D: All of them
答案: B
當通過 set
方法添加一個鍵值對,一個傳遞給 set
方法的參數將會是鍵名,第二個參數將會是值。在這個case裏,鍵名爲 函數 () => 'greeting'
,值爲'Hello world'
。 myMap
現在就是 { () => 'greeting' => 'Hello world!' }
。
1 是錯的,因爲鍵名不是 'greeting'
而是 () => 'greeting'
。
3 是錯的,因爲我們給get
方法傳遞了一個新的函數。對象受 引用 影響。函數也是對象,因此兩個函數嚴格上並不等價,儘管他們相同:他們有兩個不同的內存引用地址。
116. 輸出什麼?
const person = {
name: "Lydia",
age: 21
}
const changeAge = (x = { ...person }) => x.age += 1
const changeAgeAndName = (x = { ...person }) => {
x.age += 1
x.name = "Sarah"
}
changeAge(person)
changeAgeAndName()
console.log(person)
- A:
{name: "Sarah", age: 22}
- B:
{name: "Sarah", age: 23}
- C:
{name: "Lydia", age: 22}
- D:
{name: "Lydia", age: 23}
答案: C
函數 changeAge
和函數 changeAgeAndName
有着不同的參數,定義一個 新 生成的對象 { ...person }
。這個對象有着所有 person
對象 中 k/v 值的副本。
首項, 我們調用 changeAge
函數並傳遞 person
對象作爲它的參數。這個函數對 age
屬性進行加一操作。person
現在是 { name: "Lydia", age: 22 }
。
然後,我們調用函數 changeAgeAndName
,然而我們沒有傳遞參數。取而代之,x
的值等價 new 生成的對象: { ...person }
。因爲它是一個新生成的對象,它並不會對對象 person
造成任何副作用。person
仍然等價於 { name: "Lydia", age: 22 }
。
117. 下面那個選項將會返回 6
?
function sumValues(x, y, z) {
return x + y + z;
}
- A:
sumValues([...1, 2, 3])
- B:
sumValues([...[1, 2, 3]])
- C:
sumValues(...[1, 2, 3])
- D:
sumValues([1, 2, 3])
答案: C
通過展開操作符 ...
,我們可以 暫開 單個可迭代的元素。函數 sumValues
function 接收三個參數: x
, y
和 z
。...[1, 2, 3]
的執行結果爲 1, 2, 3
,將會傳遞給函數 sumValues
。
118. 輸出什麼?
let num = 1;
const list = ["🥳", "🤠", "🥰", "🤪"];
console.log(list[(num += 1)]);
- A:
🤠
- B:
🥰
- C:
SyntaxError
- D:
ReferenceError
答案: B
通過 +=
操作符,我們對值 num
進行加 1
操作。 num
有初始值 1
,因此 1 + 1
的執行結果爲 2
。數組 list
的第二項爲 🥰,console.log(list[2])
輸出 🥰.
119. 輸出什麼?
const person = {
firstName: "Lydia",
lastName: "Hallie",
pet: {
name: "Mara",
breed: "Dutch Tulip Hound"
},
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.pet?.name);
console.log(person.pet?.family?.name);
console.log(person.getFullName?.());
console.log(member.getLastName?.());
- A:
undefined
undefined
undefined
undefined
- B:
Mara
undefined
Lydia Hallie
undefined
- C:
Mara
null
Lydia Hallie
null
- D:
null
ReferenceError
null
ReferenceError
答案: B
通過 ES10 或 TS3.7+可選鏈操作符 ?.
,我們不再需要顯式檢測更深層的嵌套值是否有效。如果我們嘗試獲取 undefined
或 null
的值 (nullish),表達將會短路並返回 undefined
.
person.pet?.name
: person
有一個名爲 pet
的屬性: person.pet
不是 nullish。它有個名爲 name
的屬性,並返回字符串 Mara
。
person.pet?.family?.name
: person
有一個名爲 pet
的屬性: person.pet
不是 nullish. pet
並沒有 一個名爲 family
的屬性, person.pet.family
是 nullish。表達式返回 undefined
。
person.getFullName?.()
: person
有一個名爲 getFullName
的屬性: person.getFullName()
不是 nullish 並可以被調用,返回字符串 Lydia Hallie
。
member.getLastName?.()
: member
is not defined: member.getLastName()
is nullish. The expression returns undefined
.
120. 輸出什麼?
const groceries = ["banana", "apple", "peanuts"];
if (groceries.indexOf("banana")) {
console.log("We have to buy bananas!");
} else {
console.log(`We don't have to buy bananas!`);
}
- A: We have to buy bananas!
- B: We don’t have to buy bananas
- C:
undefined
- D:
1
答案: B
我們傳遞了一個狀態 groceries.indexOf("banana")
給if條件語句。groceries.indexOf("banana")
返回 0
, 一個 falsy 的值。因爲if條件語句的狀態爲 falsy,else
塊區內的代碼執行,並且 We don't have to buy bananas!
被輸出.
121. 輸出什麼?
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language);
- A:
function language(lang) { this.languages.push(lang }
- B:
0
- C:
[]
- D:
undefined
答案: D
方法 language
是一個 setter
。Setters 並不保存一個實際值,它們的使命在於 修改 屬性。當調用方法 setter
, 返回 undefined
。
122. 輸出什麼?
const name = "Lydia Hallie";
console.log(!typeof name === "object");
console.log(!typeof name === "string");
- A:
false
true
- B:
true
false
- C:
false
false
- D:
true
true
答案: C
typeof name
返回 "string"
。字符串 "string"
是一個 truthy 的值,因此 !typeof name
返回一個布爾值 false
。 false === "object"
和 false === "string"
都返回 false
。
(如果我們想檢測一個值的類型,我們應該用 !==
而不是 !typeof
)
123. 輸出什麼?
const add = x => y => z => {
console.log(x, y, z);
return x + y + z;
};
add(4)(5)(6);
- A:
4
5
6
- B:
6
5
4
- C:
4
function
function
- D:
undefined
undefined
6
答案: A
函數 add
是一個返回 返回箭頭函數的箭頭函數 的箭頭函數(still with me?)。第一個函數接收一個值爲 4
的參數 x
。我們調用第二個函數,它接收一個值爲 5
的參數 y
。然後我們調用第三個函數,它接收一個值爲 6
的參數 z
。當我們嘗試在最後一個箭頭函數中獲取 x
, y
和 z
的值,JS 引擎根據作用域鏈去找 x
和 y
的值。得到 4
5
6
.
124. 輸出什麼?
async function* range(start, end) {
for (let i = start; i <= end; i++) {
yield Promise.resolve(i);
}
}
(async () => {
const gen = range(1, 3);
for await (const item of gen) {
console.log(item);
}
})();
- A:
Promise {1}
Promise {2}
Promise {3}
- B:
Promise {<pending>}
Promise {<pending>}
Promise {<pending>}
- C:
1
2
3
- D:
undefined
undefined
undefined
答案: C
我們給 函數range 傳遞: Promise{1}
, Promise{2}
, Promise{3}
,Generator 函數 range
返回一個全是 async object promise 數組。我們將 async object 賦值給變量 gen
,之後我們使用for await ... of
進行循環遍歷。我們將返回的 Promise 實例賦值給 item
: 第一個返回 Promise{1}
, 第二個返回 Promise{2}
,之後是 Promise{3}
。因爲我們正 awaiting item
的值,resolved 狀態的 promsie,promise數組的resolved 值 以此爲: 1
,2
,3
.
125. 輸出什麼?
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc(1, 2, 3);
- A:
1
2
3
- B:
{1: 1}
{2: 2}
{3: 3}
- C:
{ 1: undefined }
undefined
undefined
- D:
undefined
undefined
undefined
答案: D
myFunc
期望接收一個包含 x
, y
和 z
屬性的對象作爲它的參數。因爲我們僅僅傳遞三個單獨的數字值 (1, 2, 3) 而不是一個含有 x
, y
和 z
屬性的對象 ({x: 1, y: 2, z: 3}), x
, y
和 z
有着各自的默認值 undefined
.
126. 輸出什麼?
function getFine(speed, amount) {
const formattedSpeed = new Intl.NumberFormat({
'en-US',
{ style: 'unit', unit: 'mile-per-hour' }
}).format(speed)
const formattedAmount = new Intl.NumberFormat({
'en-US',
{ style: 'currency', currency: 'USD' }
}).format(amount)
return `The driver drove ${formattedSpeed} and has to pay ${formattedAmount}`
}
console.log(getFine(130, 300))
- A: The driver drove 130 and has to pay 300
- B: The driver drove 130 mph and has to pay $300.00
- C: The driver drove undefined and has to pay undefined
- D: The driver drove 130.00 and has to pay 300.00
答案: B
通過方法 Intl.NumberFormat
,我們可以格式化任意區域的數字值。我們對數字值 130
進行 mile-per-hour
作爲 unit
的 en-US
區域 格式化,結果爲 130 mph
。對數字值 300
進行 USD
作爲 currentcy
的 en-US
區域格式化,結果爲 $300.00
.
127. 輸出什麼?
const spookyItems = ["👻", "🎃", "🕸"];
({ item: spookyItems[3] } = { item: "💀" });
console.log(spookyItems);
- A:
["👻", "🎃", "🕸"]
- B:
["👻", "🎃", "🕸", "💀"]
- C:
["👻", "🎃", "🕸", { item: "💀" }]
- D:
["👻", "🎃", "🕸", "[object Object]"]
答案: B
通過解構對象們,我們可以從右手邊的對象中拆出值,並且將拆出的值分配給左手邊對象同名的屬性。在這種情況下,我們將值 “💀” 分配給 spookyItems[3]
。相當於我們正在篡改數組 spookyItems
,我們給它添加了值 “💀”。當輸出 spookyItems
時,結果爲 ["👻", "🎃", "🕸", "💀"]
。
128. 輸出什麼?
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name));
console.log(Number.isNaN(age));
console.log(isNaN(name));
console.log(isNaN(age));
- A:
true
false
true
false
- B:
true
false
false
false
- C:
false
false
true
false
- D:
false
true
false
true
答案: C
通過方法 Number.isNaN
,你可以檢測你傳遞的值是否爲 數字值 並且是否等價於 NaN
。name
不是一個數字值,因此 Number.isNaN(name)
返回 false
。age
是一個數字值,但它不等價於 NaN
,因此 Number.isNaN(age)
返回 false
.
通過方法 isNaN
, 你可以檢測你傳遞的值是否一個 number。name
不是一個 number
,因此 isNaN(name)
返回 true
. age
是一個 number
因此 isNaN(age)
返回 false
.
129. 輸出什麼?
const randomValue = 21;
function getInfo() {
console.log(typeof randomValue);
const randomValue = "Lydia Hallie";
}
getInfo();
- A:
"number"
- B:
"string"
- C:
undefined
- D:
ReferenceError
答案: D
通過 const
關鍵字聲明的變量在被初始化之前不可被引用:這被稱之爲 暫時性死去。在函數 getInfo
中, 變量 randomValue
聲明在getInfo
的作用域的此法環境中。在想要對 typeof randomValue
進行log之前,變量 randomValue
仍未被初始化: 錯誤ReferenceError
被拋出! JS引擎並不會根據作用域鏈網上尋找該變量,因爲我們已經在 getInfo
函數中聲明瞭 randomValue
變量。
130. 輸出什麼?
const myPromise = Promise.resolve("Woah some cool data");
(async () => {
try {
console.log(await myPromise);
} catch {
throw new Error(`Oops didn't work`);
} finally {
console.log("Oh finally!");
}
})();
- A:
Woah some cool data
- B:
Oh finally!
- C:
Woah some cool data
Oh finally!
- D:
Oops didn't work
Oh finally!
答案: C
在 try
塊區,我們打印 myPromise
變量的 awaited 值: "Woah some cool data"
。因爲try
塊區沒有錯誤拋出,catch
塊區的代碼並不執行。finally
塊區的代碼 總是 執行,"Oh finally!"
被輸出。
131. 輸出什麼?
const emojis = ["🥑", ["✨", "✨", ["🍕", "🍕"]]];
console.log(emojis.flat(1));
- A:
['🥑', ['✨', '✨', ['🍕', '🍕']]]
- B:
['🥑', '✨', '✨', ['🍕', '🍕']]
- C:
['🥑', ['✨', '✨', '🍕', '🍕']]
- D:
['🥑', '✨', '✨', '🍕', '🍕']
答案: B
通過方法 flat
, 我們可以創建一個新的, 已被扁平化的數組。被扁平化的深度取決於我們傳遞的值。在這個case裏,我們傳遞了值 1
(並不必要,這是默認值),相當於只有第一層的數組纔會被連接。即這個 case 裏的 ['🥑']
and ['✨', '✨', ['🍕', '🍕']]
。連接這兩個數組得到結果 ['🥑', '✨', '✨', ['🍕', '🍕']]
.
132. 輸出什麼?
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
const counterOne = new Counter();
counterOne.increment();
counterOne.increment();
const counterTwo = counterOne;
counterTwo.increment();
console.log(counterOne.count);
- A:
0
- B:
1
- C:
2
- D:
3
答案: D
counterOne
是類 Counter
的一個實例。類 Counter 包含一個count
屬性在它的構造函數裏, 和一個 increment
方法。首先,我們通過 counterOne.increment()
調用方法 increment
兩次。現在, counterOne.count
爲 2
.
然後,我們創建一個新的變量 counterTwo
並將 counterOne
的引用地址賦值給它。因爲對象受引用地址的影響,我們剛剛創建了一個新的對象,其引用地址和 counterOne
的等價。因此它們指向同一塊內存地址,任何對其的副作用都會影響 counterTwo
。現在 counterTwo.count
爲 2
。
我們調用 counterTwo.increment()
將 count
的值設爲 3
。然後,我們打印 counterOne
裏的count,結果爲 3
。
133. 輸出什麼?
const myPromise = Promise.resolve(Promise.resolve("Promise!"));
function funcOne() {
myPromise.then(res => res).then(res => console.log(res));
setTimeout(() => console.log("Timeout!", 0));
console.log("Last line!");
}
async function funcTwo() {
const res = await myPromise;
console.log(await res);
setTimeout(() => console.log("Timeout!", 0));
console.log("Last line!");
}
funcOne();
funcTwo();
- A:
Promise! Last line! Promise! Last line! Last line! Promise!
- B:
Last line! Timeout! Promise! Last line! Timeout! Promise!
- C:
Promise! Last line! Last line! Promise! Timeout! Timeout!
- D:
Last line! Promise! Promise! Last line! Timeout! Timeout!
答案: D
首先,我們調用 funcOne
。在函數 funcOne
的第一行,我們調用myPromise
promise 異步操作。當JS引擎在忙於執行 promise,它繼續執行函數 funcOne
。下一行 異步操作 setTimeout
,其回調函數被 Web API 調用。 (詳情請參考我關於event loop的文章.)
promise 和 timeout 都是異步操作,函數繼續執行當JS引擎忙於執行promise 和 處理 setTimeout
的回調。相當於 Last line!
首先被輸出, 因爲它不是異步操作。執行完 funcOne
的最後一行,promise 狀態轉變爲 resolved,Promise!
被打印。然而,因爲我們調用了 funcTwo()
, 調用棧不爲空,setTimeout
的回調仍不能入棧。
我們現在處於 funcTwo
,先 awaiting myPromise。通過 await
關鍵字, 我們暫停了函數的執行直到 promise 狀態變爲 resolved (或 rejected)。然後,我們輸出 res
的 awaited 值(因爲 promise 本身返回一個 promise)。 接着輸出 Promise!
。
下一行就是 異步操作 setTimeout
,其回調函數被 Web API 調用。
我們執行到函數 funcTwo
的最後一行,輸出 Last line!
。現在,因爲 funcTwo
出棧,調用棧爲空。在事件隊列中等待的回調函數(() => console.log("Timeout!")
from funcOne
, and () => console.log("Timeout!")
from funcTwo
)以此入棧。第一個回調輸出 Timeout!
,並出棧。然後,第二個回調輸出 Timeout!
,並出棧。得到結果 Last line! Promise! Promise! Last line! Timeout! Timeout!
134. 我們怎樣才能在 index.js
中調用 sum.js?
中的 sum
?
// sum.js
export default function sum(x) {
return x + x;
}
// index.js
import * as sum from "./sum";
- A:
sum(4)
- B:
sum.sum(4)
- C:
sum.default(4)
- D: 默認導出不用
*
來導入,只能具名導出
答案: C
使用符號 *
,我們引入文件中的所有值,包括默認和具名。如果我們有以下文件:
// info.js
export const name = "Lydia";
export const age = 21;
export default "I love JavaScript";
// index.js
import * as info from "./info";
console.log(info);
將會輸出以下內容:
{
default: "I love JavaScript",
name: "Lydia",
age: 21
}
以 sum
爲例,相當於以下形式引入值 sum
:
{ default: function sum(x) { return x + x } }
我們可以通過調用 sum.default
來調用該函數
135. 輸出什麼?
const handler = {
set: () => console.log("Added a new property!"),
get: () => console.log("Accessed a property!")
};
const person = new Proxy({}, handler);
person.name = "Lydia";
person.name;
- A:
Added a new property!
- B:
Accessed a property!
- C:
Added a new property!
Accessed a property!
- D: 沒有任何輸出
答案: C
使用 Proxy 對象,我們可以給一個對象添加自定義行爲。在這個 case,我們傳遞一個包含以下屬性的對象 handler
: set
and get
。每當我門 設置 屬性值時 set
被調用,每當我們 獲取 時 get
被調用。
第一個參數是一個空對象 {}
,作爲 person
的值。對於這個對象,自定義行爲被定義在對象 handler
。如果我們向對象 person
添加屬性,set
將被調用。如果我們獲取 person
的屬性, get
將被調用。
首先,我們向 proxy 對象(person.name = "Lydia"
)添加一個屬性 name
。set
被調用並輸出 "Added a new property!"
。
然後,我們獲取 proxy 對象的一個屬性,對象 handler 的屬性 get
被調用。輸出 "Accessed a property!"
。
136. 以下哪一項會對對象 person
有副作用?
const person = { name: "Lydia Hallie" };
Object.seal(person);
- A:
person.name = "Evan Bacon"
- B:
person.age = 21
- C:
delete person.name
- D:
Object.assign(person, { age: 21 })
答案: A
使用 Object.seal
我們可以防止新屬性 被添加,或者存在屬性 被移除.
然而,你仍然可以對存在屬性進行更改。
137. 以下哪一項會對對象 person
有副作用?
const person = {
name: "Lydia Hallie",
address: {
street: "100 Main St"
}
};
Object.freeze(person);
- A:
person.name = "Evan Bacon"
- B:
delete person.address
- C:
person.address.street = "101 Main St"
- D:
person.pet = { name: "Mara" }
答案: C
使用方法 Object.freeze
對一個對象進行 凍結。不能對屬性進行添加,修改,刪除。
然而,它僅 對對象進行 淺 凍結,意味着只有 對象中的 直接 屬性被凍結。如果屬性是另一個 object,像案例中的 address
,address
中的屬性沒有被凍結,仍然可以被修改。
138. 輸出什麼?
const add = x => x + x;
function myFunc(num = 2, value = add(num)) {
console.log(num, value);
}
myFunc();
myFunc(3);
- A:
2
4
and3
6
- B:
2
NaN
and3
NaN
- C:
2
Error
and3
6
- D:
2
4
and3
Error
答案: A
首先我們不傳遞任何參數調用 myFunc()
。因爲我們沒有傳遞參數,num
和 value
獲取它們各自的默認值:num 爲 2
, 而 value
爲函數 add
的返回值。對於函數 add
,我們傳遞值爲2的 num
作爲參數。函數 add
返回 4
作爲 value
的值。
然後,我們調用 myFunc(3)
並傳遞值 3
參數 num
的值。我們沒有給 value
傳遞值。因爲我們沒有給參數 value
傳遞值,它獲取默認值:函數 add
的返回值。對於函數 add
,我們傳遞值爲3的 num
給它。函數 add
返回 6
作爲 value
的值。
139. 輸出什麼?
class Counter {
#number = 10
increment() {
this.#number++
}
getNum() {
return this.#number
}
}
const counter = new Counter()
counter.increment()
console.log(counter.#number)
- A:
10
- B:
11
- C:
undefined
- D:
SyntaxError
答案: D
在 ES2020 中,通過 #
我們可以給 class 添加私有變量。在 class 的外部我們無法獲取該值。當我們嘗試輸出 counter.#number
,語法錯誤被拋出:我們無法在 class Counter
外部獲取它!
140. 選擇哪一個?
const teams = [
{ name: "Team 1", members: ["Paul", "Lisa"] },
{ name: "Team 2", members: ["Laura", "Tim"] }
];
function* getMembers(members) {
for (let i = 0; i < members.length; i++) {
yield members[i];
}
}
function* getTeams(teams) {
for (let i = 0; i < teams.length; i++) {
// ✨ SOMETHING IS MISSING HERE ✨
}
}
const obj = getTeams(teams);
obj.next(); // { value: "Paul", done: false }
obj.next(); // { value: "Lisa", done: false }
- A:
yield getMembers(teams[i].members)
- B:
yield* getMembers(teams[i].members)
- C:
return getMembers(teams[i].members)
- D:
return yield getMembers(teams[i].members)
答案: B
爲了遍歷 teams
數組中對象的屬性 members
中的每一項,我們需要將 teams[i].members
傳遞給 Generator 函數 getMembers
。Generator 函數返回一個 generator 對象。爲了遍歷這個 generator 對象中的每一項,我們需要使用 yield*
.
如果我們沒有寫 yield
,return yield
或者 return
,整個 Generator 函數不會第一時間 return 當我們調用 next
方法.
141. 輸出什麼?
const person = {
name: "Lydia Hallie",
hobbies: ["coding"]
};
function addHobby(hobby, hobbies = person.hobbies) {
hobbies.push(hobby);
return hobbies;
}
addHobby("running", []);
addHobby("dancing");
addHobby("baking", person.hobbies);
console.log(person.hobbies);
- A:
["coding"]
- B:
["coding", "dancing"]
- C:
["coding", "dancing", "baking"]
- D:
["coding", "running", "dancing", "baking"]
答案: C
函數 addHobby
接受兩個參數,hobby
和有着對象 person
中數組 hobbies
默認值的 hobbies
。
首相,我們調用函數 addHobby
,並給 hobby
傳遞 "running"
以及給 hobbies
傳遞一個空數組。因爲我們給 hobbies
傳遞了空數組,"running"
被添加到這個空數組。
然後,我們調用函數 addHobby
,並給 hobby
傳遞 "dancing"
。我們不向 hobbies
傳遞值,因此它獲取其默認值 —— 對象 person
的 屬性 hobbies
。我們向數組 person.hobbies
push dancing
。
最後,我們調用函數 addHobby
,並向 hobby
傳遞 值 "bdaking"
,並且向 hobbies
傳遞 person.hobbies
。我們向數組 person.hobbies
push dancing
。
pushing dancing
和 baking
之後,person.hobbies
的值爲 ["coding", "dancing", "baking"]
142. 輸出什麼?
class Bird {
constructor() {
console.log("I'm a bird. 🦢");
}
}
class Flamingo extends Bird {
constructor() {
console.log("I'm pink. 🌸");
super();
}
}
const pet = new Flamingo();
- A:
I'm pink. 🌸
- B:
I'm pink. 🌸
I'm a bird. 🦢
- C:
I'm a bird. 🦢
I'm pink. 🌸
- D: Nothing, we didn’t call any method
答案: B
我們創建了類 Flamingo
的實例 pet
。當我們實例化這個實例,Flamingo
中的 constructor
被調用。首相,輸出 "I'm pink. 🌸"
, 之後我們調用super()
。super()
調用父類的構造函數,Bird
。Bird
的構造函數被調用,並輸出 "I'm a bird. 🦢"
。
143. 哪一個選項會導致報錯?
const emojis = ["🎄", "🎅🏼", "🎁", "⭐"];
/* 1 */ emojis.push("🦌");
/* 2 */ emojis.splice(0, 2);
/* 3 */ emojis = [...emojis, "🥂"];
/* 4 */ emojis.length = 0;
- A: 1
- B: 1 and 2
- C: 3 and 4
- D: 3
答案: D
const
關鍵字意味着我們不能 重定義 變量中的值,它 僅可讀。而然,值本身不可修改。數組 emojis
中的值可被修改,如 push 新的值, 拼接,又或者將數組的長度設置爲0。
144. 我們需要向對象 person
添加什麼,以致執行 [...person]
時獲得形如 ["Lydia Hallie", 21]
的輸出?
const person = {
name: "Lydia Hallie",
age: 21
}
[...person] // ["Lydia Hallie", 21]
- A: 不需要,對象默認就是可迭代的
- B:
*[Symbol.iterator]() { for (let x in this) yield* this[x] }
- C:
*[Symbol.iterator]() { for (let x in this) yield* Object.values(this) }
- D:
*[Symbol.iterator]() { for (let x in this) yield this }
答案: C
對象默認並不是可迭代的。如果迭代規則被定義,則一個對象是可迭代的(An iterable is an iterable if the iterator protocol is present)。我們可以通過添加迭代器symbol [Symbol.iterator]
來定義迭代規則,其返回一個 generator 對象,比如說構建一個 generator 函數 *[Symbol.iterator]() {}
。如果我們想要返回數組 ["Lydia Hallie", 21]
: yield* Object.values(this)
,這個 generator 函數一定要 yield 對象 person
的Object.values
。