總結一些前端常見面試題(附答案),帶你鞏固知識點!

2022-07-29 10:00:29

釋出文章主要也是鞏固自己的知識更加熟練,全憑自己的理解和網上查資料總結出來的,如有不對的地方還望多多指點。下面是我總結的一下常見面試題,為了督促自己還可以會繼續更新

js部分

1、談一談你對原型鏈的理解

在js語言中,每個範例物件都有一個__proto__屬性,改屬性指向他的原型物件,且這個範例物件的建構函式都有一個原型屬性prototype,與範例物件的__proto__屬性指向同一個物件,當這個物件在查詢一個屬性的值時,自身沒有就會根據__proto__向他的原型上尋找,如果不存在,則會到生成這個範例物件的建構函式的原型物件上尋找,如果還是不存在,就繼續道Object的原型物件上找,在往上找就為null了,這個鏈式尋找的過程,就被稱為原型鏈

2、原型、建構函式、範例物件三者的關係

首先從建構函式說起,建構函式通過prototype指向他的原型物件,原型物件通過他的constructor屬性指回這個建構函式,表明原型物件是由哪個建構函式生成的。原型物件通過new關鍵字生成的範例物件,這個範例物件可以通過__proto__屬性指向生成這個範例物件的建構函式的原型物件,實現一個三角關係。

1.png

3、實現繼承的方式

繼承的方式有很多種,網上的答案都有很多,我自己總結且大致說的明白的有這五種

1)原型鏈繼承

藉助原型可以基於已有的物件建立物件,同時還不必因此建立自定義型別。 在 object()函數內部,先建立一個臨時的建構函式,然後將傳入的物件作為這個構造 函數的原型,最後返回了這個臨時型別的一個新範例。關鍵程式碼:Star.proyotype = new Person(), Star.proyotype.constructor = Star缺點:只能繼承父類別的方法

2)借用建構函式繼承

在子類建構函式的內部呼叫超型別建構函式。可以通過使用 apply()call()方 法在新建立的物件上執行建構函式。關鍵程式碼:Person.call(this,age,name)缺點:無法複用,只能繼承父類別的屬性

3)組合繼承

也叫偽經典繼承。指的是將原型鏈和借用建構函式的技術組合到一 起,從而發揮二者之長。使用原型鏈實現對原型屬性屬性和方法的繼承,通過借用建構函式來實現範例 屬性的繼承。 既通過在原型上定義方法實現了函數複用,又能保證每一個範例都有它自己的屬性。但是會有一個小bug,裡面的age,name,有兩份,有一份的值為undefined,因為裡面的apply()call()方法會自動多呼叫一次。

4)寄生組合繼承

通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。 本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型。是公認繼承比較全面的一種方法,要寫全的話還是非常多的,我只會一個簡單的?,關鍵程式碼:Star.prototype = Object.create(Person.prototype)

5)ES6的Class類繼承方式

可利用class關鍵字配合extends關鍵字來實現繼承。ES6中引入了class關鍵字來宣告類,而class(類)可通過extends來繼承父類別中屬性和方法,super指向父類別的原型物件,可以呼叫父類別的屬性和方法,且子類constructor方法中必須有super關鍵字,且必須出現在this之前。

4、js資料型別

資料型別從大的方向來說分為兩種

  • 基本資料型別:字串(String),數位(Number),布林(Boolean),空(Null),未定義(Undefined),Symbol(nbs)
  • 複雜資料型別:物件(Object),陣列(Array),函數(Function)
    Symbol表示獨一無二的值,避免屬性名的衝突

5、檢測資料型別

  • typeof 檢測 存在的問題:null 或者陣列列印出來也是 object

  • instanceof (只能檢測複雜資料型別)
    返回值是 true 或者 false 相關的建構函式只要在原型鏈上,就是 true,否則就是 false 可以用於檢測是不是陣列

  • Object.prototype.toString.call(要檢測的資料值)
    為什麼要借 Object.prototype.toString,因為自己的 toString 被自己原型重寫了,得不到類似[object Object]

6、如何檢測一個資料是陣列

var arr = [2, 3, 4]
console.log(arr instanceof Array)
console.log(Array.isArray(arr))
console.log(Object.prototype.toString.call(arr))

