這45道面試可能被問到的JS判斷題!你能答對幾道?

語言: CN / TW / HK

小知識,大挑戰!本文正在參與“程式設計師必備小知識”創作活動。


前言

先想一想再看答案喲~

第1題

輸出是什麼?

```javascript function sayHi() { console.log(name) console.log(age) var name = 'Lydia' let age = 21 }

sayHi() ``` 1. Lydia 和 undefined 2. Lydia 和 ReferenceError 3. ReferenceError 和 21 4. undefined 和 ReferenceError

⏳ 點選檢視答案 > 答案:4 在函式內部,我們首先通過 `var` 關鍵字聲明瞭 `name` 變數。這意味著變數被提升了(記憶體空間在建立階段就被設定好了),直到程式執行到定義變數位置之前預設值都是 `undefined`。因為當我們列印 `name` 變數時還沒有執行到定義變數的位置,因此變數的值保持為 `undefined`。 通過 `let` 和 `const` 關鍵字宣告的變數也會提升,但是和 `var` 不同,它們**不會被初始化**。在我們宣告(初始化)之前是不能訪問它們的。這個行為被稱之為暫時性死區。當我們試圖在宣告之前訪問它們時,JavaScript 將會丟擲一個 `ReferenceError` 錯誤。

第2題

下面那個選項將會返回 '6' ?

javascript function sumValues(x, y, z) { return x + y + z; } 1. sumValues([...1, 2, 3]) 2. sumValues([...[1, 2, 3]]) 3. sumValues(...[1, 2, 3]) 4. sumValues([1, 2, 3])

⏳ 點選檢視答案 > 答案:3 通過展開操作符 `...`,我們可以 _暫開_ 單個可迭代的元素。函式 `sumValues` function 接收三個引數: `x`, `y` 和 `z`。`...[1, 2, 3]` 的執行結果為 `1, 2, 3`,將會傳遞給函式 `sumValues`。

第3題

哪一個方法會返回 'Hello world!' ?

```javascript const myMap = new Map() const myFunc = () => 'greeting'

myMap.set(myFunc, 'Hello world!')

//1 myMap.get('greeting') //2 myMap.get(myFunc) //3 myMap.get(() => 'greeting') ``` 1. 1 2. 2 3. 2 and 3 4. All of them

⏳ 點選檢視答案 > 答案:2 當通過 `set` 方法新增一個鍵值對,一個傳遞給 `set`方法的引數將會是鍵名,第二個引數將會是值。在這個case裡,鍵名為 _函式_ `() => 'greeting'`,值為`'Hello world'`。 `myMap` 現在就是 `{ () => 'greeting' => 'Hello world!' }`。 1 是錯的,因為鍵名不是 `'greeting'` 而是 `() => 'greeting'`。 3 是錯的,因為我們給`get` 方法傳遞了一個新的函式。物件受 _引用_ 影響。函式也是物件,因此兩個函式嚴格上並不等價,儘管他們相同:他們有兩個不同的記憶體引用地址。

第4題

setInterval 方法的返回值是什麼?

javascript setInterval(() => console.log('Hi'), 1000) 1. 一個唯一的id 2. 該方法指定的毫秒數 3. 傳遞的函式 4. undefined

⏳ 點選檢視答案 > 答案:1 `setInterval` 返回一個唯一的 id。此 id 可被用於 `clearInterval` 函式來取消定時。

第5題

輸出什麼?

```javascript let randomValue = { name: "Lydia" } randomValue = 23

if (!typeof randomValue === "string") { console.log("It's not a string!") } else { console.log("Yay it's a string!") } ``` 1. It's not a string! 2. Yay it's a string! 3. TypeError 4. undefined

⏳ 點選檢視答案 > 答案:2 `if` 語句的條件判斷 `!typeof randomValue` 的值是否等於 `"string"`。 `!` 操作符將這個值轉化為一個布林值。如果值是truthy的話,返回值會是 `false`,如果值是falsy,返回值會是 `true`。在這裡, `typeof randomValue` 的返回值是一個truthy值 `"number"`,意味著 `!typeof randomValue` 的值是一個布林值 `false`。 `!typeof randomValue === "string"` 總是返回false,因為我們實際上是在執行 `false === "string"`。因為條件返回的是 `false`,所以 `else` 語句中的程式碼塊會被執行,因此列印 `Yay it's a string!` 。

