前端面試題(親身面試經驗)
最近面試了一些公司,趁著疫情期間,總結一波。大家可以看看 會有用的。
webpack
1、webpack中entry和output的作用
webpack中的entry標記入口檔案,它可以是一個字串(單入口)或者一個數組(多入口),output用來標記輸出,主要有兩個屬性 path和 filename。其次就是publicPath 和chunkFileName
2、webpack中loader和plugin的作用
loader 用於載入某些資原始檔。 因為webpack 本身只能打包commonjs規範的js檔案,對於其他資源例如 css,圖片,或者其他的語法集,比如 jsx, coffee,是沒有辦法載入的。 這就需要對應的loader將資源轉化,載入進來。loader是用於載入的,它作用於一個個檔案上。 plugin 用於擴充套件webpack的功能。它直接作用於 webpack,擴充套件了它的功能。當然loader也是變相的擴充套件了 webpack ,但是它只專注於轉化檔案。而plugin的功能更加的豐富,而不僅侷限於資源的載入。
3、webpack中可以有哪些優化
1、優化Loader的檔案搜尋範圍,指定include、exclude 2、把Babel編譯過的檔案快取起來 loader: 'babel-loader?cacheDirectory=ture' 3、懶載入、按需載入、分包、壓縮 4、提取公共程式碼與第三方程式碼 5、使用HappyPack外掛開啟多程序Loader轉換 6、區分dev、product環境,減少不必要 7、減少不必要的編譯,提升開發時構建速度(devServer.watchOptions.aggregateTimeout)
4、你是依據什麼來確定你要優化哪個模組
我會使用一個webpack-bundle-analyzer的外掛,這個外掛(plugin常規使用,需要在package.json 中寫npm_config_report=true)可以檢視打包後文件包的大小,進而可以找出比較大的包,對他進行優化
5、webpack構建流程
1、初始化引數,從配置檔案和shell語句中讀到的引數合併,得到最後的引數 2、開始編譯:用合併得到的引數初始化complier物件,載入是所有配置的外掛,執行run方法開始編譯 3、確定入口,通過entry找到入口檔案 4、編譯模組,從入口檔案出發,呼叫所有配置的loader對模組進行解析翻譯,在找到該模組依賴的模組進行處理 5、 完成模組編譯,得到每個模組被翻譯之後的最終的內容和依賴關係 6、輸出資源,根據入口和模組之間的依賴關係,組裝成一個個包含多個模組的chunk,在把每個chunk轉換成一個單獨的檔案載入到輸出列表、 7、輸出完成,確定輸出的路徑和檔名,把內容寫到檔案系統中 簡單版 初始化引數 -> 編譯開始 -> 找到入口 -> 編譯入口及其依賴 -> 完成編譯確定關係 -> 輸出資源 -> 輸出完成
6、webpack熱更新原理
首先可以這麼理解 webpack是一個基於node的小伺服器,這麼就好理解了,簡單來說: 1、watch 編譯過程、devServer 推送更新訊息到瀏覽器 2、瀏覽器接收到服務端訊息做出響應 3、對模組進行熱更新或重新整理頁面
7、webpack-dev-server和http伺服器如nginx有什麼區別?
webpack-dev-server使用記憶體來儲存webpack開發環境下的打包檔案,並且可以使用模組熱更新,他比傳統的http服務對開發更加簡單高效。
8、happypack使用方法
module:{ rules:[{ test:/\.js$/, use:['happypack/loader?id=babel'] exclude:path.resolve(__dirname, 'node_modules') },{ test:/\.css/, use:['happypack/loader?id=css'] }], plugins:[ new HappyPack({ id:'babel', loaders:['babel-loader?cacheDirectory'] }), new HappyPack({ id:'css', loaders:['css-loader'] }) ] }
js
1、array map和foreach的區別
map有返回值而且必須return返回一個數組才行,而forEach沒有返回值可直接列印結果。
2、array some和filter的區別
some方法返回的是boolean值,可用於檢察陣列中是否有某物件 filter方法返回的是一個新陣列,可用於過濾陣列中的物件
3、new一個物件過程發生了什麼
1、建立一個新物件,如:var person = {}; 2、新物件的_proto_屬性指向建構函式的原型物件。 3、將建構函式的作用域賦值給新物件。(也所以this物件指向新物件) 4、執行建構函式內部的程式碼,將屬性新增給person中的this物件。 5、返回新物件person。
4、陣列排序的方式
主要有像快速排序、插入排序、氣泡排序
插入排序
方法:將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。 從頭到尾依次掃描未排序序列,將掃描到的每個元素插入有序序列的適當位置。 程式碼: var insertSort = function(arr) { var len = arr.length; var preIndex, current; for(var i = 1; i < len; i++){ preIndex = i - 1; current = arr[i]; while(preIndex >= 0 && arr[preIndex] > current){ arr[preIndex + 1] = arr[preIndex]; preIndex--; } arr[preIndex + 1] = current; } return arr; }
快速排序
方法 :基本原理是將陣列內的數分成三組,取陣列中間的數為基準,將較小數放在左邊,較大數放在右邊,分別將三類數存放在一個數組內,最後遞迴進行排序。 程式碼: function quickSort(arr) { if(arr.length<=1) { return arr; } let leftArr = []; let rightArr = []; let q = arr[0]; for(let i = 1,l=arr.length; i<l; i++) { if(arr[i]>q) { rightArr.push(arr[i]); }else{ leftArr.push(arr[i]); } } return [].concat(quickSort(leftArr),[q],quickSort(rightArr)); }
氣泡排序
方法:比較兩個相鄰的元素,如果後一個比前一個大,則交換位置,然後類推,每次比較完成後可以少比較最後一個 程式碼: function bubbleSort(arr) { for(let i = 0,l=arr.length;i<l-1;i++) { for(let j = i+1;j<l;j++) { if(arr[i]>arr[j]) { let tem = arr[i]; arr[i] = arr[j]; arr[j] = tem; } } } return arr; }
5、陣列去重的幾種方式
1、利用ES6 Set去重 程式碼: Array.from(new Set(arr))或者[...new Set(arr)]
2、健值去重法 原理: 利用物件的屬性不能相同的特點進行去重 function unique(arr) { var arrry= []; var obj = {}; for (var i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { arrry.push(arr[i]) obj[arr[i]] = 1 } else { obj[arr[i]]++ } } return arrry; }
3、includes function unique(arr) { var array =[]; for(var i = 0; i < arr.length; i++) { if( !array.includes( arr[i]) ) { array.push(arr[i]); } } return array } 高大上寫法:(reduce+includes) function unique(arr){ return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]); }
4、排序後相鄰去除法 原理:sort之後會把所有元素排序,只需要比較當前和下一個元素是否完全一致,不完全一致則增加 function uniq(array){ array.sort(); var temp=[array[0]]; for(var i = 1; i < array.length; i++){ if( array[i] !== temp[temp.length-1]){ temp.push(array[i]); } } return temp; }
5、indexof 原理:如果當前陣列的第i項在當前陣列中第一次出現的位置不是i,那麼表示第i項是重複的,忽略掉。否則存入結果陣列。 function uniq(array){ var temp = []; for(var i = 0; i < array.length; i++) { //如果當前陣列的第i項在當前陣列中第一次出現的位置是i,才存入陣列;否則代表是重複的 if(array.indexOf(array[i]) == i){ temp.push(array[i]) } } return temp; }
6、字串出現最多的次數
1、物件法 原理:利用js健值對唯一的特性,如果包含key則value++ function countStr(str){ var obj = {}; for(var i = 0, l = str.length,k; i < l ;i++){ k = str.charAt(i); if(obj[k]){ obj[k]++; }else{ obj[k] = 1; } } var m = 0,i=null; for(var k in obj){ if(obj[k] > m){ m = obj[k]; i = k; } } return i + ':' + m; }
7、reduce兩個引數分別是什麼?
第一個引數是一個函式,函式包含4個引數分別是:total(本次總和、初始值)、currentValue(當前元素)、currentIndex(當前index)、arr(原陣列) 第二個引數是一個初始值
8、怎麼可以將一個包含id的陣列變成一個用id做key的json
arr.reduce((total,currentItem) => { total[currentItem.id] = currentItem; return total },{})
9、下面這個程式碼段輸出什麼
console.log(1); setTimeout(function(){ console.log(2) }) new Promise(function(res,rej){ console.log(3) res() }) .then(function(){ console.log(4) }) .catch(function(){ console.log(5) }) async function a(){ console.log(6); await console.log(7); console.log(8) } a(); console.log(9) 1,3,6,7,9,4,8,2 本題花式比較多,遵循 同步>非同步 微任務>巨集任務
10、promise有幾種狀態?
三種,分別是pending、fulfilled、rejected(未決定,履行,拒絕),resolve時候會由padding->fulfilled,reject會由padding->rejected
11、聊聊原型鏈
這個。。。不知道怎麼描述,也不給個題目,大致說下我是怎麼說的吧 js是一個萬物皆物件的語言,每一個物件都會包含一個原型物件,物件以原型為模版,從他的原型繼承方法和屬性。當然物件也是可以由原型的,一層一層類似於一個鏈條,我們叫做原型鏈。比如舉個例子,man->person->object
12、判斷是不是array
1、instanceof (檢測建構函式的 prototype 屬性是否出現在某個例項物件的原型鏈) a instanceof Array
2、原型prototype + toString + call() Object.prototype.toString.call(a) === "[object Array]" Object.getPrototypeOf(a) === "[object Array]"
3、原型prototype + isPrototypeOf() Array.prototype.isPrototypeOf(variable)
4、 Array.isArray
13、判斷是不是物件
1、原型prototype + toString + call() Object.prototype.toString.call(obj) === '[object Object]' Object.getPrototypeOf(b).toString() === "[object Object]"
2、instanceof(需要處理array) a instanceof Object && !(a instanceof Array)
14、閉包是什麼?怎麼寫?
主要是為了從函式外部訪問函式內部的變數,且這個變數永遠不會被記憶體回收(會回收閉包內函式的變數不會回收閉包的變數),寫法為函式內reuturn一個function
15、null和undefined的區別?
1、null是一個表示”無”的物件,轉為數值時為0;undefined是一個表示”無”的原始值,轉為數值時為NaN。 2、undefined表示”缺少值”,就是此處應該有一個值,但是還沒有定義。 3、null表示”沒有物件”,即該處不應該有值。
16、前端幾種資料儲存方式
localstorage、sessionstorage、cookie、websql等
17、localstorage和sessionStorage以及cookie的區別
首先是cookie他的儲存大小對比local和session會小很多,local他會一直存在於瀏覽器記憶體,即使視窗關閉或者是瀏覽器關閉哪怕是新開一個頁面local都是共享的。而session他是屬於會話級的,我們哪怕開啟相同的頁面,session也不會共享過來。
18、解決跨域的方式
1、通過jsonp跨域(callback) 2、CORS (服務端配置Access-Control-Allow-Origin) 3、domain+iframe(做法:頁面寫一個iframe,將sec設定為要跨域的域名,通過document.getElementById('iframe').contentWindow.$,來取到對應的$,再通過$.ajax呼叫即可,不過僅限於主域名相同)
4、nginx
5、iframe
19、eventloop介紹
js是一個單執行緒的東西(為什麼單執行緒自己百度), 在JavaScript中,存在一個執行棧,當我們呼叫一個函式時,JavaScript引擎會將函式push到執行棧(Stack)中, 執行完畢之後pop出去(先進先出),當遇到非同步任務時,就將任務放進任務佇列中。非同步任務有兩種,一種叫做巨集任務一種叫做微任務, 當主執行緒執行完畢,也就是執行棧為空時,會先確認微任務佇列是否為空,不為空就取出微任務佇列中所有微任務然後順序執行,微任務執行完畢後 會在巨集任務佇列中取出下一個任務新增到執行棧,如果這時候再遇到微任務繼續加到微任務佇列,如此迴圈。
20、物件深拷貝方式
1、最簡單的方式 JSON.parse(JSON.stringify(Obj)) 這種方法使用較為簡單,可以滿足基本的深拷貝需求,而且能夠處理JSON格式能表示的所有資料型別,但是對於正則表示式型別、函式型別等無法進行深拷貝(而且會直接丟失相應的值)。
2、jQuery深拷貝 var copiedObject = $.extend(true, {}, originalObject)
3、手動寫遞迴方式 function deepClone (obj) { var newobj = obj.constructor === Array ? [] : {}; if(typeof obj !== 'object'){ return; } for(var i in obj){ newobj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]; } return newobj }
21、object.assign 是深拷貝還是淺拷貝
Object.assign()拷貝的是屬性值。假如源物件的屬性值是一個物件的引用,那麼它也只指向那個引用。 也就是說,如果物件的屬性值為簡單型別(如string, number),通過Object.assign({},srcObj); 得到的新物件為深拷貝;如果屬性值為物件或其它引用型別,那對於這個物件而言其實是淺拷貝的。
22、聊聊平時你都會做哪些優化
這個比較多了,我之前總結過。可以移步https://www.cnblogs.com/jinzhenzong/p/11777065.html,可以整理一下剪短的話語。 我們可以分為幾塊去說,首先是快取方面,我們可以藉助如:localstorage、sessionStorage、cookie、瀏覽器自帶快取、h5 minifest等對資料、ajax介面、頁面等進行快取, 在請求時,我們應該儘量減少重定向,做一些dns預解析、提高瀏覽器併發數等。之後就是頁面解析和渲染時候,可以做關鍵路徑的渲染,避免一下複雜的佈局,用flex替換原始的佈局模型 還有一些像長列表的尾優化啊、圖片懶載入啊,儘量的減少js的佔用時間,比如使用jq時候我們經常會遇到一些操作樣式或者設定內容等,這個時候我們可以對dom進行快取和批量處理,來減少操作dom和迴流重繪等。比如我們 需要監聽使用者輸入時候都要做一下事件的防抖節流,還有一些如減少閉包(為了變數能夠被記憶體回收機制回收)、使用效能好的api如requestanimationframe代替settimeout、setinterval等。還有一塊比較大的就是 資源的處理,更多還是藉助webpack,比如像分包、程式碼合併、檔案的快取、小圖片base64等,還有像綜合考慮是否使用框架、圖片選質量比較低的,視訊的預載入等。 ps:強烈建議去看我原文章至少吧xmind下下來註釋都看一編,不然深入問一下你就廢了
23、前端開啟頁面的過程
同樣之前總結過https://www.cnblogs.com/jinzhenzong/p/11753559.html 大概過程如下: 1、輸入網址 2、瀏覽器解析ip 3、通過ip發起連結 4、伺服器接受請求 5、處理請求並返回 6、頁面渲染 -> 解析HTML檔案,建立DOM樹(解析執行JS指令碼時,會停止解析後續HTML) 解析CSS,形成CSS物件模型 將CSS與DOM合併,構建渲染樹 佈局渲染樹 繪製渲染樹(可能觸發迴流和重繪) 7、渲染完畢 經過幾個公司問題總結出,一般多會問第六步。前面就是聽聽而已。
24、promise,async函式區別與理解
promise 將原來的用 回撥函式 的 非同步程式設計 方法轉成用relsove和reject觸發事件, 用 then 和 catch 捕獲成功或者失敗的狀態 async函式就相當於自執行的Generator函式,async函式就相當於自執行的Generator函式,較於Promise,async 的優越性就是把每次非同步返回的結果從 then 中拿到最外層的方法中,不需要鏈式呼叫,只要用同步的寫法就可以了。 比 promise 直觀,但是 async 必須以一個 Promise 物件開始 ,所以 async 通常是和Promise 結合使用的。
js的比較多,我只是選了一部分比較常問的,其他還有像設計模式、mvc、mvvm、jq元件、還有各種陣列物件的方法的作用引數等就不總結了
vue篇
1、watch和計算屬性區別
watch適合處理的場景是,偵聽一個數的變化,當該資料變化,來處理其他與之相關資料的變化(該資料影響別的多個數據) computed適合處理的場景是,獲得一個值或者結果,該結果受其他的依賴的影響。(一個數據受多個數據影響)
2、watch也可以實現監聽為什麼還要計算屬性這個?
1、首先vue官方推薦的是如果可以用計算屬性就不要用watch,為什麼呢?因為計算屬性是有快取的,對效能來說是更好的。 2、其次兩種功能是不一樣的,computed針對於一個值依賴於多個,watch雖然也可以,但是程式碼會很臃腫
3、data為什麼是一個函式?
元件中的data寫成一個函式,資料以函式返回值形式定義,這樣每複用一次元件,就會返回一份新的data,類似於給每個元件例項建立一個私有的資料空間,讓各個元件例項維護各自的資料。而單純的寫成物件形式,就使得所有元件例項共用了一份data,就會造成一個變了全都會變的結果。
4、雙向資料繫結原理?
利用了 Object.defineProperty() ,讓資料變得可觀測,結合訂閱者釋出者模式,當資料變化時候會由釋出者來通知訂閱者更新資料和view vue3利用proxy來讓資料可觀測。
5、元件怎麼實現雙向資料繫結
元件 內部props寫上value,watch value進行資料的初始化,data中宣告一個newvalue,監聽這個newvalue,如果頁面發生變化使用emit/on向上丟擲input事件
6、生命週期(8+3)官網去看
7、extend和混合
extend用於建立vue例項 mixins可以混入多個mixin,extends只能繼承一個 mixins類似於面向切面的程式設計(AOP),extends類似於面向物件的程式設計
8、什麼是單向資料流
資料流向是單向的,即父能給子,子不能修改父,寫元件時我都會宣告一個類似box的東西,當我子元件發生變化我都會通知到box再由box進行分發,這麼做可以保證出現bug時候的除錯
9、事件通訊
父子:props 子父:emit/on 同級:新宣告一個vue作為中間,來轉發。或者原生寫一個eventbus 跨層級:provide 和 inject(不建議日常開發使用,主要在開發高階外掛/元件庫時使用。)、同級的在這裡也可以使用
10、vue中 key 值的作用
1、不加key的話,更新機制的進行diff的時候是會全部比較的,對效能不好。 2、不加可能會出現資料變化而沒有渲染檢視 3、根據diff演算法,同一層級的一組節點,他們需要通過唯一的id進行區分。
11、vue中如果資料更新了檢視沒有渲染怎麼辦
一般這種都會出現在json\jsonArray中,需要使用$set(目標,要修改的key,值),如果是層級特別多可能set還是不會生效,需要this.$forceUpdate();
12、如果我需要在檢視更新後進行一些操作可以怎麼做
$nextTick
13、vue v-show/v-if
v-show控制display,適用於切換頻繁的場景 v-if如果為false,頁面都不會渲染這個dom,適用於不經常切換的場景。
14、vue-router有哪幾種導航鉤子
1、全域性的 beforeEach、afterEach 2、單個路由獨享的beforeEnter 3、頁面級的 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
15、hash和history路由
hash 值變化不會導致瀏覽器向伺服器發出請求 hash改變會觸發 hashchange 事件(hashchange只能改變 # 後面的url片段) hash發生變化的url都會被瀏覽器記錄下來,從而你會發現瀏覽器的前進後退都可以用了 history 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。但是history路由需要後端配置 如果路由找不到,就始終返回一個html,不然會由重新整理404的問題
更多的會慢慢完善,我過濾掉了很多比如根據自己理解去解答的,程式碼題也過濾了不少,因為最近電話面試居多,程式題幾乎沒有。過濾了很多基礎的,基本用過的人都會的。只取其中最經典,最常問,最有誤區的,可能會由一些疏漏我之後想起來或者遇到再補上
&n