最新BAT前端面試題:大公司問的核心都在於JavaScript!

BAT面試對基礎比較看重,然後需要你有兩三個比較好的項目,一面重視面試者對前端基礎的把握,還要手寫代碼,不過不難,二面部門的leader面,這一面比較難,面試官會對你的項目細節進行深挖,所以說項目要牛逼一點,最後還會有一道邏輯題,三面是HR面,如果你想進大公司的話,下面這些技術是肯定要掌握的:html5,css3,JavaScript,略懂一點jQuery源碼,Node.js,express,mongoose,數據庫mongodb。大公司問的核心在於JavaScript。

如果下面的知識點你都可以答上來,恭喜你拿下bat不是問題。

一、html+css部分、

(1)css盒模型,可能會要求手寫一個佈局,這個佈局基本上用到的css是margin的負值,boxing-sizing:border-box,佈局儘量往這方面想。瀏覽器佈局的基本元素是盒,在w3c的標準模式下,width=width,但是在怪異模式下,width=border2+padding2+width;其中後代元素的width:100%;參照的是右邊的那個width,

(2)html5的新特性

1、標籤語義化,比如header,footer,nav,aside,article,section等,新增了很多表單元素,入email,url等,除去了center等樣式標籤,還有除去了有性能問題的frame,frameset等標籤

2、音視頻元素,video,audio的增加使得我們不需要在依賴外部的插件就可以往網頁中加入音視頻元素。

3、新增很多api,比如獲取用戶地理位置的window.navigator.geoloaction,

4、websocket

websocket是一種協議,可以讓我們建立客戶端到服務器端的全雙工通信,這就意味着服務器端可以主動推送數據到客戶端,

5、webstorage,webstorage是本地存儲,存儲在客戶端,包括localeStorage和sessionStorage,localeStorage是持久化存儲在客戶端,只要用戶不主動刪除,就不會消失,sessionStorage也是存儲在客戶端,但是他的存在時間是一個回話,一旦瀏覽器的關於該回話的頁面關閉了,sessionStorage就消失了,

6、緩存

html5允許我們自己控制哪些文件需要緩存,哪些不需要,具體的做法如下:

1、首先給html添加manifest屬性,並賦值爲cache.manifest
2、cache.manifest的內容爲:

CACHE MANIFEST

v1.2

CACHE : //表示需要緩存的文件
a.js
b.js
NETWORK: //表示只在用戶在線的時候才需要的文件,不會緩存
c.js
FALLBACK
/ /index.html //表示如果找不到第一個資源就用第二個資源代替

7、web worker,web worker是運行在瀏覽器後臺的js程序,他不影響主程序的運行,是另開的一個js線程,可以用這個線程執行復雜的數據操作,然後把操作結果通過postMessage傳遞給主線程,這樣在進行復雜且耗時的操作時就不會阻塞主線程了。

(3)對html5的語義話的理解

html5的語義化指的是用正確的標籤包含正確的內容,比如nav標籤,裏面就應該包含導航條的內容,而不是用做其他的用途,標籤語義化的好處就是結構良好,便於閱讀,方便威化,也有利於爬蟲的查找,提高搜索率。

(4)cookie,sessionStorage,localeStorage的區別

cookie是存儲在瀏覽器端,並且隨瀏覽器的請求一起發送到服務器端的,它有一定的過期時間,到了過期時間自動會消失。sessionStorage和localeStorage也是存儲在客戶端的,同屬於web Storage,比cookie的存儲大小要大有8m,cookie只有4kb,localeStorage是持久化的存儲在客戶端,如果用戶不手動清除的話,不會自動消失,會一直存在,sessionStorage也是存儲在客戶端,但是它的存活時間是在一個回話期間,只要瀏覽器的回話關閉了就會自動消失。

(5)多個頁面之間如何進行通信

使用cookie,使用web worker,使用localeStorage和sessionStorage

(6)瀏覽器的渲染過程

