面試系列——JavaScript基礎篇

前言

本篇是面試系列的JavaScript的基礎篇。文章主要圍繞着基礎類型、做用域、閉包、原型、繼承、this的指向、深淺拷貝、字符串和數組的方法、節流、防抖、函數式編程等知識展開的。面試

正文

基礎類型

JavaScript的數據類型主要分爲兩類:原始類型、引用類型ajax

原始類型:6種編程

  • null 【JS的數據底層都是用二進制進行存儲的,前三位爲0會被判斷成對象,null是全都爲0】
  • undefined
  • Number【雙精度64位二進制格式的值——數字、±Infinity、NaN】
  • String
  • Boolean
  • Symbol【ES6新增定義,實例是惟一且不可變的】

引用類型:windows

  • Object
  • Array
  • Function

在內存中,存儲的形式:數組

  • 原始類型,會被保存到棧內存中
  • 引用類型,會被保存到堆內存中

類型之間的賦值方式:promise

  • 原始類型的賦值:對值類型的拷貝,會複製一份值
  • 引用類型的賦值:對引用地址的拷貝

基礎類型的隱式轉換也是面試考察的重點,Symbol類型有興趣的小夥伴也能夠手寫實現一下【面試系列——手寫代碼實現(一)瀏覽器

執行上下文 | 做用域 | 閉包

JS中存在的執行上下文類型緩存

  • 全局上下文:windows對象
  • 函數上下文:每次調用函數時,建立一個新的上下文
  • eval上下文

每一個執行上下文,都存在三個屬性:做用域變量this閉包

做用域:JS只有全局做用域函數做用域app

  • var建立的變量只有函數做用域
  • let和const建立的變量既有函數做用域,也有塊級做用域

做用域鏈:JavaScript引擎在尋找一個變量名的時候,會在當前做用域進行查找,若是沒有,就會繼續往外層做用域進行查找,直到全局做用域爲止,這就造成了一個做用域鏈。

閉包:引用外部函數變量的內部函數【紅寶書的定義】

主要使用場景:

  • setTimeout、setInterval、setImmediate之類的定時器、事件回調、ajax請求的回調
  • 被外部函數做爲函數返回,或者返回對象中引用內部函數的狀況

能夠使用當即執行函數(IIFE)來實現閉包,代碼以下:

(function(i){
  console.log(i);
})(i);
複製代碼

閉包只是存儲外部變量的引用,不會拷貝外部變量的值;閉包引用的變量會被存放到堆內存中

this | apply | call | bind

this的指向,是在執行上下文被建立的時候,被肯定的。

this的綁定規則總共有下面五種:

  • 默認綁定【嚴格 | 非嚴格】
    • 默認模式下,this指向全局對象
    • 嚴格模式下,this指向undefined
  • 隱式綁定:函數調用時,會有一個上下文對象,函數的this會綁定在這個上下文對象上面
  • 顯式綁定:經過apply/call/bind的方式,來實現this的顯式綁定
  • new綁定:JS構造函數經過new操做符進行調用,此處的this指向新建立的對象實例
  • 箭頭函數綁定:引用外層上下文的this
    • 箭頭函數的this,至關於普通變量
    • 尋找箭頭函數的this,就至關於尋找外層做用域
    • 若是改變了外層做用域的this,就能夠改變箭頭函數的this

this的指向優先級,依次按照 箭頭函數 > new操做符 > 顯示綁定 > 隱式綁定 > 默認綁定

apply | call | bind的做用:改變函數中this的指向

apply和call的區別:

  • 相同點:
    • 第一個參數都是指向函數中的this
    • 均可以接受一個參數
  • 不一樣點:
    • apply只能接受兩個參數,其中後一個參數能夠是數組、類數組、對象
    • call能夠接受多個參數

apply/call和bind的區別:

  • apply和call是函數執行時調用的
  • bind是函數定義時調用的

this的指向是面試考察的重點,通常都會以題目的形式進行考察;apply、call、bind是須要手寫實現的【面試系列——手寫代碼實現(一)

原型和繼承

原型(prototype):給其餘對象提供共享屬性的對象