7、深拷貝和淺拷貝

  • 淺拷貝:只是拷貝一層,更深層次物件級別的只拷貝了地址

  • 深拷貝:層層拷貝,每一級別的資料都會拷貝

  • 淺拷貝方法:
    1. 使用 lodash 淺拷貝 clone 方法,讓他們倆指向不同地址
    2. 使用 Object.assign 方法
    3. 使用es6語法的 ... 拓展運運算元

  • 深拷貝方法:
    1. 使用JSON.parse(JSON.stringify(obj)) ,缺點:當物件有方法和undefined屬性的時候會丟失
    2. 使用遞迴

    • 如果存在迴圈參照就會出現堆疊溢位
    • 解決思路:把處理好的物件存起來,在處理新的物件的時候,會現在這個存的地方找一找有沒有處理好,如果有就直接返回就行了
let obj = {
        name: "zs",
        age: 20,
        father: [2, 3, 4],
      };

      function deepClone(target) {
        //這一行如果不用三元判斷 如果是陣列會有bug會被拷貝成偽陣列物件
        let tempObj = Array.isArray(target) ? [] : {};
        for (let key in target) {
          if (typeof target[key] === "object") {
            tempObj[key] = deepClone(target[key]);
          } else {
            tempObj[key] = target[key];
          }
        }
        return tempObj;
      }

      let obj1 = deepClone(obj);
      console.log(obj1);

8、slice和splice的區別

  • 兩者都是陣列刪除的方法

1.splice改變原陣列,slice不改變原陣列。
2.slice會返回一個新的陣列,可以用於擷取陣列
3.splice除了可以刪除之外,還可以替換,新增陣列
4.splice可傳入3個引數,slice接受2個引數

9、substr和substring的區別

  • 兩者的作用都是擷取字串

  • substr是從起始索引號開始提取指定長度的字串

  • substring是提取字串中兩個指定索引號之間的字元

10、let const var區別

let和const都是用來宣告變數的,在ES5中我們可以使用var來進行變數宣告 -使用let和const作用
- 防止for迴圈中變數提升的經典場景
- 不汙染全域性變數

var關鍵字宣告變數

1.var關鍵字宣告變數存在,變數提升的問題;
2.var宣告的變數不存在塊級作用域,如果是全域性變數在任何地方都可以呼叫;
3.var宣告變數如果名稱重複了,後面宣告的會將前面宣告的覆蓋掉;

let關鍵子宣告變數

1.不存在變數提升,let宣告變數不存在變數提升的問題:如果在let宣告變數前呼叫該變數就會報錯(提示初始化前無法存取該變數);
2.塊級作用域,let宣告變數存在塊級作用域(全域性、函數、eval嚴格模式),只在當前的程式碼塊中生效,如果在當前程式碼塊以外呼叫就會報錯(當前的變數沒有定義);
3.不影響作用域鏈的操作
4.不允許變數重複宣告,let宣告的變數是不允許重複宣告的,如果同一個名稱被重複宣告了就會報錯(當前的標識已經被宣告了);

const宣告變數

1.const宣告的變數也具有:不存在變數提升塊級作用域不允許重複宣告的特點;
2.const宣告的變數都是常數(不允許改變的量),一旦宣告就不允許被修改,如果修改就會報錯--常數變數賦值
3.一般第三方的框架中會大量使用const宣告變數,這樣可以避免使用者修改框架中的變數;
4.const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常數。

11、new的過程

  • 建立一個新的空物件。(即範例物件)

  • 讓this指向這個新物件

  • 執行建構函式裡面的程式碼,給這個新物件新增屬性和方法

  • 返回這個新物件obj。(定義的建構函式中不寫返回值。)

12、防抖節流

