這兒有20道大廠面試題等你查收

今年來,各大公司都縮減了HC,甚至是採起了「裁人」措施,在這樣的大環境之下,想要得到一份更好的工做,必然須要付出更多的努力。

本文挑選了20道大廠面試題,建議在閱讀時,先思考一番,不要直接看解析。儘管,本文全部的答案,都是我在翻閱各類資料,思考並驗證以後,纔給出的。但因水平有限,本人的答案未必是最優的,若是您有更好的答案,歡迎給我留言。若是有錯誤,也請在評論區指出,謝謝。javascript

本文篇幅較長,但願小夥伴們可以堅持讀完,若是想加入交流羣,能夠經過文末的公衆號添加我爲好友。css

更多文章可戳github.com/YvetteLau/B…html

1. new的實現原理是什麼?

查看解析

new 的實現原理:前端

  1. 建立一個空對象,構造函數中的this指向這個空對象
  2. 這個新對象被執行 [[原型]] 鏈接
  3. 執行構造函數方法,屬性和方法被添加到this引用的對象中
  4. 若是構造函數中沒有返回其它對象,那麼返回this,即建立的這個的新對象,不然,返回構造函數中返回的對象。

2. 如何正確判斷this的指向?

查看解析

若是用一句話說明 this 的指向,那麼便是: 誰調用它,this 就指向誰。html5

可是僅經過這句話,咱們不少時候並不能準確判斷 this 的指向。所以咱們須要藉助一些規則去幫助本身:java

this 的指向能夠按照如下順序判斷:node

全局環境中的 this

瀏覽器環境:不管是否在嚴格模式下,在全局執行環境中(在任何函數體外部)this 都指向全局對象 window;git

node 環境:不管是否在嚴格模式下,在全局執行環境中(在任何函數體外部),this 都是空對象 {};github

是不是 new 綁定

若是是 new 綁定,而且構造函數中沒有返回 function 或者是 object,那麼 this 指向這個新對象。以下:面試

構造函數返回值不是 function 或 object。new Super() 返回的是 this 對象。

構造函數返回值是 function 或 object,new Super()是返回的是Super種返回的對象。

函數是否經過 call,apply 調用,或者使用了 bind 綁定,若是是,那麼this綁定的就是指定的對象【歸結爲顯式綁定】。

這裏一樣須要注意一種特殊狀況,若是 call,apply 或者 bind 傳入的第一個參數值是 undefined 或者 null,嚴格模式下 this 的值爲傳入的值 null /undefined。非嚴格模式下,實際應用的默認綁定規則,this 指向全局對象(node環境爲global,瀏覽器環境爲window)

隱式綁定,函數的調用是在某個對象上觸發的,即調用位置上存在上下文對象。典型的隱式調用爲: xxx.fn()

默認綁定,在不能應用其它綁定規則時使用的默認規則,一般是獨立函數調用。

非嚴格模式: node環境,指向全局對象 global,瀏覽器環境,指向全局對象 window。

嚴格模式:執行 undefined

箭頭函數的狀況:

箭頭函數沒有本身的this,繼承外層上下文綁定的this。

3. 深拷貝和淺拷貝的區別是什麼?實現一個深拷貝

查看解析

深拷貝和淺拷貝是針對複雜數據類型來講的,淺拷貝只拷貝一層,而深拷貝是層層拷貝。

深拷貝

深拷貝複製變量值,對於非基本類型的變量,則遞歸至基本類型變量後,再複製。 深拷貝後的對象與原來的對象是徹底隔離的,互不影響,對一個對象的修改並不會影響另外一個對象。

淺拷貝

淺拷貝是會將對象的每一個屬性進行依次複製,可是當對象的屬性值是引用類型時,實質複製的是其引用,當引用指向的值改變時也會跟着變化。

可使用 for inObject.assign、 擴展運算符 ...Array.prototype.slice()Array.prototype.concat() 等,例如:

能夠看出淺拷貝只最第一層屬性進行了拷貝,當第一層的屬性值是基本數據類型時,新的對象和原對象互不影響,可是若是第一層的屬性值是複雜數據類型,那麼新對象和原對象的屬性值其指向的是同一塊內存地址。

深拷貝實現