第6題

輸出什麼?

```javascript const createMember = ({ email, address = {}}) => { const validEmail = /.+\@.+..+/.test(email) if (!validEmail) throw new Error("Valid email pls")

return {
    email,
    address: address ? address : null
}

}

const member = createMember({ email: "[email protected]" }) console.log(member) ``` 1. { email: "[email protected]", address: null } 2. { email: "[email protected]" } 3. { email: "[email protected]", address: {} } 4. { email: "[email protected]", address: undefined }

⏳ 點選檢視答案 > 答案:3 `address` 的預設值是一個空物件 `{}`。當我們設定 `member` 變數為 `createMember` 函式返回的物件,我們沒有為address引數傳值,意味著address的值為預設的空物件 `{}`。一個空物件是一個truthy值,意味著 `address ? address : null` 條件會返回 `true`。address的值為空物件 `{}`。

第7題

method 的值選擇哪個時,會輸出 { name: "Lydia", age: 22 } ?

```javascript const keys = ["name", "age"] const values = ["Lydia", 22]

const method = / ?? / Objectmethod // { name: "Lydia", age: 22 } ``` 1. entries 2. values 3. fromEntries 4. forEach

⏳ 點選檢視答案 > 答案:3 `fromEntries` 方法可以將二維陣列轉換為物件。在每個子陣列的第一個元素是key,在每個子陣列的第二個元素是value。在這個例子中,我們映射了 `keys` 陣列,它返回了一個數組,陣列的第一個元素為keys陣列當前索引的值,第二個元素為values陣列當前索引的值。 這樣就建立了一個包含正確keys和values的子陣列的陣列,因此結果為`{ name: "Lydia", age: 22 }`。

第8題

輸出什麼?

```javascript const promise1 = Promise.resolve('First') const promise2 = Promise.resolve('Second') const promise3 = Promise.reject('Third') const promise4 = Promise.resolve('Fourth')

const runPromises = async () => { const res1 = await Promise.all([promise1, promise2]) const res2 = await Promise.all([promise3, promise4]) return [res1, res2] }

runPromises() .then(res => console.log(res)) .catch(err => console.log(err)) ``` 1. [['First', 'Second'], ['Fourth']] 2. [['First', 'Second'], ['Third', 'Fourth']] 3. [['First', 'Second']] 4. 'Third'

⏳ 點選檢視答案 > 答案:4 `Promise.all` 方法可以並行式執行promise。如果其中一個promise失敗了,`Promise.all` 方法會帶上被reject的promise的值_rejects_。在這個例子中, `promise3` 帶著 `"Third"` 值reject。我們在呼叫 `runPromises` 時在 `runPromises` 函式內部的 `catch` 方法去捕獲任意error從而捕獲到被reject的值。因為 `promise3` 帶著 `"Third"` 被reject,所以只有 `"Third"` 列印。

第9題

輸出什麼?

```javascript const user = { email: "[email protected]", updateEmail: email => { this.email = email } }

user.updateEmail("[email protected]") console.log(user.email) ``` 1. [email protected] 2. [email protected] 3. undefined 4. ReferenceError

⏳ 點選檢視答案 > 答案:1 `updateEmail` 函式是一個箭頭函式,它沒有和 `user` 物件繫結。這就意味著 `this` 關鍵字不會引用到 `user` 物件,但是會引用到全域性物件。 `user` 物件內部的 `email` 的值不會更新。當列印 `user.email` 的時候, 原始值 `[email protected]` 被返回。

第10題

輸出什麼?

```javascript const animals = {}; let dog = { emoji: '🐶' } let cat = { emoji: '🐈' }

animals[dog] = { ...dog, name: "Mara" } animals[cat] = { ...cat, name: "Sara" }

console.log(animals[dog]) ``` 1. { emoji: "🐶", name: "Mara" } 2. { emoji: "🐈", name: "Sara" } 3. undefined 4. ReferenceError

