2021年前端面試題——JS


目錄:

DOM事件流有那些階段?

解釋事件冒泡以及如何阻止它?

事件委派/事件委托是什么?

如何理解 JS 中的this關鍵字?

更改this指向的方法有那些?

apply、call、bind 區別?

講講JavaScript 作用域

js函數聲明三種方式

js變量聲明

JS的數據類型有哪些?

如何判斷JS變量的數據類型?

什么是閉包?

談談你對JavaScript原型,原型鏈的理解 

談談你對面向對象編程思想的理解,它有什么特點?

js 執行機制、事件循環

同步和異步的區別?

對前端性能優化有什么了解?一般都通過那幾個方面去優化的?

JavaScript的數組的常用方法

DOM事件流有那些階段?

通常,我們將DOM事件流向分為三個階段:捕獲階段,目標階段,冒泡階段。

  • 捕獲階段是指事件響應從最外層的Window開始,逐級向內層前進,直到具體事件目標元素。在捕獲階段,不會處理響應元素注冊的冒泡事件。
  • 目標階段指觸發事件的最底層的元素。
  • 冒泡階段與捕獲階段相反,事件的響應是從最底層開始一層一層往外傳遞到最外層的Window。
參考: 事件捕獲、事件冒泡以及事件代理

解釋事件冒泡以及如何阻止它?

事件由具體的dom節點接收,然后這個事件順着嵌套順序在父元素上觸發。
防止事件冒泡的一種方法是使用 event.stopPropagation()或 event.cancelBubble (低於 IE 9)。

參考:10.解釋事件冒泡以及如何阻止它?

事件委派/事件委托是什么?

事件委派/事件委托適用於未來的元素(動態添加的元素)
利用事件冒泡,將后代元素上事件的處理程序委派給祖先元素。

參考:事件委派的使用及作用

如何理解 JS 中的this關鍵字?

this表示當前對象,this的指向是根據調用的上下文來決定的,默認指向window對象。
上下文環境:全局上下文、函數上下文
全局上下文中的 this:this 都指向全局對象window
函數上下文:
  1. 全局函數:this 指向 window 對象 
  2. 作為對象的方法:當函數作為對象的方法調用時,它的 this 值是調用該函數的對象。(箭頭函數除外)
  3. 作為構造函數:函數作為構造函數,那函數當中的 this 便 new 出來的對象
  4. 函數調用 apply、call、 bind 時: this 的值就取傳入對象的值  (與 apply、call 不同,使用 bind 只會改變一次this的值,無論之后怎么調用)
  5. 箭頭函數:this 的值與創建箭頭函數的上下文的 this 一致

也可以用簡潔的方式來回答:

  • ES5中:this 永遠指向最后調用它的那個對象
  • ES6箭頭函數:箭頭函數的 this 始終指向函數定義時的 this,而非執行時。

更改this指向的方法有那些?

  • 使用 ES6 的箭頭函數
  • 在函數內部使用 _this = this
  • 使用 apply、call、bind
  • new 實例化一個對象

apply、call、bind 區別?

apply 和 call 的區別
其實 apply 和 call 基本類似,他們的區別只是傳入的參數不同。
call 的參數是直接放進去的,第二第三第 n 個參數全都用逗號分隔,直接放到后面
apply 的所有參數都必須放在一個數組里面傳進去
bind與apply、call最大的區別就是:bind不會立即調用,其他兩個會立即調用

參考:JavaScript 中 call()、apply()、bind() 的用法更改this指向的方法及其區別 

講講JavaScript 作用域

全局作用域:定義在所有函數之外的變量,其作用范圍是在整個腳本中
局部作用域(函數作用域):使用var定義在函數內部的變量,其作用范圍是整個函數結構,超出函數 {} 花括號的范圍則不能使用。
塊級作用域:ES6聲明變量的方式:let / const 在變量聲明的代碼段之外是不可見的

參考:JS作用域與聲名提升

js函數聲明三種方式