1.深拷貝最簡單的實現是: JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj)) 是最簡單的實現方式,可是有一些缺陷:

  1. 對象的屬性值是函數時,沒法拷貝。
  2. 原型鏈上的屬性沒法拷貝
  3. 不能正確的處理 Date 類型的數據
  4. 不能處理 RegExp
  5. 會忽略 symbol
  6. 會忽略 undefined

2.實現一個 deepClone 函數

  1. 若是是基本數據類型,直接返回
  2. 若是是 RegExp 或者 Date 類型,返回對應類型
  3. 若是是複雜數據類型,遞歸。
  4. 考慮循環引用的問題

4. call/apply 的實現原理是什麼?

查看解析

callapply 的功能相同,都是改變 this 的執行,並當即執行函數。區別在於傳參方式不一樣。

  • func.call(thisArg, arg1, arg2, ...):第一個參數是 this 指向的對象,其它參數依次傳入。

  • func.apply(thisArg, [argsArray]):第一個參數是 this 指向的對象,第二個參數是數組或類數組。

一塊兒思考一下,如何模擬實現 call

首先,咱們知道,函數均可以調用 call,說明 call 是函數原型上的方法,全部的實例均可以調用。即: Function.prototype.call

  • call 方法中獲取調用call()函數
  • 若是第一個參數沒有傳入,那麼默認指向 window / global(非嚴格模式)
  • 傳入 call 的第一個參數是 this 指向的對象,根據隱式綁定的規則,咱們知道 obj.foo(), foo() 中的 this 指向 obj;所以咱們能夠這樣調用函數 thisArgs.func(...args)
  • 返回執行結果

apply 的實現思路和 call 一致,僅參數處理略有差異。以下:

5. 柯里化函數實現

查看解析

在開始以前,咱們首先須要搞清楚函數柯里化的概念。

函數柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。

函數柯里化的主要做用:

  • 參數複用
  • 提早返回 – 返回接受餘下的參數且返回結果的新函數
  • 延遲執行 – 返回新函數,等待執行

6. 如何讓 (a == 1 && a == 2 && a == 3) 的值爲true?

查看解析
  1. 利用隱式類型轉換

== 操做符在左右數據類型不一致時,會先進行隱式轉換。

a == 1 && a == 2 && a == 3 的值意味着其不多是基本數據類型。由於若是 a 是 null 或者是 undefined bool類型,都不可能返回true。

所以能夠推測 a 是複雜數據類型,JS 中複雜數據類型只有 object,回憶一下,Object 轉換爲原始類型會調用什麼方法?

  • 若是部署了 [Symbol.toPrimitive] 接口,那麼調用此接口,若返回的不是基本數據類型,拋出錯誤。

  • 若是沒有部署 [Symbol.toPrimitive] 接口,那麼根據要轉換的類型,先調用 valueOf / toString

    1. 非Date類型對象,hintdefault 時,調用順序爲:valueOf >>> toString,即valueOf 返回的不是基本數據類型,纔會繼續調用 valueOf,若是toString 返回的還不是基本數據類型,那麼拋出錯誤。
    2. 若是 hintstring(Date對象的hint默認是string) ,調用順序爲:toString >>> valueOf,即toString 返回的不是基本數據類型,纔會繼續調用 valueOf,若是valueOf 返回的還不是基本數據類型,那麼拋出錯誤。
    3. 若是 hintnumber,調用順序爲: valueOf >>> toString

  1. 利用數據劫持(Proxy/Object.defineProperty)

  1. 數組的 toString 接口默認調用數組的 join 方法,重寫 join 方法

7. 什麼是BFC?BFC的佈局規則是什麼?如何建立BFC?

查看解析

Box 是 CSS 佈局的對象和基本單位,頁面是由若干個Box組成的。

元素的類型 和 display 屬性,決定了這個 Box 的類型。不一樣類型的 Box 會參與不一樣的 Formatting Context。

Formatting Context

Formatting Context 是頁面的一塊渲染區域,而且有一套渲染規則,決定了其子元素將如何定位,以及和其它元素的關係和相互做用。

Formatting Context 有 BFC (Block formatting context),IFC (Inline formatting context),FFC (Flex formatting context) 和 GFC (Grid formatting context)。FFC 和 GFC 爲 CC3 中新增。

