面試的時候面試官是這樣問我JavaScript基礎的,角度真刁鑽

語言: CN / TW / HK

大佬們好,我是江湖不渡i,專業切圖仔。

看了目錄可能大佬們會覺得我寫都是一些老生常談的問題,基本上面試題彙總的文章都有。其實不是這樣的,我並沒有寫一些大家都知道的東西,那些東西掘金上大佬們已經總結的很詳細了。下面的內容都是在一些常見問題換一個角度來寫的分析,比如說這個知識依賴於另外什麼樣一個知識,還有就是可能有些意外的可能平常我們自己回顧的時候想不到的點。我相信總會有那麼一兩個是你之前沒曾想到過的,如果真的有幫助到哪位,希望大佬點個贊,哈哈。:soccer:️:soccer:️ ️

變數宣告

三個關鍵字:var、let、const

Tipes: var在所有ECMAScript版本都能使用,let、const只能在ECMAScript6和以後的版本使用。

提問環節:

:question::question::question:三個關鍵字有啥區別

  • var宣告的範圍是函式作用域,let、const宣告的是 塊級作用域
  • var宣告變數會提升到函式作用域頂部,let、const因為 暫時性死區 所以不會發生 變數提升
  • var可以重複宣告覆蓋自己宣告過的變數但是不能重複宣告覆蓋let、const宣告的變數,let、const不連自己宣告過的變數都不能重複宣告覆蓋。
  • var、let宣告的時候可以不賦初始值並且可以隨時修改,const宣告的時候必須初始化,而且不能修改。(引用資料型別不能修改指標,可以修改內容)
  • 全域性宣告的時候,var宣告變數掛在在window物件,let、const因為ES6中,全域性物件的屬性和全域性變數脫鉤,所以沒有在window上在對應的塊級作用域的活動物件上。
  • let、const相比var,在塊級作用域比函式作用域更早終止的情況下有助於垃圾回收提升效能。

:question::question::question:什麼是塊級作用域?

對應的還有全域性作用域,函式作用域。塊級作用域由ES6新增,一對{}包裹的就是塊級作用域,塊級作用域要配合let、const使用,對於var來說是沒有塊級作用域的。

:question::question::question:為什麼要引入塊級作用域?

為了解決var宣告變數具有變數提升特性的缺陷。

:question::question::question:變數提升是怎麼造成的?

因為JS引擎並非一行一行地分析和執行程式,而是一段一段地分析執行。當執行一段程式碼的時候,會進行一個“準備工作”,js在執行上下文的時候,會進行兩個階段:1.分析 2.執行,進入執行上下文時,首先會處理函式宣告,其次會處理變數宣告,在分析的過程中變數已經被宣告掛在了變數物件上面,只不過沒有具體賦值,所以這些變數和函式能在它們真正被宣告之前使用。

:question::question::question:怎麼解決變數提升?

使用let、const代替var宣告變數,在塊級作用域裡面let、const 宣告之前都被稱為暫時性死區,這個區域裡面引用後面才宣告的變數都會報錯。:exclamation:️暫時性死區會導致之前百分百安全的typeof不再安全。

資料型別

基本資料型別 :字串(String)、數字(Number)、布林(Boolean)、對空(Null)、未定義(Undefined)、Symbol、Bigint

引用資料型別 :物件(Object)(包括:陣列(Array)、函式(Function))

Tipes: Symbol、Bigint是在ECMAScript6和以後的版本新增。Object型別是一個基礎型別,除此之外的引用型別都從它繼承了基本的行為。

提問環節:

:question::question::question:基本資料型別和引用資料型別有啥區別?

基本資料型別只包括自身一般儲存在棧記憶體中, 任何方法都不能修改基本資料型別本身的值,都是返回了一個新的 。引用資料型別包括指標和內容,指標儲存在棧記憶體,內容儲存在堆記憶體。

:question::question::question:怎麼判斷資料是什麼型別?

  • typeof :通過查詢js底層的實現,所以只能判斷出後面這幾種。〈000:物件、010:浮點數、100:字串、110:布林、1:整數〉 判斷null為物件 :所有機器碼均為0 所以判斷null會判斷為物件。
  • instanceof :沿著原型鏈判斷左邊的原型鏈是否擁有和右邊原型相同的指標,有的話就是相等,沒有的話就是不相等,用它可以直接判斷是不是陣列。
  • Array.isArray() :封裝了Object.prototype.toString.call(),專門判斷是不是陣列。
  • Object.prototype.toString.call() :原始資料型別在建立的時候都會有一個內部屬性[[class]],這個屬性表示資料型別的種類,Object.prototype.toStrin方法能訪問到這個屬性會返回這個內部屬性(es6之後刪除了[[class]],用[NativeBrand],實現效果更加相容)。