⏳ 點選檢視答案 > 答案:2 物件的鍵會被轉換為字串。 因為 `dog` 的值是一個物件, `animals[dog]` 實際上意味著我們建立了一個叫做 `"object Object"` 的屬性來代表新的物件。 `animals["object Object"]` 現在等於 `{ emoji: "🐶", name: "Mara"}`。 `cat` 也是一個物件,`animals[cat]` 實際上意味著我們在用新的cat的屬性覆蓋 `animals[``"``object Object``"``]` 的值。 列印 `animals[dog]`,實際上是`animals["object Object"]`,這是因為轉化`dog`物件為一個字串結果 `"object Object"` ,所以返回 `{ emoji: "🐈", name: "Sara" }`。

第11題

輸出什麼?

```javascript const fruit = ['🍌', '🍊', '🍎']

fruit.slice(0, 1) fruit.splice(0, 1) fruit.unshift('🍇')

console.log(fruit) ``` 1. ['🍌', '🍊', '🍎'] 2. ['🍊', '🍎'] 3. ['🍇', '🍊', '🍎'] 4. ['🍇', '🍌', '🍊', '🍎']

⏳ 點選檢視答案 > 答案:3 首先,我們在fruit陣列上呼叫 `slice` 方法。 slice方法不會修改原始陣列,但是會返回從陣列切片下來的值:香蕉emoji。 其次,我們在fruit陣列上呼叫 `splice` 方法。 splice方法會修改原始陣列,也就意味著fruit陣列此時為 `['🍊', '🍎']`。 最後,我們在fruit陣列上呼叫 `unshift` 方法,通過新增一個值的方式改變了原始陣列,新增的是'🍇',它成為了陣列的第一個元素。現在fruit陣列的組成為 `['🍇', '🍊', '🍎']`。

第12題

輸出什麼?

```javascript const user = { email: "[email protected]", password: "12345" }

const updateUser = ({ email, password }) => { if (email) { Object.assign(user, { email }) }

if (password) {
    user.password = password
}

return user

}

const updatedUser = updateUser({ email: "[email protected]" })

console.log(updatedUser === user) ``` 1. false 2. true 3. TypeError 4. ReferenceError

⏳ 點選檢視答案 > 答案:2 `updateUser` 函式更新user的 `email` 和 `password` 屬性的值, 如果它們的值傳入函式, 函式返回的就是 `user` 物件。 `updateUser` 函式的返回值是 `user` 物件,意味著updatedUser的值與 `user` 指向的是同一個 `user` 物件。`updatedUser === user` 為 `true`.

第13題

輸出什麼?

```javascript class Calc { constructor() { this.count = 0 }

increase() {
    this.count ++
}

}

const calc = new Calc() new Calc().increase()

console.log(calc.count) ``` 1. 0 2. 1 3. undefined 4. ReferenceError

⏳ 點選檢視答案 > 答案:1 我們設定 `calc` 變數為 `Calc` 類的一個新例項。 然後,我們初始化一個 `Calc` 的新例項,而且呼叫了這個例項的 `increase` 方法。因為count屬性是在 `Calc` class的constructor內部的,所以count屬性不會在 `Calc` 的原型鏈上共享出去。這就意味著calc例項的count值不會被更新,count仍然是 `0`。

第14題

輸出是什麼?

```javascript function getFruit(fruits) { console.log(fruits?.[1]?.[1]) }

getFruit([['🍊', '🍌'], ['🍍']]) getFruit() getFruit([['🍍'], ['🍊', '🍌']]) ``` 1. null, undefined, 🍌 2. [], null, 🍌 3. [], [], 🍌 4. undefined, undefined, 🍌