//(1)  Function()構造器
var f =new Function()
//(2)   函數聲明
function f (){
     console.log(2);
}
//(3)   函數表達式
var f = function() {
      console.log(1);  
}

參考:1-1 聲明 

js變量聲明

var、let、const
var聲明的變量會掛載在window上,而let和const聲明的變量不會
var聲明變量存在變量提升,let和const不存在變量提升(嚴格來說,let也存在)
let和const聲明形成塊作用域存在暫存死區
const聲明必須賦值

參考:1-1 聲明 、較詳細的:JS聲明變量的六種方式

拓展:如果用  const  聲明一個對象,改變對象中某個屬性的值會發生什么?

如圖:

為什么    對象中的b的值變了?如果換成直接給  a={b:3}可以嗎?

 

 為什么會報錯?

const實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。
對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同於常量。但對於復合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針, const只能保證這個指針是固定的(即總是指向另一個固定的地址),至於它指向的數據結構是不是可變的,就完全不能控制了。

 JS的數據類型有哪些?

基本類型:

  • string(字符串)--原始類型
  • boolean(布爾值)--原始類型
  • number(數字)--原始類型
  • symbol(符號)--原始類型
  • null(空值)undefined(未定義)
  • BigInt(BigInt數據類型的目的是比Number數據類型支持的范圍更大的整數值,精度在(2^53-1)范圍內,BigInt(10)值為:10n)

 對象類型(引用類型):Array - - (數組)、Function - - (函數)、Date - - (時間)等

 參考:1-2 數據類型的分類:JavaScript的數據類型詳細介紹

如何判斷JS變量的數據類型?

typeof:只能判斷string、boolean、number、null、symbol

instanceof:判斷對象類型:測試構造函數的 prototype 是否出現在被檢測對象的原型鏈上

延展:為什么typeof null為object?

js 在底層存儲變量的時候,會在變量的機器碼的低位1-3位存儲其類型信息,而 000代表對象,null的所有機器碼均為0,所以,typeof 在判斷 null 的時候就出現問題了,由於null 的所有機器碼均為0,因此直接被當做了對象來看待。

參考:1-3 數據類型的判斷:

什么是閉包?

閉包指的是能夠訪問另一個函數作用域的變量的函數。
閉包就是一個函數,這個函數能夠訪問其他函數的作用域中的變量。

閉包經典理解

  • 由於var 變量的提升,循環的時候賦值都是用一個i;
  • 因為setTimeout為宏任務,由於JS中單線程eventLoop機制,在主線程同步任務執行完后才去執行宏任務,因此循環結束后 才執行setTimeout,但輸出i的時候當前作用域沒有,往上一級再找,發現了i,此時循環已經結束,i變成了10;
  • 使用let可以解決 let形成塊級作用域不會提升
for (var i = 0; i < 10; i++) {
    //這個時候 i已經 = 10了  var i提升 每次進行賦值都是同一個i;
    setTimeout(() => {
        console.log(i);// 打印10個10
    }, 1000)
}

「使用閉包 」

for (var i = 0; i < 10; i++) {
    ((i) => {
        setTimeout(() => {
            console.log(i);//打印0-9
        }, 1000)
    })(i)
}

優點:避免全局變量的污染、希望一個變量長期存儲在內存中(緩存變量)
缺點:內存泄露(消耗)、常駐內存,增加內存使用量

參考:4 閉包閉包到底是什么?閉包的概念?優缺點?破解前端面試(80% 應聘者不及格系列):從閉包說起

談談你對JavaScript原型,原型鏈的理解 

prototype:構造函數的原型對象
原型鏈:JS在創建對象的時候,會在新對象上產生一個__proto__的屬性,這個屬性指向了它構造函數的原型的prototype。由此一級一級向上直到到達Object.prototype.proto === null的這個鏈條我們稱之為原型鏈。

參考:用小豬佩奇說明Javascript的原型和原型鏈

談談你對面向對象編程思想的理解,它有什么特點?

