前端進階面試-JS面試題

目錄

  • ES5 和 ES6 分別幾種方式聲明變量
  • DOM 事件有哪些階段?談談對事件代理的理解
  • ES6 的 class 和構造函數(shù)的區(qū)別
  • transform、translate、transition 分別是什么屬性?CSS 中常用的實現(xiàn)動畫方式
  • 介紹一下rAF(requestAnimationFrame)
  • javascript 的垃圾回收機制講一下
  • 對前端性能優(yōu)化有什么了解?一般都通過那幾個方面去優(yōu)化的?

判斷 js 類型的方式

1. typeof

可以判斷出'string','number','boolean','undefined','symbol'

但判斷 typeof(null) 時值為 'object'; 判斷數(shù)組和對象時值均為 'object'

2. instanceof

原理是 構造函數(shù)的 prototype 屬性是否出現(xiàn)在對象的原型鏈中的任何位置

復制function A() {}
let a = new A();
a instanceof A     //true,因為 Object.getPrototypeOf(a) === A.prototype;
  1. Object.prototype.toString.call()
    常用于判斷瀏覽器內置對象,對于所有基本的數(shù)據(jù)類型都能進行判斷,即使是 null 和 undefined

  2. Array.isArray()
    用于判斷是否為數(shù)組

ES5 和 ES6 分別幾種方式聲明變量
ES5 有倆種:varfunction
ES6 有六種:增加四種, let、 constclassimport

注意: let 、 const 、 class 聲明的全局變量再也不會和全局對象的屬性掛鉤

閉包的概念?優(yōu)缺點?
閉包的概念?優(yōu)缺點?
閉包的概念:閉包就是能讀取其他函數(shù)內部變量的函數(shù)。

優(yōu)點:

1.避免全局變量的污染
2.希望一個變量長期存儲在內存中(緩存變量)
缺點:

內存泄露(消耗)
1.常駐內存,增加內存使用量
2.淺拷貝和深拷貝
淺拷貝

復制// 第一層為深拷貝
Object.assign()
Array.prototype.slice()
擴展運算符 ...

深拷貝

復制JSON.parse(JSON.stringify())

遞歸函數(shù)

復制function cloneObject(obj) {
  var newObj = {} //如果不是引用類型,直接返回
  if (typeof obj !== 'object') {
    return obj
  }
  //如果是引用類型,遍歷屬性
  else {
    for (var attr in obj) {
      //如果某個屬性還是引用類型,遞歸調用
      newObj[attr] = cloneObject(obj[attr])
    }
  }
  return newObj
}

數(shù)組去重的方法
1.ES6 的 Set

復制let arr = [1,1,2,3,4,5,5,6]
let arr2 = [...new Set(arr)]

2.reduce()

let arr = [1,1,2,3,4,5,5,6]

let arr2 = arr.reduce(function(ar,cur) {

if(!ar.includes(cur)) {

ar.push(cur)

}

return ar
 },[])

3.filter()

復制// 這種方法會有一個問題:[1,'1']會被當做相同元素,最終輸入[1]
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
  // indexOf() 方法可返回某個指定的 字符串值 在字符串中首次出現(xiàn)的位置
  return arr.indexOf(item) === index
})

DOM 事件有哪些階段?談談對事件代理的理解
分為三大階段:捕獲階段--目標階段--冒泡階段

事件代理簡單說就是:事件不直接綁定到某元素上,而是綁定到該元素的父元素上,進行觸發(fā)事件操作時(例如'click'),再通過條件判斷,執(zhí)行事件觸發(fā)后的語句(例如'alert(e.target.innerHTML)')

好處:(1)使代碼更簡潔;(2)節(jié)省內存開銷

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

當我們打開網(wǎng)站時,網(wǎng)頁的渲染過程就是一大堆同步任務,比如頁面骨架和頁面元素的渲染。而像加載圖片音樂之類占用資源大耗時久的任務,就是異步任務。關于這部分有嚴格的文字定義,但本文的目的是用最小的學習成本徹底弄懂執(zhí)行機制,所以我們用導圖來說明:


image.png