1、首先獲取html,然後構建dom樹

2、其次根據css構建render樹,render樹中不包含定位和幾何信息

3、最後構建佈局數,佈局是含有元素的定位和幾何信息

(7)重構、迴流

瀏覽器的重構指的是改變每個元素外觀時所觸發的瀏覽器行爲,比如顏色,背景等樣式發生了改變而進行的重新構造新外觀的過程。重構不會引發頁面的重新佈局,不一定伴隨着迴流,

迴流指的是瀏覽器爲了重新渲染頁面的需要而進行的重新計算元素的幾何大小和位置的,他的開銷是非常大的,迴流可以理解爲渲染樹需要重新進行計算,一般最好觸發元素的重構,避免元素的迴流;比如通過通過添加類來添加css樣式,而不是直接在DOM上設置,當需要操作某一塊元素時候,最好使其脫離文檔流,這樣就不會引起迴流了,比如設置position:absolute或者fixed,或者display:none,等操作結束後在顯示。

二、JavaScript部分

(1)JavaScript的數據類型

基本數據類型:Number,String,Boolean,Undefined,Null

複雜數據類型:Object,Array,Function,RegExp,Date,Error

全局數據類型:Math

(2)JavaScript的閉包

閉包簡單的說就是一個函數能訪問外部函數的變量,這就是閉包,比如說:

function a(x){
 var tem=3;
 function b(y){
 console.log(x+y+(++tem));
 }
}

a函數中的b函數就是閉包了,b函數可以使用a函數的局部變量,參數,最典型的閉包應該是下面這樣,將定義在函數中的函數作爲返回值

function a(x){
 var tem=3;
 function b(y){
 console.log(x+y+(++tem));
 }
return b;
}

閉包的缺點是,因爲內部閉包函數可以訪問外部函數的變量,所以外部函數的變量不能被釋放,如果閉包嵌套過多,會導致內存佔用大,要合理使用閉包。

(3)new 操作符到底做了什麼

首先,new操作符爲我們創建一個新的空對象,然後this變量指向該對象,

其次,空對象的原型執行函數的原型,

最後,改變構造函數內部的this的指向

代碼如下:

var obj={};
obj.__proto__=fn.prototype;
fn.call(obj);

(4)改變函數內部this指針的指向函數

call和apply,假設要改變fn函數內部的this的指向,指向obj,那麼可以fn.call(obj);或者fn.apply(obj);那麼問題來了,call和apply的區別是什麼,其是call和apply的區別在於參數,他們兩個的第一個參數都是一樣的,表示調用該函數的對象,apply的第二個參數是數組,是[arg1,arg2,arg3]這種形式,而call是arg1,arg2,arg3這樣的形式。還有一個bind函數,

var bar=fn.bind(obj);那麼fn中的this就指向obj對象了,bind函數返回新的函數,這個函數內的this指針指向obj對象。

(5)JavaScript的作用域和作用域鏈

JavaScript的作用域指的是變量的作用範圍,內部作用域由函數的形參,實參,局部變量,函數構成,內部作用域和外部的作用域一層層的鏈接起來形成作用域鏈,當在函數內部要訪問一個變量的時候,首先查找自己的內部作用域有沒有這個變量,如果沒有就到外部的作用域中去查找,還是沒有的話,就到在外面一層作用域中找,直到到window所在的作用域,每個函數在聲明的時候就默認有一個外部作用域鏈存在了,比如:

var t=4;
function foo(){
 var tem=12;
 funciton bar(){
 var temo=34;
 console.log(t+" "+tem+" "+temo);
 }
}

在bar函數中找t變量的過程就是,先到自己的內部作用域中找,發現沒有找到,然後到bar所在的最近的外部環境中找,也就是foo的內部作用域,還是沒有找到,再到window的作用域中找,結果找到了

(6)JavaScript的繼承

function A(name){ this.name=name; }
A.prototype.sayName=function(){ console.log(this.name); }
function B(age){ this.age=age; }