面向對象編程是一種解決軟件復用的設計和編程方法。 這種方法把軟件系統中相近相似的操作邏輯和操作 應用數據、狀態,以類的型式描述出來,以對象實例的形式在軟件系統中復用,以達到提高軟件開發效率的作用。

面向對象的理解:

面向對象是一種設計思想

  1. 符合人們的思考習慣
  2. 把執行者變成指揮者
  3. 簡化功能,把復雜的事情簡單化
面向對象有三大特征:、封裝、繼承、多態
 
拓展:

什么是封裝?

類是一種封裝,將屬性和方法封裝。
函數也是一種封裝,將具有一定共的邏輯代碼封裝到一個函數中,使用的時候調用即可

什么是繼承?

將公共的(共性的)屬性和方法放在父類中,子類只關注自己特有的屬性和方法。

js 執行機制、事件循環

JavaScript 語言的一大特點就是單線程,同一個時間只能做一件事。單線程就意味着,所有任務需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等着。JavaScript 語言的設計者意識到這個問題,將所有任務分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous),在所有同步任務執行完之前,任何的異步任務是不會執行的。

同步和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入 Event Table 並注冊函數。當指定的事情完成時,Event Table 會將這個函數移入 Event Queue。主線程內的任務執行完畢為空,會去 Event Queue 讀取對應的函數,進入主線程執行。上述過程會不斷重復,也就是常說的 Event Loop(事件循環)

主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為 Event Loop(事件循環)。只要主線程空了,就會去讀取"任務隊列",這就是 JavaScript 的運行機制

參考:js 執行機制、事件循環

拓展:宏任務、微任務

宏任務:整體代碼 script,setTimeout,setInterval
微任務:Promise,process.nextTick
我們知道setTimeout這個函數,是經過指定時間后,把要執行的任務(本例中為task())加入到Event Queue中,又因為是單線程任務要一個一個執行,
如果前面的任務需要的時間太久,那么只能等着,導致真正的延遲時間遠遠大於你定義的延時時間。https://juejin.cn/post/6844903512845860872#heading-1
我們還經常遇到setTimeout(fn,0)這樣的代碼,0秒后執行又是什么意思呢?是不是可以立即執行呢?
答案是不會的,setTimeout(fn,0)的含義是,指定某個任務在主線程最早可得的空閑時間執行,意思就是不用再等多少秒了,只要主線程執行棧內的同步任務全部執行完成,棧為空就馬上執行。

參考:這一次,徹底弄懂 JavaScript 執行機制

同步和異步的區別?

異步:客戶端與服務器請求數據的過程中,可以做其他的事情
同步:客戶端與服務器請求數據的過程中,不能做其他的事情

參考:同步和異步的區別?

對前端性能優化有什么了解?一般都通過那幾個方面去優化的?

前端性能優化的七大手段

  1. 減少請求數量
  2. 減小資源大小
  3. 優化網絡連接
  4. 優化資源加載
  5. 減少重繪回流
  6. 性能更好的API
  7. webpack優化

參考:對前端性能優化有什么了解?一般都通過那幾個方面去優化的?

JavaScript的數組的常用方法

向數組添加元素的方法:

  1. Array.push:向數組的末尾追加       返回值是添加數據后數組的新長度,改變原有數組
  2. Array.unshift:向數組的開頭添加    返回值是添加數據后數組的新長度,改變原有數組
  3. splice:向數組的指定index處插入   返回的是被刪除掉的元素的集合,會改變原有數組

向數組刪除元素的方法:

  1. pop():從尾部刪除一個元素   返回被刪除掉的元素,改變原有數組
  2. shift():從頭部刪除一個元素   返回被刪除掉的元素,改變原有數組
  3. splice:在index處刪除howmany個元素   返回的是被刪除掉的元素的集合,會改變原有數組

數組排序的方法:

  1. everse():反轉,倒置  改變原有數組
  2. sort():按指定規則排序 改變原有數組

參考:JavaScript的數組的常用方法(一)js常用數組方法

持續更新呦!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


免責聲明!

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



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