⏳ 點選檢視答案 > 答案:4 `?` 允許我們去選擇性地訪問物件內部更深層的巢狀屬性。 我們嘗試列印 `fruits` 陣列索引值為 `1` 的子陣列內部的索引值為 `1` 的元素。 如果在 `fruits` 陣列索引值 為 `1` 的位置不存在元素,會直接返回 `undefined`。 如果 `fruits` 陣列在索引值為 `1` 的位置存在元素,但是子陣列在索引值為 `1` 的位置不存在元素,也會返回 `undefined`。 首先,我們嘗試列印 `[['🍊', '🍌'], ['🍍']]` 的子陣列 `['🍍']` 的第2個元素。這個子陣列只包含一個元素,也就意味著在索引值為 `1` 的位置不存在元素,所以返回的是 `undefined` 。 其次,我們在沒有傳入任何引數呼叫了 `getFruits` 函式,也就意味著形參 `fruits` 的預設值為`undefined`。因為我們選擇性地連結了 `fruits` 在索引值為 `1` 的元素,因為在索引值為 `1` 的位置不存在元素,因此返回的是 `undefined` 。 最後,我們嘗試列印 `['🍍'], ['🍊', '🍌']` 的子陣列 `['🍊', '🍌']` 的第2個元素。子陣列索引值為 `1`的位置為 `🍌` ,因此它被打印出了。

第15題

輸出什麼?

```javascript let count = 0; const nums = [0, 1, 2, 3];

nums.forEach(num => { if (num) count += 1 })

console.log(count) ``` 1. 1 2. 2 3. 3 4. 4

⏳ 點選檢視答案 > 答案:3 在 `forEach` 迴圈內部的 `if` 會判斷 `num` 的值是truthy或者是falsy。因為 `nums` 陣列的第一個數字是 `0`,一個falsy值, `if` 語句程式碼塊不會被執行。`count` 僅僅在 `nums` 陣列的其他3個數字 `1`,`2`,`3` 時加1。因為 `count` 執行了3次加 `1` 運算,所以 `count` 的值為 `3`。

第16題

向物件 person 新增什麼時,可以通過執行 [...person] 獲得類似 ["Lydia Hallie", 21] 的輸出?

```javascript const person = { name: "Lydia Hallie", age: 21 }

[...person] // ["Lydia Hallie", 21] ``` 1. 不需要,物件預設就是可迭代的 2. Symbol.iterator { for (let x in this) yield this[x] } 3. Symbol.iterator { for (let x in this) yield Object.values(this) } 4. *Symbol.iterator { for (let x in this) yield this }

⏳ 點選檢視答案 > 答案:3 物件預設並不是可迭代的。如果迭代規則被定義,則一個物件是可迭代的(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`。

第17題

哪一個選項會導致報錯?

```javascript const emojis = ["🎄", "🎅🏼", "🎁", "⭐"];

/ 1 / emojis.push("🦌"); / 2 / emojis.splice(0, 2); / 3 / emojis = [...emojis, "🥂"]; / 4 / emojis.length = 0; ``` 1. 1 2. 1 and 2 3. 3 and 4 4. 3

⏳ 點選檢視答案 > 答案:4 `const` 關鍵字意味著我們不能 _重定義_ 變數中的值,它 _僅可讀_。而然,值本身不可修改。陣列 `emojis` 中的值可被修改,如 push 新的值, 拼接,又或者將陣列的長度設定為0。

第18題

輸出什麼?

```javascript class Bird { constructor() { console.log("I'm a bird. 🦢"); } }

class Flamingo extends Bird { constructor() { console.log("I'm pink. 🌸"); super(); } }

const pet = new Flamingo(); ``` 1. I'm pink. 🌸 2. I'm pink. 🌸 I'm a bird. 🦢 3. I'm a bird. 🦢 I'm pink. 🌸 4. Nothing, we didn't call any method

⏳ 點選檢視答案 > 答案:2 我們建立了類 `Flamingo` 的例項 `pet`。當我們例項化這個例項,`Flamingo` 中的 `constructor` 被呼叫。首相,輸出 `"I'm pink. 🌸"`, 之後我們呼叫`super()`。`super()` 呼叫父類的建構函式,`Bird`。`Bird` 的建構函式被呼叫,並輸出 `"I'm a bird. 🦢"`。

第19題

輸出什麼?

```javascript 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); ``` 1. ["coding"] 2. ["coding", "dancing"] 3. ["coding", "dancing", "baking"] 4. ["coding", "running", "dancing", "baking"]

⏳ 點選檢視答案 > 答案:3 函式 `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"]`

