web前端面試寶典-基礎篇(二)

2.5 Ajax是什么? 有什么作用?

Ajax這個東西,很多童鞋在學習的時候都很茫然,聽起來很高大上的趕腳,而且也看不懂的樣子。其實則不然,Ajax很簡單,AJAX 就是一個與服務器交換數(shù)據(jù)并更新部分網(wǎng)頁的技術,并且是在不重新加載整個頁面的情況下。如果您學過計算機網(wǎng)絡基礎的話,或者學過TCP/IP協(xié)議的話,網(wǎng)絡通訊對于你來說輕而易舉,分分鐘搞定(因為http協(xié)議就是在TCP的基礎上寫的應用級協(xié)議)。
先將Ajax原生代碼貼出來,使用了的XMLHttpRequest接口(目前應該都使用了xmlhttprequest level 2的接口了吧)

         window.onload = function() {
            function myajax (method, url, st, success, fail) {
                // 1.創(chuàng)建一個Ajax的對象
                var ajax = new XMLHttpRequest();
                // 2.鏈接服務器
                ajax.open(method, url, st);

                // 3.發(fā)送請求
                ajax.send();

                // 4. 請求回調(diào)函數(shù)(當服務器給出響應時執(zhí)行)
                ajax.onreadystatechange = function() {
                    if(ajax.readyState == 4) {
                        if(ajax.status == 200) {
                            success(ajax.responseText);
                        }else if(ajax.status == 404) {
                            fail("請求文件不存在");
                        }
                    }
                }
            }
            myajax('GET', 'a.txt', true, function(str) {
                console.log(eval(str));
            },function(str) {
                
            });
        }

這個就是原生js的Ajax的請求,這里要說明一下的是Ajax的請求的方式有很多,目前只學習POST和GET這兩種方式來完成。至于兩者區(qū)別,你們自行研究(So easy),下面咱們來說一下HTTP的返回狀態(tài)碼的總結。

2.6 HTTP返回狀態(tài)碼總結

狀態(tài)碼        |        描述
------------- | -------------
1xx           |   請求收到,繼續(xù)處理
2xx           |   操作成功收到,分析、接受
3xx           |   完成此請求必須進一步處理
4xx           |   請求包含一個錯誤語法或不能完成
5xx           |   服務器執(zhí)行一個完全有效請求失敗

1xx的狀態(tài)碼

狀態(tài)碼        |        描述
------------- | -------------
100(繼續(xù))     |  請求者應當繼續(xù)提出請求。 服務器返回此代碼表示已收到請求的第一部分,正在等待其余部分。
101(切換協(xié)議)|  請求者已要求服務器切換協(xié)議,服務器已確認并準備切換。

2xx的狀態(tài)碼

狀態(tài)碼        |        描述
------------- | -------------
200(成功)       | 請求者應當繼續(xù)提出請求。 服務器返回此代碼表示已收到請求的第一部分,正在等待其余部分。
201(已創(chuàng)建)   |  請求成功并且服務器創(chuàng)建了新的資源。
202(已接受)   |  服務器已接受請求,但尚未處理。
203(非授權信息)| 服務器已成功處理了請求,但返回的信息可能來自另一來源。
204(無內(nèi)容)   |  服務器成功處理了請求,但沒有返回任何內(nèi)容。
205(重置內(nèi)容) |  服務器成功處理了請求,但沒有返回任何內(nèi)容。 與 204 響應不同,此響應要求請求者重置文檔視圖(例如,清除表單內(nèi)容以輸入新內(nèi)容)。
206(部分內(nèi)容) |  服務器成功處理了部分 GET 請求。

3xx的狀態(tài)碼