BFC佈局規則

  • BFC內,盒子依次垂直排列。
  • BFC內,兩個盒子的垂直距離由 margin 屬性決定。屬於同一個BFC的兩個相鄰Box的margin會發生重疊【符合合併原則的margin合併後是使用大的margin】
  • BFC內,每一個盒子的左外邊緣接觸內部盒子的左邊緣(對於從右到左的格式,右邊緣接觸)。即便在存在浮動的狀況下也是如此。除非建立新的BFC。
  • BFC的區域不會與float box重疊。
  • BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。
  • 計算BFC的高度時,浮動元素也參與計算。

如何建立BFC

  • 根元素
  • 浮動元素(float 屬性不爲 none)
  • position 爲 absolute 或 fixed
  • overflow 不爲 visible 的塊元素
  • display 爲 inline-block, table-cell, table-caption

BFC 的應用

  1. 防止 margin 重疊 (同一個BFC內的兩個兩個相鄰Box的 margin 會發生重疊,觸發生成兩個BFC,即不會重疊)
  2. 清除內部浮動 (建立一個新的 BFC,由於根據 BFC 的規則,計算 BFC 的高度時,浮動元素也參與計算)
  3. 自適應多欄佈局 (BFC的區域不會與float box重疊。所以,能夠觸發生成一個新的BFC)

8. 異步加載JS腳本的方式有哪些?

查看解析

<script> 標籤中增長 async(html5) 或者 defer(html4) 屬性,腳本就會異步加載。

<script src="../XXX.js" defer></script>

deferasync 的區別在於:

  • defer 要等到整個頁面在內存中正常渲染結束(DOM 結構徹底生成,以及其餘腳本執行完成),在window.onload 以前執行;
  • async 一旦下載完,渲染引擎就會中斷渲染,執行這個腳本之後,再繼續渲染。
  • 若是有多個 defer 腳本,會按照它們在頁面出現的順序加載
  • 多個 async 腳本不能保證加載順序

動態建立 script 標籤

動態建立的 script ,設置 src 並不會開始下載,而是要添加到文檔中,JS文件纔會開始下載。

XHR 異步加載JS

9. ES5有幾種方式能夠實現繼承?分別有哪些優缺點?

查看解析

ES5 有 6 種方式能夠實現繼承,分別爲:

1. 原型鏈繼承

原型鏈繼承的基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。

缺點:

  1. 經過原型來實現繼承時,原型會變成另外一個類型的實例,原先的實例屬性變成了如今的原型屬性,該原型的引用類型屬性會被全部的實例共享。
  2. 在建立子類型的實例時,沒有辦法在不影響全部對象實例的狀況下給超類型的構造函數中傳遞參數。
2. 借用構造函數

借用構造函數的技術,其基本思想爲:

在子類型的構造函數中調用超類型構造函數。

優勢:

  1. 能夠向超類傳遞參數
  2. 解決了原型中包含引用類型值被全部實例共享的問題

缺點:

  1. 方法都在構造函數中定義,函數複用無從談起,另外超類型原型中定義的方法對於子類型而言都是不可見的。
3. 組合繼承(原型鏈 + 借用構造函數)

組合繼承指的是將原型鏈和借用構造函數技術組合到一塊,從而發揮兩者之長的一種繼承模式。基本思路:

使用原型鏈實現對原型屬性和方法的繼承,經過借用構造函數來實現對實例屬性的繼承,既經過在原型上定義方法來實現了函數複用,又保證了每一個實例都有本身的屬性。

缺點:

  • 不管什麼狀況下,都會調用兩次超類型構造函數:一次是在建立子類型原型的時候,另外一次是在子類型構造函數內部。

優勢:

  • 能夠向超類傳遞參數
  • 每一個實例都有本身的屬性
  • 實現了函數複用
4. 原型式繼承

原型繼承的基本思想:

藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。

object() 函數內部,先穿甲一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回了這個臨時類型的一個新實例,從本質上講,object() 對傳入的對象執行了一次淺拷貝。

ECMAScript5經過新增 Object.create()方法規範了原型式繼承。這個方法接收兩個參數:一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象(能夠覆蓋原型對象上的同名屬性),在傳入一個參數的狀況下,Object.create()object() 方法的行爲相同。

