前端面試100問(1-10)


內容的起源來自於掘金上的一篇文章——《前端 100 問:能搞懂 80% 的請把簡歷給我》

 

系列筆記:

 

 

題1:(滴滴、餓了么)寫React/Vue項目時為什么要在列表組件中寫key,其作用是什么?

答:(以Vue舉例回答)

1.維護狀態。

Vue默認使用“就地更新”策略,如果數據項的順序被改變,Vue將不會移動DOM元素來匹配數據項的順序。

這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時DOM狀態的列表渲染輸出。

為了給Vue一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一key屬性。

<div v-for="item in items" v-bind:key="item.id">
  <!-- 內容 -->
</div>

2.key的特殊屬性主要用在Vue的虛擬DOM算法,再新舊nodes對比時辨識VNodes。

使用key,它會基於key的變化重新排列元素順序,並且會移除key不存在的元素。

有相同父元素的子元素必須有獨特的key。重復的key會造成渲染錯誤。

 

它也可以用於強制替換元素/組件而不是重復使用它。場景如下:

  • 完整地觸發組件的生命周期鈎子
  • 觸發過渡

例如:

<transition>
  <span :key="text">{{ text }}</span>
</transition>

當text發生改變時,<span>會隨時被更新,因此會觸發過渡。

 

 

題2:['1', '2', '3'].map(parseInt) what & why?

答:看到這題的時候,我快速按下f12打開開發者工具,console打印結果是[1, NaN, NaN],納尼~

當時我就懵了。於是我又去看了下map方法的文檔,沒毛病,常用的~等等,就在最下面,竟然出現了和題目一樣的代碼~

MDN開發者文檔:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map

見最下面的【使用技巧案例】

// 下面的語句返回什么呢:
["1", "2", "3"].map(parseInt);
// 你可能覺的會是[1, 2, 3]
// 但實際的結果是 [1, NaN, NaN]

// 通常使用parseInt時,只需要傳遞一個參數.
// 但實際上,parseInt可以有兩個參數.第二個參數是進制數.
// 可以通過語句"alert(parseInt.length)===2"來驗證.
// map方法在調用callback函數時,會給它傳遞三個參數:當前正在遍歷的元素, 
// 元素索引, 原數組本身.
// 第三個參數parseInt會忽視, 但第二個參數不會,也就是說,
// parseInt把傳過來的索引值當成進制數來使用.從而返回了NaN.

 

 

題3:什么是防抖和節流?有什么區別?如何實現?

答:說真的,看到這題我是懵的。趕緊查了下什么是防抖和節流。

研究了一天,准備單獨寫一篇防抖和節流。

相關文章:

一個防抖和節流的實用例子

一個Vue表單提交防抖的實用例子

 

 

題4:介紹下Set、Map、WeakSet和WeakMap的區別?

答:

Set 一種類似於數組但沒有重復值的數據結構

const s = new Set();

[2, 3, 5, 4, 2, 2].forEach(x => s.add(x));

for (let i of s) {
  console.log(i);  
}

// 2 3 5 4

詳細內容參見:阮一峰的《ECMAScript 6 入門》 http://es6.ruanyifeng.com/#docs/set-map

WeakSet 結構與Set類似,但有2個區別:

WeakSet的成員只能是對象,而不能是其他類型的值,比如數值和Symbol

WeakSet中的對象都是弱引用

 

Map數據結構類似於對象,“鍵”的范圍不限於字符串,各種類型的值都可以當作鍵

Map結構提供了“值-值”的對應,是一種更完善的Hash結構實現

 

WeakMap結構與Map結構類似,也是用於生成鍵值對的集合。

WeakMap與Map的區別:

WeakMap只接受對象作為鍵名(null除外),不接受其他類型的值作為鍵名

WeakMap的鍵名所指向的對象,不計入垃圾回收機制

(有點暈~)

 

題5:介紹下深度優先遍歷和廣度優先遍歷,如何實現?

題6:請分別用深度優先思想和廣度優先思想實現一個拷貝函數?

這兩題我看了很久,有點暈,先跳過

貼上木易楊說的答案:

題5答案:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/9

題6答案:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/10

 

題7:ES5/ES6的繼承除了寫法以外還有什么區別?

答:

1.class聲明不會提升(有人說是會提升但不賦值)

2.class聲明內部會啟用嚴格模式

3.class的所有方法(包括靜態方法和實例方法)都是不可枚舉的

4.class的所有方法(包括靜態方法和實例方法)都沒有原型對象prototype

5.必須使用new調用class

6.class內部無法重寫類名

//普通函數
function Bar() {
    Bar = 'Baz'; //沒問題
}

//
class Bar {
    constructor() {
        Bar = 'Baz'; // 錯誤
    }
}

7.因為this生成順序不同,所以子類的constructor中需要使用super()

答案摘自:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/20

 

 

題8:setTimeout、Promise、Async/Await的區別?

答:那天首先花了一天時間去了解下什么是JS事件循環、宏任務和微任務這些概念,整理成筆記如下:

 

學習筆記:初識JS事件循環機制

以下是來自github木易楊的blog的答案,原鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/33

1.setTimeout

console.log('script start')
setTimeout(function () {
    console.log('settimeout')
})
console.log('script end')

// 輸出順序:script start -> script end -> settimeout

2.Promise

Promise本身是同步的立即執行函數。當在executor中執行resolve或者reject時是異步操作,會先執行then/cathc等,當主棧完成后,才會去調用resolve/reject中存放的方法執行。

console.log('script start')
let promise1 = new Promise(function(resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function(){
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')

//輸出順序: script start -> promise1 -> promise1 end -> script end -> promise2 -> settimeout

當JS主線程執行到Promise對象時

  • promise1.then()的回調就是一個task
  • promise1是resolved或者rejected:那這個task就會放入當前事件循環回合的microtask queue
  • promise1是pending:這個task就會放入事件循環的未來的某個(可能下一個)回合的microtask queue中
  • setTimeout的回調也是個task,它會被放入microtask queue即使是0ms的情況

 

3.async/await

async function async1 () {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}

async function async2 () {
    console.log('async2')
}

console.log('script start')
async1()
console.log('start end')

// 輸出順序:script start -> async1 start -> async2 -> script end -> async1 end

async函數返回一個Promise對象,當函數執行的時候,一旦遇到await就會先返回,等到觸發的異步操作完成,再執行函數體內后面的語句。可以理解為,是讓出了線程,跳出了async函數體。

舉個例子:

async function fn1 () {
    return 1
}
console.log(fn1())

 

fn1的運行結果是 一個Promise對象。因為也可以用then來處理后續邏輯。

 

fn1().then(res => {
    console.log(res)
})

await的含義為等待,也就是async函數需要等待await后的函數執行完成並且有了返回結果(Promise對象)之后,才能繼續執行下面的代碼。

await通過返回一個Promise對象來實現同步的效果。

 

await/async是通過Generator/function*來實現的。所以async/await的相關優勢也來自於generator.

Generator是一個可以暫停function.

function* generator(i) {
    console.log('inside before')
    yield i;
    yield i + 10;
    console.log('inside after')
}

var gen = generator(10)
console.log('outside before')
console.log(gen.next().value)
console.log(gen.next().value);
console.log('outside after')
gen.next();

//結果如下:
// outside before
// inside before
// 10
// 20
// outside after
// inside after

 

題9:Async/Await如何通過同步的方式實現異步?

答:

針對這個問題的學習筆記: 每日技術:Promise和Async/Await用法

 

 

 題10:異步筆試題

請寫出下面代碼的運行結果

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

答:每日技術:關於promise,async,setTimeout的執行順序

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM