摘要:在掘金上看到了一位大佬發了一篇很詳細的面試記錄文章一年半經驗,百度有贊阿里面試總結,為了查漏補缺,抽空就詳細做了下。
在掘金上看到了一位大佬發了一篇很詳細的面試記錄文章-《一年半經驗,百度、有贊、阿里面試總結》,為了查漏補缺,抽空就詳細做了下。(估計只有我這么無聊了哈哈哈)
有給出的或者有些不完善的答案,也盡力給出/完善了(可能有錯,大家自行辨別)。有些很困難的題目(例如實現Promise),附帶相關鏈接(懶癌患者福利)。
總的來說,將這些題目分成了“Javascript”、“CSS”、“瀏覽器/協議”、“算法”和“Web工程化”5個部分進行回答和代碼實現。
最后,歡迎來我的博客和我扯犢子:godbmw.com。直接戳本篇原文的地址:刷《一年半經驗,百度、有贊、阿里面試總結》·手記
1. Javascript相關 1.1 回文字符串題目:實現一個函數,判斷是不是回文字符串
原文的思路是將字符串轉化成數組=>反轉數組=>拼接成字符串。這種做法充分利用了js的BIF,但性能有所損耗:
function run(input) { if (typeof input !== "string") return false; return input.split("").reverse().join("") === input; }
其實正常思路也很簡單就能實現,性能更高,但是沒有利用js的特性:
// 回文字符串 const palindrome = (str) => { // 類型判斷 if(typeof str !== "string") { return false; } let len = str.length; for(let i = 0; i < len / 2; ++i){ if(str[i] !== str[len - i - 1]){ return false; } } return true; }1.2 實現Storage
題目:實現Storage,使得該對象為單例,并對localStorage進行封裝設置值setItem(key,value)和getItem(key)
題目重點是單例模式,需要注意的是借助localStorage,不是讓自己手動實現!
感謝@whiteyork_和@chenzesam的提醒:箭頭函數沒有prototype
function Storage(){} Storage.getInstance = (function(){ var instance = null return function(){ if(!instance){ instance = new Storage() } return instance } })() Storage.prototype.setItem = function(key, value) { return localStorage.setItem(key, value) } Storage.prototype.getItem = function(key){ return localStorage.getItem(key) } // 測試代碼:Chrome環境 let a = Storage.getInstance() let b = Storage.getInstance() console.log(a === b) a.setItem("key", 1) console.log(b.getItem("key"))1.3 JS事件流
題目:說說事件流吧
事件流分為冒泡和捕獲。
事件冒泡:子元素的觸發事件會一直向父節點傳遞,一直到根結點停止。此過程中,可以在每個節點捕捉到相關事件。可以通過stopPropagation方法終止冒泡。
事件捕獲:和“事件冒泡”相反,從根節點開始執行,一直向子節點傳遞,直到目標節點。印象中只有少數瀏覽器的老舊版本才是這種事件流,可以忽略。這里說的確實有問題,更正下:addEventLister給出了第三個參數同時支持冒泡與捕獲。
感謝@junior-yang的提醒
1.4 實現函數繼承題目:現在有一個函數A和函數B,請你實現B繼承A。并且說明他們優缺點。
方法一:綁定構造函數
優點:可以實現多繼承
缺點:不能繼承父類原型方法/屬性
function Animal(){ this.species = "動物"; } function Cat(){ Animal.apply(this, arguments); // 父對象的構造函數綁定到子節點上 } var cat = new Cat() console.log(cat.species) // 輸出:動物
方法二:原型鏈繼承
優點:能夠繼承父類原型和實例方法/屬性,并且可以捕獲父類的原型鏈改動
缺點:無法實現多繼承,會浪費一些內存(Cat.prototype.constructor = Cat)。除此之外,需要注意應該將Cat.prototype.constructor重新指向本身。
js中交換原型鏈,均需要修復prototype.constructor指向問題。
function Animal(){ this.species = "動物"; } Animal.prototype.func = function(){ console.log("heel") } function Cat(){} Cat.prototype = new Animal() Cat.prototype.constructor = Cat var cat = new Cat() console.log(cat.func, cat.species)
方法3:結合上面2種方法
function Animal(){ this.species = "動物"; } Animal.prototype.func = function(){ console.log("heel") } function Cat(){ Animal.apply(this, arguments) } Cat.prototype = new Animal() Cat.prototype.constructor = Cat; var cat = new Cat() console.log(cat.func, cat.species)1.5 ES5對象 vs ES6對象
題目:es6 class 的new實例和es5的new實例有什么區別?
在ES6中(和ES5相比),class的new實例有以下特點:
class的構造參數必須是new來調用,不可以將其作為普通函數執行
es6 的class不存在變量提升
最重要的是:es6內部方法不可以枚舉。es5的prototype上的方法可以枚舉。
為此我做了以下測試代碼進行驗證:
console.log(ES5Class()) // es5:可以直接作為函數運行 // console.log(new ES6Class()) // 會報錯:不存在變量提升 function ES5Class(){ console.log("hello") } ES5Class.prototype.func = function(){ console.log("Hello world") } class ES6Class{ constructor(){} func(){ console.log("Hello world") } } let es5 = new ES5Class() let es6 = new ES6Class() console.log("ES5 :") for(let _ in es5){ console.log(_) } // es6:不可枚舉 console.log("ES6 :") for(let _ in es6){ console.log(_) }
這篇《JavaScript創建對象—從es5到es6》對這個問題的深入解釋很好,推薦觀看!
1.6 實現MVVM題目:請簡單實現雙向數據綁定mvvm
vuejs是利用Object.defineProperty來實現的MVVM,采用的是訂閱發布模式。每個data中都有set和get屬性,這種點對點的效率,比Angular實現MVVM的方式的效率更高。
1.7 實現Promise
這是一位大佬實現的Promise版本:過了Promie/A+標準的測試!!!網上能搜到的基本都是從這篇文章變形而來或者直接照搬!!!原文地址,直接戳:剖析Promise內部結構,一步一步實現一個完整的、能通過所有Test case的Promise類
下面附上一種近乎完美的實現:可能無法和其他Promise庫的實現無縫對接。但是,上面的原文實現了全部的,歡迎Mark!
function MyPromise(executor){ var that = this this.status = "pending" // 當前狀態 this.data = undefined this.onResolvedCallback = [] // Promise resolve時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 this.onRejectedCallback = [] // Promise reject時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 // 更改狀態 => 綁定數據 => 執行回調函數集 function resolve(value){ if(that.status === "pending"){ that.status = "resolved" that.data = value for(var i = 0; i < that.onResolvedCallback.length; ++i){ that.onResolvedCallback[i](value) } } } function reject(reason){ if(that.status === "pending"){ that.status = "rejected" that.data = reason for(var i = 0; i < that.onResolvedCallback.length; ++i){ that.onRejectedCallback[i](reason) } } } try{ executor(resolve, reject) // resolve, reject兩個函數可以在外部傳入的函數(executor)中調用 } catch(e) { // 考慮到執行過程可能有錯 reject(e) } } // 標準是沒有catch方法的,實現了then,就實現了catch // then/catch 均要返回一個新的Promise實例 MyPromise.prototype.then = function(onResolved, onRejected){ var that = this var promise2 // 值穿透 onResolved = typeof onResolved === "function" ? onResolved : function(v){ return v } onRejected = typeof onRejected === "function" ? onRejected : function(r){ return r } if(that.status === "resolved"){ return promise2 = new MyPromise(function(resolve, reject){ try{ var x = onResolved(that.data) if(x instanceof MyPromise){ // 如果onResolved的返回值是一個Promise對象,直接取它的結果做為promise2的結果 x.then(resolve, reject) } resolve(x) // 否則,以它的返回值做為promise2的結果 } catch(e) { reject(e) // 如果出錯,以捕獲到的錯誤做為promise2的結果 } }) } if(that.status === "rejected"){ return promise2 = new MyPromise(function(resolve, reject){ try{ var x = onRejected(that.data) if(x instanceof MyPromise){ x.then(resolve, reject) } } catch(e) { reject(e) } }) } if(that.status === "pending"){ return promise2 = new MyPromise(function(resolve, reject){ self.onResolvedCallback.push(function(reason){ try{ var x = onResolved(that.data) if(x instanceof MyPromise){ x.then(resolve, reject) } } catch(e) { reject(e) } }) self.onRejectedCallback.push(function(value){ try{ var x = onRejected(that.data) if(x instanceof MyPromise){ x.then(resolve, reject) } } catch(e) { reject(e) } }) }) } } MyPromise.prototype.catch = function(onRejected){ return this.then(null, onRejected) } // 以下是簡單的測試樣例: new MyPromise(resolve => resolve(8)).then(value => { console.log(value) })1.8 Event Loop
題目:說一下JS的EventLoop
其實阮一峰老師這篇《JavaScript 運行機制詳解:再談Event Loop》已經講的很清晰了(手動贊)!
這里簡單總結下:
JS是單線程的,其上面的所有任務都是在兩個地方執行:執行棧和任務隊列。前者是存放同步任務;后者是異步任務有結果后,就在其中放入一個事件。
當執行棧的任務都執行完了(棧空),js會讀取任務隊列,并將可以執行的任務從任務隊列丟到執行棧中執行。
這個過程是循環進行,所以稱作Loop。
2. CSS相關 2.1 水平垂直居中題目: 兩種以上方式實現已知或者未知寬度的垂直水平居中
第一種方法就是利用CSS3的translate進行偏移定位,注意:兩個參數的百分比都是針對元素本身計算的。
.wrap { position: relative; width: 100vw; height: 100vh; } .box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
第二種方法是利用CSS3的flex布局,父元素diplay屬性設置為flex,并且定義元素在兩條軸線的布局方式均為center
.wrap { display: flex; justify-content: center; align-items: center; width: 100vw; height: 100vh; } .wrap .box { width: 100px; height: 100px; }
第三種方法是利用margin負值來進行元素偏移,優點是瀏覽器兼容好,缺點是不夠靈活(要自行計算margin的值):
.wrap { position: relative; width: 100vw; height: 100vh; } .box { position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; margin: -50px 0 0 -50px; }2.2 “點擊”改變樣式
題目:實現效果,點擊容器內的圖標,圖標邊框變成border 1px solid red,點擊空白處重置。
利用event.target可以判斷是否是指定元素本身(判斷“空白處”),除此之外,注意禁止冒泡(題目指明了“容器內”)。
3. 瀏覽器/協議相關 3.1 緩存機制Document 123456
題目:說一下瀏覽器的緩存機制。
瀏覽器緩存分為強緩存和協商緩存。緩存的作用是提高客戶端速度、節省網絡流量、降低服務器壓力。
強緩存:瀏覽器請求資源,如果header中的Cache-Control和Expires沒有過期,直接從緩存(本地)讀取資源,不需要再向服務器請求資源。
協商緩存:瀏覽器請求的資源如果是過期的,那么會向服務器發送請求,header中帶有Etag字段。服務器再進行判斷,如果ETag匹配,則返回給客戶端300系列狀態碼,客戶端繼續使用本地緩存;否則,客戶端會重新獲取數據資源。
關于過程中詳細的字段,可以參考這篇《http協商緩存VS強緩存》
3.2 從URL到頁面生成題目:輸入URL到看到頁面發生的全過程,越詳細越好
DNS解析
建立TCP連接(3次握手)
發送HTTP請求,從服務器下載相關內容
瀏覽器構建DOM樹和CSS樹,然后生成渲染樹。這個一個漸進式過程,引擎會力求最快將內容呈現給用戶。
在第四步的過程中,的位置和加載方式會影響響應速度。
搞定了,關閉TCP連接(4次握手)
3.3 TCP握手題目:解釋TCP建立的時候的3次握手和關閉時候的4次握手
看這題的時候,我也是突然懵(手動捂臉)。推薦翻一下計算機網絡的相關書籍,對于FIN、ACK等字段的講解很贊!
3.4 CSS和JS位置題目:CSS和JS的位置會影響頁面效率,為什么?
先說CSS。CSS的位置不會影響加載速度,但是CSS一般放在標簽中。前面有說DOM樹和CSS樹共同生成渲染樹,CSS位置太靠后的話,在CSS加載之前,可能會出現閃屏、樣式混亂、白屏等情況。
再說JS。JS是阻塞加載,默認的標簽會加載并且立即執行腳本,如果腳本很復雜或者網絡不好,會出現很久的白屏。所以,JS標簽一般放到標簽最后。
現在,也可以為標簽設置async或者defer屬性。前者是js腳本的加載和執行將與后續文檔的加載和渲染同步執行。后者是js腳本的加載將與后續文檔的加載和渲染同步執行,當所有元素解析完,再執行js腳本。
4. 算法相關 4.1 數組全排列題目:現在有一個數組[1,2,3,4],請實現算法,得到這個數組的全排列的數組,如[2,1,3,4],[2,1,4,3]。。。。你這個算法的時間復雜度是多少
實現思路:從“開始元素”起,每個元素都和開始元素進行交換;不斷縮小范圍,最后輸出這種排列。暴力法的時間復雜度是 $O(N_N)$,遞歸實現的時間復雜度是 $O(N!)$
如何去重?去重的全排列就是從第一個數字起每個數分別與它后面非重復出現的數字交換。對于有重復元素的數組,例如:[1, 2, 2],應該剔除重復的情況。每次只需要檢查arr[start, i)中是不是有和arr[i]相同的元素,有的話,說明之前已經輸出過了,不需要考慮。
代碼實現:
const swap = (arr, i, j) => { let tmp = arr[i] arr[i] = arr[j] arr[j] = tmp } const permutation = arr => { const _permutation = (arr, start) => { if(start === arr.length){ console.log(arr) return } for(let i = start; i < arr.length; ++i){ // 全排列:去重操作 if(arr.slice(start, i).indexOf(arr[i]) !== -1){ continue } swap(arr, i, start) // 和開始元素進行交換 _permutation(arr, start + 1) swap(arr, i, start) // 恢復數組 } return } return _permutation(arr, 0) } permutation([1, 2, 2]) console.log("**********") permutation([1, 2, 3, 4])4.2 背包問題
題目:我現在有一個背包,容量為m,然后有n個貨物,重量分別為w1,w2,w3...wn,每個貨物的價值是v1,v2,v3...vn,w和v沒有任何關系,請求背包能裝下的最大價值。
這個還在學習中,背包問題博大精深。。。
4.3 圖的連通分量題目:我現在有一個canvas,上面隨機布著一些黑塊,請實現方法,計算canvas上有多少個黑塊。
這一題可以轉化成圖的聯通分量問題。通過getImageData獲得像素數組,從頭到尾遍歷一遍,就可以判斷每個像素是否是黑色。同時,準備一個width * height大小的二維數組,這個數組的每個元素是1/0。如果是黑色,二維數組對應元素就置1;否則置0。
然后問題就被轉換成了圖的連通分量問題。可以通過深度優先遍歷或者并查集來實現。之前我用C++實現了,這里不再冗贅:
圖的遍歷和應用
并查集
5. Web工程化 5.1 Dialog組件思路題目:現在要你完成一個Dialog組件,說說你設計的思路?它應該有什么功能?
可以指定寬度、高度和位置
需要一個遮蓋層,遮住底層內容
由頭部、尾部和正文構成
需要監聽事件和自定義事件,非單向數據流:例如點擊組件右上角,修改父組件的visible屬性,關閉組件。
關于工程化組件封裝,可以去試試ElementUI。這個是ElementUI的Dialog組件:Element-Dialog
5.2 React的Diff算法和虛擬DOM題目: react 的虛擬dom是怎么實現的
原答案寫的挺好滴,這里直接貼了。
首先說說為什么要使用Virturl DOM,因為操作真實DOM的耗費的性能代價太高,所以react內部使用js實現了一套dom結構。 在每次操作在和真實dom之前,使用實現好的diff算法,對虛擬dom進行比較,遞歸找出有變化的dom節點,然后對其進行更新操作。 為了實現虛擬DOM,我們需要把每一種節點類型抽象成對象,每一種節點類型有自己的屬性,也就是prop,每次進行diff的時候,react會先比較該節點類型: 假如節點類型不一樣,那么react會直接刪除該節點,然后直接創建新的節點插入到其中; 假如節點類型一樣,那么會比較prop是否有更新,假如有prop不一樣,那么react會判定該節點有更新,那么重渲染該節點,然后在對其子節點進行比較,一層一層往下,直到沒有子節點。
參考鏈接:React源碼之Diff算法
最后,歡迎來我的博客和我扯犢子:godbmw.com。直接戳本篇原文的地址:刷《一年半經驗,百度、有贊、阿里面試總結》·手記
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/114332.html
摘要:在掘金上看到了一位大佬發了一篇很詳細的面試記錄文章一年半經驗,百度有贊阿里面試總結,為了查漏補缺,抽空就詳細做了下。 在掘金上看到了一位大佬發了一篇很詳細的面試記錄文章-《一年半經驗,百度、有贊、阿里面試總結》,為了查漏補缺,抽空就詳細做了下。(估計只有我這么無聊了哈哈哈) 有給出的或者有些不完善的答案,也盡力給出/完善了(可能有錯,大家自行辨別)。有些很困難的題目(例如實現Promi...
摘要:獲取的對象范圍方法獲取的是最終應用在元素上的所有屬性對象即使沒有代碼,也會把默認的祖宗八代都顯示出來而只能獲取元素屬性中的樣式。因此對于一個光禿禿的元素,方法返回對象中屬性值如果有就是據我測試不同環境結果可能有差異而就是。 花了很長時間整理的前端面試資源,喜歡請大家不要吝嗇star~ 別只收藏,點個贊,點個star再走哈~ 持續更新中……,可以關注下github 項目地址 https:...
摘要:面經因為我完全沒有面試經驗,從來沒有經歷過面試,于是想著在去這類大公司面試之前先找成都的小公司練練手,積累點面試經驗。于是三月份開始就有成都的小公司開始約我面試。 前序 從我高考成績出來那一刻開始,從我在高考志愿上填上計算機科學與技術這幾個當時在心中堪稱神圣的幾個字開始,我就已經把進入中國互聯網最高殿堂BAT作為我整個大學奮斗的目標,哪怕我就讀的是一所位于內陸的雙非一本大學我也認為我能...
摘要:年求職面經及總結我的求職之路差不多走到盡頭了感覺真是精疲力盡了把這大半年的經歷和面試總結寫下來希望能給和我一樣在求職路上煎熬的人一點幫助先說背景微電子科學與工程專業學過兩門和相關的課程語言和單片機這個專業的唯一好處就是大部分人并不知道這個專 18年求職面經及總結 我的求職之路差不多走到盡頭了,感覺真是精疲力盡了.把這大半年的經歷和面試總結寫下來,希望能給和我一樣在求職路上煎熬的人一點幫...
閱讀 1026·2021-11-23 09:51
閱讀 2344·2021-10-08 10:22
閱讀 2544·2021-09-29 09:35
閱讀 853·2021-09-22 15:20
閱讀 2858·2019-08-30 15:53
閱讀 2413·2019-08-30 13:55
閱讀 1097·2019-08-29 17:27
閱讀 2869·2019-08-29 17:26