狀態(tài)碼        |        描述
------------- | -------------
300(多種選擇)    |  針對請求,服務器可執(zhí)行多種操作。 服務器可根據(jù)請求者(用戶代理)選擇一項操作,或提供操作列表供請求者選擇。
301(永久移動)    |  請求的網(wǎng)頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉(zhuǎn)到新位置。 您應使用此代碼告訴 Googlebot 某個網(wǎng)頁或網(wǎng)站已永久移動到新位置。
302(暫時移動)    |  服 務器目前從不同位置的網(wǎng)頁響應請求,但請求者應繼續(xù)使用原有位置來進行以后的請求。 此代碼與響應 GET 或 HEAD 請求的 301 代碼類似,會自動將請求者轉(zhuǎn)到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個網(wǎng)頁或網(wǎng)站已經(jīng)移動,因為 Googlebot 會繼續(xù)抓取原有位置并編入索引。
303(查看其他位置) |  請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。 對于除 HEAD 之外的所有請求,服務器會自動轉(zhuǎn)到其他位置。
304(未修改)      |  自從上次請求后,請求的網(wǎng)頁未修改過。服務器返回此響應時,不會返回網(wǎng)頁內(nèi)容。如果網(wǎng)頁自請求者上次請求后再也沒有更改 過,您應當將服務器配置為返回此響應(稱為 If-Modified-Since HTTP 標頭)。 由于服務器可以告訴 Googlebot 自從上次抓取后網(wǎng)頁沒有更改過,因此可節(jié)省帶寬和開銷。
305(使用代理)    |  請求者只能使用代理訪問請求的網(wǎng)頁。 如果服務器返回此響應,還表示請求者應使用代理。
307(暫時重定向)  |  服務器目前從不同位置的網(wǎng)頁響應請求,但請求者應繼續(xù)使用原有位置來進行以后的請求。 此代碼與響應 GET 和 HEAD 請求的 301 代碼類似,會自動將請求者轉(zhuǎn)到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個頁面或網(wǎng)站已經(jīng)移動,因為 Googlebot 會繼續(xù)抓取原有位置并編入索引。

4xx狀態(tài)碼

狀態(tài)碼        |        描述
------------- | -------------
400(錯誤請求)     | 服務器不理解請求的語法。
401(未授權)       |  請求要求身份驗證。 對于需要登錄的網(wǎng)頁,服務器可能返回此響應。
403(禁止)         |  服務器拒絕請求。 如果您看到 Googlebot 在嘗試抓取您網(wǎng)站上的有效網(wǎng)頁時收到此狀態(tài)代碼(可以在 Google 網(wǎng)站管理員工具診 斷 下的網(wǎng)絡抓取 頁面上看到此信息),可能是您的服務器或主機拒絕 Googlebot 訪問。
404(未找到)       | 服務器找不到請求的網(wǎng)頁。 例如,如果請求服務器上不存在的網(wǎng)頁,服務器通常會返回此代碼。
405(禁用的方法)   |  禁用請求中指定的方法。
406(不可接受)     |  無法使用請求的內(nèi)容特性響應請求的網(wǎng)頁。
407(需要代理授權) |  此狀態(tài)代碼與 401(未授權)類似,但指定請求者應當授權使用代理。 如果服務器返回此響應,還會指明請求者應當使用的代理。
408(請求超時)     | 服務器等候請求時發(fā)生超時。
409(沖突)         | 服務器在完成請求時發(fā)生沖突。 服務器必須在響應中包含有關沖突的信息。 服務器在響應與前一個請求相沖突的 PUT 請求時可能會返回此代碼,同時會附上兩個請求的差異列表。
410(已刪除)       | 如果請求的資源已永久刪除,服務器就會返回此響應。 該代碼與 404(未找到)代碼相似,但在資源以前存在而現(xiàn)在不存在的情況下,有時會用來替代 404 代碼。 如果資源已永久刪除,您應當使用 301 指定資源的新位置。
411(需要有效長度)  | 服務器不接受不含有效內(nèi)容長度標頭字段的請求。
412(未滿足前提條件)| 服務器未滿足請求者在請求中設置的其中一個前提條件。
413(請求實體過大)  | 服務器無法處理請求,因為請求實體過大,超出服務器的處理能力。
414(請求的 URI 過長)   | 請求的 URI(通常為網(wǎng)址)過長,服務器無法處理。
415(不支持的媒體類型)   | 請求的格式不受請求頁面的支持。
416(請求范圍不符合要求) | 如果頁面無法提供請求的范圍,則服務器會返回此狀態(tài)代碼。
417(未滿足期望要求)     | 服務器未滿足”期望”請求標頭字段的要求。

5xx狀態(tài)碼

狀態(tài)碼        |        描述
------------- | -------------
500(服務器內(nèi)部錯誤)     |   服務器遇到錯誤,無法完成請求。
501(尚未實施)           |   服務器不具備完成請求的功能。 例如,服務器無法識別請求方法時可能會返回此代碼。
502(錯誤網(wǎng)關)           |   服務器充當網(wǎng)關或代理,從上游服務器收到無效響應。
503(服務不可用)         |   服務器目前無法使用(由于超載或停機維護)。 通常,這只是暫時狀態(tài)。
505(HTTP 版本不受支持)  |   服務器不支持請求中所用的 HTTP 協(xié)議版本。