原型繼承

B.prototype=new A("mbj"); //被B的實例共享

var foo=new B(18);
foo.age; //18,age是本身攜帶的屬性
foo.name; //mbj,等價於foo.__proto__.name
foo.sayName(); //mbj,等價於foo.__proto__.proto__.sayName()
foo.toString(); //"[object Object]",等價於foo.__proto__.__proto__.__proto__.toString();

這樣B通過原型繼承了A,在new B的時候,foo中有個隱藏的屬性proto指向構造函數的prototype對象,在這裏是A對象實例,A對象裏面也有一個隱藏的屬性proto,指向A構造函數的prototype對象,這個對象裏面又有一個proto指向Object的prototype

這種方式的缺第一個缺點是所有子類共享父類實例,如果某一個子類修改了父類,其他的子類在繼承的時候,會造成意想不到的後果。第二個缺點是在構造子類實例的時候,不能給父類傳遞參數。

構造函數繼承

function B(age,name){ this.age=age;A.call(this,name); }
var foo=new B(18,"wmy");
foo.name; //wmy
foo.age; //18
foo.sayName(); //undefined

採用這種方式繼承是把A中的屬性加到this上面,這樣name相當於就是B的屬性,sayName不在A的構造函數中,所以訪問不到sayName。這種方法的缺點是父類的prototype中的函數不能複用。

原型繼承+構造函數繼承

function B(age,name){ this.age=age;A.call(this,name); }
B.prototype=new A("mbj");
var foo=new B(18,"wmy");
foo.name; //wmy
foo.age; //18
foo.sayName(); //wmy

這樣就可以成功訪問sayName函數了,結合了上述兩種方式的優點,但是這種方式也有缺點,那就是佔用的空間更大了。

(7)JavaScript變量提升

請看下面代碼

var bar=1;
function test(){
 console.log(bar); //undeifned
 var bar=2; 
 console.log(bar); //2
}
test();

爲什麼在test函數中會出現上述結果呢,這就是JavaScript的變量提升了,雖然變量bar的定義在後面,不過瀏覽器在解析的時候,會把變量的定義放到最前面,上面的test函數相當於

function test(){
 var bar;
 console.log(bar); //undefined
 bar=2; 
 console.log(bar); //2
}

再看

var foo=function(){ console.log(1); }
function foo(){ console.log(2); }
foo(); //結果爲1
同樣的,函數的定義也會到提升到最前面,上面的代碼相當於
function foo(){ console.log(2); }
var foo;
foo=funciton(){ console.log(1); }
foo(); //1

(8)JavaScript事件模型

原始事件模型,捕獲型事件模型,冒泡事件模型,

原始事件模型就是ele.onclick=function(){}這種類型的事件模型

冒泡事件模型是指事件從事件的發生地(目標元素),一直向上傳遞,直到document,

捕獲型則恰好相反,事件是從document向下傳遞,直到事件的發生地(目標元素)

IE是隻支持冒泡事件模型的,下面是兼容各個瀏覽器的事件監聽代碼

EventUtil={
 addListener:function(target,type,handler){
 if(target.addEventListener){
 target.addEventListener(type,handler);
 }else if(target.attachEvent){
 target.attach("on"+type,function(){
 handler.call(target); //讓handler中的this指向目標元素
 });
 }else{
 target["on"+type]=handler;
 }
 },
 removeListener:function(target,type,handler){ 
 if(target.removeEventListener){ 
 target.removeEventListener(type,handler); 
 }else if(target.detachEvent){
 target.detachEvent("on"+type,handler);
 }else{
 target["on"+type]=null;
 }
 },
 getEvent:function(e){ //獲取事件對象
 var evt=window.event||e;
 return evt;
 },
 getTarget:function(e){ //獲得目標對象
 var evt=EventUtil.getEvent(e);
 var target;
 if(evt.target){ target=evt.target;}
 else {target=evt.srcElement;}
 return target;
 },
 stopPropagation:function(e){ //停止冒泡
 var evt=EventUtil.getEvent(e);
 if(evt.stopPropagation) {evt.stopPropagation();}
 else {evt.cancelBubble=true;}
 },
 preventDefault:function(e){ //阻值默認行爲的發生
 var evt=EventUtil.getEvent(e);
 if(evt.preventDefault){ evt.preventDefault(); }
 else {e.returnValue=false;}
 }
}

