2019前端面試系列——JS面試題

奔跑的瓜牛發表於2019-07-27

判斷 js 型別的方式

1. typeof

可以判斷出'string','number','boolean','undefined','symbol'
但判斷 typeof(null) 時值為 'object'; 判斷陣列和物件時值均為 'object'

2. instanceof

原理是 建構函式的 prototype 屬性是否出現在物件的原型鏈中的任何位置

function A() {}
let a = new A();
a instanceof A     //true,因為 Object.getPrototypeOf(a) === A.prototype;
3. Object.prototype.toString.call()

常用於判斷瀏覽器內建物件,對於所有基本的資料型別都能進行判斷,即使是 null 和 undefined

4. Array.isArray()

用於判斷是否為陣列

淺拷貝和深拷貝

  • 淺拷貝
    Object.assign()
    Array.prototype.slice()
    擴充套件運算子 ...
  • 深拷貝
    JSON.parse(JSON.stringify())
    遞迴函式
function cloneObject (obj) {
     var newObj = {}  //如果不是引用型別,直接返回
      if (typeof (obj) !== 'object') {
          return obj
     }
     //如果是引用型別,遍歷屬性
    else{
        for (var attr in obj) {
        //如果某個屬性還是引用型別,遞迴呼叫
        newObj[attr] = cloneObject(obj[attr])
                }
       }
    return newObj
  }

如何實現一個深拷貝
詳細解析賦值、淺拷貝和深拷貝的區別

陣列去重的方法

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() 方法可返回某個指定的 字串值 在字串中首次出現的位置
  return arr.indexOf(item) === index
})

DOM 事件有哪些階段?談談對事件代理的理解

分為三大階段:捕獲階段--目標階段--冒泡階段

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

好處:(1)使程式碼更簡潔;(2)節省記憶體開銷

介紹下 promise.all

      Promise.all()方法將多個Promise例項包裝成一個Promise物件(p),接受一個陣列(p1,p2,p3)作為引數,陣列中不一定需要都是Promise物件,但是一定具有Iterator介面,如果不是的話,就會呼叫Promise.resolve將其轉化為Promise物件之後再進行處理。
      使用Promise.all()生成的Promise物件(p)的狀態是由陣列中的Promise物件(p1,p2,p3)決定的。

  1. 如果所有的Promise物件(p1,p2,p3)都變成fullfilled狀態的話,生成的Promise物件(p)也會變成fullfilled狀態,
    p1,p2,p3三個Promise物件產生的結果會組成一個陣列返回給傳遞給p的回撥函式。
  2. 如果p1,p2,p3中有一個Promise物件變為rejected狀態的話,p也會變成rejected狀態,第一個被rejected的物件的返回值會傳遞給p的回撥函式。
    Promise.all()方法生成的Promise物件也會有一個catch方法來捕獲錯誤處理,但是如果陣列中的Promise物件變成rejected狀態時,
    並且這個物件還定義了catch的方法,那麼rejected的物件會執行自己的catch方法。
    並且返回一個狀態為fullfilled的Promise物件,Promise.all()生成的物件會接受這個Promise物件,不會返回rejected狀態。

async 和 await

主要考察巨集任務和微任務,搭配promise,詢問一些輸出的順序

原理:async 和 await 用了同步的方式去做非同步,async 定義的函式的返回值都是 promise,await 後面的函式會先執行一遍,然後就會跳出整個 async 函式來執行後面js棧的程式碼

ES6 的 class 和建構函式的區別

class 的寫法只是語法糖,和之前 prototype 差不多,但還是有細微差別的,下面看看:

  1. 嚴格模式
    類和模組的內部,預設就是嚴格模式,所以不需要使用use strict指定執行模式。只要你的程式碼寫在類或模組之中,就只有嚴格模式可用。考慮到未來所有的程式碼,其實都是執行在模組之中,所以 ES6 實際上把整個語言升級到了嚴格模式。
  2. 不存在提升
    類不存在變數提升(hoist),這一點與 ES5 完全不同。
