經典大廠前端面試題(含解析)基礎篇(一)

妹紅發表於2019-12-18

年關將近,金三銀四跳槽季要來了,開始整理面試題

基礎型別有哪些?

基本型別有七種

  1. null
  2. undefined
  3. boolean
  4. number
  5. string
  6. symbol(較新標準)
  7. BigInt(新標準)

NaN 也屬於 number 型別,並且 NaN 不等於自身

基礎型別的特性是什麼?

  1. 基本型別的值是儲存在棧記憶體中的簡單資料段
  2. 基礎型別是不可變的
    • 即不能強行修改:Array.prototype.sort.call('abc');(會報錯)
  3. 基礎型別上沒有__proto__沒有屬性
  4. 基礎型別可以通過 基本包裝型別 訪問的屬性/方法
        // 通過包裝型別訪問基礎型別特性
        let str = 'abc';
        console.log(str.length)
    
        // 當你呼叫 `str.length` 時,實際過程是這樣的:
        // -> 建立String型別的一個例項
        // -> 在例項上呼叫指定的方法
        // -> 銷燬這個例項
    
        let _str = new String(str);
        let len = _str.length;
        _str = null;
    複製程式碼

怎麼檢測基礎型別?

  1. 檢測基礎型別可以用 typeof,但是 typeof null === 'object'
    • null 是基礎型別,不是 Object
    • 由於null的歷史遺留問題(前三位為000),所以使用typeof檢測null會產生BUG
        // 借鑑 Vue 原始碼的 object 檢測方法
        function isObject (obj: any): Boolean {
            return obj !== null && typeof obj === 'object'
        }
    複製程式碼
  2. 通過 Object.prototype.toString.call (萬能方法)
    • 檢測 [[class]]
    • 在不覆蓋 toString 方法前提下,任何一個物件呼叫 Object 原生的 toString 方法都會返回 [object type]
        // 借鑑 Vue 原始碼的檢測方法
    
        let _toString = Object.prototype.toString;
    
        function toRawType (value: any): String {
            // 獲取 從第九個到倒數第二個 字元
            // 比如 [object String]  獲取 String
            return _toString.call(value).slice(8, -1)
        }
    複製程式碼

基礎型別是如何轉換的?

  1. 基本型別轉換時,首先會呼叫 valueOf,然後呼叫 toString。(這兩個方法可以被重寫)
  2. 在四則運算中,除了 + 其他操作都會以數字進行計算,如果是 + 運算,如果不是所有字面量都是number(都是number就是數字的加法咯),那麼會轉換為字串(toString)進行拼接

為什麼 0.1 + 0.2 為什麼不等於 0.3 ?

遵循IEEE 754 雙精度版本(64位)標準的語言都有的問題。計算機無法識別十進位制,JS會將十進位制轉換為對應的二進位制(二進位制即:01)。

那麼 怎麼用 01 來表示 0.10.2 呢?

    console.log(0.1.toString(2));
    // -> 0.0001100110011001100110011001100110011001100110011001101

    console.log(0.2.toString(2));
    // -> 0.001100110011001100110011001100110011001100110011001101
複製程式碼

這樣看似沒問題啊。為什麼會有BUG呢?

別忘了:JS的精確度區間 約為正負 2^53,超出限制會截斷。所以你看到的 0.1 不是真的 0.1。

那麼怎麼解決 JS 的精確度問題?

  1. 目前主流的解決方案是 先乘再除
    • 比如精確到小數點後2位
    • 先把需要計算的數字都 乘1000
    • 計算完成後再把結果 除1000
  2. 使用新基礎型別 BigInt (相容性很差)

JS的"真"值有哪些?

JS中除了 "假" 值以外就是 "真" 值。

"假"值包括 7 個

  1. undefined
  2. null
  3. false
  4. NaN
  5. ''
  6. 0
  7. -0

在條件判斷的隱式轉換中:"假" 值會轉換為 false,"真" 值會轉換為 true;

那說一下引用型別吧?

  1. 除了基礎型別,都是引用型別。
  2. 引用型別正在建立時會分配兩個空間
    • 一塊在上,儲存引用型別本身的資料(當然資料量會比較大)
    • 一塊在上,儲存對上資料的引用(儲存堆上的記憶體地址,也就是指標)
  3. 引用型別是可變的:即let a={}; a.x=1;
  4. function引數是值傳遞,要注意不能修改引用

怎麼檢測引用型別?

  1. 通過 Object.prototype.toString.call 檢測 [[class]]
  2. 通過 instanceof 判斷引用型別
  3. 通過 constructor 判斷引用型別(constructor是可寫的,慎用)