(9)內存泄漏

內存泄漏指的是瀏覽器不能正常的回收內存的現象

(10)瀏覽器的垃圾回收機制

垃圾收集器必須跟蹤哪個變量有用哪個變量沒用,對於不再有用的變量打上標記,以備將來收回其佔用的內存,內存泄露和瀏覽器實現的垃圾回收機制息息相關, 而瀏覽器實現標識無用變量的策略主要有下兩個方法:

第一,引用計數法

跟蹤記錄每個值被引用的次數。當聲明一個變量並將引用類型的值賦給該變量時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變量,則該值的引用次 數加1.相反,如果包含對這個值引用的變量又取得另外一個值,則這個值的引用次數減1.當這個值的引用次數變成0時,則說明沒有辦法訪問這個值了,因此就 可以將其佔用的內存空間回收回來。

如: var a = {}; //對象{}的引用計數爲1
b = a; //對象{}的引用計數爲 1+1
a = null; //對象{}的引用計數爲2-1
所以這時對象{}不會被回收;

IE 6, 7 對DOM對象進行引用計數回收, 這樣簡單的垃圾回收機制,非常容易出現循環引用問題導致內存不能被回收, 進行導致內存泄露等問題,一般不用引用計數法。

第二,標記清除法

到2008年爲止,IE,Firefox,Opera,Chrome和Safari的javascript實現使用的都是標記清除式的垃圾收集策略(或類似的策略),只不過垃圾收集的時間間隔互有不同。

標記清除的算法分爲兩個階段,標記(mark)和清除(sweep). 第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。

(11)同源策略

同源策略是瀏覽器有一個很重要的概念。所謂同源是指,域名,協議,端口相同。不同源的客戶端腳本(javascript、ActionScript)在沒明確授權的情況下,不能讀寫對方的資源。簡單的來說,瀏覽器允許包含在頁面A的腳本訪問第二個頁面B的數據資源,這一切是建立在A和B頁面是同源的基礎上。

(12)跨域的幾種方式

jsonp(利用script標籤的跨域能力)跨域、websocket(html5的新特性,是一種新協議)跨域、設置代理服務器(由服務器替我們向不同源的服務器請求數據)、CORS(跨源資源共享,cross origin resource sharing)、iframe跨域、postMessage(包含iframe的頁面向iframe傳遞消息),document.domain跨域(比如:在一個文件中設置了document.domain="http://qq.com",那麼另一個設置了document.domain="http://qq.com"的,他們兩個就是同源)

(13)異步和同步

同步指下一個程序的執行需要等到上一個程序執行完畢,也就是得出結果後下一個才能執行,

異步指的是上一個程序指向後,下一個程序不用等到上一個程序出結果就能執行,等上一個出結果了調用回調函數處理結果就好。

(14)JavaScript的值類型和引用類型

JavaScript有兩種類型的數據,值類型和引用類型,一般的數字,字符串,布爾值都是值類型,存放在棧中,而對象,函數,數組等是引用類型,存放在堆中,對引用類型的複製其實是引用複製,相當於複製着地址,對象並沒有真正的複製。

var a=5;var b=a;a=null; //那麼b是5
var a={},var b=a;b.name="mbj";
console.log(a.name); //mbj,因爲a,b指向同一個對象
a=null;console.log(typeof b); //object,a=null,只是a不再指向該對象,但是這個對象還是在堆中確確實實的存在,b依然指向它。

(15)瀏覽器js解析引擎的兩個隊列