第20題

選擇哪一個?

```javascript 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 } ``` 1. yield getMembers(teams[i].members) 2. yield* getMembers(teams[i].members) 3. return getMembers(teams[i].members) 4. return yield getMembers(teams[i].members)

⏳ 點選檢視答案 > 答案:2 為了遍歷 `teams` 陣列中物件的屬性 `members` 中的每一項,我們需要將 `teams[i].members` 傳遞給 Generator 函式 `getMembers`。Generator 函式返回一個 generator 物件。為了遍歷這個 generator 物件中的每一項,我們需要使用 `yield*`. 如果我們沒有寫 `yield`,`return yield` 或者 `return`,整個 Generator 函式不會第一時間 return 當我們呼叫 `next` 方法.

第21題

輸出什麼?

```javascript class Counter { #number = 10

increment() { this.#number++ }

getNum() { return this.#number } }

const counter = new Counter() counter.increment()

console.log(counter.#number) ``` 1. 10 2. 11 3. undefined 4. SyntaxError

⏳ 點選檢視答案 > 答案:4 在 ES2020 中,通過 `#` 我們可以給 class 新增私有變數。在 class 的外部我們無法獲取該值。當我們嘗試輸出 `counter.#number`,語法錯誤被丟擲:我們無法在 class `Counter` 外部獲取它!

第22題

輸出什麼?

```javascript const add = x => x + x;

function myFunc(num = 2, value = add(num)) { console.log(num, value); }

myFunc(); myFunc(3); ``` 1. 2 4 and 3 6 2. 2 NaN and 3 NaN 3. 2 Error and 3 6 4. 2 4 and 3 Error

⏳ 點選檢視答案 > 答案:1 首先我們不傳遞任何引數呼叫 `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` 的值。

第23題

以下哪一項會對物件 person 有副作用?

```javascript const person = { name: "Lydia Hallie", address: { street: "100 Main St" } };

Object.freeze(person); ``` 1. person.name = "Evan Bacon" 2. delete person.address 3. person.address.street = "101 Main St" 4. person.pet = { name: "Mara" }

⏳ 點選檢視答案 > 答案:3 使用方法 `Object.freeze` 對一個物件進行 _凍結_。不能對屬性進行新增,修改,刪除。 然而,它僅 對物件進行 _淺_ 凍結,意味著只有 物件中的 _直接_ 屬性被凍結。如果屬性是另一個 object,像案例中的 `address`,`address` 中的屬性沒有被凍結,仍然可以被修改。

第24題

以下哪一項會對物件 person 有副作用?

```javascript const person = { name: "Lydia Hallie" };

Object.seal(person); ``` 1. person.name = "Evan Bacon" 2. person.age = 21 3. delete person.name 4. Object.assign(person, { age: 21 })

⏳ 點選檢視答案 > 答案:1 使用 `Object.seal` 我們可以防止新屬性 _被新增_,或者存在屬性 _被移除_. 然而,你仍然可以對存在屬性進行更改。

第25題

輸出什麼?

```javascript 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; ``` 1. Added a new property! 2. Accessed a property! 3. Added a new property! Accessed a property! 4. 沒有任何輸出

⏳ 點選檢視答案 > 答案:3 使用 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!"`。

第26題

怎樣能在 index.js 中呼叫 sum.js 中的 sum 方法?

```javascript // sum.js export default function sum(x) { return x + x; }

// index.js import * as sum from "./sum"; ``` 1. sum(4) 2. sum.sum(4) 3. sum.default(4) 4. 預設匯出不用 * 來匯入,只能具名匯出