:question::question::question:陣列的length是元素的個數,函式有length嗎?length是什麼?

函式的length就是第一個有預設值的引數之前的引數的個數,也可以說是必須要穿的形參的個數。

:question::question::question:const a = {}; const b = Symbol(123); const c = Symbol(123); a[b] = 'b'; a[c] = 'c'; console.log(a[b]); 輸出是是什麼?為什麼?

答案:b;

分析:先看這個:

// 前置問題
const a = {}; 
const b = {key: 123}; 
const c = {key: 123}; 
a[b] = 'b'; 
a[c] = 'c'; 
console.log(a[b]); // c
// 題目
const a = {}; 
const b = Symbol(123); 
const c = Symbol(123); 
a[b] = 'b'; 
a[c] = 'c'; 
console.log(a[b]); // b

先看我們的前置問題,因為物件的key只能是字串,所以任何資料型別當作物件的key的時候都會預設轉化為字串,所以前置題目的b和c都被轉化成[Object, Object],所以列印c就不用過多說明了。那麼題目為什麼列印的是c?因為 Symbol宣告的是一個獨一無二的值 ,所以他們也是不相等的。 Tipes: Symbol物件是不能強制轉化型別成為字串或者陣列,作為物件的key的時候還是自身而且不能通過常規的手段去獲取,只能通過Object.getOwnPropertySymbols來獲取(返回的是Symbol陣列)。Symbol物件可以轉化成陣列或者布林值,所以Symbol物件可以用於邏輯運算,但是不能用於數學運算。

閉包和作用域鏈

閉包 :有權訪問另一個函式作用域中變數的函式,可以突破作用鏈。實現變數和函式的無序訪問。

作用域鏈 :當訪問一個變數時,直譯器會首先在當前作用域查詢標示符,如果沒有找到,就去父作用域找,直到找到該變數的標示符或者不在父作用域中,這就是作用域鏈,由當前環境與上層環境的一系列變數物件組成,它保證了當前執行環境對符合訪問許可權的變數和函式的有序訪問。

Tipes: 利用閉包可以突破作用鏈,作用域鏈能合理的解釋閉包的原理。

提問環節:

:question::question::question:閉包為什麼會造成變數不被回收造成記憶體洩漏?

首先我們都知道閉包是有權訪問另外一個函式作用域變數的函式,這說明什麼?就是閉包裡面一直保持著對另外一個函式作用域內部變數的引用,那麼變量回收是什麼時候吶?當變數被使用 js垃圾回收機制 會給他的引用次數加一,當變數的 引用次數為0 的時候,就會把變量回收了。那麼閉包一直保持著引用,那麼這個變數還有變數所在的函式作用域都不會被回收,js執行記憶體就那麼多,閉包數量多了,一來而去記憶體就洩漏了。

:question::question::question:為什麼閉包能突破作用域鏈?

我們要先知道突破是什麼意思?當訪問一個變數時,直譯器會首先在當前作用域查詢標示符,如果沒有找到,就去父作用域找,直到找到該變數的標示符或者不在父作用域中,這就是作用域鏈,由當前環境與上層環境的一系列變數物件組成,它保證了當前執行環境對符合訪問許可權的變數和函式的有序訪問。 如果我們不想按照順序來訪問,我就想訪問和我同級的函式內部的變數怎麼辦 ?這時候閉包就發揮了它的作用了,因為閉包保持著對一個函式作用域的引用,那麼這個函式作用域就一直不會被銷燬,而且我們在哪裡使用閉包都可以,只要閉包被建立了,這樣不就突破了作用域鏈了嗎?

非同步處理方案

發展:Callback -> Promise-> Generator -> Async/await

Tipes: Promise、Generator、Async/await是在ECMAScript6和以後的版本新增。

提問環節:

:question::question::question:為什麼會有非同步程式設計機制?

首先要知道什麼是同步什麼是非同步,同步就是一個任務執行完之後才能執行下一個任務,非同步就是多個任務可以同時執行。從概念就能知道,如果說有一個任務的執行花費了大量的時間,只有同步的話肯定會阻塞程式碼的執行,所以非同步程式設計就是為了 優化因為計算量大而時間長的操作,保證系統的穩定性