防抖

  • 防抖是指在事件觸發n秒後再執行,如果在n秒內再次被觸發,則重新計算時間。(就是在觸發某個事件後,在下一次觸發之前,中間的間隔時間如果超過設定的時間才會傳送請求,一直觸發就不會傳送請求 應用場景:
    a、scroll事件捲動觸發,
    b、搜尋方塊輸入查詢
    c、表單驗證
    d、按鈕提交事件
    e、瀏覽器視窗縮放,resize事件
function debounce(func, delay) {
        let timer = null // 計時器
        return function (...args) {
          clearTimeout(timer) // 清除上一次計時器
          timer = setTimeout(() => {
            // 重新定時
            func.apply(this, args)
          }, delay)
        }
      }

節流

  • 節流是指如果持續觸發某個事件,則每隔n秒執行一次。
function throtte(func, time) {
        let timer = null // 計時器
        return function (...args) {
          if (timer) return // 無視,直接返回
          timer = setTimeout(() => {
            func.apply(this, args)
          }, time)
        }
      }

13、promise的3種狀態

這點簡單介紹概念,用法後面在詳細介紹

1) . 初始態pending

- pending。它的意思是 "待定的,將發生的",相當於是一個初始狀態。建立[Promise]物件時,且沒有呼叫resolve或者是reject方法,相當於是初始狀態。這個初始狀態會隨著你呼叫resolve,或者是reject函數而切換到另一種狀態。

2 ). 成功態resolved--也叫fulfilled

- resolved。表示解決了,就是說這個承諾實現了。 要實現從pending到resolved的轉變,需要在 建立Promise物件時,在函數體中呼叫了resolve方法(即第一個引數)。

3) . 失敗態rejected

- rejected。拒絕,失敗。表示這個承諾沒有做到,失敗了。要實現從pending到rejected的轉換,只需要在建立Promise物件時,呼叫reject函數。

14、氣泡排序

// 上口訣 雙層for迴圈 外層長度-1 內層長度-1-i
      let arr = [4, 3, 1, 7, 8, 10]
      for (let i = 0; i < arr.length - 1; i++) {
        for (let j = 0; j < arr.length - 1 - i; j++) {
          if (arr[j] > arr[j + 1]) {
            let temp = arr[j]
            arr[j] = arr[j + 1]
            arr[j + 1] = temp
          }
        }
      }
      console.log(arr)

Vue部分

1、MVVM

MVVM是三個單詞的縮寫,model(資料,一般來自ajax或本地儲存)+view(檢視template)+viewmodel(vue範例)

  • model資料變了,檢視會跟著改變,如果用的是v-model,資料也會跟著改變,viewmodel在中間起一個橋樑作用
  • model 和 view 就像現實中房東和租客一樣,他們是不認識的,通過中介 viewmodel
    好處
    • 資料驅動
      • 因為資料變了。檢視也會跟著變,所以在 vue 中不用操作dom來改變檢視
    • 解耦(降低了耦合性)
      • 由於 model 和 view 是沒有關係的,是通過 viewmodel 結合在一起的,所以維護起來很方便,因為 model 邏輯代買改了,view 不用改

2、vue生命週期

  • vue中的生命週期是指元件從建立到銷燬的過程,主要分為4個週期8個勾點函數

1.分別是建立階段的beforeCreatecreated,一般在beforeCreate寫loading載入效果,使使用者體驗更好,一般在created中傳送ajax請求獲取資料

2.然後是掛載階段的beforeMountmounted,一般會在mounted中操作DOM元素

3.更新階段的是beforeUpdateupdated,當資料更新時需要做統一的業務處理時,拿到最新的dom,可以使用updated 這個勾點函數

4.最後是銷燬階段的beforeDestroydestroyed,可以在beforeDestroy做一些清理的工作,比如說定時器 和解綁一些addEventListener監聽的事件

  • 補充:(還有keep-alive的兩個勾點函數,使用場景是當元件切換時會進行銷燬,因此元件中的初始化的4個勾點函數會多次執行,比較浪費資源,此時可以使用keep-alive紀行元件的快取,可以讓元件切換時不被銷燬,keep-alive有兩個獨有的勾點函數,分別是activateddeactivated,是元件啟用和失活時會執行的兩個勾點函數)

3、單向資料流

單向資料流是指父元件向子元件傳遞資料,子元件通過props接收,當父元件中的值改變了,子元件中對應的資料也會改變,因為props是唯讀的,所以無法直接在子元件中對父元件傳遞過來的值進行修改,但是如果這個資料是一個參照資料型別,是可以直接在子元件中修改資料中的某個屬性的,只要不改變這個資料的記憶體地址就可以

4、雙向資料繫結

  • 資料 -> 檢視
  • 檢視 -> 資料