請看下面代碼

console.log(1); //(1)
setTimeout(function(){
 console.log(2); //(2)
},0)
console.log(3); //(3)
//輸出的結果是1,3,2

可能會有人疑問,爲什麼不是輸出1,2,3,這是因爲上面代碼在執行的時候被放到js解析引擎的同步隊列中,然後先執行語句(1),在把setTimeout的回調函數放到異步隊列,然後再執行語句(3),這樣同步隊列裏面就沒有代碼需要執行了,然後在執行異步隊列中的回調函數。

瀏覽器的js解析引擎在解析js代碼的時候,把代碼放入到兩個隊列,放入到同步隊列中的代碼會優先被執行,放入異步隊列中的代碼等同步隊列中的代碼被執行完了之後纔會執行。需要異步等待回調的代碼一般都是放到異步隊列中的。

如果能明白下面代碼,說明你對setTimeout,異步和同步隊列掌握的比較好了

for(var i=1;i<=4;i++){
    var time=setTimeout(function(i){
        clearTimeout(time);
 console.log(i);
    },1000,i);
}
//輸出結果1,2,3
//PS:setTimeout的第三個以及第三個後面的參數都是分別傳給setTimeout的回調函數的

(16)封裝cookie的添加,刪除,查詢方法

cookie是存儲在瀏覽器端的,可以用於存儲sessionID,也可以用於自動登陸,記住密碼等,但是在瀏覽器端並沒有官方的操作cookie的方法,下面我們來封裝一下:

CookieUtil={
 addCookie:function(key,value,options){
 var str=key+"="+escape(value);
 if(options.expires){
 var curr=new Date(); //options.expires的單位是小時
 curr.setTime(curr.getTime()+options.expires*3600*1000);
 options.expires=curr.toGMTString();
 }
 for(var k in options){ //有可能指定了cookie的path,cookie的domain
 str+=";"+k+"="+options[k];
 }
 document.cookie=str;
 },
 queryCookie:function(key){
 var cookies=document.cookie;
 //獲得瀏覽器端存儲的cookie,格式是key=value;key=value;key=value
 cookies+=";";
 var start=cookies.indexOf(key);
 if(start<=-1){ return null; } //說明不存在該cookie
 var end=cookies.indexOf(";",start);
 var value=cookies.slice(start+key.length+1,end);
 return unescape(value);
 },
 deleteCookie:function(key){
 var value=CookieUtil.queryCookie(key);
 if(value===null){return false;}
 CookieUtil.addCookie(key,value,{expires:0});//把過期時間設置爲0,瀏覽器會馬上自動幫我們刪除cookie
 }
}

(17)事件委託機制

事件委託指的是,不再事件的發生地設立監聽函數,而是在事件發生地的父元素或者祖先元素設置監聽器函數,這樣可以大大提高性能,因爲可以減少綁定事件的元素,比如:

<ul>
 <li></li>
 <li></li>
 <li></li>
</ul>

要給li元素綁定click事件,使用事件委託機制的話,就只需要給ul綁定click事件就行了,這樣就不需要給每個li'綁定click事件,減小內存佔用,提高效率,有興趣的童鞋可以去看看jQuery的live,bind,on,delegate函數的區別,這幾個函數就採用了事件委託機制。

三、其他部分

(1)http狀態碼

http狀態碼是表示服務器對請求的響應狀態,主要分爲以下幾個部分

1**:這類響應是臨時響應,只包含狀態行和某些可選的響應頭信息,並以空行結束

2**:表示請求成功,

3**:表示重定向

4**:表示客戶端錯誤

5**:表示服務器端錯誤

100(continue),客戶端應當繼續發送請求。這個臨時響應是用來通知客戶端它的部分請求已經被服務器接收

200(OK),表示請求成功,請求所希望的響應頭或數據體將隨此響應返回。

202(Accepted),服務器已接受請求,但尚未處理。

204(No-Content),服務器成功處理了請求,但不需要返回任何實體內容

