Web 前端面試題整理(不定時更新)

重要知識須要系統學習、透徹學習,造成本身的知識鏈。萬不可投機取巧,臨時抱佛腳只求面試僥倖混過關是錯誤的!javascript

面試有幾點需注意:css

面試題目: 根據你的等級和職位的變化,入門級到專家級,廣度和深度都會有所增長。

題目類型: 理論知識、算法、項目細節、技術視野、開放性題、工做案例。

細節追問: 能夠確保問到你開始不懂或面試官開始不懂爲止,這樣能夠大大延展題目的區分度和深度,知道你的實際能力。由於這種知識關聯是長時期的學習,臨時抱佛腳絕對是記不住的。

回答問題再棒,面試官(多是你面試職位的直接領導),會考慮我要不要這我的作個人同事?因此態度很重要、除了能作事,還要會作人。

資深的前端開發能把absolute和relative弄混,這樣的人不要也罷,由於團隊須要的是:你這我的具備能夠依靠的才能(靠譜)。

前端開發知識點:html

HTML&CSS:
    對Web標準的理解、瀏覽器內核差別、兼容性、hack、CSS基本功:佈局、盒子模型、選擇器優先級、
    HTML五、CSS三、Flexbox

JavaScript:
    數據類型、運算、對象、Function、繼承、閉包、做用域、原型鏈、事件、RegExp、JSON、Ajax、
    DOM、BOM、內存泄漏、跨域、異步裝載、模板引擎、前端MVC、路由、模塊化、Canvas、ECMAScript 6、Nodejs

其餘:
    移動端、響應式、自動化構建、HTTP、離線存儲、WEB安全、優化、重構、團隊協做、可維護、易用性、SEO、UED、架構、職業生涯、快速學習能力

如下是面試題及其答案(這些知識點不要死記硬背,須要深刻理解):前端

 

【HTML + CSS 】java

1.對WEB標準以及W3C的理解與認識node

標籤閉合、標籤小寫、不亂嵌套,提升搜索機器人搜索概率;使用外鏈css和js腳本、結構行爲表現的分離、文件下載與頁面速度更快、內容能被更多的用戶所訪問、內容能被更普遍的設備所訪問、更少的代碼和組件,容易維 護、改版方便,不須要變更頁面內容、提供打印版本而不須要複製內容、提升網站易用性;

 

2.xhtml和html有什麼區別jquery

HTML是一種基本的WEB網頁設計語言,XHTML是一個基於XML的置標語言
最主要的不一樣:
XHTML 元素必須被正確地嵌套。
XHTML 元素必須被關閉。
標籤名必須用小寫字母。
XHTML 文檔必須擁有根元素。

 

3.Doctype? 嚴格模式與混雜模式-如何觸發這兩種模式,區分它們有何意義?git

歷史緣由:當早期的瀏覽器Netscape 4和Explorer 4對css進行解析時,並未遵照W3C標準,這時的解析方式就被咱們稱之爲quirks mode(怪異模式),但隨着W3C的標準愈來愈重要,衆多的瀏覽器開始依照W3C標準解析CSS,仿照W3C標準解析CSS的模式咱們叫作strict mode(嚴格模式) web

區別:嚴格模式是瀏覽器根據規範去顯示頁面;混雜模式是以一種向後兼容的方式去顯示面試

意義:決定瀏覽器如何渲染網站(瀏覽器使用那種規範去解析網頁)

觸發:瀏覽器根據doctype是否存在和使用的是那種dtd來決定。

所謂的標準模式(嚴格模式)是指,瀏覽器按W3C標準解析執行代碼;
怪異模式(混雜模式)則是使用瀏覽器本身的方式解析執行代碼,由於不一樣瀏覽器解析執行的方式不同,因此咱們稱之爲怪異模式。
瀏覽器解析時到底使用標準模式仍是怪異模式,與你網頁中的DTD聲明直接相關,DTD聲明定義了標準文檔的類型(標準模式解析)文檔類型,會使瀏覽器使用相應的方式加載網頁並顯示,忽略DTD聲明,將使網頁進入怪異模式(quirks mode)。 使用 window.top.document.compatMode 可顯示當前的模式,取值有
CSS1Compat 和 BackCompat

CSS1Compat 爲標準模式,瀏覽器寬度 = document.documentElement.clientWidth; 元素的寬度則是元素的實際寬度,內容寬度 = width - (padding-left + padding-right + border-left-width + border-right-width)
BackCompat 爲怪異模式,瀏覽器寬度 = document.body.clientWidth; 元素實際的寬度 = border-left-width + padding-left + width + padding-right + border-right-width;

咱們經過簡單代碼試驗下:

<div id="element"></div>
<style>
    #element{ width: 100px; height: 60px; padding: 10px;margin: 20px;border: 1px solid #9f1c24;}
</style>
<script>
    var compatMode = window.top.document.compatMode;
    var elem = document.getElementById("element");
    document.write("The compatMode is "+compatMode+".The Element's width is " + elem.offsetWidth + " px and it's height is "+elem.offsetHeight+" px");
</script>

IE6以上顯示(標準模式):

The compatMode is CSS1Compat.The Element's width is 122 px and it's height is 82 px 

IE5顯示(怪異模式):

The compatMode is BackCompat.The Element's width is 100 px and it's height is 60 px 

相關連接:嚴格模式與混雜模式-如何觸發這兩種模式,區分它們有何意義 || 深刻理解瀏覽器兼容性模式

 

4.行內元素有哪些?塊級元素有哪些?CSS的盒模型?

塊級元素:div p h1-h6 form ol ul li 
行內元素: a b br i img span lable input select textarea
CSS盒模型:內容,border ,margin,padding

補充:塊級元素和行內元素的區別

 

相關連接:行內元素與塊級元素比較全面的區別和轉換  

 

5.CSS引入的方式有哪些? link和@import的區別是?

 

引入的方式:內聯、內嵌、外鏈、導入。
區別 :
1.link屬於XHTML標籤,而@import徹底是CSS提供的一種方式。
link標籤除了能夠加載CSS外,還能夠作不少其它的事情,好比定義RSS,定義rel鏈接屬性,等,
@import就只能加載CSS了。

2.加載時間及順序不一樣
使用link連接的css是客戶端瀏覽你的網頁時先將外部的CSS文件加載到網頁當中,而後再進行編譯顯示,因此這種狀況下顯示出來的網頁跟咱們預期的效果同樣,即便一個頁面link多個css文件,網速再慢也是同樣的效果;
而使用@import導入的CSS就不一樣了,客戶端在瀏覽網頁時是先將html的結構呈現出來,再把外部的CSS文件加載到網頁當中,固然最終的效果也是跟前者是同樣的,只是當網速較慢時會出現先顯示沒有CSS統一佈局時的html網頁,這樣就會給閱讀者很很差的感受。這也是如今大部分網站的CSS都採用連接方式的最主要緣由。

3.兼容性不一樣
因爲@import是CSS2.1提出的因此老的瀏覽器不支持,
@import只有在IE5以上的才能識別,而link標籤無此問題。

補充1:

CSS的瀏覽器支持以下:

CSS1:

CSS2:

CSS3:

相關連接:CSS選擇器的瀏覽器支持

補充2:

CSS有三種引用方式:

a.行間樣式,也稱行內樣式

最簡單直接,直接對HTML標籤使用style="..."

<div style="width: 300px;height: 100px;"></div>

缺點:HTML頁面不純淨,文件體積太大,不利於蜘蛛爬行,後期維護不方便。

b.內部樣式,也稱內嵌樣式

將CSS代碼在寫在<head></head>之間,而且用<style></style>進行聲明

<style>
   #div { background-color: red;width: 300px;height: 100px;} </style>

缺點:使用公共CSS代碼時,每一個頁面都要定義。若是有不少頁面,那麼每一個文件都會很大,後期維護難度也會很大。

c.外部樣式,也稱連接樣式

只須要在在<head></head>之間經過link標籤引用CSS樣式表的連接就行。

<link rel="stylesheet" href="css/import-css-03.css">

優勢:實現了頁面框架代碼和表現CSS代碼的徹底分離,使得前期製做和後期維護都十分方便。

 

另外,還有一種導入CSS樣式的方法,採用@import導入CSS樣式。在HTML初始化時,會被導入到HTML或者CSS文件中,稱爲文件的一部分。

HTML中使用以下:

<style>
      @import "css/import-css-03.css";
</style>

CSS樣式表中使用以下:

@import "import-css-03.css";

不過不建議使用這種方法。

一直在HTML頁面使用@import是能夠的,可是你必須時刻記得要將@import 放到樣式表的最前面,不然它將不會起做用。

並且若是在HTML中混合 link方式和@import方式,會破壞並行下載,而去進行逐個加載,這樣會致使頁面須要更多的時間才能加載完成。