instanceof 的原理是什麼?

instanceof 內部機制是通過判斷物件的原型鏈中是不是能找到對應的的prototype

所以在驗證iframe時會有BUG,因為 window.Array.prototype !== window.frames[0].Array.prototype,所以不存在繼承關係

  // 實現 instanceof

  function instanceof(obj, target) {
      // 獲得物件的原型
      obj = obj.__proto__
      // 判斷物件的型別是否等於型別的原型
      while (true) {
        // 如果__proto__ === null 說明原型鏈遍歷完畢
        if (obj === null) {
          return false
        }
        // 如果存在 obj.__proto__ === target.prototype
        // 說明物件是該型別的例項
        if (obj === target.prototype) {
          return true
        }
        // 原型鏈上查詢
        obj = obj.__proto__
      }
  }
複製程式碼

如果用 instanceof 判斷基礎型別會怎麼樣?

會返回 false 因為基礎型別沒有 __proto__

    let str = '123';

    console.log(str instanceof String) // -> false
複製程式碼

但是如果更改了 靜態方法Symbol.hasInstance就可以判斷了

    class StringType {
        static [Symbol.hasInstance](val) {
            return typeof val === 'string'
        }
    }
    console.log(str instanceof StringType) // -> true
複製程式碼

說一下陣列吧?

陣列是一種類列表物件,其資料在記憶體中也可以不連續

陣列應該是一段線性分配的記憶體,但是JS的Array的檢索和更新方式和物件一模一樣

  • Array它把下標變成數字,用其作屬性。它比真正的陣列慢,但用起來更方便。
  • Array本質還是物件,其原型繼承自Array.prototype,向上再繼承自Object.prototype
  • Array的方法是設計為物件通用的,物件也能呼叫陣列的方法
        let obj = {
            '2': 3,
            '3': 4,
            'length': 2,
            'splice': Array.prototype.splice,
            'push': Array.prototype.push
        }
        obj.push(1)
        obj.push(2)
    
        console.log(obj);
        // Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]
    複製程式碼
  • 使用delete arr[2],並不能減少length,而只是刪除了對應的屬性(變成empty)

什麼是類陣列?

  1. 類陣列不是陣列,通過 Array.isArray() 會返回 false
  2. 類陣列通過 Array.from 可以轉換為陣列
  3. 屬性要為索引(數字)屬性
  4. 必須有length屬性

經常遇見的類陣列

  • 字串
    • 唯一的原生類陣列
  • arguments
    • arguments完全可以使用...args代替,這樣不定引數就是真陣列
    • arguments在箭頭函式中被移除
  • DOM

[] == ![] 結果是什麼?

PS: 感謝 @沉末_ 的補充

型別轉換都是先 valueOftoString;

右邊

  1. 由於 ! 優先順序比 == 高,先執行 !
  2. ![] 得到 false
  3. 進行 相等性判斷
  4. false 轉化為數字 0

左邊

  1. 執行 [].valueOf() 原始值 還是 []
  2. 執行 [].toString() 得到 ''
  3. '' 轉化為數字 0

所以:0 == 0 ,答案是 true

驗證:

    let arr1 = [];
    let arr2 = [];

    console.log(arr1 == !arr2) // -> true

    arr1.toString = () => {
        console.log(111)
        return 1
    }

    console.log(arr1 == !arr2) 
    // -> 111
    // -> false
複製程式碼

== 和 === 的區別 ?

  1. === 不進行隱式轉換
  2. == 會進行隱式轉換
    • {a: 1} == "[object Object]" 左邊會執行 .toString()

如何讓 (a == 1 && a == 2)條件成立?

依然是型別轉換邏輯:基礎型別通過 valueOf 進行隱式轉換

更改 valueOf 方法就可以實現

    let a = {
        value: 0,
        valueOf: function() {
            this.value++;
            return this.value;
        }
    };
    console.log(a == 1 && a == 2);
複製程式碼

Object.is 和 === 的區別 ?

Object.is(v1, v2) 修復了 === 的一些BUG (-0和+0, NaN和NaN)

    // === 案例
    -0 === +0       // -> true
    NaN !== NaN     // -> false

    Object.is(-0, +0)       // -> false
    Object.is(NaN, NaN)     // -> true
複製程式碼

關注微訊號"前端進階課" 回覆"Vue"、"React"、"KOA"、"全棧" 有相關課程贈送哦。文章將不定期傳送閱讀紅包,敬請期待

經典大廠前端面試題(含解析)基礎篇(一)

相關文章