205(Reset-Content),服務器成功處理了請求,且沒有返回任何內容。但是與204響應不同,返回此狀態碼的響應要求請求者重置文檔視圖。該響應主要是被用於接受用戶輸入後,立即重置表單,以便用戶能夠輕鬆地開始另一次輸入。

206(Partial-Content),服務器已經成功處理了部分 GET 請求。

301(Moved-Permanently),永久性重定向

302(Moved-Temporarily),暫時性重定向

304(Not-Modified),瀏覽器端緩存的資源依然有效

400(Bad-Reques),請求有誤,當前請求無法被服務器理解。

401(Unauthorized),當前請求需要用戶驗證。

403(Forbidden),服務器已經理解請求,但是拒絕執行它。

404(Not-Found),請求的資源沒有被找到

500(Interval Server Error),服務器內部錯誤

502(Bad GateWay),網關出錯

503(Service Unavailable),由於臨時的服務器維護或者過載,服務器當前無法處理請求。

504(Gateway Timeout),作爲網關或者代理工作的服務器嘗試執行請求時,未能及時從上游服務器(URI標識出的服務器,例如HTTP、FTP、LDAP)或者輔助服務器(例如DNS)收到響應。

(2)xss,csrf的概念以及防範方法

大公司如bat在面試的時候,web安全問題是必問的問題,所以一定要懂,要徹底理解xss和csrf的概念和防範方式,最好在項目中有用到對這兩種攻擊的防範,這樣會給你的面試加很多分。由xss和csrf涉及的東西比較多,我就不具體給出了,詳情請看XSS攻擊及防禦,CSRF攻擊

(3)CommonJs,AMD,CMD規範

對於前端模塊化來說,這三個規範是必須要了解的,詳情請看我的這篇文章CommonJS,AMD,CMD

(4)談談對前端模塊化的理解

前端模塊話就是把複雜的文件分成一個個獨立的模塊,比如js文件,分成獨立的模塊之後有利於代碼的重用和維護,但是這樣又會引來模塊與模塊之間的依賴問題,所以就有了CommonJS、AMD、CMD規範,最後出現了webpack,webpack就是前端模塊話的一種解決方案,基本上大公司都會使用webpack,想要詳細的學習webpack的話請看webpack簡明使用教程

(5)優雅降級和漸進增強

優雅降級指的是一開始就構建功能完好的網站,然後在慢慢兼容低版本的瀏覽器,使得各個瀏覽器之間的差異不要太大。

漸進增強是指在基本功能得到滿足的情況下,對支持新特性的瀏覽器使用新特性,帶給用戶更換的體驗。

優雅降級和漸進增強的出發點不同,前者是慢慢向下兼容,是向後看,後着是慢慢向上,增強功能,是向前看。

(6)前端優化(提高網頁的加載速度)

1、使用css sprites,可以有效的減少http請求數

2、使用緩存

3、壓縮js,css文件,減小文件體積

4、使用cdn,減小服務器負擔

5、懶加載圖片

6、預加載css,js文件

7、避免dom結構的深層次嵌套

8、給DOM元素添加樣式時,把樣式放到類中,直接給元素添加類,減少重構,迴流

自己是從事了五年的前端工程師,不少人私下問我,2019年前端該怎麼學,方法有沒有?

沒錯,年初我花了一個多月的時間整理出來的學習資料,希望能幫助那些想學習前端,卻又不知道怎麼開始學習的朋友。

如果你依然在編程的世界裏迷茫,不知道自己的未來規劃,可以加入web前端學習交流羣:731771211 裏面可以與大神一起交流並走出迷茫。新手可進羣免費領取學習資料,看看前輩們是如何在編程的世界裏傲然前行!羣裏不停更新最新的教程和學習方法(進羣送web前端系統學習路線,詳細的前端項目實戰教學視頻),有想學習web前端的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小夥伴歡迎加入

點擊:加入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章