導圖要表達的內容用文字來表述的話:

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

我們不禁要問了,那怎么知道主線程執(zhí)行棧為空???js 引擎存在 monitoring process 進程,會持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會去 Event Queue 那里檢查是否有等待被調用的函數(shù)。換一張圖片也許更好理解主線程的執(zhí)行過程:


image.png

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

說完 JS 主線程的執(zhí)行機制,下面說說經(jīng)常被問到的 JS 異步中 宏任務(macrotasks)、微任務(microtasks)執(zhí)行順序。 JS 異步有一個機制,就是遇到宏任務,先執(zhí)行宏任務,將宏任務放入 Event Queue,然后再執(zhí)行微任務,將微任務放入 Event Queue,但是,這兩個 Queue 不是一個 Queue。當你往外拿的時候先從微任務里拿這個回調函數(shù),然后再從宏任務的 Queue 拿宏任務的回調函數(shù) 。如下圖:


image.png

宏任務:整體代碼 script,setTimeout,setInterval

微任務:Promise,process.nextTick
介紹下 promise.all
Promise.all()方法將多個Promise實例包裝成一個Promise對象(p),接受一個數(shù)組(p1,p2,p3)作為參數(shù),數(shù)組中不一定需要都是Promise對象,但是一定具有Iterator接口,如果不是的話,就會調用Promise.resolve將其轉化為Promise對象之后再進行處理。

使用Promise.all()生成的Promise對象(p)的狀態(tài)是由數(shù)組中的Promise對象(p1,p2,p3)決定的。

如果所有的Promise對象(p1,p2,p3)都變成fullfilled狀態(tài)的話,生成的Promise對象(p)也會變成fullfilled狀態(tài),
p1,p2,p3三個Promise對象產(chǎn)生的結果會組成一個數(shù)組返回給傳遞給p的回調函數(shù)。
如果p1,p2,p3中有一個Promise對象變?yōu)閞ejected狀態(tài)的話,p也會變成rejected狀態(tài),第一個被rejected的對象的返回值會傳遞給p的回調函數(shù)。
Promise.all()方法生成的Promise對象也會有一個catch方法來捕獲錯誤處理,但是如果數(shù)組中的Promise對象變成rejected狀態(tài)時,
并且這個對象還定義了catch的方法,那么rejected的對象會執(zhí)行自己的catch方法。
并且返回一個狀態(tài)為fullfilled的Promise對象,Promise.all()生成的對象會接受這個Promise對象,不會返回rejected狀態(tài)。
async 和 await
主要考察宏任務和微任務,搭配promise,詢問一些輸出的順序

原理:async 和 await 用了同步的方式去做異步,async 定義的函數(shù)的返回值都是 promise,await 后面的函數(shù)會先執(zhí)行一遍,然后就會跳出整個 async 函數(shù)來執(zhí)行后面js棧的代碼
ES6 的 class 和構造函數(shù)的區(qū)別
class 的寫法只是語法糖,和之前 prototype 差不多,但還是有細微差別的,下面看看:

  1. 嚴格模式
    類和模塊的內部,默認就是嚴格模式,所以不需要使用 use strict 指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用??紤]到未來所有的代碼,其實都是運行在模塊之中,所以 ES6 實際上把整個語言升級到了嚴格模式。

  2. 不存在提升
    類不存在變量提升(hoist),這一點與 ES5 完全不同。

復制new Foo(); // ReferenceError
class Foo {}

3. 方法默認是不可枚舉的

ES6 中的 class,它的方法(包括靜態(tài)方法和實例方法)默認是不可枚舉的,而構造函數(shù)默認是可枚舉的。細想一下,這其實是個優(yōu)化,讓你在遍歷時候,不需要再判斷 hasOwnProperty 了

4. class 的所有方法(包括靜態(tài)方法和實例方法)都沒有原型對象 prototype,所以也沒有[[construct]],不能使用 new 來調用。

5. class 必須使用 new 調用,否則會報錯。這是它跟普通構造函數(shù)的一個主要區(qū)別,后者不用 new 也可以執(zhí)行。

6. ES5 和 ES6 子類 this 生成順序不同

