前端面試題
進程和線程的區別?
2.同一個進程可以包括多個線程,並且線程共享整個進程的資源(寄存器、堆棧、上下文),一個進程至少包括一個線程)
死鎖是什麽?產生的原因?必要條件?怎麽處理?
1.相互等待資源所產生的一種僵持狀態,如果沒有外力幹預將一直保持這個狀態。
2.系統資源不足、相互爭搶資源、資源請求順序不當
3.互斥、不可搶占、循環等待、請求與保持
4.互斥是不可改變的,所以只能破壞其他三個條件中的一個來解除死鎖,方法:剝奪資源、殺死其中一個線程
什麽是HTML語義化?
根據內容使代碼結構化,選擇合適的標簽能使代碼語義化,便於開發者閱讀和寫出更優雅的代碼,同時也讓瀏覽器的爬蟲和機器更好地解析
為什麽要語義化?
-
為了在沒有CSS的情況下也能呈現出很好的內容結構
-
用戶體驗更好,如title解釋名詞信息,alt解釋圖片信息
-
有利於SEO(search engine optimization):和搜索引擎建立良好的溝通,有利於爬蟲抓取更多信息:爬蟲依賴標簽來確定上下文和各個關鍵字的權重
-
方便其他設備解析(屏幕閱讀器、盲人閱讀器、移動設備)
-
便於團隊開發維護
寫代碼時要註意什麽?
-
盡可能減少無語義標簽的使用,如div和span
-
語義不明顯時,如果可以用div或p,盡量用p,它默認有上下間距,對兼容特殊終端有利
-
不要用純樣式標簽,如b,font,u等
-
需要強調的文本,可以包含在strong(加粗)或者em(斜體)中
-
使用表格時,標題用caption,表頭用thead,主體部分用tbody包圍,尾部用tfoot包圍。表頭和一般單元格要區分開,表頭用th,單元格用td
-
表單域要用fieldset標簽包起來,並用legend標簽說明表單的用途
-
每個input標簽對應的文本都用label標簽,並通過為input設置id屬性,在label中設置for=someid讓說明文本和對應的input關聯起來
HTML5新增的語義標簽
header
footer
hgroup
nav
aside
section
article
行內元素有哪些?
a b(粗體) span img input strong select label em button textarea
塊級元素有哪些?
div ul li dl(定義列表) dt(列表中的項目) dd(描述列表中的項目) p h1-h6 blockquote
空元素有哪些?
br meta hr(水平線分隔) link input img
inline、block、inline-block的區別?
inline:和其他元素都在一行上,高、行高及頂和底邊距不可變,寬度就是其文字或圖片的寬度,不可改變
block:總在新行上開始,高度,行高,及頂和底邊距都可變,寬度缺省是它容器的100%,除非設定一個寬度
inline-block:將對象呈現為內聯對象,但是對象的內容作為塊對象呈現,塊對象在一行內呈現,允許空格,可以設置塊元素寬度和高度
inline和inline-block之間產生空隙的原因
因為標簽段之間的空格
移除空格的方法
-
去掉HTML中的空格
-
使用margin負值,這個負值的大小與上下文的字體和文字大小相關,大約在3-4像素之間
-
只在最後一個a標簽後加上</a>(兼容IE),在HTML5直接不加結束標簽
-
使用font-size:0
-
使用letter-spacing(字符間距)、word-spacing(單詞間距)
position的取值
static:默認值,對象遵循常規流
relative:對象遵循常規流,並參照自身在常規流中通過top、right、bottom、left這4個定位偏移屬性進行偏移時不會影響常規流中的任何元素
absolute:對象脫離常規流,此時偏移屬性參照的是離自身最近的定位祖先元素,如果沒有定位的祖先元素,則一直回溯到body元素。盒子的偏移位置不影響常規流中的任何元素,其中margin不與其他任何margin折疊
fixed:與absolute一致,但偏移定位是以窗口為參考。當出現滾動條時,對象不會隨著滾動
center:與absolute一致,但偏移定位是以定位祖先元素的中心點為參考,盒子在其包含容器內水平居中(CSS3)
清除浮動的方法?
1.父級div定義height
-
原理:父級div手動定義height,解決了父級div無法自動取得高度的問題
-
優點:簡單,代碼少
-
缺點:只適合高度固定的布局,要給出精確的高度
2.在結尾加一個空div標簽,style="clear:both"
-
原理:添加一個空div,利用css的clear:both清除浮動,讓父級div能自動獲取高度
-
優點:簡單,代碼少,瀏覽器支持好
-
缺點:如果頁面浮動布局過多,就要增加很多空div,不建議使用
3.父級div定義,偽類:after和zoom(IE專有屬性,解決IE6,7浮動問題)
father:after{display:block;clear:both;content:"";visibility:hidden;height:0}
father{zoom:1}
-
優點:瀏覽器支持好
-
缺點:代碼多
4.父級div定義 overflow:hidden
-
原理:必須定義width,同時不能定義height,使用overflow:hidden時,瀏覽器會自動檢查浮動區域的高度
-
優點:簡單代碼少,瀏覽器支持好
-
缺點:不能和position配合使用,因為超出的尺寸會被隱藏
5.父級定義 overflow:auto
-
原理同上
-
優點:簡單,瀏覽器支持好
-
缺點:內部寬高超過父級div時,會出現滾動條
BFC
定義:(Block formatting context)塊級格式化上下文,是一個獨立的渲染區域,只有Block-level box參與,它規定了內部的Block-level Box如何布局,並且與這個區域外部毫不相幹
BFC布局規則:
-
內部的Box會在垂直方向,一個接一個地放置
-
box垂直方向的距離有margin決定。屬於同一個BFC的兩個相鄰的box的margin會發生重疊
-
每個元素的margin box的左邊,與包含快border box的左邊相接觸,即使存在浮動也是如此
-
BFC的區域不會與float box重疊
-
BFC就是一個獨立的隔離的容器,容器裏面的子元素不會影響到外面的元素,反之也如此
-
計算BFC高度時,浮動元素也參與運算
哪些元素會產生BFC:
-
根元素
-
float屬性不為none
-
position為absolute或者fixed
-
display屬性為inline-block、table-cell、table-caption、flex、inline-flex
-
overflow不為visible
盒模型
盒模型分為IE盒模型和W3C標準盒模型
1.W3C盒模型:width,height只包含content,不包含border和padding
2.IE盒模型:width,height包含border和padding,指的是content+padding+border
在IE8+的瀏覽器中可以設置box-sizing控制,默認值為content-box,如果設為border-box則用的是IE盒模型
Transform/transition/animation
transform:一個靜態屬性,一旦寫到style裏面會直接顯示作用,無任何變化過程
transform/animation:這兩個屬性實現的功能都是一樣的,通過控制屬性變化的過程,實現動畫,都是立足於控制本身dom和css屬性變化過程,來實現動畫的視覺效果;區別在於,兩者的控制力度不一樣,transition更加粗糙一點,對過渡的速度進行了封裝,可以控制是勻速改變還是貝塞爾曲線等,而animation指定的keyframe方法,可以手動去指定每個階段的屬性,此外還有循環次數、動畫延遲等功能,更加自由強大
padding/margin使用百分比單位
這種情況下都是根據最近的父級容器的寬度width進行計算的
使用百分比單位的目的:彌補元素高度無法自適應地與元素寬度保持一致的缺陷
css元素權重
-
內聯樣式表的權值最高 1000。
-
ID
選擇器的權值為 100。 -
Class
類選擇器的權值為 10。 -
HTML
標簽(類型)選擇器的權值為 1。 -
屬性選擇器的權重為10
-
通配符權重為0
-
偽類選擇器權重為10
-
偽元素選擇器權重為1
JavaScript數據類型
一共7種:字符串、數字、布爾值、數組、對象、Null、Undefined
this的指向問題
this:在執行上下文創建時確定的一個在執行過程中不可更改的變量
this只在函數調用階段確定,也就是執行上下文創建的階段進行賦值,保存在變量對象中,所以也導致了this的多變性,函數在不同的調用方式下都可能導致this的值不同
但是this的指向在嚴格模式下和非嚴格模式下有所不同。當函數獨立調用時,嚴格模式下this指向undefined,非嚴格模式下,當this指向undefined時,自動指向全局對象(瀏覽器中就是window)
當一個對象在全局聲明時,對象內部屬性中的this指向全局對象,當對象在一個函數中聲明時,嚴格模式下this會指向undefined,非嚴格模式下轉為指向全局對象
函數的調用方式:
-
在全局環境或是普通函數中直接調用
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function (){ 5 function fun(){ 6 return this.a 7 } 8 console.log(fun()); 9 } 10 } 11 obj.b();//1
-
作為對象的方法
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function() { 5 return this.a 6 } 7 } 8 console.log(obj.b())//2
b所引用的匿名函數作為obj的一個方法調用,這時候this指向調用它的對象,這裏也就是obj
那麽不作為對象的方法調用是什麽意思呢?
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function(){ 5 return this.a; 6 } 7 } 8 var t = obj.b; 9 console.log(t());//1
這裏的t應該理解為指向b屬性的指針
-
使用apply或者call
-
作為構造函數
構造函數:通過new關鍵字批量生產我們需要的對象的函數,如FUNCTION、object、Array、Date等全局定義的構造函數,註意,構造函數首字母要大寫
1 function Category(){ 2 this.name = ‘annika‘; 3 this.school = ‘college‘; 4 this.height = ‘160‘; 5 this.run = function(){ 6 return this.name + ‘正在跑步‘; 7 } 8 } 9 Fun.prototype = { 10 constructor:Fun, 11 say: function(){ 12 return this.name + ‘正在說話‘; 13 } 14 } 15 var f = new Fun(); 16 f.run(); //annika正在跑步 17 f.say(); //annika正在說話
如果函數作為構造函數用,那麽其中的this就代表它即將new出來的對象
new做了如下的事:
-
創建一個臨時對象
-
給臨時對象綁定原型
-
給臨時對象對應的屬性復制
-
將臨時對象返回
箭頭函數:
箭頭函數不可以用call和apply改變this
1 var a = 1; 2 var obj = { 3 a:2 4 }; 5 var fun = () => console.log(this.a); 6 fun(); //1 7 fun.call(obj); //1 8 var a = 1; 9 var obj={ 10 a:2 11 }; 12 function fun(){ 13 var a = 3; 14 let f = () => console.log(this.a); 15 fun(); 16 } 17 fun();//1 18 fun.call(obj);//2
註意,此時fun的上下文就是箭頭函數所在的上下文,因此此時的f的this為fun的this,也就是window,當fun.call(obj)再次調用時,新的上下文被創建,fun此時的this為obj,也就是箭頭函數的this值
1 function Fun(){ 2 this.name = ‘annika‘; 3 } 4 Fun.prototype.say = () => { 5 console.log(this); 6 } 7 var f = new Fun(); 8 f.say(); //window
此時箭頭函數所在的上下文是__proto__所在的上下文也就是Object函數的上下文,而object的this值就是全局對象
1 function Fun(){ 2 this.name = ‘annika‘ 3 this.say = () => { 4 console.log(this); 5 } 6 } 7 var f = new Fun(); 8 f.say(); //Fun的實例對象
此時箭頭函數所在的上下文變成了Fun的上下文環境,因為當函數作為構造函數調用的時候,上下文環境的this指向實例對象
作用域和作用域鏈
變量的作用域分為兩種:全局變量和局部變量
全局作用域:最外層定義的變量擁有全局作用域,對於其內部函數來說,都是可以訪問的
局部作用域:一般只在固定的代碼片段內可以訪問到,對於函數外部是無法訪問的,最常見的例如函數內部
作用域鏈:內部函數可以訪問外部函數變量的機制,用鏈式查找決定哪些數據能被內部函數訪問。
執行環境(execution context):每個函數運行時都會產生一個執行環境js為每一個執行環境關聯了一個變量對象。環境中定義的所有變量和函數都保存在這個對象中。
全局執行環是最外圍的執行環境,被認為是window對象,因此所有的全局變量和函數都是作為window的屬性和方法創建的。
js的執行順序是根據函數的調用來決定的,當一個函數被調用時,該函數環境的變量對象就被壓入一個環境棧中,而在函數執行之後,棧將該函數的變量對象彈出,把控制權交給之前的執行環境變量對象
當某個函數第一次被調用時,就會創建一個執行環境,及相應的作用域鏈,並把作用域鏈賦值給一個特殊的內部屬性[scope]。然後用this,arguments和其他的命名參數的值來初始化函數的活動對象。當前執行環境的變量對象始終在作用域鏈的第0位。
標識符解析是沿著作用域鏈一級一級地搜索標識符的過程。搜索過程始終從作用域鏈的前端開始,然後逐級向後回溯,直到找到標識符為止
閉包
定義:一種內部函數的作用域鏈依然保持著對父函數活動對象的引用的函數
作用:
-
可以讀取自身函數外部的變量(沿著作用域鏈查找)
-
可以讓這些外部變量始終保存在內存之中
缺點:
1.由於閉包會使得函數中的變量都保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄漏,解決方法是,在退出函數之前,將不使用的局部變臉全部刪除
2.閉包會在父函數外部,改變父函數內部變量的值。
原型與原型鏈
函數的原型對象:在js中,我們在聲明一個函數的時候,瀏覽器的內存中會創建一個對象B,而且而且每個函數都默認會有一個屬性prototype指向了這個對象,這個B就是函數A的原型對象,簡稱函數的原型。這個原型對象B默認會有一個屬性constructor指向這個函數A。原型對象默認只有屬性:constructor。其他都是從Object繼承而來
使用構造函數創建對象:把一個函數作為構造函數,使用new創建對象的時候,那麽這個對象就會存在一個默認的不可見的屬性,來指向了構造函數的原型對象。這個不可見的屬性一般用[[prototype]]來表示,只是這個屬性沒有辦法直接訪問到
與原型有關的幾個屬性和方法:
1.constructor屬性:存在於原型對象中,它指向構造函數。我們有時候可以根據prototype屬性,來修改原來的原型對象,但是經過修改,原型對象會失去對原來的constructor的指針,此時可以手動再綁定回去
2.__proto__屬性:個別瀏覽器提供了對[[prototype]]的訪問方法,就是這個屬性,這個是對象的屬性,但是盡量不要用這種方法訪問,他有可能改變這個對象的繼承原型鏈
3.hasOwnProperty()方法
用來判斷一個屬性是來自對象本身,還是來源於其所在的原型鏈。註意只能判斷是不是來自對象,不能判斷原型中存不存在,因為不管存不存在都會返回false
4.in操作符
用來判斷一個屬性是否存在於這個對象中。但是在查找這個屬性的時候,先在對象自身中找,如果對象找不到,再到原型中去找。只要對象和原型中有一個地方存在這個屬性,就返回true
原型創建對象的缺陷:因為原型中的所有屬性都是共享的,所以如果對一個對象的原型的屬性做了修改,會反應到所有的對象上面。這個共享的形式對屬性值是函數的屬性很方便
構造函數模型創建對象的缺陷:構造函數中的所有屬性和方法,每個對象獨有,不會共享,但是對方法又不太合適,因為有時只需要一個方法
組合模式解決缺陷:基於以上兩臺產生了組合模式,原型模式適合封裝方法,構造函數模式適合封裝屬性,綜合兩種模式的優點就有了組合模式
動態原型模式創建對象:組合模式有一點不完美,就是把構造方法和原型分開寫,讓人感覺不舒服,所以應該想辦法把構造方法和原型封裝在一起,所以有了動態原型模式。動態原型模式把所有的屬性和方法都封裝在構造方法中,僅在需要的時候才去構造方法中初始化原型,又保持了原型和構造函數的優點。
1 function(name,age){ 2 this.name = name; 3 this.age = age; 4 if(typeof this.eat !== "function"){ 5 Person.prototype.eat = function(){ 6 return(this.name + ‘eating‘) 7 } 8 } 9 } 10 var p1 = new Person(‘annika‘,21); 11 p1.eat();
宏任務與微任務
-
宏任務按順序執行,且瀏覽器在每個宏任務之間渲染頁面
-
所有的微任務也按順序執行,且在以下場景會立即執行所有微任務
-
每個回調之後,且js執行棧中為空
-
每個宏任務結束之後
-
跨域問題
-
為什麽會出現跨域問題
-
處於瀏覽器的同源策略限制,瀏覽器會拒絕跨域請求,但不是所有的跨域都會被拒絕,有如下三種情況:
-
通常瀏覽器允許跨域寫操作(cross-site writes),如鏈接、重定向
-
通常允許跨域資源嵌入(cross-site embedding),如img,script
-
通常瀏覽器不允許跨域讀操作(cross-site reads)
-
-
-
什麽情況算作跨域
-
非同源請求,同源——兩個頁面擁有相同的協議(protocol),端口(port)和主機(host),那麽這兩個頁面就屬於同一個源(origin)
-
-
為什麽有跨域需求
-
工程服務化以後,不同職責額服務分散在不同的工程中,往往這些工程的域名是不同的,一個需求可能對應多個服務,這是就需要使用不同的服務接口,因此會出現跨域
-
-
如何實現跨域
-
最常用的跨域方式有三種:JSONP、CORS、postMessage
-
JSONP
-
實現原理:雖然不能通過XHR請求不同域上的數據,但是在頁面上引入不同域的js腳本是可以的,在js文件載入完畢後,觸發回調,可以將需要的data作為參數傳入
-
實現方式(需前後端配合)
1 <script type="text/javascript"> 2 function dosomething(data){ 3 //處理獲得的數據 4 } 5 </script> 6 <script src="http://example.com/data.php?callback=dosomething"></script> 7 <?php 8 $callback = $_GET[‘callback‘];//得到回調函數名 9 $data = array(‘a‘,‘b‘,‘c‘);//要返回的數據 10 echo $callback.‘(‘.json_encode($data).‘)‘;//輸出 11 ?>
-
優缺點
優點:兼容性好
缺點:1.JSONP只支持GET請求;2.XMLHTTPRequest相對於JSONP有更好地錯誤處理機制
-
-
CORS(cross-origin resource sharing )跨域源資源共享
-
基本思想:使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功還是失敗。
-
IE實現
1 var xdr = new XDomainRequest(); 2 xdr.onload = function(){ 3 alert(xdr.responseText); 4 } 5 xdr.open("get","http://www.somewhere-else.com/page/"); 6 xdr.send(null);
-
其他瀏覽器:原生支持CORS
1 var xhr = createXHR(); 2 xhr.onreadyStatechange = function(){ 3 if(xhr.readyState == 4){ 4 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 5 alert(xhr.responseText); 6 } else{ 7 alert("Request was unsuccessful:" + xhr.status); 8 } 9 } 10 }; 11 xhr.open("get","http://www.somewhere-else.com/page/",true); 12 xhr.send(null);
-
-
postMessage
-
window.postMessage(message,targetOrigin)方法是html5新引進的特性,可以用它來向其他window對象發送信息,無論同源不同源
1 otherWindow.postMessage(message,targetOrigin,[transfer]);
-
-
前端攻擊
-
XSS攻擊
深拷貝和淺拷貝
-
主要區別:在內存中的儲存類型不同
棧是自動分配的內存空間,它由系統自動釋放,而堆是動態分配的內存,大小不定,也不會自動釋放
-
基本數據類型(boolean/number/string/undefined/null)存放在棧中,是直接按值存放的,可以直接訪問,且基本數據類型的值不可變,基本類型的比較是值得比較,只要值相等就認為其相等
-
引用類型(object)存放在堆中,變量實際上是一個存放在棧內存的指針,這個指針指向堆內存中的地址。每個空間大小不一樣,要根據情況進行特定的分配。引用類型的值可變,引用類型的比較是引用的比較,看其引用是否指向同一個對象
-
傳值與傳址:基本數據類型的賦值是在內存中新開辟一段棧內存,然後再把值賦到新的棧中,所以兩個變量相互獨立不影響;引用類型的賦值是傳址,只是改變指針的指向,也就是說引用類型的賦值是對象保存在棧中的地址的賦值,兩個變量指向同一個對象,因此兩者之間操作相互有影響
-
淺拷貝只復制一層對象的屬性,並不包括對象裏面的為引用類型的數據。
-
深拷貝是對對象及所有的子對象進行拷貝
ajax的過程
0:未初始化,尚未調用open方法
1:啟動,已經調用open方法,但尚未調用send方法
2:發送,已經調用send方法,但是尚未收到響應
3:接收,已經收到部分響應數據
4:完成,已經接收到全部響應數據,可以在客戶端使用了
1 //創建XMLHttpRequest對象 2 var xmlhttp = null; 3 if(window.XMLHttpRequest) 4 { 5 xmlhttp = new XMLHttpRequest(); 6 } 7 else if(window.ActiveXObject) 8 { 9 xmlhttp = new ActiveXobject("Microsoft.XMLHTTP"); 10 } 11 ? 12 if(xmlhttp != null) 13 { 14 //指定響應函數 15 xmlhttp.onreadyStatechange=State_change; 16 //打開連接(指定請求) 17 xmlhttp.open("GET","/example/note.xml",true); 18 //發送請求 19 xmlhttp.send(null) 20 } 21 ? 22 //創建響應函數 23 function State_change() 24 { 25 if(xmlhttp.readyState == 4) 26 { 27 if(xmlhttp.status == 200) 28 { 29 //具體邏輯 30 } 31 } 32 else 33 { 34 alert("error"); 35 } 36 }
es6新特性
-
let和const
-
變量的解構賦值
-
箭頭函數
-
Set和Map結構
-
Iterator和for..of循環
-
Generator函數
-
promise對象
-
三個狀態(resolve/reject/pending)
-
async函數
-
Genarator函數的語法糖
-
優點在於內置執行器,擁有更好地語義和更廣的適用性
-
-
-
class
事件捕獲和事件冒泡
事件冒泡:先觸發子元素的處理器,再觸發父元素的事件處理器,每向上走一層都會檢查這一層有沒有事件處理器,如果有就觸發,如果沒有就繼續向上查找,直到頂層body
事件捕獲:從body向下找,如果父級元素有事件處理器就先觸發父級元素,再向下一層,直到這個點擊位置的最底層,也就是target
DOM2級事件流總共分為三個階段:事件捕獲、在目標元素上、事件冒泡
兩個方法:addEventListener()和removeEventListener(),接受三個參數:事件名稱、事件處理器,和布爾值,布爾值默認為false,代表事件處理器在冒泡階段觸發,true代表在捕獲階段觸發
Dom0級事件只能在冒泡階段觸發
如果要一次性註冊多個事件:
-
對於非target節點先執行捕獲再執行冒泡
-
對於target節點先執行先註冊事件
處理事件代理時,event有兩個特殊屬性,event.target和event.currentTargret,前者是事件觸發元素,後者是事件綁定元素,大部分情況下,在使用事件代理時,event.target是子元素,event.currentTarget是父元素
前端面試題