在沒有必要建立構造函數,僅讓一個對象與另外一個對象保持類似的狀況下,原型式繼承是能夠勝任的。

缺點:

同原型鏈實現繼承同樣,包含引用類型值的屬性會被全部實例共享。

5. 寄生式繼承

寄生式繼承是與原型式繼承緊密相關的一種思路。寄生式繼承的思路與寄生構造函數和工廠模式相似,即建立一個僅用於封裝繼承過程的函數,該函數在內部已某種方式來加強對象,最後再像真地是它作了全部工做同樣返回對象。

基於 person 返回了一個新對象 -—— person2,新對象不只具備 person 的全部屬性和方法,並且還有本身的 sayHi() 方法。在考慮對象而不是自定義類型和構造函數的狀況下,寄生式繼承也是一種有用的模式。

缺點:

  • 使用寄生式繼承來爲對象添加函數,會因爲不能作到函數複用而效率低下。
  • 同原型鏈實現繼承同樣,包含引用類型值的屬性會被全部實例共享。
6. 寄生組合式繼承

所謂寄生組合式繼承,即經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法,基本思路:

沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們須要的僅是超類型原型的一個副本,本質上就是使用寄生式繼承來繼承超類型的原型,而後再將結果指定給子類型的原型。寄生組合式繼承的基本模式以下所示:

  • 第一步:建立超類型原型的一個副本
  • 第二步:爲建立的副本添加 constructor 屬性
  • 第三步:將新建立的對象賦值給子類型的原型

至此,咱們就能夠經過調用 inheritPrototype 來替換爲子類型原型賦值的語句:

優勢:

只調用了一次超類構造函數,效率更高。避免在SuberType.prototype上面建立沒必要要的、多餘的屬性,與其同時,原型鏈還能保持不變。

所以寄生組合繼承是引用類型最理性的繼承範式。

10. 隱藏頁面中的某個元素的方法有哪些?

查看解析

隱藏類型

屏幕並非惟一的輸出機制,好比說屏幕上看不見的元素(隱藏的元素),其中一些依然可以被讀屏軟件閱讀出來(由於讀屏軟件依賴於可訪問性樹來闡述)。爲了消除它們之間的歧義,咱們將其歸爲三大類:

  • 徹底隱藏:元素從渲染樹中消失,不佔據空間。
  • 視覺上的隱藏:屏幕中不可見,佔據空間。
  • 語義上的隱藏:讀屏軟件不可讀,但正常佔據空。

徹底隱藏

1.display 屬性
display: none;
複製代碼
2.hidden 屬性

HTML5 新增屬性,至關於 display: none

<div hidden>
</div>
複製代碼

視覺上的隱藏

1.利用 position 和 盒模型 將元素移出可視區範圍
  1. 設置 posoitionabsolutefixed,經過設置 topleft 等值,將其移出可視區域。
position:absolute;
left: -99999px;
複製代碼
  1. 設置 positionrelative,經過設置 topleft 等值,將其移出可視區域。
position: relative;
left: -99999px;
height: 0
複製代碼
  1. 設置 margin 值,將其移出可視區域範圍(可視區域佔位)。
margin-left: -99999px;
height: 0;
複製代碼
2.利用 transfrom
  1. 縮放
transform: scale(0);
height: 0;
複製代碼
  1. 移動 translateX, translateY
transform: translateX(-99999px);
height: 0
複製代碼
  1. 旋轉 rotate
transform: rotateY(90deg);
複製代碼
3.設置其大小爲0
  1. 寬高爲0,字體大小爲0:
height: 0;
width: 0;
font-size: 0;
複製代碼
  1. 寬高爲0,超出隱藏:
height: 0;
width: 0;
overflow: hidden;
複製代碼
4.設置透明度爲0
opacity: 0;
複製代碼
5.visibility屬性
visibility: hidden;
複製代碼
6.層級覆蓋,z-index 屬性
position: relative;
z-index: -999;
複製代碼

再設置一個層級較高的元素覆蓋在此元素上。

7.clip-path 裁剪
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
複製代碼

語義上的隱藏

aria-hidden 屬性

讀屏軟件不可讀,佔據空間,可見。

<div aria-hidden="true">
</div>
複製代碼