2.7 this指向問題?

this指向的這個問題網(wǎng)上很多文章說了一大堆,說到了重點,但是有點啰嗦。自認為只要一句話就行了。
this永遠指向的是最后調(diào)用它的對象,也就是看它執(zhí)行的時候是誰調(diào)用的
但是要注意的一點是:
this在嚴格模式下,默認的指向不是window而是undefined;

在真正開發(fā)中經(jīng)常會使用var that = this, 來保存第一次執(zhí)行環(huán)境下的this。(在ES6中箭頭函數(shù)的this指向問題,箭頭函數(shù)里的this是定義時所在的作用域,而不是運行時所在的作用域)

2.8 說一下作用域鏈是什么?

最近好幾天要不在老家要不在學校,沒有時間去更新, 今天有點時間,下面就談談作用域鏈。(網(wǎng)上的案例很多,我拿過來分析,并且將閉包放到這里來說明)
在JavaScript中,變量的作用域有全局作用域局部作用域兩種。

☆ 作用域鏈

在JavaScript中,函數(shù)也是對象,實際上,JavaScript里一切都是對象。函數(shù)對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性。其中一個內(nèi)部屬性是[[Scope]],由ECMA-262標準第三版定義,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。

1. 當一個函數(shù)創(chuàng)建后,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象填充。例如定義下面這樣一個函數(shù):

function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}

在函數(shù)add創(chuàng)建時,它的作用域鏈中會填入一個全局對象,該全局對象包含了所有全局變量(不要問我哪里產(chǎn)生了[[scope]],因為是自己生成的。),如下圖所示(注意:圖片只例舉了全部變量中的一部分):

函數(shù)作用域鏈

2. 當函數(shù)執(zhí)行時,作用域鏈會發(fā)生變化的。

比如執(zhí)行下列代碼:

var total = add(5, 10);

執(zhí)行此函數(shù)時會創(chuàng)建一個稱為“運行期上下文(execution context)”的內(nèi)部對象,運行期上下文定義了函數(shù)執(zhí)行時的環(huán)境。每個運行期上下文都有自己的作用域鏈,用于標識符解析,當運行期上下文被創(chuàng)建時,而它的作用域鏈初始化為當前運行函數(shù)的[[Scope]]所包含的對象。
這些值按照它們出現(xiàn)在函數(shù)中的順序被復制到運行期上下文的作用域鏈中。它們共同組成了一個新的對象,叫“活動對象(activation object)”,該對象包含了函數(shù)的所有局部變量、命名參數(shù)、參數(shù)集合以及this,然后此對象會被推入作用域鏈的前端,當運行期上下文被銷毀,活動對象也隨之銷毀。新的作用域鏈如下圖所示:

代碼執(zhí)行時作用域鏈

這里說明一下scope對象中的0的指向變成了活動對象,1的指向變成了全局對象。

3. 使用with和catch可以改變執(zhí)行時期的作用域鏈

如果使用如下代碼:

function initUI(){
    with(document){
        var bd=body,
            links=getElementsByTagName("a"),
            i=0,
            len=links.length;
        while(i < len){
            update(links[i++]);
        }
        getElementById("btnInit").onclick=function(){
            doSomething();
        };
    }
}

這里使用width語句來避免多次書寫document,看上去更高效,實際上產(chǎn)生了性能問題。
當代碼運行到with語句時,運行期上下文的作用域鏈臨時被改變了。一個新的可變對象被創(chuàng)建,它包含了參數(shù)指定的對象的所有屬性。這個對象將被推入作用域鏈的頭部,這意味著函數(shù)的所有局部變量現(xiàn)在處于第二個作用域鏈對象中,因此訪問代價更高了。如下圖所示:

使用with將改變作用域鏈

☆ 談一下閉包(結合作用域鏈)

閉包其實就是一個函數(shù)在運行期間創(chuàng)建了另外一個函數(shù)。如下代碼:

function outside() {
  var a  = 1;
  function inside() {
    console.log(a);
 }
}
閉包時作用域鏈

來說一下形成閉包的過程。當執(zhí)行outside函數(shù)時, js引擎會創(chuàng)建outside函數(shù)的上下文的作用域鏈這個作用鏈包含了outside的執(zhí)行時的活動對象和全局對象。當定義inside的時候,inside函數(shù)上的作用域鏈包含了兩大塊:outside函數(shù)的活動對象、全局對象。(當inside執(zhí)行時,會在作用域鏈上增加一個新的對象,也就是它自身的活動對象。)