⏳ 點選檢視答案 > 答案:3 使用符號 `*`,我們引入檔案中的所有值,包括預設和具名。如果我們有以下檔案: ```javascript // 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); ``` 將會輸出以下內容: ```javascript { default: "I love JavaScript", name: "Lydia", age: 21 } ``` 以 `sum` 為例,相當於以下形式引入值 `sum`: ```javascript { default: function sum(x) { return x + x } } ``` 我們可以通過呼叫 `sum.default` 來呼叫該函式

第27題

輸出什麼?

```javascript 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(); ``` 1. Promise! Last line! Promise! Last line! Last line! Promise! 2. Last line! Timeout! Promise! Last line! Timeout! Promise! 3. Promise! Last line! Last line! Promise! Timeout! Timeout! 4. Last line! Promise! Promise! Last line! Timeout! Timeout!

⏳ 點選檢視答案 > 答案:4 首先,我們呼叫 `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!`

第28題

輸出什麼?

```javascript 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); ``` 1. 0 2. 1 3. 2 4. 3

⏳ 點選檢視答案 > 答案:4 `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`。

第29題

輸出什麼?

```javascript const emojis = ["🥑", ["✨", "✨", ["🍕", "🍕"]]];

console.log(emojis.flat(1)); ``` 1. ['🥑', ['✨', '✨', ['🍕', '🍕']]] 2. ['🥑', '✨', '✨', ['🍕', '🍕']] 3. ['🥑', ['✨', '✨', '🍕', '🍕']] 4. ['🥑', '✨', '✨', '🍕', '🍕']

⏳ 點選檢視答案 > 答案:2 通過方法 `flat`, 我們可以建立一個新的, 已被扁平化的陣列。被扁平化的深度取決於我們傳遞的值。在這個case裡,我們傳遞了值 `1` (並不必要,這是預設值),相當於只有第一層的陣列才會被連線。即這個 case 裡的 `['🥑']` and `['✨', '✨', ['🍕', '🍕']]`。連線這兩個陣列得到結果 `['🥑', '✨', '✨', ['🍕', '🍕']]`.

第30題

輸出什麼?

```javascript 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!"); } })(); ``` 1. Woah some cool data 2. Oh finally! 3. Woah some cool data Oh finally! 4. Oops didn't work Oh finally!

⏳ 點選檢視答案 > 答案:3 在 `try` 塊區,我們列印 `myPromise` 變數的 awaited 值: `"Woah some cool data"`。因為`try` 塊區沒有錯誤丟擲,`catch` 塊區的程式碼並不執行。`finally` 塊區的程式碼 _總是_ 執行,`"Oh finally!"` 被輸出。

第31題

輸出什麼?

```javascript const randomValue = 21;

function getInfo() { console.log(typeof randomValue); const randomValue = "Lydia Hallie"; }

getInfo(); ``` 1. "number" 2. "string" 3. undefined 4. ReferenceError

⏳ 點選檢視答案 > 答案:4 通過 `const` 關鍵字宣告的變數在被初始化之前不可被引用:這被稱之為 _暫時性死區_。在函式 `getInfo` 中, 變數 `randomValue` 宣告在`getInfo` 的作用域的此法環境中。在想要對 `typeof randomValue` 進行log之前,變數 `randomValue` 仍未被初始化: 錯誤`ReferenceError` 被丟擲! JS引擎並不會根據作用域鏈網上尋找該變數,因為我們已經在 `getInfo` 函式中聲明瞭 `randomValue` 變數。

第32題

輸出什麼?

```javascript 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)); ``` 1. true false true false 2. true false false false 3. false false true false 4. false true false true

⏳ 點選檢視答案 > 答案:3 通過方法 `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`.

第33題

輸出什麼?

```javascript const spookyItems = ["👻", "🎃", "🕸"]; ({ item: spookyItems[3] } = { item: "💀" });

console.log(spookyItems); ``` 1. ["👻", "🎃", "🕸"] 2. ["👻", "🎃", "🕸", "💀"] 3. ["👻", "🎃", "🕸", { item: "💀" }] 4. ["👻", "🎃", "🕸", "[object Object]"]