11. let、const、var 的區別有哪些?

查看解析
聲明方式 變量提高 暫時性死區 重複聲明 塊做用域有效 初始值 從新賦值
var 不存在 容許 不是 非必須 容許
let 不會 存在 不容許 非必須 容許
const 不會 存在 不容許 必須 不容許

1.let/const 定義的變量不會出現變量提高,而 var 定義的變量會提高。

2.相同做用域中,let 和 const 不容許重複聲明,var 容許重複聲明。

3.const 聲明變量時必須設置初始值

4.const 聲明一個只讀的常量,這個常量不可改變。

這裏有一個很是重要的點便是:在JS中,複雜數據類型,存儲在棧中的是堆內存的地址,存在棧中的這個地址是不變的,可是存在堆中的值是能夠變得。有沒有至關常量指針/指針常量~

一圖勝萬言,以下圖所示,不變的是棧內存中 a 存儲的 20,和 b 中存儲的 0x0012ff21(瞎編的一個數字)。而 {age: 18, star: 200} 是可變的。

12. 說一說你對JS執行上下文棧和做用域鏈的理解?

查看解析
在開始說明JS上下文棧和做用域以前,咱們先說明下JS上下文以及做用域的概念。

JS執行上下文

執行上下文就是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念, JavaScript 中運行任何的代碼都是在執行上下文中運行。

執行上下文類型分爲:

  • 全局執行上下文
  • 函數執行上下文

執行上下文建立過程當中,須要作如下幾件事:

  1. 建立變量對象:首先初始化函數的參數arguments,提高函數聲明和變量聲明。
  2. 建立做用域鏈(Scope Chain):在執行期上下文的建立階段,做用域鏈是在變量對象以後建立的。
  3. 肯定this的值,即 ResolveThisBinding

做用域

做用域負責收集和維護由全部聲明的標識符(變量)組成的一系列查詢,並實施一套很是嚴格的規則,肯定當前執行的代碼對這些標識符的訪問權限。—— 摘錄自《你不知道的JavaScript》(上卷)

做用域有兩種工做模型:詞法做用域和動態做用域,JS採用的是詞法做用域工做模型,詞法做用域意味着做用域是由書寫代碼時變量和函數聲明的位置決定的。(witheval 可以修改詞法做用域,可是不推薦使用,對此不作特別說明)

做用域分爲:

  • 全局做用域
  • 函數做用域
  • 塊級做用域

JS執行上下文棧(後面簡稱執行棧)

執行棧,也叫作調用棧,具備 LIFO (後進先出) 結構,用於存儲在代碼執行期間建立的全部執行上下文。

規則以下:

  • 首次運行JavaScript代碼的時候,會建立一個全局執行的上下文並Push到當前的執行棧中,每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並Push當前執行棧的棧頂。
  • 當棧頂的函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文的控制權將移動到當前執行棧的下一個執行上下文。

以一段代碼具體說明:

Global Execution Context (即全局執行上下文)首先入棧,過程以下:

僞代碼:

//全局執行上下文首先入棧
ECStack.push(globalContext);

//執行fun1();
ECStack.push(<fun1> functionContext);

//fun1中又調用了fun2;
ECStack.push(<fun2> functionContext);

//fun2中又調用了fun3;
ECStack.push(<fun3> functionContext);

//fun3執行完畢
ECStack.pop();

//fun2執行完畢
ECStack.pop();

//fun1執行完畢
ECStack.pop();

//javascript繼續順序執行下面的代碼,但ECStack底部始終有一個 全局上下文(globalContext);
複製代碼

做用域鏈

做用域鏈就是從當前做用域開始一層一層向上尋找某個變量,直到找到全局做用域仍是沒找到,就宣佈放棄。這種一層一層的關係,就是做用域鏈。

如:

fn2做用域鏈 = [fn2做用域, fn1做用域,全局做用域]

13. 防抖函數的做用是什麼?請實現一個防抖函數

查看解析
> 防抖函數的做用

防抖函數的做用就是控制函數在必定時間內的執行次數。防抖意味着N秒內函數只會被執行一次,若是N秒內再次被觸發,則從新計算延遲時間。