new Foo(); // ReferenceError
class Foo {}
  1. 方法預設是不可列舉的
    ES6 中的 class,它的方法(包括靜態方法和例項方法)預設是不可列舉的,而建構函式預設是可列舉的。細想一下,這其實是個優化,讓你在遍歷時候,不需要再判斷 hasOwnProperty 了
  2. class 的所有方法(包括靜態方法和例項方法)都沒有原型物件 prototype,所以也沒有[[construct]],不能使用 new 來呼叫。
  3. class 必須使用 new 呼叫,否則會報錯。這是它跟普通建構函式的一個主要區別,後者不用 new 也可以執行。
  4. ES5 和 ES6 子類 this 生成順序不同
    ES5 的繼承先 生成了子類例項,再 呼叫父類的建構函式修飾子類例項。ES6 的繼承先 生成父類例項,再 呼叫子類的建構函式修飾父類例項。這個差別使得 ES6 可以繼承內建物件。
  5. ES6可以繼承靜態方法,而建構函式不能

transform、translate、transition 分別是什麼屬性?CSS 中常用的實現動畫方式

三者屬性說明
transform 是指變換、變形,是 css3 的一個屬性,和 width,height 屬性一樣;
translate 是 transform 的屬性值,是指元素進行 2D(3D)維度上位移或範圍變換;
transition 是指過渡效果,往往理解成簡單的動畫,需要有觸發條件。
這裡可以補充下 transition 和 animation 的比較,前者一般定義開始結束兩個狀態,需要有觸發條件;而後者引入了關鍵幀、速度曲線、播放次數等概念,更符合動畫的定義,且無需觸發條件

介紹一下rAF(requestAnimationFrame)

      對 rAF 的闡述 MDN 資料

      定時器一直是 js 動畫的核心技術,但它們不夠精準,因為定時器時間引數是指將執行程式碼放入 UI 執行緒佇列中等待的時間,如果前面有其他任務佇列執行時間過長,則會導致動畫延遲,效果不精確等問題。
所以處理動畫迴圈的關鍵是知道延遲多長時間合適:時間要足夠短,才能讓動畫看起來比較柔滑平順,避免多餘效能損耗;時間要足夠長,才能讓瀏覽器準備好變化渲染。
這個時候 rAF 就出現了,採用系統時間間隔(大多瀏覽器重新整理頻率是 60Hz,相當於 1000ms/60≈16.6ms),保持最佳繪製效率,不會因為間隔時間過短,造成過度繪製,增加開銷;也不會因為間隔時間太長,使用動畫卡頓不流暢,讓各種網頁動畫效果能夠有一個統一的重新整理機制。並且 rAF 會把每一幀中的所有 DOM 操作集中起來,在一次重繪或迴流中就完成。

javascript 的垃圾回收機制講一下

定義:指一塊被分配的記憶體既不能使用,又不能回收,直到瀏覽器程式結束。

像 C 這樣的程式語言,具有低階記憶體管理原語,如 malloc()和 free()。開發人員使用這些原語顯式地對作業系統的記憶體進行分配和釋放。
而 JavaScript 在建立物件(物件、字串等)時會為它們分配記憶體,不再使用對時會“自動”釋放記憶體,這個過程稱為垃圾收集。

記憶體生命週期中的每一個階段:
分配記憶體 —  記憶體是由作業系統分配的,它允許您的程式使用它。在低階語言(例如 C 語言)中,這是一個開發人員需要自己處理的顯式執行的操作。然而,在高階語言中,系統會自動為你分配內在。
使用記憶體 — 這是程式實際使用之前分配的記憶體,在程式碼中使用分配的變數時,就會發生讀和寫操作。
釋放記憶體 — 釋放所有不再使用的記憶體,使之成為自由記憶體,並可以被重利用。與分配記憶體操作一樣,這一操作在低階語言中也是需要顯式地執行。

四種常見的記憶體洩漏:全域性變數,未清除的定時器,閉包,以及 dom 的引用
  1. 全域性變數 不用 var 宣告的變數,相當於掛載到 window 物件上。如:b=1; 解決:使用嚴格模式
  2. 被遺忘的定時器和回撥函式
  3. 閉包
  4. 沒有清理的 DOM 元素引用

對前端效能優化有什麼瞭解?一般都通過那幾個方面去優化的?

前端效能優化的七大手段

  1. 減少請求數量
  2. 減小資源大小
  3. 優化網路連線
  4. 優化資源載入
  5. 減少重繪迴流
  6. 效能更好的API
  7. webpack優化

前端安全也經常被問到的,常見的有兩種——XSS、CSRF,詳見前端安全
找到工作前還會不定期補充,未完待續...

相關文章