vue中普通指令都可以實現資料變了,檢視會跟著變,但是有一個特殊的指令叫v-model,它一般用於表單控制元件,它可以實現雙向資料繫結,所謂的雙向資料就是資料變了,檢視就會跟著改變,反過來也是

5、v-model原理

v-model一般配合input框使用,實現雙向資料繫結的效果,它是v-bindv-on的語法糖,原理是通過v-bind將資料繫結給input框,再通過v-on:input,在input中的值改變時,通過$event可以獲取到事件源物件 再通過target.value獲取到input中更新後的值 將這個值再賦值給繫結的資料即可

2.png

6、事件傳參

在vue的元件使用自定義事件時,$event代表子元件丟擲的資料,當這個自定義事件觸發一個方法時,
可以不傳$event而且可以在方法中進行接收,但是如果寫的話就一定要寫成$event的形式,這是一個固定寫法,
或者這個方法既要傳參又要使用事件物件,這個時候$event也是必須要寫的
- @click='fn' 在回撥函數直接通過引數可以拿到事件物件
- @click='fn($event)' 這個時候@event是固定寫法

7、父子元件的宣告週期執行順序

1.初始化階段時,先執行父元件的beforeCreatecreatedbeforeMount三個勾點函數,然後執行子元件的beforeCreatecreatedbeforeMountmounted四個勾點函數,最後執行父元件的mounted勾點函數

2.更新階段,先執行父元件的beforeUpdate,然後執行子元件的beforeUpdateupdated,最後執行父元件的updated

3.銷燬階段,先執行父元件的beforeDestroy,然後執行子元件的eforeDestroydestroyed,最後執行父元件的destroyed

8、v-if和v-show的區別

v-ifv-show都可以控制標籤,實現元件的顯示與隱藏,不同點是v-show是通過display的block和none屬性來控制的,當元素隱藏時,頁面結構依然存在

  • v-if是通過將元素建立和銷燬來控制顯示與隱藏的,當v-if的條件為否時,會直接銷燬該元素,當滿足時會重新建立出來,有可能會影響頁面的迴流或重繪

  • 如果該元素需要頻繁切換時可以使用v-show,不需要頻繁切換時可以使用v-if,提高效能

9、v-for和v-if為什麼要避免一起使用

  • 因為v-for的優先順序比v-if要高,兩者同時作用於一個標籤或元件時,v-for會優先執行,執行後再進行v-if的判斷,但是不滿足v-if的條件的時候是可以不執行v-for的,這時候就會造成資源浪費,效能比較差
  • 解決辦法是可以通過計算屬性將滿足v-if判斷條件的資料篩選出來,再使用v-if直接渲染篩選後的資料,或者是當v-if不依賴v-for時,可以通過template將v-if寫在迴圈的外部,這樣當不滿足v-if的判斷條件時,就不會再執行v-for了,也可以將資料放在計算屬性裡面計算過濾出來的資料在交給v-for迴圈,代替v-if的作用,即可解決。

10、自定義指令:directive

應用場景

  • v-imgerror 公司專案中有的使用者頭像可能載入報錯,可以給他一張預設圖片, onerror this.img=預設圖片

  • v-focus 開啟帶有搜尋的頁面的時候,實現自動把遊標定位到 input 中

  • 自定義指令的勾點函數

1.bind 屬性繫結的時候執行 只會有一次
2. inserted 當前指令所在的元素插入到頁面中的時候執行一次
3. update 當前指令所在的元件中的 data 資料有更新就會執行,可以執行多次

// 指令的勾點有三個 bind inserted update
// bind inserted 只會執行一次
// update 會反覆執行
Vue.directive('focus', {
  inserted(el) {
    el.focus()
  },
})

Vue.directive('red', {
  bind(el) {
    el.style.color = 'red'
  },
})

Vue.directive('check', {
  update(el) {
    const reg = /^[a-zA-Z0-9]+$/
    if (reg.test(el.value)) {
      el.style.color = 'green'
    } else {
      el.style.color = 'red'
    }
  },
})

瀏覽器的快取機制