舉例說明: 小思最近在減肥,可是她很是吃吃零食。爲此,與其男友約定好,若是10天不吃零食,就能夠購買一個包(不要問爲何是包,由於包治百病)。可是若是中間吃了一次零食,那麼就要從新計算時間,直到小思堅持10天沒有吃零食,才能購買一個包。因此,管不住嘴的小思,沒有機會買包(悲傷的故事)... 這就是 防抖

防抖函數實現

  1. 事件第一次觸發時,timeoutnull,調用 later(),若 immediatetrue,那麼當即調用 func.apply(this, params);若是 immediatefalse,那麼過 wait 以後,調用 func.apply(this, params)
  2. 事件第二次觸發時,若是 timeout 已經重置爲 null(即 setTimeout 的倒計時結束),那麼流程與第一次觸發時同樣,若 timeout 不爲 null(即 setTimeout 的倒計時未結束),那麼清空定時器,從新開始計時。

immediate 爲 true 時,表示函數在每一個等待時延的開始被調用。immediate 爲 false 時,表示函數在每一個等待時延的結束被調用。

防抖的應用場景

  1. 搜索框輸入查詢,若是用戶一直在輸入中,沒有必要不停地調用去請求服務端接口,等用戶中止輸入的時候,再調用,設置一個合適的時間間隔,有效減輕服務端壓力。
  2. 表單驗證
  3. 按鈕提交事件。
  4. 瀏覽器窗口縮放,resize事件(如窗口中止改變大小以後從新計算佈局)等。

14. 節流函數的做用是什麼?有哪些應用場景,請實現一個節流函數

查看解析
> 節流函數的做用

節流函數的做用是規定一個單位時間,在這個單位時間內最多隻能觸發一次函數執行,若是這個單位時間內屢次觸發函數,只能有一次生效。

節流函數實現

禁用第一次首先執行,傳遞 {leading: false} ;想禁用最後一次執行,傳遞 {trailing: false}

節流的應用場景

  1. 按鈕點擊事件
  2. 拖拽事件
  3. onScoll
  4. 計算鼠標移動的距離(mousemove)

15. 什麼是閉包?閉包的做用是什麼?

查看解析
閉包的定義

《JavaScript高級程序設計》:

閉包是指有權訪問另外一個函數做用域中的變量的函數

《JavaScript權威指南》:

從技術的角度講,全部的JavaScript函數都是閉包:它們都是對象,它們都關聯到做用域鏈。

《你不知道的JavaScript》

當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域以外執行。

建立一個閉包

閉包使得函數能夠繼續訪問定義時的詞法做用域。拜 fn 所賜,在 foo() 執行後,foo 內部做用域不會被銷燬。

閉包的做用
  1. 可以訪問函數定義時所在的詞法做用域(阻止其被回收)。

  2. 私有化變量

  1. 模擬塊級做用域

  1. 建立模塊

模塊模式具備兩個必備的條件(來自《你不知道的JavaScript》)

  • 必須有外部的封閉函數,該函數必須至少被調用一次(每次調用都會建立一個新的模塊實例)
  • 封閉函數必須返回至少一個內部函數,這樣內部函數才能在私有做用域中造成閉包,而且能夠訪問或者修改私有的狀態。

16. 實現 Promise.all 方法

查看解析
在實現 Promise.all 方法以前,咱們首先要知道 Promise.all 的功能和特色,由於在清楚了 Promise.all 功能和特色的狀況下,咱們才能進一步去寫實現。

Promise.all 功能

Promise.all(iterable) 返回一個新的 Promise 實例。此實例在 iterable 參數內全部的 promisefulfilled 或者參數中不包含 promise 時,狀態變成 fulfilled;若是參數中 promise 有一個失敗rejected,此實例回調失敗,失敗緣由的是第一個失敗 promise 的返回結果。

let p = Promise.all([p1, p2, p3]);
複製代碼

p的狀態由 p1,p2,p3決定,分紅如下;兩種狀況:

(1)只有p一、p二、p3的狀態都變成 fulfilled,p的狀態纔會變成 fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。

(2)只要p一、p二、p3之中有一個被 rejected,p的狀態就變成 rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

Promise.all 的特色