另外在link的樣式表裏嵌套@import,一樣會阻止並行加載代碼,這樣這是由於@import引用的文件只有在引用它的那個文件被下載、解析後,瀏覽器纔會知道還有另外一個CSS樣式表須要下載,而後纔去下載、構建 render tree等一系列操做。所以 css @import引發的CSS解析延遲會加長頁面留白期。因此,要儘可能避免使用CSS @import 而採用 link 標籤的方式引入CSS樣式表。

相關連接:不要使用@import (英文版:don’t use @import

 

6.css的position屬性有哪些取值,它們的行爲是什麼?

position屬性經常使用的取值static、relative、absolute、fixed
static:
元素框正常生成。塊級元素生成一個矩形框,做爲文檔流的一部分,行內元素則會建立一個或多個行框,置於其父元素中。
relative(相對定位):
元素框偏移某個距離。元素仍保持其未定位前的形狀,它本來所佔的空間仍保留。
absolute(絕對定位):
元素框從文檔流徹底刪除,並相對於其包含塊定位。
包含塊多是文檔中的另外一個元素或者是初始包含塊。元素原先在正常文檔流中所佔的空間會關閉,就好像元素原來不存在同樣。元素定位後生成一個塊級框,而不論原來它在正常流中生成何種類型的框。 fixed(固定定位): 元素框的表現相似於將 position 設置爲 absolute,不過其包含塊是視窗自己。
PS:fixed舊版本IE不支持;absolute的containing block計算方式跟正常流不一樣

相關連接:談談面試與面試題

 

7.CSS 中text-align:center在IE7和IE8下的區別

在IE7中,元素與文字都居中;

而在IE8下僅文字居中,元素並不居中

解決方法:能夠在其子元素中加入CSS屬性:margin:0 auto;

  

8.CSS選擇符有哪些?哪些屬性能夠繼承?優先級算法如何計算?內聯和important哪一個優先級高?

1.id選擇器( # myid)
2.類選擇器(.myclassname)
3.標籤選擇器(div, h1, p)
4.相鄰選擇器(h1 + p)
5.子選擇器(ul > li)
6.後代選擇器(li a)
7.通配符選擇器( *8.屬性選擇器(a[rel = "external"])
9.僞類選擇器(a:hover, li:nth-child)
visibility和cursor可以被全部元素繼承;
letter-spacing,word-spacing,white-space,line-height,colo,font,font-family,font-size,font-style,font-variant,font-weight,text-decoration,text-transform,direction可以被內聯元素繼承;
text-indent和text-align會被終端元素繼承;
list-style,list-style-type,list-style-posi-tion,list-style-image會被列表元素所繼承;
border-col-lapse會被表格元素所繼承
如下屬性是不可繼承的:border,height,display,margin,background,padding,min-height,overflow,max-height,width,min-width,max-width,position,right,top,left,bottom,float,z-index,clear,table-lay-out,ertical-align,nicode-bidi,age-break-after,age-break-before
每一個ID選擇符(#someid),加 0,1,0,0。
每一個class選擇符(.someclass)、每一個屬性選擇符(形如[attr=」"]等)、每一個僞類(形如:hover等)加0,0,1,0
每一個元素或僞元素(:firstchild)等,加0,0,0,1
其餘選擇符包括全局選擇符*,加0,0,0,0。至關於沒加,不過這也是一種specificity,後面會解釋。
按這些規則將數字串逐位相加,就獲得最終計算得的specificity,而後在比較取捨時按照從左到右的順序逐位比較。
!important 比內聯樣式的優先級高

相關連接:css優先級計算規則

 

9. img與background的區別

簡單來講,img是內容部分的東西,background-image是修飾性的東西。

img:從頁面元素來講,若是是頁面中的圖片是做爲內容出現的,好比廣告圖片,好比產品圖片,那麼這個必然是用img了,由於這個是頁面元素內容。頁面元素內容最關鍵的一點就是,當頁面沒有樣式的時候,仍是能一眼看過去就知道什麼是什麼
background
-image:背景圖片,修飾性的內容,在頁面中無關緊要。有,是爲了讓頁面中視覺感覺上更美;無,並不影響用戶瀏覽網頁獲取內容。 其實說白了,背景圖片就是經過樣式加載後,讓頁面更漂亮而已,內容圖片就是爲了展現給用戶的。假設有一天你的網頁沒有任何樣式的時候,那麼這個時候請想一想你的網站上哪些圖片是給用戶看的,這樣就足夠了。

 

10.前端頁面有哪三層構成,分別是什麼?做用是什麼?

網頁分紅三個層次,即:結構層、表示層、行爲層

網頁的結構層(structural layer)由 HTML 或 XHTML 之類的標記語言負責建立。
標籤,也就是那些出如今尖括號裏的單詞,對網頁內容的語義含義作出了描述,但這些標籤不包含任何關於如何顯示有關內容的信息。例如,P 標籤表達了這樣一種語義:「這是一個文本段。」 網頁的表示層(presentation layer) 由 CSS 負責建立。 CSS 對「如何顯示有關內容」的問題作出了回答。 網頁的行爲層(behavior layer)負責回答「內容應該如何對事件作出反應」這一問題。這是 Javascript 語言和 DOM 主宰的領域。

相關連接:http://www.cnblogs.com/hellman/p/4172220.html

 

11.css的基本語句構成是?

選擇器{屬性1:值1;屬性2:值2;……}

 

12.你作的頁面在哪些流覽器測試過?這些瀏覽器的內核分別是什麼?

IE(Trident)  火狐(Gecko) 谷歌(webkit) Opera(Presto)

相關連接:四種主要瀏覽器內核簡介(Trident/Gecko/webkit/Presto)

 

13.寫出幾種IE6 BUG的解決方法

a. IE6雙倍邊距bug

當頁面內有多個連續浮動時,如本頁的圖標列表是採用左浮動,此時設置li的左側margin值時,在最左側呈現雙倍狀況。如外邊距設置爲10px, 而左側則呈現出20px,解決它的方法是在浮動元素上加上display:inline;的樣式,這樣就可避免雙倍邊距bug。

b.IE6下這兩個層中間怎麼有間隙(3像素問題及解決辦法)

 

當使用float浮動容器後,在IE6下會產生3px的空隙,解決的辦法是給.right也一樣浮動 float:left 或者相對IE6定義.left margin-right:-3px;

c. 當子元素浮動且未知高度時,怎麼使父容器適應子元素的高度?

這種狀況可在父窗口加上 overflow:auto;zoom:1;這兩個樣式屬性,overflow:auto;是讓父容器來自適應內部容器的高度,zoom:1;是爲了兼容IE6而使用的CSS HACK。zoom:1;通不過W3C的驗證,這也是遺憾的一點,幸虧IE支持<!–[if IE]>這種寫法,能夠專門針對IE來寫單獨的樣式,因此能夠把這個屬性寫在頁面內的<!–[if IE]>中,這樣就能夠經過驗證了。

d. 超連接訪問事後hover樣式就不出現的問題

解決方法是改變CSS屬性的排列順序: L-V-H-A
a:link { }   a:visited { }   a:hover { }   a:active { }

e.IE6文字溢出BUG

修正註釋的寫法。將 <!-- 這裏是註釋內容 -->寫成<!--[if !IE]>這裏是註釋內容<![endif]-->

相關連接:IE6文字溢出bug解決辦法

f.一個空格引起CSS失效

這段代碼對<p>的首字符樣式定義在IE6上看是沒有效果的(IE7沒測試),而在p:first-letter和{font- size:300%}加上空格,也就是p:first-letter {font-size:300%}後,顯示就正常了。可是一樣的代碼,在FireFox下看是正常的。按道理說,p:first- letter{font-size:300%}的寫法是沒錯的。那麼問題出在哪裏呢?答案是僞類中的連字符」-」。IE有個BUG,在處理僞類時,若是僞 類的名稱中帶有連字符」-」,僞類名稱後面就得跟一個空格,否則樣式的定義就無效。而在FF中,加不加空格均可以正常處理。

g. IE6中奇數寬高的BUG

解決方案就是將外部相對定位的div寬度改爲偶數。

h.IE6下爲何圖片下方有空隙產生

解決這個BUG的方法也有不少,能夠是改變html的排版,或者定義img 爲display:block
或者定義vertical-align屬性值爲vertical-align:top | bottom |middle |text-bottom
還能夠設置父容器的字體大小爲零,font-size:0

i. ie6下空標籤高度問題

一個空div若是高度設置爲0到19px,IE6下高度默認始終19PX。
例如:
.c{background-color:#f00;height:0px;/*給定任何小於20px的高度 */}
<div></div>
若是不讓它默認爲19PX。而是0PX的話
解決方法有3種:
1.css裏面加上overflow:hidden;
2.div裏面加上註釋,<div><!– –></div>
3.css裏面加上line-height:0;而後div裏面加上&nbsp;,<div>&nbsp;</div>

j.修正重複文字bug

複雜的佈局能夠觸發在浮動元素的最後一些字符可能出如今出如今清除元素下面的bug。這裏有幾個解決方法,有些是完美的,可是作一些反覆試驗也是必須的:確保全部的元素使用」display:inline;」在最後一個元素上使用一個」margin-right:-3px;」# 爲浮動元素的最後一個條目使用一個條件註釋,好比:<!–[if !IE]>Put your commentary in here…<![endif]–>在容器的最後元素使用一個空的div(它也有必要設置寬度爲90%或相似寬度。)

 

14.標籤上title與alt屬性的區別是什麼? 

1.含義不一樣
alt是當圖片不存在時的替代文字;title是對圖片的描述與進一步說明

2.在瀏覽器中的表現不一樣
在firefox和ie8中,當鼠標通過圖片時title值會顯示,而alt的值不會顯示;只有在ie7如下版本中,若是沒有設置title,當鼠標通過圖片時alt的值會顯示,若是設置了,優先顯示title的值,即便是空值

對於網站seo優化來講,title與alt還有最重要的一點:
搜索引擎對圖片意思的判斷,主要靠alt屬性。因此在圖片alt屬性中以簡要文字說明,同時包含關鍵詞,也是頁面優化的一部分。條件容許的話,能夠在title屬性裏,進一步對圖片說明。

 

15.描述css reset的做用和用途

由於瀏覽器的品種不少,每一個瀏覽器的默認樣式也是不一樣的。經過從新定義標籤樣式。「覆蓋」瀏覽器的CSS默認屬性。
最簡單的就是 *{margin:0 ;  padding:0}

 

16.解釋css sprites,如何使用

CSS Sprites 將許太小的圖片組合在一塊兒,使用css定義背景屬性,再利用 CSS的"background-image","background-repeat","background-position"的組合來控制圖片的顯示位置和方式,background-position能夠用數字能精確的定位出背景圖片的位置。 

這樣就大大減小了HTTP請求的次數,減輕服務器壓力,同時縮短了懸停加載圖片所須要的時間延遲,使效果更流暢

相關連接:CSS Sprites: CSS圖片合併技術詳解

  

17.如何對網站的文件和資源進行優化?

1.儘量減小http請求數(文件合併) 
2.使用CDN(內容分發網絡) 
3.添加Expire/Cache-Control頭 
4.啓用Gzip壓縮 
5.css放在頁面最上面 
6.scrip放在頁面最下面 
7.避免在css中使用Expressions 
8.把js和css放在外部文件中 
9.減小dns查詢 
10.壓縮javascript和css 
11.避免重定向 
12.移除重複腳本 
13.配置實體標籤 
14.使用ajax緩存

 

18.什麼是語義化的HTML?

根據內容的結構化(內容語義化),選擇合式的標籤(代碼語義化),便於開發者的閱讀和寫出更加優雅的代碼的同時讓瀏覽器的爬蟲和機器更好地解析

 

19.清除浮動的幾種方式,各自的優缺點

1.使用空標籤清除浮動:.clear{clear:both}(會添加大量無語義標籤,結構與表現未分離,不利於維護)
2.父元素設置overflow:hidden(內容增多時容易形成不會自動換行的後果,致使內容被隱藏,沒法顯示須要一處的元素)
3.父元素設置overflow:auto (多個嵌套後,FF在某種狀況下會形成內容全選;IE在mouseover形成寬度改變時會形成最外層模塊出現滾動條)
4.父元素設置height(只適合高度固定的佈局,要給出精確的高度,不利於維護)
3.是用:afert僞元素清除浮動(用於低版本IE瀏覽器沒法識別)

相關連接:清除浮動的幾種方法

 

 

【JavaScript】

1.javascript的typeof返回哪些數據類型

Object number function boolean underfind symbol(ES6)

 

2.例舉3種強制類型轉換和2種隱式類型轉換?

強制(parseInt,parseFloat,Number)
隱式(== –)

1)咱們先來進行強制轉換操做:

<script>
    var strNum = '1.645';
    var strAnotherNum = "-1";
    var strAnotherNum2 = "2";
    var isTrue = true;

    //parseInt(參數1,參數2)將字符串轉換成整數
    console.log(parseInt(strNum)); // 1
    console.log(parseInt(strAnotherNum + strNum)); //  -11
    console.log(parseInt(strNum + strAnotherNum)); // 1

    //parseFloat()將字符串轉換成浮點數字
    console.log(parseFloat(strNum));//  1.645
    console.log(parseFloat(strAnotherNum + strNum));//  -11.645
    console.log(parseFloat(strNum + strAnotherNum));//  1.645
    console.log(parseFloat(strNum + strAnotherNum2));//  1.6452

    //Number() 函數把對象的值轉換爲數字
    console.log(Number(strNum));// 返回 1.645
    console.log(Number(strAnotherNum + strNum)); // -11.645
    console.log(Number(strNum + strAnotherNum)); // NaN
    console.log(Number(strNum + strAnotherNum2)); // 1.6452

    //string(參數):能夠將任何類型轉換成字符串
    console.log(String(isTrue)); // "true"
    console.log(String(strAnotherNum + strNum)); // "-11.645"
    console.log(String(strNum + strAnotherNum)); // "1.645-1"
    console.log(String(strNum + strAnotherNum2)); // "1.6452"

    // Boolean()能夠將任何類型的值轉換成布爾值
    console.log(Boolean(strNum)); //  true
    console.log(Boolean(strAnotherNum + strNum)); //  true
    console.log(Boolean(strNum + strAnotherNum)); //  true
</script>

2)咱們再來進行隱式轉換

<script>
    // (1) 四則運算
    // 加法運算符+是雙目運算符,只要其中一個是string類型,表達式的值即是一個String
    // 對於其餘的四則運算,只有其中一個是Number類型,表達式的即是一個Number
    // 對於非法字符的狀況一般會返回NaN:'1'*'a'    // => NaN,這是由於parseInt(a)值爲NaN,1*NaN仍是NaN
    var a = "11";
    var b = 11;
    console.log(a+b); // "1111"
    console.log(a-b); // 0
    console.log(a*b); // 121
    console.log(a/b); // 1

    //(2).判斷語句
    // 判斷語句中的判斷條件須要是 Boolean類型,因此條件表達式會被隱式轉換爲Boolean。其轉換規則則同Boolean的構造函數。
    console.log(a==b);// true
    console.log(a!==b); // true
    console.log(a===b); //  false

    //(3) Native代碼調用
    // JavaScript宿主環境都會提供大量的對象,它們每每很多經過JavaScript來實現的。
    // JavaScript給這些函數傳入的參數也會進行隱式轉換。
    // 例如BOM提供的alert方法接受String類型的參數:alert({a:1});  //=>[object Object]
    console.log({a:1}); // Object {a: 1}
</script>

補充:NaN這個特殊的Number與全部其餘值都不相等,包括它本身:

NaN === NaN; // false

惟一能判斷NaN的方法是經過isNaN()函數:

isNaN(NaN); // true

 

3.join() 和 split()的區別

join() 函數是把數組的全部元素放入一個字符串。元素經過指定的分隔符進行分隔。
split() 函數是把字符串分割爲字符串數組。

下面簡單試驗下:

<script>
    var arr = ["How","Are","You!"]
    var strArr = "1,2,3,4,5,6,7,8";
    console.log(arr.join()); // How,Are,You!
    console.log(arr.join('')); // HowAreYou!
    console.log(arr.join(' ')); // How Are You!
    console.log(strArr.split('')); // ["1", ",", "2", ",", "3", ",", "4", ",", "5", ",", "6", ",", "7", ",", "8"]
    console.log(strArr.split(' '));// ["1,2,3,4,5,6,7,8"]
    console.log(strArr.split(',')); // ["1", "2", "3", "4", "5", "6", "7", "8"]
</script>

 

4.數組方法pop()、push()、unshift()、shift()

pop()  -  刪除並返回數組的最後一個元素
push() - 向數組的末尾添加一個或更多元素,並返回新的長度
unshift() - 向數組的開頭添加一個或更多元素,並返回新的長度
shift() - 刪除並返回數組的第一個元素

一樣,咱們簡單試驗下:

<script>
    var arr = [1,2,3,4,5];
    console.log(arr.pop()); // 5
    console.log(arr.join(","));// 1,2,3,4
    console.log(arr.push("Hello","Luka")); // 6
    console.log(arr.join(",")); // 1,2,3,4,Hello,Luka
    console.log(arr.unshift("Hi","Luka")); // 8
    console.log(arr.join(",")); // Hi,Luka,1,2,3,4,Hello,Luka
    console.log(arr.shift()); // Hi
    console.log(arr.join(",")); // Luka,1,2,3,4,Hello,Luka
</script>

 

5.事件綁定和普通事件有什麼區別 

普通事件中的onclick是DOM 0級事件只支持單個事件,會被其餘onclick事件覆蓋
事件綁定中的addEventListener是DOM 2級事件能夠添加多個事件而不用擔憂被覆蓋

一樣,咱們簡單試驗下:

<input type="button" id="theButton" value="Click it"/>
<script>
    var btnClick = document.getElementById('theButton');
    // 用普通方法添加兩個事件
    btnClick.onclick = function () {
        console.log("The is the first click event!"); // 不執行,不顯示
    }
    btnClick.onclick = function () {
        console.log("The is the second click event!"); // 執行,會顯示
    }
    // 用事件綁定添加兩個事件
    btnClick.addEventListener('click',function () {
        console.log("The is the first click event listener!");// 執行,會顯示
    })
    btnClick.addEventListener('click',function () {
        console.log("The is the second click event listener!");// 執行,會顯示
    })
    // PS:這裏只使用W3C的標準寫法添加事件,沒有兼容低版本的IE。
</script>

  

 

6.IE和DOM事件流的區別

a.事件流執行順序的區別

IE採用冒泡型事件,Netscape使用捕獲型事件,DOM使用先捕獲後冒泡型事件.

示例:

<body> 
<div> 
<button>點擊這裏</button> 
</div> 
</body> 

冒泡型事件模型: button->div->body (IE事件流) 

捕獲型事件模型:body->div->button (Netscape事件流) 

DOM 事件模型:body->div->button->button->div->body (先捕獲後冒泡)

b.事件偵聽函數的區別

IE 使用:

target.attachEvent(event, listener); //綁定函數 
target.detachEvent(event, listener); //移除綁定  

target: 文檔節點、document、window 或 XMLHttpRequest

type: 字符串,事件名稱,含「on」,好比「onclick」、「onmouseover」、「onkeydown」等。

listener :實現了 EventListener 接口或者是 JavaScript 中的函數。

this 指向 window對象。

示例:

<input type="button" id="theButton" value="Click it"/>
<script>
    var btnClick = document.getElementById('theButton');
    // 用事件綁定添加兩個事件
    btnClick.attachEvent('onclick',function () {
        console.log("The is the first click event listener!");// 執行
    })
    btnClick.attachEvent('onclick',function () {
        console.log("The is the second click event listener!");// 執行
    })
    btnClick.attachEvent('onclick',LogThrid);
    btnClick.detachEvent('onclick',LogThrid);
    function LogThrid() {
        console.log("The is the third click event listener!");
    }
</script>

IE8及如下顯示:

The is the second click event listener!
The is the first click event listener!

IE9-IE10顯示:

The is the first click event listener!
The is the second click event listener!

IE11以上不支持此方法。 

DOM 使用:

target.addEventListener(type, listener, useCapture);  //綁定函數 
target.removeEventListener(type, listener, useCapture); //移除綁定 

target: 文檔節點、document、window 或 XMLHttpRequest。

type: 字符串,事件名稱,不含「on」,好比「click」、「mouseover」、「keydown」等。

listener :實現了 EventListener 接口或者是 JavaScript 中的函數。

useCapture :是否使用捕捉,通常用 false 。true爲捕獲階段,false爲冒泡階段。

this 觸發該事件的對象。

示例

<input type="button" id="theButton" value="Click it"/>
<script>
    var btnClick = document.getElementById('theButton');
    // 用普通方法添加兩個事件
    btnClick.onclick = function () {
        console.log("The is the first click event!"); // 不執行,不顯示
    }
    btnClick.onclick = function () {
        console.log("The is the second click event!"); // 執行,會顯示
    }
    // 用事件綁定添加兩個事件
    btnClick.addEventListener('click',function () {
        console.log("The is the first click event listener!");// 執行,會顯示
    })
    btnClick.addEventListener('click',function () {
        console.log("The is the second click event listener!");// 執行,會顯示
    })
    btnClick.addEventListener('click',LogThrid);
    btnClick.removeEventListener('click',LogThrid);
    function LogThrid() {
        console.log("The is the third click event listener!");
    }
    // PS:這裏只使用W3C的標準寫法添加事件,沒有兼容低版本的IE。
</script>

控制檯顯示:

The is the second click event!
The is the first click event listener!
The is the second click event listener!

簡單來講,區別有如下幾點:

事件流執行順序不同;
參數不同;
this 指向不同。

 

 7.IE和標準下有哪些兼容性的寫法

var ev = event || window.event;
var target = ev.srcElement||ev.target;
var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;

 

8.Ajax請求的時候 get 和 post 方式的區別

簡單區別說明以下:

1.Get請求會將數據添加到URL中,經過這種方式傳遞到服務器,一般利用一個問號?表明URL地址的結尾與數據參數的開端,後面的參數每個數據參數以「名稱=值」的形式出現,參數與參數之間利用一個鏈接符&來區分。
而Post請求是將數據在HTTP主體中的,其組織方式不僅一種,有&鏈接方式,也有分割符方式,可隱藏參數,傳遞大批數據,比較方便。
2.get傳送的數據量較小,不能大於2KB。post傳送的數據量較大,通常被默認爲不受限制。但理論上,因服務器的不一樣而異.
3.get安全性很是低,post安全性較高。
4.應用不一樣。通常咱們使用get進行簡單的數據查詢操做,好比經過產品ID查詢對應的產品信息;而使用post進行復雜的增刪查改數據操做,好比把產品添加到購物車。

咱們來舉個例子,能夠進行簡單的GET和POST操做,HTML代碼以下:

    <div>
        <label for="txt_username">姓名:</label>
        <input id="txt_username" type="text" />
        <input type="button" value="Get" id="btnGet" />
        <input type="button" value="Post" id="btnPost" />
    </div>
    <div id="result"></div>

而後是咱們的GET方法:

        document.getElementById('btnGet').onclick = handleGet;
        function handleGet() {
            //建立一個新的 XMLHttpRequest 對象
            var httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            var username = document.getElementById("txt_username").value;
            //給 onreadystatechange 事件設置一個事件處理器
            httpRequest.onreadystatechange = handleResponse;
            //添加參數,以求每次訪問不一樣的url,以免緩存問題
            var url = "/ajax/html4getpost.aspx?username=" + encodeURIComponent(username) + "&random=" + Math.random(); //使用 open 方法來指定 HTTP 方法和須要請求的 URL (即告訴 httpRequest 對象你想要作的事)
            httpRequest.open("GET", url);
            //這裏沒有向服務器發送任何數據,因此 send 方法無參數可用
            httpRequest.send();
        }

POST方法:

        document.getElementById('btnPost').onclick = handlePost;
        function handlePost() {
            var httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            var username = document.getElementById("txt_username").value;
            httpRequest.onreadystatechange = handleResponse;
            var data = "username=" + encodeURIComponent(username) + "&random=" + Math.random(); //不用擔憂緩存問題
            httpRequest.open("POST", "/ajax/html4getpost.aspx");
            //必須設置,不然服務器端收不到參數
            httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            //發送請求,要data數據
            httpRequest.send(data);
        }

公用的處理響應的方法:

        //處理響應
        //一旦腳本調用了 send 方法,瀏覽器就會在後臺發送請求到服務器。由於請求是在後臺處理的,因此Ajax 依靠事件來通知這個請求的進展狀況。
        function handleResponse(e) {
            //當 onreadystatechange 事件被觸發後,瀏覽器會把一個 Event 對象傳遞給指定的處理函數,target 屬性則會被設爲與此事件關聯的XMLHttpRequest
            if (e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200) { //請求成功
                document.getElementById("result").innerHTML = e.target.responseText; //顯示被請求文檔的內容
            }
        }

經過JavaScript代碼咱們能夠看到區別以下:

a.由於若是經過get方式獲取數據,若是Url和以前的一致,IE瀏覽器會從緩存中獲取數據,而不會去請求服務器端,因此get方法在IE瀏覽器下有緩存問題;而post方法不需擔憂這個問題。

b.上面post方法須要設置Content-Type的值爲application/x-www-form-urlencoded (窗體數據被編碼爲名稱/值對)。

c.發送請求時,由於get請求的參數都在URL裏,因此send函數沒有發送的參數,而post請求在使用send方法時須要賦予參數。

 

咱們看下客戶端中請求的 html4getpost.aspx 的代碼:

        protected void Page_Load(object sender, EventArgs e)
        {
            string username = string.Empty;
            if (Request.HttpMethod.ToUpper().Equals("GET"))
            {
                username = Request.QueryString["username"];
            }
            else {
                username = Request.Form["username"];
            }
            Response.Clear();
            string str = "姓名:"+username+"<br />時間:"+DateTime.Now.ToString();
            Response.Write(str);
            Response.End();
        }

這裏服務器端是經過ASP.NET實現的,能夠看到其區別:

在客戶端使用get請求,服務器端使用 Request.QueryString 來獲取參數,而post請求,服務器端使用 Request.Form 來獲取參數。

另外固然服務器端還可使用一個通用的獲取參數的方式,即 Request["username"]。可是此方法存在一個問題,它會對QueryString,Form,ServerVariable進行遍歷,若是發現符合要求的數據,那麼就會中止向後搜尋。

好比咱們簡單的修改下上面post方法:

httpRequest.open("POST", "/ajax/html4request.aspx?username=LukaChao");

若是使用Request["username"]方法,即便咱們在文本框中填寫任何其餘名稱,輸出的名稱也會是 LukaChao:

 

下面咱們簡單看看在network下get請求和post請求的數據:

GET請求:

POST請求:

a.從請求URL,咱們能夠看出 GET請求是帶着參數,而POST請求是不帶參數的。

b.另外POST的請求頭文件比GET請求多了三個參數,分部爲Content-Type、Content-Length 和 Origin

c.而若是請求數據是同樣的,那響應獲得的數據也是同樣的:

 

9.call和apply的區別

簡單來講,其區別主要就是傳遞參數的不徹底相同。

對於第一個參數意義都同樣,但對於第二個參數:apply傳入的是一個參數數組,也就是將多個參數合成爲一個數組傳入,而call則做爲call的參數傳入(從第二個參數開始)。

func.call(func1,var1,var2,var3) 對應的 apply寫法爲:func.apply(func1,[var1,var2,var3]);

因此當你的參數是明確知道數量時用call,而不肯定時用apply,而後把經過 push 方法把參數傳遞進數組。

相關連接:JS中call和apply區別

 

10. B繼承A的方法 

   function A(name,age) {
        this.name = name;
        this.age = age;
        this.showName = function () {
            console.log(this.name+"'s age is "+this.age+".")
        }
    }

    //1. 原型鏈(prototype)實現繼承
    function B1() { }
    B1.prototype = new A("Luka",27);
    var b1 = new B1();
    B1.prototype.zodiac = "Snake";
    B1.prototype.showZodiac = function () {
        console.log(this.name +"'s zodiac is "+this.zodiac +".")
    }
    b1.showName();
    b1.showZodiac();

    //2. 構造函數實現繼承
    function B2(name, age,starSign) {
        this.temp = A;
        this.temp(name,age);
        delete this.temp;
        this.name = name;
        this.age = age;
        this.starSign = starSign;
        this.showZodiac = function () {
            console.log(this.name+"'s star sign is "+this.starSign +".")
        }
    }
    var b2 = new B2("Luka",27,"Sagittarius");
    b2.showName();
    b2.showZodiac();

    //3. call , apply實現繼承

    //call與aplly的異同:
    //1,第一個參數this都同樣,指當前對象
    //2,第二個參數不同:call的是一個個的參數列表;apply的是一個數組(arguments也能夠)
    function B3(name,age) {
        A.call(this,name,age);
    }
    var b3 = new B3("Luka",27);
    b3.showName();

    function B4(name,age) {
        A.apply(this,[name,age]); // 等於 A.apply(this.arguments);
    }
    var b4 = new B3("Luka",27);
    b4.showName();

顯示效果:

相關連接:js中繼承的幾種用法總結(apply,call,prototype)

 

11.AJAX請求時,如何解釋 JSON 數據

eval();  //此方法不推薦,由於此方法解析時不會判斷字符串是否合法,並且JSON對象中的JS方法也會被執行,這是很是危險的。
JSON.parse();  //推薦方法

簡單舉個栗子:

<script>
    var jsonData1 = '{"name":"Luka","age":"18"}';
    var jsonObj11 = eval('('+jsonData1+')'); // eval() 方法
    console.log(jsonObj11.name+"'age is "+jsonObj11.age); // 輸出:Luka'age is 18
    var jsonObj12 = JSON.parse(jsonData1); // JSON.parse()方法
    console.log(jsonObj12.name+"'age is "+jsonObj12.age); // 輸出:Luka'age is 18

    var jsonData2 = '{"name":console.log("Hello,Luka!"),"age":"18"}';
    var jsonObj21 = eval('('+jsonData2+')');
    console.log(jsonObj21.name+"'age is "+jsonObj21.age); // 會先輸出:Hello,Luka!  而後輸出  undefined'age is 18
    var jsonObj22 = JSON.parse(jsonData2);
    console.log(jsonObj22.name+"'age is "+jsonObj22.age); // 直接報錯
</script>

相關連接:Ajax中解析Json的兩種方法詳解

 

12.寫一個獲取非行間樣式的函數

    function getStyle(obj, attr) { //獲取非行間樣式,obj是對象,attr是值
        if(obj.currentStyle){ //針對IE 獲取非行間樣式
            return obj.currentStyle[attr];
        }else {
            return getComputedStyle(obj,false)[attr];
        }
    }

示例以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>獲取非行間樣式的函數</title>
    <style>
        * { text-align: center;}
        input { margin-top: 30px;padding:10px 20px;}
        #div { width: 500px;height: 300px;background: red;margin: 10px auto;}
    </style>
</head>
<body>
<input type="button" value="Style" id="btn" />
<div id="div"></div>
<script>
    function getStyle(obj, attr) { //獲取非行間樣式,obj是對象,attr是值
        if(obj.currentStyle){ //針對IE 獲取非行間樣式
            return obj.currentStyle[attr];
        }else {
            return getComputedStyle(obj,false)[attr];
        }
    }

    function css(obj, attr, value) { //對象,樣式,值。傳兩個參數的時候爲獲取樣式,3個是設置樣式
        if(arguments.length == 2){ //arguments 參數數組。當參數數組長度爲2 時,表示獲取獲取css樣式
            return getStyle(obj,attr); // 經過getStyle 函數返回對象的非行間樣式
        }else if(arguments.length == 3){ //傳遞參數參數時,設置某個對象的值
            obj.style[attr] = value;
        }
    }

    window.onload = function () {
        var oDiv = document.getElementById("div");
        var oBtn = document.getElementById("btn");
        oBtn.onclick = function () {
            console.log("width:"+getStyle(oDiv,"width")); // width:500px
            css(oDiv,"width","300px");
            console.log("width:"+css(oDiv,"width")); // width:300px
        }
    }
</script>
</body>
</html>

補充:

下面看一個例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>獲取CSS的值</title>
    <style>
        #div1 { width: 200px;height: 80px;background: red;}
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2" style="width: 100px;height: 40px;background: blue;"></div>
<script>
    var objDiv1 = document.getElementById("div1");
    console.log("Div1's width:"+objDiv1.style.width); // Div1's width:

    var objDiv2 = document.getElementById("div2");
    console.log("Div2's width:"+objDiv2.style.width); // Div2's width:100px

    objDiv1.style.width = "250px";
    console.log("Div1's width:"+objDiv1.style.width); // Div1's width:250px
</script>
</body>
</html>

經過最終控制檯的顯示結果,咱們能夠發現用document.getElementById(‘element').style.xxx能夠獲取元素的樣式信息,但是它獲取的只是DOM元素style屬性裏的樣式規則,對於經過class屬性引用的外部樣式表,就拿不到咱們要的信息了。 而經過js來改變對象的樣式時,改變的則是對象的行間樣式。

可是咱們經過上面的方法就能夠獲取咱們想要的CSS數據:

<script>
    function getStyle(obj, attr) { //獲取非行間樣式,obj是對象,attr是值
        if(obj.currentStyle){ //針對IE 獲取非行間樣式
            return obj.currentStyle[attr];
        }else {
            return getComputedStyle(obj,false)[attr];
        }
    }
    var objDiv1 = document.getElementById("div1");
    console.log("Div1's width:"+getStyle(objDiv1,"width")); // Div1's width:200px
    var objDiv2 = document.getElementById("div2");
    console.log("Div2's width:"+getStyle(objDiv2,"width")); // Div2's width:100px
</script>

 

 

13.事件委託是什麼

事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。

通俗的講,事件就是onlick、onmouseover、onmouseout等;而委託就是讓別人來作,這個事件原本是加在某些元素上的,然而你能夠加到別人身上來作,完成這個事件。就像收快遞同樣,你沒必要本身親自去收,可讓別人代收。

這原理就是利用事件冒泡,把事件加到父級元素上,觸發執行效果。

咱們來舉個例子(每一個li元素均可以觸發改變其背景色):

<ul id="names">
    <li>劉備</li>
    <li>關羽</li>
    <li>張飛</li>
</ul>
<script>
    window.onload = function () {
        var oUl = document.getElementById("names");
        var aLi = oUl.getElementsByTagName("li");
        for(var i=0;i<aLi.length;i++){
            aLi[i].onmousemove = function () {
                this.style.backgroundColor = "red";
            }
            aLi[i].onmouseout = function () {
                this.style.backgroundColor = "";
            }
        }
    }
</script>

可是若是咱們可能有不少個li元素,而後使用for循環的話會比較影響性能。

下面咱們經過事件委託的方法來實現,HTML不變:

<script>
    window.onload = function () {
        var oUl = document.getElementById("names");/*
        * 這裏要用到事件源:event對象,事件源無論在哪一個事件中,只要你操做的元素就是事件源
        * IE: window.event.srcElement
        * 標準下:event.target
        * nodeName:元素的標籤名
        * */
        oUl.onmouseover = function (e) {
            var target = TargetGet(e);
            if(target.nodeName.toLowerCase() === "li"){
                target.style.backgroundColor = "red";
            }
        }
        oUl.onmouseout = function (e) {
            var target = TargetGet(e);
            if(target.nodeName.toLowerCase() === "li"){
                target.style.backgroundColor = "";
            }
        }

        function TargetGet(e) {
            // 兼容性的寫法,替代if...else...
            e = e||window.event;
            var target = e.target||e.srcElement;
            return target;
        }
    }
</script>

很明顯,這裏沒有經過for去循環添加事件,而是把事件委託給了其父元素ul,這樣大大提升了性能。

咱們再來舉個例子(在上面的基礎上,咱們添加一個按鈕,點擊按鈕,能夠動態添加li元素):

不用委託,咱們的代碼是這樣的:

<input type="button" id="btn" value="添加武將" />
<ul id="names">
    <li>劉備</li>
    <li>關羽</li>
    <li>張飛</li>
</ul>
<script>
    var oUl = document.getElementById("names");
    var oLi = oUl.getElementsByTagName("li");
    var oBtn = document.getElementById("btn");
    var index = 1;
    for(var i=0;i<oLi.length;i++){
        oLi[i].onmouseover = function () {
            this.style.backgroundColor = "red";
        }
        oLi[i].onmouseout = function () {
            this.style.backgroundColor ="";
        }
    }
    oBtn.onclick = function () {
        var oLi = document.createElement("li");
        oLi.innerText = "武將"+index;
        index++;
        oUl.appendChild(oLi);
    }
</script>

可是這樣,你會發現新添加的li元素並不能經過鼠標的移入移出改變其背景色。這是由於變色事件在添加li元素以前已經遍歷完了。

咱們再來經過事件委託來實現這個例子,HTML代碼同樣(咱們只須要在原先的基礎上添加li元素事件就好了,不須要改變事件委託事件):

<script>
    window.onload = function () {
        var oUl = document.getElementById("names");
        var oBtn = document.getElementById("btn");
        var index = 1;
        oUl.onmouseover = function (e) {
            var target = TargetGet(e);
            if(target.nodeName.toLowerCase() === "li"){
                target.style.backgroundColor = "red";
            }
        }
        oUl.onmouseout = function (e) {
            var target = TargetGet(e);
            if(target.nodeName.toLowerCase() === "li"){
                target.style.backgroundColor = "";
            }
        }

        oBtn.onclick = function () {
            var oLi = document.createElement("li");
            oLi.innerText = "武將"+index;
            index++;
            oUl.appendChild(oLi);
        }
        function TargetGet(e) {
            // 兼容性的寫法,替代if...else...
            e = e||window.event;
            var target = e.target||e.srcElement;
            return target;
        }
    }
</script>

你能夠看到即便咱們新添了li元素,可是咱們把事件委託給了其父元素ul,因此li元素的改變背景色的效果還在。

相關連接:js中的事件委託或是事件代理詳解

 

14.閉包是什麼,有什麼特性,對頁面有什麼影響

官方的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(一般是一個函數),於是這些變量也是該表達式的一部分。

通俗的說法是,函數定義和函數表達式位於另外一個函數的函數體內。並且這些內部函數能夠訪問它們所在的外部函數中聲明的全部局部變量、參數和聲明的其餘內部函數。當其中一個這樣的內部函數在包含它們的外部函數以外被調用時,就會造成閉包。也就是說,內部函數會在外部函數返回後被執行。而當這個內部函數執行時,它仍然必須訪問其外部函數的局部變量、參數以及其餘內部函數。這些局部變量、參數和函數聲明(最初時)的值是外部函數返回時的值,但也會受到內部函數的影響。

簡單來講,就是閉包可以讀取其餘函數內部變量的函數,即在外面能夠調用函數中的函數變量,其實它就是將函數內外鏈接起來的橋樑。

舉個例子:

<script>
    function a() {
        var i = 0;
        function b() {
            console.log(++i);
        }
        return b;
    }
    var c = a();
    c();
</script>

這段代碼有兩個特色:a.函數b嵌套在函數a內部;b.函數a返回函數b;

引用關係圖以下:

這樣在執行完 var c = a()  後,變量c 其實是指向了函數b,b中用到了變量i,在執行c() 後就會在控制檯顯示i的值(第一次爲1)。這段代碼其實就建立了一個閉包。由於函數a 外的變量c 引用了函數a 內的函數b,也就是說:

當函數a的內部函數b被函數a外的一個變量引用時,就建立了咱們一般所謂的「閉包」。

當函數b執行的時候也會像以上步驟同樣。所以,執行時b的做用域鏈包含了三個對象:b的活動對象、a的活動對象和window對象,以下圖所示:

如圖所示,當在函數b中訪問一個變量的時候,搜索的順序是:

a.先搜索自身的活動對象,若是存在則返回,若是不存在將繼續搜索函數a的活動對象,以此查找,直到找到爲止。

b.若是函數b存在 prototype 原型對象,則在查找完自身的活動對象後查找自身的原型對象,再繼續查找。這就是JavaScript中的變量查找機制。

c.若是整個做用域鏈上都沒法找到,則返回 underfined。

用途:

a.閉包能夠讀取函數內部變量

b.將函數內部變量的值始終保持在內存中

再舉個例子:

<script>
    function a() {
        var i = 1;
        iAdd = function () {
            i++;
        }
        function b() {
            console.log(i);
        }
        return b;
    }
    var c = a();
    c(); // 1
    iAdd();
    c(); // 2
</script>

此例中的c 實際上就是閉包函數b,它一共運行了兩次,第一次值爲1,第二次值爲2,這就說明i 一直在內存中,而不是第一次a函數調用以後就自動清除了。

另外注意 iAdd = function(){ i++; } ,這裏的iAdd 是全局變量,且它的值爲匿名函數,其實也是一個閉包。

優點:

a.保護函數內的變量安全,以最開始的例子爲例。函數a 中變量i 只有函數b才能訪問,而沒法經過其餘途徑訪問到,所以保護了i 的安全性。

b.在內存中一直維持一個變量。之前面的例子爲例,因爲閉包,函數a中的變量i一直存在與內存中,所以每次執行c(),都會給i 自增長1。

c.經過保護變量的安全實現了JS私有屬性和私有訪問(不能被外部訪問)。

特性:

a.封閉性:外界沒法訪問閉包內部的數據,若是在閉包內聲明變量,外界是沒法訪問的,除非閉包主動向外界提供訪問接口;

b.持久性:通常的函數,調用完畢後,系統自動註銷,而對於閉包來講,在外部函數被調用後,閉包結構依然保存着。系統中,閉包中的數據依然存在,從而實現對數據的持久利用。

優勢:

a.減小全局變量

b.減小傳遞函數的參數量

c.封裝

缺點:

使用閉包會佔有內存資源,過多的使用會致使內存溢出等。因此用時須要及時刪除變量或者少用。


相關連接:深刻理解JavaScript的閉包特性 如何給循環中的對象添加事件 

補充 - 變量的做用域:

當JS當中的一個變量的做用域(scope)是程序中定義這個變量的區域。變量分爲兩類:全局(global)變量和局部變量。其中全局變量的做用域是全局性的,即在JavaScript代碼中,它到處有定義。

而在函數以內聲明的變量,就只有在函數體內有定義。它們是局部變量,做用域是局部性的。函數的參數也是局部參數,它們只在函數體內部有定義。

咱們能夠藉助JavaScript的做用域鏈(scope chain)更好地瞭解變量的做用域。每一個JavaScript執行環境都有一個和它關聯在一塊兒的做用域。這個做用域是一個對象列表或對象鏈。當JavaScript代碼須要查詢變量x(以下圖)的值時(這個過程叫作變量解析( variable name resolution)),它就開始查看該鏈的第一個對象。若是那個對象有一個名爲x的函數,那麼就採用那個屬性的值。若是第一個對象沒有名爲x的屬性,JavaScript就會繼續查詢鏈中的第二個對象。若是第二個對象仍然沒有名爲x的屬性,那麼就繼續查詢下一個對象,以此類推。若是查到最後(指頂層代碼中)不存在這個屬性,那麼這個變量的值就是未定義的。

咱們來舉個簡單的例子(例子1):

//定義全局變量
var i = 1;
function one(){
  console.log(i);
}
one();

很明顯這是個全局變量,最後控制檯會顯示1

再來看一個例子(例子2):

function two(){
  var i = 1;
}
console.log(i);

這裏i是個局部變量,外部不能引用。因此會報錯。

咱們在看一個例子(例子3):

var i = 1;
function one(){
   console.log(i);  
   var i = 2;
}
one();

乍一看,你也許會以爲結果會顯示1,但結果實際上是 undefined。

做用域鏈圖中很明確的表示出:

在變量解析過程當中,首先查找局部的做用域,而後再查找上層做用域。在例子1中的函數中沒有定義變量i,因此查找上層做用域(全局做用域),進而進行輸出其值。可是在例子2中的函數內定義了變量i (不管在after以後仍是以前定義變量,都認爲在此做用域擁有變量i),因而再也不向上層的做用域進行查找,直接輸出i。然而在例子3中,函數內部依然定義了變量i,因而不會查找全局做用域,然而此時的局部變量沒有賦值,因此輸出是 undefined。

 

15.如何阻止事件冒泡和默認事件

在使JavaScript編程時會遇到一個問題,就是當你給HTML添加事件時,因爲瀏覽器默認的冒泡型事件觸發機制,因此會觸發你不想觸發的事件,那麼經過下面的函數能夠解決這個問題:

a.阻止事件冒泡,使成爲捕獲型事件觸發機制:

function stopBubble(e) {
      // 若是提供了事件對象,則這是個非IE瀏覽器
      if(e && e.stopPropagation){
          // 所以它支持W3C的stopPropagation()方法
          e.stopPropagation();
      }else {
          // 不然,咱們須要使用IE的方式來取消事件冒泡
            window.event.cancelBubble = true;
      }
 }

b.當按鍵後,不但願按鍵繼續傳遞給如HTML文本框對象時,能夠取消返回值.即中止默認事件默認行爲. 

// 阻止瀏覽器的默認行爲
  function stopDefault(e) {
      // 阻止默認瀏覽器動做(W3C)
      if(e && e.preventDefault){
          e.preventDefault();
      }else {
          //IE 中阻止函數器默認動做的方式
          window.event.returnValue = false;
      }
      return false;
 }

咱們來經過下面的例子看下效果:

<div id="c1">測試的文字,這裏是樣式C1,單擊以冒泡的形式觸發事件.</div><hr/>
<div id="c2">測試的文字,這裏是樣式C2,單擊以捕獲的形式觸發事件.</div><hr/>
<div><input id="txt1" name="txt1" type="text" /></div><hr/>
<script>
    window.onload = function () {
        var txt = document.getElementById("txt1");
        document.getElementById("c1").onclick = function () {
            console.log("你點擊了第一個div");
        }
        document.getElementById("c2").onclick = function (e) {
            console.log("你點擊了第二個div");
            stopBubble(e);
        }
        document.onclick = function () {
            console.log("你點擊了document");
        }
        txt.value = "123";
        txt.onclick = function (e) {
            stopBubble(e);
        }
        txt.onkeydown = function (e) {
            stopDefault(e);
            var lKeyCode = (navigator.appname=="Netscape")?event.which:event.keyCode;
            console.log("你按下了鍵值:"+ lKeyCode);
        }
    };
    function stopBubble(e) {
        // 若是提供了事件對象,則這是個非IE瀏覽器
        if(e && e.stopPropagation){
            // 所以它支持W3C的stopPropagation()方法
            e.stopPropagation();
        }else {
            // 不然,咱們須要使用IE的方式來取消事件冒泡
            window.event.cancelBubble = true;
        }
    }
    function stopDefault(e) {
        // 阻止默認瀏覽器動做(W3C)
        if(e && e.preventDefault){
            e.preventDefault();
        }else {
            //IE 中阻止函數器默認動做的方式
            window.event.returnValue = false;
        }
        return false;
    }
</script>

咱們點擊第一個div文本,會發現控制檯會顯示:

你點擊了第一個div
你點擊了document

而咱們點擊第二個div文本時,控制檯會顯示:

你點擊了第二個div

這是由於 stopBubble()方法阻止了事件冒泡。

而後,咱們點擊文本框,而後輸入點擊數字鍵1,咱們會發現,控制檯會顯示:

你按下了鍵值:49

而且文本框內仍是原來的文本"123",沒有改變。這是由於 stopDefault 函數阻止了瀏覽器的默認動做。

相關連接:HTML DOM Event 對象

 

16.添加、刪除、替換、插入到某個接點的方法

element.appendChild(); //向元素添加新的子節點,做爲最後一個子節點
element.removeChild(); //從元素中移除子節點
element.replaceChild(); //替換元素中的子節點
element.insertBefore(); //指定的已有的子節點以前插入新節點

a.添加節點

<input type="button" id="btnAdd" value="添加武將" />
<ul id="names">
    <li>劉備</li>
    <li>關羽</li>
    <li>張飛</li>
</ul>
<script>
    var index = 1;
    document.getElementById("btnAdd").onclick = function () {
        // appendChild() 方法向節點添加最後一個子節點
        var node = document.createElement("li");
        var textnode = document.createTextNode("武將"+index++);
        node.appendChild(textnode);
        document.getElementById("names").appendChild(node);
    }
</script>

您也可使用 appendChild() 方法從一個元素向另外一個元素中移動元素,示例以下:

<ul id="names1">
    <li>劉備</li>
    <li>關羽</li>
    <li>張飛</li>
</ul>
<ul id="names2">
    <li>諸葛亮</li>
    <li>龐統</li>
</ul>
<p>請點擊按鈕把文臣從第二個列表移動到第一個列表中。</p>
<input type="button" id="btnAdd" value="移動" />
<script>
        document.getElementById("btnAdd").onclick = function () {
            // appendChild() 方法向節點添加最後一個子節點
            var eleFrom = document.getElementById("names2");
            var node = eleFrom.firstElementChild||eleFrom.firstChild;
            document.getElementById("names1").appendChild(node);
        }
</script>

b.刪除節點

<input type="button" id="btnDelete" value="刪除" />
<ul id="names">
    <li>劉備</li>
    <li>關羽</li>
    <li>張飛</li>
</ul>
<script>
    document.getElementById("btnDelete").onclick = function () {
        var names = document.getElementById("names");
        var node = names.firstElementChild||names.firstChild;
        // removeChild() 方法指定元素的某個指定的子節點。
        // 以 Node 對象返回被刪除的節點,若是節點不存在則返回 null。
        names.removeChild(node);
    }
</script>

c.替換節點

<input type="button" id="btnUpdate" value="替換主公" />
<ul id="names"><li>劉備</li><li>關羽</li><li>張飛</li>
</ul>
<script>
    document.getElementById("btnUpdate").onclick = function () {
        // 首先建立一個新的文本節點
        var textNode = document.createTextNode("曹操");
        // 而後替換首個列表項中的首個子節點
        var item = document.getElementById("names").childNodes[0];
        // node.replaceChild(newnode,oldnode) 方法用新節點替換某個子節點
        // 這個新節點能夠是文檔中某個已存在的節點,或者您也可建立新的節點
        item.replaceChild(textNode,item.childNodes[0]);
    }
</script>

d.插入節點

<input type="button" id="btnDelete" value="插入主公" />
<ul id="names">
    <li>關羽</li>
    <li>張飛</li>
</ul>
<script>
    document.getElementById("btnDelete").onclick = function () {
        // 首先請建立一個 LI 節點
        var newItem = document.createElement("li");
        // 而後建立一個文本節點
        var textNode = document.createTextNode("劉備");
        // 而後向這個 LI 節點追加文本節點
        newItem.appendChild(textNode);
        // 最後在列表中的首個子節點以前插入此 LI 節點
        var names = document.getElementById("names");
        // insertBefore() 方法在您指定的已有子節點以前插入新的子節點。
        names.insertBefore(newItem,names.childNodes[0]);
    }
</script>

您也可使用 insertBefore 方法插入/移動已有元素

<input type="button" id="btnDelete" value="插入主公" />
<ul id="names"><li>劉備</li><li>關羽</li><li>張飛</li></ul>
<script>
    document.getElementById("btnDelete").onclick = function () {
        var names = document.getElementById("names");
        var node= names.lastChild;
        var list=document.getElementById("names");
        list.insertBefore(node,list.childNodes[0]);
    }
</script>

 

17.解釋jsonp的原理,以及爲何不是真正的ajax

原理:

靜態資源是不受域策略限制的,能夠加載任意域的腳本、樣式、圖片等靜態資源,JSOP就是利用加載<script>標籤沒有跨域限制,來實現跨域獲取數據的。

區別:

1、ajax和jsonp這兩種技術在調用方式上「看起來」很像,目的也同樣,都是請求一個url,而後把服務器返回的數據進行處理,所以jquery和ext等框架都把jsonp做爲ajax的一種形式進行了封裝;
二、但ajax和jsonp其實本質上是不一樣的東西。ajax的核心是經過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標籤來調用服務器提供的js腳本。
3、因此說,其實ajax與jsonp的區別不在因而否跨域,ajax經過服務端代理同樣能夠實現跨域,jsonp自己也不排斥同域的數據的獲取。
四、還有就是,jsonp是一種方式或者說非強制性協議,如同ajax同樣,它也不必定非要用json格式來傳遞數據,若是你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。

簡單函數封裝以下:

function getJSONP(url) {
    var oScript = document.createElement('script');
    oScript.setAttribute('type', 'text/javascript');
    oScript.src = url;
    document.body.appendChild(oScript);
     oScript.onload = function () {   //  請求成功後移除標籤
    console.log('請求成功');
    oScript.remove();
    }
    oScript.onerror = function () {   //  失敗的時候也要移除標籤
     console.log('請求錯誤, 請重試');
     oScript.remove();
    };
};

相關連接:簡單描述JSON跟JSONP的區別以及實戰

 

18.Javascript的本地對象,內置對象和宿主對象 

簡單來講,
本地對象爲 Array,Object,RegExp等能夠經過 New 實例化的類;
內置對象爲Global 和Math 這兩個沒必要明確實例化的對象,它們已經被實例化了;
宿主對象爲瀏覽器自帶的document,window等對象,全部BOM 和DOM 對象都是宿主對象。

相關連接:ECMAScript 對象類型

 

19.document.load 和document.ready的區別

1.load是當頁面全部資源所有加載完成後(包括DOM文檔樹,css文件,js文件,圖片資源等),執行一個函數
問題:若是圖片資源較多,加載時間較長,onload後等待執行的函數須要等待較長時間,因此一些效果可能受到影響
2.$(document).ready()是當DOM文檔樹加載完成後執行一個函數 (不包含圖片,css等)因此會比load較快執行
在原生的jS中不包括ready()這個方法,只有load方法就是onload事件

 

20.」==」和「===」的不一樣

前者會自動轉換類型
後者不會

 

21.Javascript的同源策略

同源策略,即擁有相同的協議(protocol),端口(若是指定),主機(域名)的兩個頁面是屬於同一個源。
頁面只能在同一個源中進行資源的交互。

相關連接:JavaScript 的同源策略

 

22.編寫一個數組去重的方法

// 方法一(常規方法)
// 思路:
// 1.構建一個新的數組存放結果
// 2.for循環中每次從原數組中取出一個元素,用這個元素循環與結果數組對比
// 3.若結果數組中沒有該元素,則存到結果數組中
Array.prototype.UniqArray1 = function () {
    var res = [this[0]];
    for(var i=1;i<this.length;i++){
        var repeat = false;
        for(var j=0;j<res.length;j++){
            if(this[i] === res[j]){
                repeat = true;
                break;
            }
        }
        if(!repeat){
            // 向數組的末尾添加一個或多個元素,並返回新的長度
            res.push(this[i]);
            // 要想數組的開頭添加一個或多個元素,請使用 unshift() 方法
        }
    }
    return res;
}

// 方法二(比方法一效率要高)
//  思路:
// 1.先將原數組進行排序
// 2.檢查原數組中的第i個元素與結果數組中的最後一個元素是否相同,由於已經排序,因此重複元素會在相鄰位置
// 3.若是不相同,則將該元素存入結果數組中
// 此方法也有必定的侷限性,由於在去重前進行了排序,因此最後返回的去重結果也是排序後的。若是要求不改變數組的順序去重,那這種方法便不可取了。
Array.prototype.UniqArray2 = function () {
    this.sort(); //對數組的元素進行排序
    var res = [this[0]];
    for(var i=0;i<this.length;i++){
        if(this[i] !== res[res.length-1]){
            res.push(this[i]);
        }
    }
    return res;
}

// 方法三(推薦使用)
// 思路:
// 1.建立一個新的數組存放結果
// 2.建立一個空對象
// 3.for循環時,每次取出一個元素與對象進行對比,若是這個元素不重複,則把它存放到結果數組中,
// 同時把這個元素的內容做爲對象的一個屬性,並賦值爲1,存入到第2步創建的對象中。
// 說明:至於如何對比,就是每次從原數組中取出一個元素,而後到對象中去訪問這個屬性,若是能訪問到值,則說明重複。
Array.prototype.UniqArray3 = function () {
    var res = [];
    var obj = {};
    for(var i=0;i<this.length;i++){
        if(!obj[this[i]]){
            res.push(this[i]);
            obj[this[i]] = 1;
        }
    }
    return res;
}

 咱們來運行下:

<script>
    var array1 = [1,2,8,6,4,7,6,12,8];
    console.log(array1.UniqArray1());
    var array2 = [1,2,"Hello",6,4,2,6,"Hello",8];
    console.log(array2.UniqArray2());
    var array3 = ["Hello","Luka","Nice to meet you","Luka","Hi","Luka"];
    console.log(array3.UniqArray3());
</script>

運行結果:

 

23.JavaScript中const、var和let區別

(1)const定義的變量不能夠修改,並且必須初始化

const a = 2;
//const b;//會報語法錯誤 Uncaught SyntaxError: Missing initializer in const declaration
console.log('a的值爲:'+a); //會輸出:a的值爲:2
//a = 5; //會報類型錯誤:Uncaught TypeError: Assignment to constant variable.

(2) var定義的變量能夠修改,若是不初始化會輸出undefined,不會報錯

var b = 2;
var c;
console.log("b的值爲:"+b+",c的值爲:"+c);//b的值爲:2,c的值爲:undefined
function changeValue1(){
 var b = 6;
 c = 9;
 console.log("b的值爲:"+b+",c的值爲:"+c);//b的值爲:6,c的值爲:9
}
changeValue1();
console.log("b的值爲:"+b+",c的值爲:"+c);//b的值爲:2,c的值爲:9

(3) let是塊級做用域,函數內部使用let定義後,對函數外部無影響。

var d = 3;var e = 3;
console.log("d的值爲:"+d+",e的值爲:"+e);//d的值爲:3,d的值爲:3
function changeValue2(){
 var d = 6;
 e = 9;
 console.log("d的值爲:"+d+",e的值爲:"+e);//d的值爲:6,d的值爲:9
}
changeValue2();
console.log("d的值爲:"+d+",e的值爲:"+e);//d的值爲:3,d的值爲:9

PS:

(1) let必定在嚴格模式下執行!

var varText1 = "varText OK.";
let letText1 = "letText OK.";
console.log(varText1);//varText OK.
console.log(letText1);//letText OK.

console.log(varText2);//undefined
console.log(letText2);//Uncaught ReferenceError: letText2 is not defined
var varText2 = "varText OK.";
let letText2 = "letText OK.";

(2) 聲明後未賦值,表現相同;

var varText3;
let letText3;
console.log(varText3);//undefined
console.log(letText3);//undefined

(3) 重複聲明同一個變量let報錯,而 var 不會;

let letText4 = "letText OK.";
let letText4 = "letText changed.";// 直接報錯 Uncaught SyntaxError: Identifier 'letText4' has already been declared
var varText4 = "varText OK.";
var varText4 = "varText changed.";
console.log(varText4);//varText changed.

 

24.Math.round(),Math.ceil(),Math.floor()的區別

(1) Math.round():把數四捨五入爲最接近的整數。

根據「round」的字面意思「附近、周圍」,能夠猜想該函數是求一個附近的整數,看下面幾個例子就明白。

小數點後第一位<5
正數:Math.round(11.46)=11
負數:Math.round(-11.46)=-11

小數點後第一位>5
正數:Math.round(11.68)=12
負數:Math.round(-11.68)=-12

小數點後第一位=5
正數:Math.round(11.5)=12
負數:Math.round(-11.5)=-11

總結:(小數點後第一位)大於五所有加,等於五正數加,小於五全不加。

(2) Math.ceil():對數進行上舍入。

根據「ceil」的字面意思「天花板」去理解;

例如:
Math.ceil(11.46)= Math.ceil(11.68)= Math.ceil(11.5)=12
Math.ceil(-11.46)= Math.ceil(-11.68)= Math.ceil(-11.5)=-11

(3) Math.floor():對數進行下舍入。

根據「floor」的字面意思「地板」去理解;

例如:
Math.ceil(11.46)= Math.ceil(11.68)= Math.ceil(11.5)= 11
Math.ceil(-11.46)= Math.ceil(-11.68)= Math.ceil(-11.5)= -12

 

 

(待續...)

相關文章
相關標籤/搜索