ES5 的繼承先生成了子類實例,再調用父類的構造函數(shù)修飾子類實例。ES6 的繼承先 生成父類實例,再調用子類的構造函數(shù)修飾父類實例。這個差別使得 ES6 可以繼承內置對象。

7. ES6可以繼承靜態(tài)方法,而構造函數(shù)不能

transform、translate、transition 分別是什么屬性?CSS 中常用的實現(xiàn)動畫方式

三者屬性說明

transform 是指變換、變形,是 css3 的一個屬性,和 width,height 屬性一樣;

translate 是 transform 的屬性值,是指元素進行 2D(3D)維度上位移或范圍變換;

transition 是指過渡效果,往往理解成簡單的動畫,需要有觸發(fā)條件。

這里可以補充下 transition 和 animation 的比較,前者一般定義開始結束兩個狀態(tài),需要有觸發(fā)條件;而后者引入了關鍵幀、速度曲線、播放次數(shù)等概念,更符合動畫的定義,且無需觸發(fā)條件

介紹一下rAF(requestAnimationFrame)

專門用來做動畫,不卡頓,用法和setTimeout一樣。對 rAF 的闡述 MDN 資料

定時器一直是 js 動畫的核心技術,但它們不夠精準,因為定時器時間參數(shù)是指將執(zhí)行代碼放入 UI 線程隊列中等待的時間,如果前面有其他任務隊列執(zhí)行時間過長,則會導致動畫延遲,效果不精確等問題。

所以處理動畫循環(huán)的關鍵是知道延遲多長時間合適:時間要足夠短,才能讓動畫看起來比較柔滑平順,避免多余性能損耗;時間要足夠長,才能讓瀏覽器準備好變化渲染。這個時候 rAF 就出現(xiàn)了,采用系統(tǒng)時間間隔(大多瀏覽器刷新頻率是 60Hz,相當于 1000ms/60≈16.6ms),保持最佳繪制效率,不會因為間隔時間過短,造成過度繪制,增加開銷;也不會因為間隔時間太長,使用動畫卡頓不流暢,讓各種網(wǎng)頁動畫效果能夠有一個統(tǒng)一的刷新機制。并且 rAF 會把每一幀中的所有 DOM 操作集中起來,在一次重繪或回流中就完成。
javascript 的垃圾回收機制講一下
定義:指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。

像 C 這樣的編程語言,具有低級內存管理原語,如 malloc()和 free()。開發(fā)人員使用這些原語顯式地對操作系統(tǒng)的內存進行分配和釋放。

而 JavaScript 在創(chuàng)建對象(對象、字符串等)時會為它們分配內存,不再使用對時會“自動”釋放內存,這個過程稱為垃圾收集。

內存生命周期中的每一個階段:

分配內存 —? 內存是由操作系統(tǒng)分配的,它允許您的程序使用它。在低級語言(例如 C 語言)中,這是一個開發(fā)人員需要自己處理的顯式執(zhí)行的操作。然而,在高級語言中,系統(tǒng)會自動為你分配內在。

使用內存 — 這是程序實際使用之前分配的內存,在代碼中使用分配的變量時,就會發(fā)生讀和寫操作。

釋放內存 — 釋放所有不再使用的內存,使之成為自由內存,并可以被重利用。與分配內存操作一樣,這一操作在低級語言中也是需要顯式地執(zhí)行。

四種常見的內存泄漏:全局變量,未清除的定時器,閉包,以及 dom 的引用
全局變量 不用 var 聲明的變量,相當于掛載到 window 對象上。如:b=1; 解決:使用嚴格模式
被遺忘的定時器和回調函數(shù)
閉包
沒有清理的 DOM 元素引用
對前端性能優(yōu)化有什么了解?一般都通過那幾個方面去優(yōu)化的?
減少請求數(shù)量
減小資源大小
優(yōu)化網(wǎng)絡連接
優(yōu)化資源加載
減少重繪回流
性能更好的API
webpack優(yōu)化

有想了解更多的小伙伴可以加Q群鏈接里面看一下,應該對你們能夠有所幫助。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容