Promise.all 的返回值是一個 promise 實例

  • 若是傳入的參數爲空的可迭代對象,Promise.all同步 返回一個已完成狀態的 promise
  • 若是傳入的參數中不包含任何 promise,Promise.all異步 返回一個已完成狀態的 promise
  • 其它狀況下,Promise.all 返回一個 處理中(pending) 狀態的 promise.

Promise.all 返回的 promise 的狀態

  • 若是傳入的參數中的 promise 都變成完成狀態,Promise.all 返回的 promise 異步地變爲完成。
  • 若是傳入的參數中,有一個 promise 失敗,Promise.all 異步地將失敗的那個結果給失敗狀態的回調函數,而無論其它 promise 是否完成
  • 在任何狀況下,Promise.all 返回的 promise 的完成狀態的結果都是一個數組

Promise.all 實現

17. 請實現一個 flattenDeep 函數,把嵌套的數組扁平化

例如:

flattenDeep([1, [2, [3, [4]], 5]]); //[1, 2, 3, 4, 5]
複製代碼
查看解析
> 利用 Array.prototype.flat

ES6 爲數組實例新增了 flat 方法,用於將嵌套的數組「拉平」,變成一維的數組。該方法返回一個新數組,對原數組沒有影響。

flat 默認只會 「拉平」 一層,若是想要 「拉平」 多層的嵌套數組,須要給 flat 傳遞一個整數,表示想要拉平的層數。

當傳遞的整數大於數組嵌套的層數時,會將數組拉平爲一維數組,JS能表示的最大數字爲 Math.pow(2, 53) - 1,所以咱們能夠這樣定義 flattenDeep 函數

利用 reduce 和 concat

使用 stack 無限反嵌套多層嵌套數組

18. 請實現一個 uniq 函數,實現數組去重

例如:

uniq([1, 2, 3, 5, 3, 2]);//[1, 2, 3, 5]
複製代碼
查看解析

法1: 利用ES6新增數據類型 Set

Set相似於數組,可是成員的值都是惟一的,沒有重複的值。

法2: 利用 indexOf

法3: 利用 includes

法4:利用 reduce

法5:利用 Map

19. 可迭代對象有哪些特色

查看解析
ES6 規定,默認的 `Iterator` 接口部署在數據結構的 `Symbol.iterator` 屬性,換個角度,也能夠認爲,一個數據結構只要具備 `Symbol.iterator` 屬性(`Symbol.iterator` 方法對應的是遍歷器生成函數,返回的是一個遍歷器對象),那麼就能夠其認爲是可迭代的。

可迭代對象的特色

  • 具備 Symbol.iterator 屬性,Symbol.iterator() 返回的是一個遍歷器對象
  • 可使用 for ... of 進行循環
  • 經過被 Array.from 轉換爲數組

原生具備 Iterator 接口的數據結構:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數的 arguments 對象
  • NodeList 對象

20. JSONP 的原理是什麼?

查看解析

儘管瀏覽器有同源策略,可是 <script> 標籤的 src 屬性不會被同源策略所約束,能夠獲取任意服務器上的腳本並執行。jsonp 經過插入 script 標籤的方式來實現跨域,參數只能經過 url 傳入,僅能支持 get 請求。

實現原理:

  • Step1: 建立 callback 方法
  • Step2: 插入 script 標籤
  • Step3: 後臺接受到請求,解析前端傳過去的 callback 方法,返回該方法的調用,而且數據做爲參數傳入該方法
  • Step4: 前端執行服務端返回的方法調用

jsonp源碼實現

使用:

服務端代碼(node):

參考文章:

[1] [JavaScript高級程序設計第六章]

[2] Step-By-Step】高頻面試題深刻解析 / 週刊01

[3] Step-By-Step】高頻面試題深刻解析 / 週刊02

[4] Step-By-Step】高頻面試題深刻解析 / 週刊03

[5] Step-By-Step】高頻面試題深刻解析 / 週刊04

謝謝各位小夥伴願意花費寶貴的時間閱讀本文,若是本文給了您一點幫助或者是啓發,請不要吝嗇你的贊和Star,您的確定是我前進的最大動力。 github.com/YvetteLau/B…

本文中的代碼使用了圖片,若是您想直接複製代碼,請移步這兒有20道大廠面試題等你查收

關注公衆號,加入技術交流羣

相關文章
相關標籤/搜索