隱式引用(proto:全部對象,都存在一個隱式引用,指向它的原型

構造函數(Constructor):構造函數,它的原型指向實例的原型

  • 構造函數和普通函數的區別:
    • 使用new操做符生成實例的函數就是構造函數
    • 直接調用的就是普通函數
  • Symbol是基礎類型,不是構造函數,它經過Symbol()建立實例

原型鏈:如圖所示

圖中,__proto__造成的鏈條組合,就是原型鏈

繼承方式:

  • 原型鏈繼承:本質就是重寫原型對象,代之以新對象的實例
    • 缺點:
      • 多個實例對引用類型進行篡改
      • 子類型原型上的構造函數被重寫了
      • 給子類型添加屬性和方法須要在替換原型以後
      • 建立子類型以後,沒法向父類構造函數傳參
  • 構造函數繼承:使用父類構造函數來加強子類實例,等同於複製父類的實例給子類(不使用原型)
    • 核心:superType.call(this)
    • 缺點:
      • 只能繼承父類實例的屬性和方法,不能複製父類原型上的屬性和方法
      • 沒法實現複用,每一個實例都會有父類實例的副本,影響性能
  • 組合繼承:上述兩種方式的組合,用原型鏈的方式繼承原型上的屬性和方法,用構造函數的方式,來實現實例屬性和方法的繼承
    • 缺點:
      • 在實例和原型上面有一份相同的屬性和方法
  • 寄生組合式繼承:ES6 Class的繼承原理
    • 子類實例繼承了父類實例的屬性和方法
    • 原型鏈的繼承
    • 父類構造函數上的屬性繼承

原型,基本只要記住原型鏈的圖,面試都沒有太大的問題;繼承的概念比較深澀難懂,須要多加實踐。new、instanceof、ES6的繼承都是須要手寫實現的。詳見【面試系列——手寫代碼實現(一)

淺拷貝和深拷貝

淺拷貝:建立一個新對象,將原對象的屬性值賦值給新對象

  • 使用場景:Object.assign、展開符(...)、Array.prototype.slice

Object.assign:主要將一個和多個對象可枚舉屬性進行合併

深拷貝:拷貝一個對象的所有屬性,拷貝完成以後,兩個對象相互不影響

  • 使用場景:JSON.parse(JSON.stringify(obj))

其中JSON拷貝的缺點

  • 會忽略undefined
  • 會忽略Symbol
  • 不能序列化函數
  • 不能拷貝循環引用對象
  • 不能正確調用new Date()
  • 不能處理正則

深拷貝和淺拷貝是面試考察的重點,主要是手寫實現深拷貝,在面試題中出現次數不少【面試系列——手寫代碼實現(一)

異步處理

異步解決方案:

  • Raw Callback Style:樸素函數做爲回調函數,接受error,data等參數
  • Promise Callback Style:經過{ then }對象,去處理onFulfilled和onRejected兩個回調函數
  • Generator Callback Style:經過 * 號和yield關鍵詞,將多層嵌套的callback扁平化的語法糖
  • Async/await Callback Style:經過async和await關鍵詞,將Promise+Generator標準化和語法化的產物

Promise:是一個類,經過new來進行聲明。

Promise有三種狀態:

  • pending:等待狀態,初始化狀態,可轉變成fulfilled或rejected
  • fulfilled:成功狀態,不可轉變狀態,且有一個不可變值(value)
  • rejected:失敗狀態,不可轉變狀態,且有一個不可變的失敗緣由(reason)

then方法:參數包含兩個函數

  • onFulfilled - 成功時執行的函數【參數是上一次返回的成功值】
  • onRejected - 失敗時執行的函數【參數是上一次返回的失敗緣由】

all方法:返回一個Promise,Promise返回一個結果是全部promise的結果數組

race方法:返回一個Promise,Promise返回一個結果值【最快響應的那個Promise的值】

Generator函數就是一個狀態機,內部包含多個狀態

  • yield做爲一種暫停標誌
  • next方法能夠訪問下一個狀態

Async:Generator的語法糖,是一個經過異步執行並隱式返回Promise對象的函數

async對generator的改進:

  • 內置執行器
  • 返回Promise
  • await後面能夠是Promise,也能夠是原始值
  • 更好的語義

異步處理,是一個常考的命題。Promise,常常被用在開發環境中,瞭解其原理是必不可少的。面試中,也會被要求去實現一個簡單的Promise或者Promise方法。async和await也會被問及,它的實現原理也是須要掌握的。具體實現部分能夠查看【面試系列——手寫代碼實現(一)

模塊化

常見的模塊化方案:

CommonJS

  • 一個文件就是一個模塊,經過執行該文件來加載模塊
  • 每一個模塊內部,module變量表明當前模塊,這個變量就是一個對象,exports是它對外的接口
  • require命令第一次加載該腳本時,就會執行整個腳本,在內存中生成一個對象(屢次加載,只有第一次會被運行,結果被緩存
  • 特色:
    • 全部代碼都運行在模塊做用域,不會污染全局做用域
    • 獨立性是模塊最重要的特色,模塊內部最好不會和程序其餘部分直接交互
    • 模塊屢次加載,只有第一次會被執行,並將結果會被緩存下來;若是想要從新執行,就得清除緩存
    • 模塊加載的順序,按照其在代碼中的位置
  • CommonJS,它是同步加載,不適合瀏覽器

AMD

  • 它採用異步的方式加載模塊,模塊的加載不會影響它後面語句的運行。全部依賴這個模塊的代碼,都會被放到回調函數裏面去
  • AMD經過define的方式來定義模塊
    • define(id?, dependencies?, factory);
  • 使用時,依然經過require關鍵詞
    • require([module], callback);

CMD

  • 它是seajs所推廣的一種模塊化方法
  • 與AMD的區別是:
    • 對於依賴的模塊,AMD是提早執行的,CMD是延遲執行的;【requirejs2.0,也改爲了延遲執行】
    • AMD推崇依賴前置,CMD推崇就近依賴

ES6 Module

  • 設計思想:儘可能的靜態化,使得在編譯的時候,就能清楚模塊之間的依賴關係,以及輸入和輸出的變量。【AMD和CMD,都是在運行時肯定的】
  • 經過import和export關鍵詞組成,export做爲對外接口,import是輸入其餘模塊的功能
  • 還有一個export default命令用於指定模塊的默認輸出【一個模塊只有一個默認輸出

ES6 Module 和 CommonJS 的區別:

  • 前者是值的引用,後者是值的拷貝
  • 前者是編譯時輸出接口,後者是運行時加載

require的性能問題:因爲它是值的拷貝,比較佔用內存

CommonJS和ES6 Module是考察的重點,尤爲是它們之間的區別。本篇概述的比較淺顯,能夠看一些詳解的文章,詳細瞭解一下。

防抖和節流

防抖:事件觸發n秒後,執行回調,若是在這n秒內再次被觸發,就從新計時

  • 實現:
    • 返回一個函數,函數內部會去清除計時器
    • 而後從新執行setTimeout進行計時

節流:事件在一段時間內只會被觸發一次,若是屢次觸發,只有一次生效。

  • 實現:
    • 返回一個函數,函數內部定義一個last【上回執行的時間】
    • 獲取當前時間now,來經過last+delay的比較,看是否執行回調

防抖和節流是平常開發工做也常常會使用的優化方法,面試必考題。面試者須要對其實現原理,聊熟於心,可以熟練編寫代碼。詳見【面試系列——手寫代碼實現(一)

函數式編程

特性:

  • 函數是一等公民——函數能夠跟其餘變量同樣,做爲其餘函數的輸入輸出
  • 不可變量
  • 純函數——沒有反作用的函數,不修改函數外部的變量
  • 引用透明——一樣的輸入,一定是一樣的輸出
  • 惰性計算

React就是函數式編程的典型表明,ReactView = render(data);

優點:

  • 更好的狀態管理——它的宗旨就是無狀態的
  • 更簡單的複用
  • 更加優雅的組合

高階函數:只要知足如下兩點中的一點便可

  • 接受一個函數或者多個函數做爲輸入
  • 輸出一個函數

函數柯里化:就是部分求值,將使用多個參數的函數轉出一系列一個參數的函數的方式,並接受剩餘參數的函數

函數式編程,近些年來挺火的。雖然JavaScript名義上不是一種函數式編程語言,可是這種方式,能夠提升編碼效率;總體的上手成本仍是比較高的,並且深澀難懂。可是將來,函數式編程仍然會持續火爆。

總結

最後但願分享的內容,對你的面試能有幫助。這些知識點只是淺顯的概述,仍然須要你去看一些文章,深刻理解。網上也有不少深刻講解的系列,好比冴羽老師的。

相關文章
相關標籤/搜索