2.9 怎么阻止冒泡事件?

既然說到冒泡事件,那么就清晰去解析一下冒泡事件和捕獲事件。(IE8及其IE8以下版本是不支持捕獲事件的。)DOM的發(fā)展經(jīng)歷了DOM0、DOM2、DOM3三個版本。目前各個瀏覽器基本上支持DOM2的標準,所以咱們從DOM2的事件流來說起。
一般來說,事件流分為三個階段: 捕獲階段目標階段.
、冒泡階段

事件流過程

1. 捕獲階段

事件的第一個階段就是捕獲階段。事件從文檔的根節(jié)點也就是window對象開始流向目標對象節(jié)點。(W3C規(guī)范是從Document出發(fā),但是瀏覽器廠商都是從Window對象)途中經(jīng)過各個層次的DOM節(jié)點,并在各個節(jié)點上觸發(fā)捕獲事件,直到到達事件的目標節(jié)點。捕獲階段的主要任務就是建立傳播途徑,在冒泡階段,事件通過這個路徑進行回溯到文檔根節(jié)點。捕獲事件就是在捕獲階段觸發(fā)的事件,并執(zhí)行相應的處理函數(shù)。

2.目標階段

事件對象到達其事件目標。 這個階段被我們稱為目標階段。一旦事件對象到達事件目標,該階段的事件監(jiān)聽器就要對它進行處理。如果一個事件對象類型被標志為不能冒泡。那么對應的事件對象在到達此階段時就會終止傳播。

3.冒泡階段

事件對象以一個與捕獲階段相反的方向從事件目標傳播經(jīng)過其祖先節(jié)點傳播到window。這個階段被稱之為冒泡階段。在此階段注冊的事件監(jiān)聽器會對相應的冒泡事件進行處理。

4. 實例驗證事件流

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css" media="screen">
        .box1 {
            width: 800px;
            height: 400px;
            background: red;
            margin: 0 auto;
        }

        .box2 {
            width: 500px;
            height: 300px;
            background: green;
            margin: auto;
        }

        .box3 {
            width: 300px;
            height: 100px;
            background: blue;
            margin: auto;
        }
    </style>
    <script type="text/javascript">
        window.onload = function() {
            var box1 = document.getElementsByClassName("box1")[0];
            var box2 = document.getElementsByClassName("box1")[0];
            var box3 = document.getElementsByClassName("box1")[0];

            window.addEventListener('click', function() {
                console.log("捕獲階段:window執(zhí)行捕獲事件");
            }, true);

            document.body.addEventListener('click', function() {
                console.log("捕獲階段:body執(zhí)行捕獲事件");
            }, true);

            window.addEventListener('click', function() {
                console.log("冒泡階段:window執(zhí)行冒泡事件");
            }, false);

            document.body.addEventListener('click', function() {
                console.log("冒泡階段:body執(zhí)行冒泡事件");
            }, false);

            box1.addEventListener('click', function() {
                /*  */
                console.log("捕獲階段:box1執(zhí)行捕獲事件");
            }, true);

            box2.addEventListener('click', function() {
                /*  */
                console.log("捕獲階段:box2執(zhí)行捕獲事件");
            }, true);

            box3.addEventListener('click', function() {
                /*  */
                console.log("捕獲階段:box3執(zhí)行捕獲事件");
            }, true);


            box3.addEventListener('click', function() {
                /*  */
                console.log("冒泡階段:box3執(zhí)行冒泡事件");
            }, true);

            box2.addEventListener('click', function() {
                /*  */
                console.log("冒泡階段:box2執(zhí)行冒泡事件");
            }, true);

            box1.addEventListener('click', function() {
                /*  */
                console.log("冒泡階段:box1執(zhí)行冒泡事件");
            }, true);

        }       
    </script>
</head>
<body>
    <div class="box1">
        <div class="box2">
            <div class="box3">
                
            </div>
        </div>
    </div>
</body>
</html>

執(zhí)行結果如下:

執(zhí)行結果

這里說明一下當執(zhí)行到達目標階段時,可能出現(xiàn)冒泡事件比捕獲事件先執(zhí)行,這種情況是因為冒泡事件比捕獲事件先注冊。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容