:question::question::question:非同步程式設計和Eventloop的關係以及為什麼有Eventloop?

我們知道了非同步程式設計的概念,多個任務同時執行,但是執行的結果總要有一個統一的佇列裡面去反饋到系統。那麼Eventloop的作用是什麼吶?為了讓這些事件有條不紊地進行,JS引擎需要對之執行的順序做一定的安排。比如我們的巨集任務和微任務的執行時機。

:question::question::question:promise真的解決了回撥地獄嗎?

回撥地獄是什麼?就是因為最開始的非同步執行方案Callback在執行復雜的事件邏輯的時候需要大量巢狀,導致程式碼閱讀性很差。那promise真的解決了回撥地獄的問題嗎?在我看來 並沒有 ,因為在複雜的邏輯操作的時候,promise的不斷then鏈式呼叫也會讓人覺得有點費解,但是從一定程度上來說promise的程式碼比起來Callback更加優雅一點,所以說promise只是緩解了回撥地獄。

:question::question::question:Generator函式的意義是什麼?

在我覺得Generator函式的出現就是使我們可以控制函式的執行和停止,我們都知道promise的狀態改變之不能終止的,Generator函式的出現就為我們提供了一種中止的可能性。

:question::question::question:async/await會阻塞程式碼執行嗎?

async/await的作用就是讓我們的非同步程式碼看著更像同步程式碼,也可以說讓我們的同步程式碼可以非同步執行。那麼async/await會不會阻塞程式碼的執行吶?畢竟我們要等待await後面的程式碼有結果之後才能繼續往下走。所以說async/await實際上是會阻塞程式碼的執行,但是 阻塞的也僅僅是那個函式作用域內部的程式碼,也就是await關鍵字後面的程式碼 ,對於外面的整個執行上下文是沒有影響的,因為async關鍵字會讓函式返回一個promise物件。

Http請求

常用方案:Ajax、fetch

Tipes: Ajax、fetch都是原生Api和axios、jq的ajax不是同一層級的東西。

提問環節:

:question::question::question:為什麼要在Ajax的基礎上升級fetch?

Ajax是基於XMLHttpRequest物件,XHR本身的架構不夠清晰,而且Ajax使用過於繁瑣,而且回撥函式會造成回撥地獄。升級到fetch,脫離的XHR語法更加簡潔,基於標準 Promise 實現,支援 async/await。

:question::question::question:Http一些不常見的狀態碼是什麼意思?

只說 304 ,當協商快取命中的時候就會返回304,告訴客戶端可以用快取。

:question::question::question:Cookie在http請求中的作用?

Cookie除了能做本地儲存之外,因為http是無狀態的,客戶端在和服務端通訊的過程中並不能根據http去判斷物件的身份,所以cookie的另外一個作用就是儲存狀態,讓服務端知道客戶端幹了什麼,這也是為什麼cookie為什麼在每次傳送請求的時候都會自動帶上。

js引擎單執行緒

一個程序由一個或多個執行緒組成,執行緒是建立在程序的基礎上的一次程式執行單位。

提問環節:

:question::question::question:為什麼設計成單執行緒?

我們要知道js主要是實現使用者與瀏覽器的互動,以及操作dom。那麼假如js是多執行緒,那麼多個執行緒修改同一個dom,那麼最後瀏覽器遵循哪個結果吶?所以為了避免產生意外的複雜操作,js只能是單執行緒。雖然HTML5提出Web Worker標準允許多執行緒,但是子執行緒是不允許修改dom的而且完全受主執行緒全程控制。

常見內建錯誤編碼

1.ReferenceError:引用的變數不存在。

2.TypeErroe:資料型別不正確的錯誤。

3.RangeError:資料值不在其所允許的範圍之內(超出邊界)。

4.4.SyntaxErroe:語法錯誤

結語

磨磨唧唧的寫了好久,過程中有好多的東西都是之前自己覺得掌握的很好的實際上表述出來的時候才發現自己瞭解的一知半解。文章裡面也沒寫那些一看大家都知道的,起碼都是我之前不知道遇到了才知道東西,也算是給自己查缺補漏了吧,哈哈。文章有什麼錯誤或者理解有偏差的地方,希望大佬們指出, 謝謝啦,線上求一個react大佬帶帶我,react太難了。

最後祝各位大佬學習進步,事業有成!

原作者姓名:江湖不渡i

原出處:掘金

原文連結: 面試的時候面試官是這樣問我Js基礎的,角度真刁鑽 - 掘金