這塊部分理解不是很透徹,大家淺看一下就可以了?

  • 概念:瀏覽器會將請求後的資源進行存貯為離線資源,當下次需要該資源時,瀏覽器會根據快取機制決定直接使用快取資源還是再次向伺服器傳送請求
    作用:
  • 減少了不必要資料的傳輸、降低伺服器的壓力
  • 加快了使用者端存取速度
  • 增強使用者體驗
  1. 強快取:過期之前一直用本地離線資源 不會和伺服器互動
    • http1.0 expire 具體的時間2023年1月1日
    • http1.1 cache-control 時間期限1年 (優先順序高)
  2. 協商快取 本質是看本地東西和伺服器有沒有變舊(伺服器上有沒有更新的資源) 強快取不會和伺服器互動 協商快取會互動一次來判斷東西有沒有變舊
    • http1.0 last-modified/if-modified-since
    • http1.1 etag/if-none-match(優先順序高)
  • 當前頁面中有一個img,它的src是logo.png
    1. 先看本地有沒有快取資源,如果沒有,就需要向伺服器發請求 拿回來這個資源同時拿回來expire,cache-control,last-modified,etag(響應報文中)
    2. 過了一段時間(不確定的),又有一個別的頁面上面有一個img,src也是logo.png,這個時候就去看一下本地有沒有快取資源,發現有,再看一下它expire,catch-control(如果有,優先順序是看cache-control),如果沒有過期,就用就行了(這塊屬於強快取) 但是發現如果過期了,就開始進入協商快取的階段,就向伺服器傳送一個請求把if-modified-since(值就是last-modifyed)/if-none-match(etag)通過請求頭髮過去, 伺服器開始對比看看伺服器上的資源有沒有比本地更新一點,如果伺服器資源還是舊的,返回一個狀態碼叫304,瀏覽器一看狀態是304就繼續用本地離線資源,如果伺服器資源有更新的資源,狀態碼就是200,伺服器就需要傳給瀏覽器一個新的logo.png,流程重新再走一遍

設計模式

本人技術棧是主要是前端vue的,所以對這方面的知識還是有所欠缺的,儘量說的明白一點,其實我也不是很懂,大致明白,如果想要全面理解透還是需要很多技術儲備的,很明顯我不是的哈哈?

1、觀察者模式

觀察者模式即一個物件被多個物件所依賴,當被依賴的物件發生更新時,會自動通知所有依賴的物件

  • 觀察者模式定義了物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都將得到通知,並自動更新
    比喻:
    寶寶 -> 父母爺爺奶奶 一對多的依賴關係
    寶寶哭 -> 父母爺爺奶奶趕緊過來服務 當一個物件的狀態發生改變時,所有依賴於它的物件都將得到通知,並自動更新

  • 模式特點: 有二個主體 一個是被觀察者 Dep 一個是觀察者 watcher,在vue中v-band就是採用這種模式理念,缺點是耦合性太高

2、釋出訂閱模式

釋出-訂閱模式其實是一種物件間一對多的依賴關係,當一個物件的狀態傳送改變時,所有依賴於它的物件都將得到狀態改變的通知。

  • 在現在的釋出訂閱模式中,稱為釋出者的訊息傳送者不會將訊息直接傳送給訂閱者,這意味著釋出者和訂閱者不知道彼此的存在。在釋出者和訂閱者之間存在第三個元件,稱為排程中心或事件通道(event bus),它維持著釋出者和訂閱者之間的聯絡,過濾所有釋出者傳入的訊息並相應地分發它們給訂閱者

  • 模式特點:有三個主體 釋出者 排程中心 訂閱者,在vue中eventBus體現出來了這種模式理念,可以實現解耦

3、兩者模式區別

  • 主體數量不一樣,觀察者模式有二個主體 分別是被觀察者 Dep 和觀察者 watcher,釋出訂閱模式有三個主體 分別是釋出者 排程中心(事件通道 ) 訂閱者

  • 釋出訂閱模式相比觀察者模式多了個事件通道,事件通道作為排程中心,管理事件的訂閱和釋出工作,徹底隔絕了訂閱者和釋出者的依賴關係,訂閱者和釋出者是解耦的(不知道彼此存在)

(學習視訊分享:、jQuery視訊教學

以上就是總結一些前端常見面試題(附答案),帶你鞏固知識點!的詳細內容,更多請關注TW511.COM其它相關文章!