⏳ 點選檢視答案 > 答案:2 通過解構物件們,我們可以從右手邊的物件中拆出值,並且將拆出的值分配給左手邊物件同名的屬性。在這種情況下,我們將值 "💀" 分配給 `spookyItems[3]`。相當於我們正在篡改陣列 `spookyItems`,我們給它添加了值 "💀"。當輸出 `spookyItems` 時,結果為 `["👻", "🎃", "🕸", "💀"]`。

第34題

輸出什麼?

```javascript 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)) ``` 1. The driver drove 130 and has to pay 300 2. The driver drove 130 mph and has to pay \$300.00 3. The driver drove undefined and has to pay undefined 4. The driver drove 130.00 and has to pay 300.00

⏳ 點選檢視答案 > 答案:2 通過方法 `Intl.NumberFormat`,我們可以格式化任意區域的數字值。我們對數字值 `130` 進行 `mile-per-hour` 作為 `unit` 的 `en-US` 區域 格式化,結果為 `130 mph`。對數字值 `300` 進行 `USD` 作為 `currentcy` 的 `en-US` 區域格式化,結果為 `$300.00`.

第35題

輸出什麼?

```javascript const myFunc = ({ x, y, z }) => { console.log(x, y, z); };

myFunc(1, 2, 3); ``` 1. 1 2 3 2. {1: 1} {2: 2} {3: 3} 3. { 1: undefined } undefined undefined 4. undefined undefined undefined

⏳ 點選檢視答案 > 答案:4 `myFunc` 期望接收一個包含 `x`, `y` 和 `z` 屬性的物件作為它的引數。因為我們僅僅傳遞三個單獨的數字值 (1, 2, 3) 而不是一個含有 `x`, `y` 和 `z` 屬性的物件 ({x: 1, y: 2, z: 3}), `x`, `y` 和 `z` 有著各自的預設值 `undefined`.

第36題

輸出什麼?

```javascript 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); } })(); ``` 1. Promise {1} Promise {2} Promise {3} 2. Promise {} Promise {} Promise {} 3. 1 2 3 4. undefined undefined undefined

⏳ 點選檢視答案 > 答案:3 我們給 函式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`.

第37題

輸出什麼?

```javascript const add = x => y => z => { console.log(x, y, z); return x + y + z; };

add(4)(5)(6); ``` 1. 4 5 6 2. 6 5 4 3. 4 function function 4. undefined undefined 6

⏳ 點選檢視答案 > 答案:1 函式 `add` 是一個返回 返回箭頭函式的箭頭函式 的箭頭函式(still with me?)。第一個函式接收一個值為 `4` 的引數 `x`。我們呼叫第二個函式,它接收一個值為 `5` 的引數 `y`。然後我們呼叫第三個函式,它接收一個值為 `6` 的引數 `z`。當我們嘗試在最後一個箭頭函式中獲取 `x`, `y` 和 `z` 的值,JS 引擎根據作用域鏈去找 `x` 和 `y` 的值。得到 `4` `5` `6`.

第38題

輸出什麼?

```javascript const name = "Lydia Hallie";

console.log(!typeof name === "object"); console.log(!typeof name === "string"); ``` 1. false true 2. true false 3. false false 4. true true

⏳ 點選檢視答案 > 答案:3 `typeof name` 返回 `"string"`。字串 `"string"` 是一個 truthy 的值,因此 `!typeof name` 返回一個布林值 `false`。 `false === "object"` 和 `false === "string"` 都返回 `false`。 (如果我們想檢測一個值的型別,我們應該用 `!==` 而不是 `!typeof`)

第39題

輸出什麼?

```javascript const config = { languages: [], set language(lang) { return this.languages.push(lang); } };

console.log(config.language); ``` 1. function language(lang) { this.languages.push(lang } 2. 0 3. [] 4. undefined

⏳ 點選檢視答案 > 答案:4 方法 `language` 是一個 `setter`。Setters 並不儲存一個實際值,它們的使命在於 _修改_ 屬性。當呼叫方法 `setter`, 返回 `undefined`。

第40題

輸出什麼?

```javascript 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!); } ``` 1. We have to buy bananas! 2. We don't have to buy bananas 3. undefined 4. 1

⏳ 點選檢視答案 > 答案:2 我們傳遞了一個狀態 `groceries.indexOf("banana")` 給if條件語句。`groceries.indexOf("banana")` 返回 `0`, 一個 falsy 的值。因為if條件語句的狀態為 falsy,`else` 塊區內的程式碼執行,並且 `We don't have to buy bananas!` 被輸出.

第41題

輸出什麼?

``javascript 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?.()); ``` 1. undefined undefined undefined undefined 2. Mara undefined Lydia Hallie undefined 3. Mara null Lydia Hallie null 4. null ReferenceError null ReferenceError

⏳ 點選檢視答案 > 答案:2 通過 ES10 或 TS3.7+[可選鏈操作符 `?.`](http://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE),我們不再需要顯式檢測更深層的巢狀值是否有效。如果我們嘗試獲取 `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`.

第42題

輸出什麼?

```javascript let num = 1; const list = ["🥳", "🤠", "🥰", "🤪"];

console.log(list[(num += 1)]); ``` 1. 🤠 2. 🥰 3. SyntaxError 4. ReferenceError

⏳ 點選檢視答案 > 答案:2 通過 `+=` 操作符,我們對值 `num` 進行加 `1` 操作。 `num` 有初始值 `1`,因此 `1 + 1` 的執行結果為 `2`。陣列 `list` 的第二項為 🥰,`console.log(list[2])` 輸出 🥰.

第43題

輸出什麼?

```javascript 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) ``` 1. {name: "Sarah", age: 22} 2. {name: "Sarah", age: 23} 3. {name: "Lydia", age: 22} 4. {name: "Lydia", age: 23}

⏳ 點選檢視答案 > 答案:3 函式 `changeAge` 和函式 `changeAgeAndName` 有著不同的引數,定義一個 _新_ 生成的物件 `{ ...person }`。這個物件有著所有 `person` 物件 中 k/v 值的副本。 首項, 我們呼叫 `changeAge` 函式並傳遞 `person` 物件作為它的引數。這個函式對 `age` 屬性進行加一操作。`person` 現在是 `{ name: "Lydia", age: 22 }`。 然後,我們呼叫函式 `changeAgeAndName` ,然而我們沒有傳遞引數。取而代之,`x` 的值等價 _new_ 生成的物件: `{ ...person }`。因為它是一個新生成的物件,它並不會對物件 `person` 造成任何副作用。`person` 仍然等價於 `{ name: "Lydia", age: 22 }`。

第44題

將會發生什麼?

```javascript let config = { alert: setInterval(() => { console.log('Alert!') }, 1000) }

config = null ``` 1. setInterval 的回撥不會被呼叫 2. setInterval 的回撥被呼叫一次 3. setInterval 的回撥仍然會被每秒鐘呼叫 4. 我們從沒呼叫過 config.alert(), config 為 null

⏳ 點選檢視答案 > 答案:3 一般情況下當我們將物件賦值為 `null`, 那些物件會被進行 _垃圾回收(garbage collected)_ 因為已經沒有對這些物件的引用了。然而,`setInterval`的引數是一個箭頭函式(所以上下文繫結到物件 `config` 了),回撥函式仍然保留著對 `config`的引用。只要存在引用,物件就不會被垃圾回收。因為沒有被垃圾回收,`setInterval` 的回撥每1000ms (1s)會被呼叫一次。

第45題

輸出什麼?

javascript console.log(`${(x => x)('I love')} to program`) 1. I love to program 2. undefined to program 3. ${(x => x)('I love') to program 4. TypeError

⏳ 點選檢視答案 > 答案:1 帶有模板字面量的表示式首先被執行。相當於字串會包含表示式,這個立即執行函式 `(x => x)('I love')` 返回的值. 我們向箭頭函式 `x => x` 傳遞 `'I love'` 作為引數。`x` 等價於返回的 `'I love'`。這就是結果 `I love to program`。

結語

這篇文章來自一個試驗...也想驗證一些東西,侵刪~