《JavaScript 闖關記》之 BOM

ECMAScript 是 JavaScript 的核心,但如果要在 Web 中使用 JavaScript猾编,那么 BOM(瀏覽器對象模型)則無疑才是真正的核心逝薪。BOM 提供了很多對象杠览,用于訪問瀏覽器的功能,這些功能與任何網(wǎng)頁內容無關当宴。多年來畜吊,缺少事實上的規(guī)范導致 BOM 有很多問題,因為瀏覽器提供商會按照各自的想法隨意去擴展它户矢。W3C 為了把瀏覽器中 JavaScript 最基本的部分標準化玲献,已經(jīng)將 BOM 的主要方面納入了 HTML5 的規(guī)范中。

window 對象

BOM 的核心對象是 window梯浪,它表示瀏覽器的一個實例捌年。在瀏覽器中,window 對象有雙重角色挂洛,它既是通過 JavaScript 訪問瀏覽器窗口的一個接口礼预,又是 ECMAScript 規(guī)定的 Global 對象。這意味著在網(wǎng)頁中定義的任何一個對象虏劲、變量和函數(shù)托酸,都以 window 作為其 Global 對象,因此有權訪問 isNaN()柒巫、isFinite()励堡、parseInt()parseFloat() 等方法堡掏。

全局作用域

由于 window 對象同時扮演著 ECMAScript 中 Global 對象的角色应结,因此所有在全局作用域中聲明的變量、函數(shù)都會變成 window 對象的屬性和方法布疼。來看下面的例子摊趾。

var age = 29;
function sayAge(){
    console.log(this.age);
}

console.log(window.age);    // 29
sayAge();                   // 29
window.sayAge();            // 29

拋開全局變量會成為 window 對象的屬性不談币狠,定義全局變量與在 window 對象上直接定義屬性還是有一點差別:全局變量不能通過 delete 運算符刪除游两,而直接在 window 對象上的定義的屬性可以。例如:

var age = 29;
window.color = "red";

// 在 IE < 9 時拋出錯誤漩绵,在其他所有瀏覽器中都返回 false 
delete window.age;

// 在 IE < 9 時拋出錯誤贱案,在其他所有瀏覽器中都返回 true
delete window.color;        // return true

console.log(window.age);    // 29
console.log(window.color);  // undefined

使用 var 語句添加的 window 屬性有一個名為 Configurable 的特性,這個特性的值被默認設置為 false,因此這樣定義的屬性不可以通過 delete 運算符刪除宝踪。IE8 及更早版本在遇到使用 delete 刪除 window 屬性的語句時侨糟,不管該屬性最初是如何創(chuàng)建的,都會拋出錯誤瘩燥,以示警告秕重。IE9 及更高版本不會拋出錯誤。

另外厉膀,還要記住一件事:嘗試訪問未聲明的變量會拋出錯誤溶耘,但是通過查詢 window 對象,可以知道某個可能未聲明的變量是否存在服鹅。例如:

// 這里會拋出錯誤凳兵,因為 oldValue 未定義
var newValue = oldValue;

// 這里不會拋出錯誤,因為這是一次屬性查詢
// newValue 的值是 undefined
var newValue = window.oldValue;

窗口關系及框架

如果頁面中包含框架企软,則每個框架都擁有自己的 window 對象庐扫,并且保存在 frames 集合中。在 frames 集合中仗哨,可以通過數(shù)值索引(從0開始形庭,從左至右,從上到下)或者框架名稱來訪問相應的 window 對象厌漂。每個 window 對象都有一個 name 屬性碘勉,其中包含框架的名稱。下面是一個包含框架的頁面:

<html>
    <head>
        <title>Frameset Example</title>
    </head>
    <frameset rows="160,*">
        <frame src="frame.htm" name="topFrame">
        <frameset cols="50%,50%">
            <frame src="anotherframe.htm" name="leftFrame">
            <frame src="yetanotherframe.htm" name="rightFrame">
        </frameset>
    </frameset>
</html>

對這個例子而言桩卵,可以通過 window.frames[0] 或者 window.frames["topFrame"] 來引用上方的框架验靡。不過最好使用 top 而非 window 來引用這些框架(例如 top.frames[0]),因為 top 對象始終指向最高(最外)層的框架雏节,也就是瀏覽器窗口胜嗓。使用它可以確保在一個框架中正確地訪問另一個框架。因為對于在一個框架中編寫的任何代碼來說钩乍,其中的 window 對象指向的都是那個框架的特定實例辞州,而非最高層的框架。

top 相對的另一個 window 對象是 parent寥粹。顧名思義变过,parent(父)對象始終指向當前框架的直接上層框架。在某些情況下涝涤,parent 有可能等于 top媚狰;但在沒有框架的情況下,parent 一定等于 top(此時它們都等于 window)阔拳。

與框架有關的最后一個對象是 self崭孤,它始終指向 window;實際上,selfwindow 對象可以互換使用辨宠。引入 self 對象的目的只是為了與 topparent 對象對應起來遗锣,因此它不格外包含其他值。

所有這些對象都是 window 對象的屬性嗤形,可以通過 window.parent精偿、window.top 等形式來訪問。同時赋兵,這也意味著可以將不同層次的 window 對象連綴起來还最,例如 window.parent.parent.frames[0]

在使用框架的情況下毡惜,瀏覽器中會存在多個 Global 對象拓轻。在每個框架中定義的全局變量會自動成為框架中 window 對象的屬性。由于每個 window 對象都包含原生類型的構造函數(shù)经伙,因此每個框架都有一套自己的構造函數(shù)扶叉,這些構造函數(shù)一一對應,但并不相等帕膜。例如枣氧,top.Object 并不等于 top.frames[0].Object。這個問題會影響到對跨框架傳遞的對象使用 instanceof 運算符垮刹。

導航和打開窗口

使用 window.open() 方法既可以導航到一個特定的 URL达吞,也可以打開一個新的瀏覽器窗口。這個方法可以接收4個參數(shù):要加載的URL荒典、窗口目標酪劫、一個特性字符串以及一個表示新頁面是否取代瀏覽器歷史記錄中當前加載頁面的布爾值。通常只須傳遞第一個參數(shù)寺董,最后一個參數(shù)只在不打開新窗口的情況下使用覆糟。

如果為 window.open() 傳遞了第二個參數(shù),而且該參數(shù)是已有窗口或框架的名稱遮咖,那么就會在具有該名稱的窗口或框架中加載第一個參數(shù)指定的 URL滩字。看下面的例子御吞。

// 等同于 <a  target="newWindow"></a>
window.open("http://shijiajie.com/", "newWindow");

彈出窗口

如果給 window.open() 傳遞的第二個參數(shù)并不是一個已經(jīng)存在的窗口或框架麦箍,那么該方法就會根據(jù)在第三個參數(shù)位置上傳入的字符串創(chuàng)建一個新窗口或新標簽頁。如果沒有傳入第三個參數(shù)陶珠,那么就會打開一個帶有全部默認設置(工具欄挟裂、地址欄和狀態(tài)欄等)的新瀏覽器窗口(或者打開一個新標簽頁)。在不打開新窗口的情況下背率,會忽略第三個參數(shù)话瞧。

第三個參數(shù)是一個逗號分隔的設置字符串嫩与,表示在新窗口中都顯示哪些特性寝姿。下表列出了可以出現(xiàn)在這個字符串中的設置選項交排。

設置 說明
fullscreen yes或no 表示瀏覽器窗口是否最大化。僅限IE
height 數(shù)值 表示新窗口的高度饵筑。不能小于100
left 數(shù)值 表示新窗口的左坐標埃篓。不能是負值
location yes或no 表示是否在瀏覽器窗口中顯示地址欄。不同瀏覽器的默認值不同根资。如果設置為no架专,地址欄可能會隱藏,也可能會被禁用(取決于瀏覽器)
menubar yes或no 表示是否在瀏覽器窗口中顯示菜單欄玄帕。默認值為no
resizable yes或no 表示是否可以通過拖動瀏覽器窗口的邊框改變其大小部脚。默認值為no
scrollbars yes或no 表示如果內容在視口中顯示不下,是否允許滾動裤纹。默認值為no
status yes或no 表示是否在瀏覽器窗口中顯示狀態(tài)欄委刘。默認值為no
toolbar yes或no 表示是否在瀏覽器窗口中顯示工具欄。默認值為no
top 數(shù)值 表示新窗口的上坐標鹰椒。不能是負值
width 數(shù)值 表示新窗口的寬度锡移。不能小于100

這行代碼會打開一個新的可以調整大小的窗口,窗口初始大小為400×400像素漆际,并且距屏幕上沿和左邊各10像素淆珊。

window.open("http://shijiajie.com/","newWindow",
    "height=400,width=400,top=10,left=10,resizable=yes");

window.open() 方法會返回一個指向新窗口的引用。引用的對象與其他 window 對象大致相似奸汇,但我們可以對其進行更多控制施符。例如,有些瀏覽器在默認情況下可能不允許我們針對主瀏覽器窗口調整大小或移動位置擂找,但卻允許我們針對通過window.open()創(chuàng)建的窗口調整大小或移動位置操刀。通過這個返回的對象,可以像操作其他窗口一樣操作新打開的窗口婴洼,如下所示骨坑。

var win = window.open("http://shijiajie.com/","newWindow",
    "height=400,width=400,top=10,left=10,resizable=yes");

// 調整大小
win.resizeTo(500,500);

// 移動位置
win.moveTo(100,100);

// 關閉窗口
win.close();

但是,close() 方法僅適用于通過 window.open() 打開的彈出窗口柬采。對于瀏覽器的主窗口欢唾,如果沒有得到用戶的允許是不能關閉它的。

新創(chuàng)建的 window 對象有一個 opener 屬性粉捻,其中保存著打開它的原始窗口對象礁遣。這個屬性只在彈出窗口中的最外層 window 對象(top)中有定義,而且指向調用 window.open() 的窗口或框架肩刃。例如:

var win = window.open("http://shijiajie.com/","newWindow",
    "height=400,width=400,top=10,left=10,resizable=yes");

console.log(win.opener === window);   // true

雖然彈出窗口中有一個指針指向打開它的原始窗口祟霍,但原始窗口中并沒有這樣的指針指向彈出窗口杏头。窗口并不跟蹤記錄它們打開的彈出窗口,因此我們只能在必要的時候自己來手動實現(xiàn)跟蹤沸呐。

彈出窗口屏蔽程序

曾經(jīng)有一段時間醇王,廣告商在網(wǎng)上使用彈出窗口達到了肆無忌憚的程度。他們經(jīng)常把彈出窗口打扮成系統(tǒng)對話框的模樣崭添,引誘用戶去點擊其中的廣告寓娩。由于看起來像是系統(tǒng)對話框,一般用戶很難分辨是真是假呼渣。為了解決這個問題棘伴,大多數(shù)瀏覽器內置有彈出窗口屏蔽程序,將絕大多數(shù)用戶不想看到彈出窗口屏蔽掉屁置。

于是焊夸,在彈出窗口被屏蔽時,就應該考慮兩種可能性蓝角。如果是瀏覽器內置的屏蔽程序阻止的彈出窗口阱穗,那么 window.open() 很可能會返回 null,如果是瀏覽器擴展或其他程序阻止的彈出窗口帅容,那么 window.open() 通常會拋出一個錯誤颇象。因此,要想準確地檢測出彈出窗口是否被屏蔽并徘,必須在檢測返回值的同時遣钳,將對 window.open() 的調用封裝在一個 try-catch 塊中,如下所示麦乞。

var blocked = false;

try {
    var win = window.open("http://shijiajie.com", "_blank");
    if (win == null){
        blocked = true;
    }
} catch (ex){
    blocked = true;
}
if (blocked){
    console.log("The popup was blocked!");
}

間歇調用和超時調用

JavaScript 是單線程語言蕴茴,但它允許通過設置超時值和間歇時間值來調度代碼在特定的時刻執(zhí)行。前者是在指定的時間過后執(zhí)行代碼姐直,而后者則是每隔指定的時間就執(zhí)行一次代碼倦淀。

超時調用需要使用 window 對象的 setTimeout() 方法,它接受兩個參數(shù):要執(zhí)行的代碼和以毫秒表示的時間(即在執(zhí)行代碼前需要等待多少毫秒)声畏。其中撞叽,第一個參數(shù)可以是一個包含 JavaScript 代碼的字符串(就和在 eval() 函數(shù)中使用的字符串一樣),也可以是一個函數(shù)插龄。例如愿棋,下面對 setTimeout() 的兩次調用都會在一秒鐘后顯示一個警告框。

// 不建議傳遞字符串
setTimeout("console.log('Hello world!') ", 1000);

// 推薦的調用方式
setTimeout(function() { 
    console.log("Hello world!"); 
}, 1000);

雖然這兩種調用方式都沒有問題均牢,但由于傳遞字符串可能導致性能損失糠雨,因此不建議以字符串作為第一個參數(shù)。

第二個參數(shù)是一個表示等待多長時間的毫秒數(shù)徘跪,但經(jīng)過該時間后指定的代碼不一定會執(zhí)行甘邀。JavaScript 是一個單線程序的解釋器琅攘,因此一定時間內只能執(zhí)行一段代碼。為了控制要執(zhí)行的代碼松邪,就有一個 JavaScript 任務隊列坞琴。這些任務會按照將它們添加到隊列的順序執(zhí)行。setTimeout() 的第二個參數(shù)告訴 JavaScript 再過多長時間把當前任務添加到隊列中测摔。如果隊列是空的置济,那么添加的代碼會立即執(zhí)行解恰;如果隊列不是空的锋八,那么它就要等前面的代碼執(zhí)行完了以后再執(zhí)行。

調用 setTimeout() 之后护盈,該方法會返回一個數(shù)值 ID挟纱,表示超時調用。這個超時調用 ID 是計劃執(zhí)行代碼的唯一標識符腐宋,可以通過它來取消超時調用。要取消尚未執(zhí)行的超時調用計劃,可以調用 clearTimeout() 方法并將相應的超時調用 ID 作為參數(shù)傳遞給它剥啤,如下所示训桶。

// 設置超時調用
var timeoutId = setTimeout(function() {
    console.log("Hello world!");
}, 1000);

// 注意:把它取消
clearTimeout(timeoutId);

只要是在指定的時間尚未過去之前調用 clearTimeout(),就可以完全取消超時調用卫枝。前面的代碼在設置超時調用之后馬上又調用了 clearTimeout()煎饼,結果就跟什么也沒有發(fā)生一樣。

間歇調用與超時調用類似校赤,只不過它會按照指定的時間間隔重復執(zhí)行代碼吆玖,直至間歇調用被取消或者頁面被卸載。設置間歇調用的方法是 setInterval()马篮,它接受的參數(shù)與 setTimeout() 相同:要執(zhí)行的代碼(字符串或函數(shù))和每次執(zhí)行之前需要等待的毫秒數(shù)沾乘。下面來看一個例子。

// 不建議傳遞字符串
setInterval ("console.log('Hello world!') ", 10000);

// 推薦的調用方式
setInterval (function() { 
    console.log("Hello world!"); 
}, 10000);

調用 setInterval() 方法同樣也會返回一個間歇調用 ID浑测,該 ID 可用于在將來某個時刻取消間歇調用翅阵。要取消尚未執(zhí)行的間歇調用,可以使用 clearInterval() 方法并傳入相應的間歇調用 ID迁央。取消間歇調用的重要性要遠遠高于取消超時調用掷匠,因為在不加干涉的情況下,間歇調用將會一直執(zhí)行到頁面卸載漱贱。以下是一個常見的使用間歇調用的例子槐雾。

var num = 0;
var max = 10;
var intervalId = null;

function incrementNumber() {
    num++;
    // 如果執(zhí)行次數(shù)達到了max設定的值,則取消后續(xù)尚未執(zhí)行的調用
    if (num == max) {
        clearInterval(intervalId);
        console.log("Done");
    }
}

intervalId = setInterval(incrementNumber, 500);

在這個例子中幅狮,變量num每半秒鐘遞增一次募强,當遞增到最大值時就會取消先前設定的間歇調用株灸。這個模式也可以使用超時調用來實現(xiàn),如下所示擎值。

var num = 0;
var max = 10;

function incrementNumber() {
    num++;

    // 如果執(zhí)行次數(shù)未達到max設定的值慌烧,則設置另一次超時調用
    if (num < max) {
        setTimeout(incrementNumber, 500);
    } else {
        console.log("Done");
    }
}

setTimeout(incrementNumber, 500);

可見,在使用超時調用時鸠儿,沒有必要跟蹤超時調用 ID屹蚊,因為每次執(zhí)行代碼之后,如果不再設置另一次超時調用进每,調用就會自行停止汹粤。一般認為,使用超時調用來模擬間歇調用的是一種最佳模式田晚。在開發(fā)環(huán)境下嘱兼,很少使用真正的間歇調用,原因是后一個間歇調用可能會在前一個間歇調用結束之前啟動贤徒。而像前面示例中那樣使用超時調用芹壕,則完全可以避免這一點。所以接奈,最好不要使用間歇調用踢涌。

系統(tǒng)對話框

瀏覽器通過 alert()confirm()prompt() 方法可以調用系統(tǒng)對話框向用戶顯示消息序宦。系統(tǒng)對話框與在瀏覽器中顯示的網(wǎng)頁沒有關系睁壁,也不包含 HTML。它們的外觀由操作系統(tǒng)及(或)瀏覽器設置決定挨厚,而不是由 CSS 決定堡僻。此外,通過這幾個方法打開的對話框都是同步和模態(tài)的疫剃。也就是說钉疫,顯示這些對話框的時候代碼會停止執(zhí)行,而關掉這些對話框后代碼又會恢復執(zhí)行巢价。

第一種對話框是調用 alert() 方法生成的牲阁。它向用戶顯示一個系統(tǒng)對話框,其中包含指定的文本和一個 OK(“確定”)按鈕壤躲。通常使用 alert() 生成的“警告”對話框向用戶顯示一些他們無法控制的消息城菊,例如錯誤消息。而用戶只能在看完消息后關閉對話框碉克。

第二種對話框是調用 confirm() 方法生成的凌唬。從向用戶顯示消息的方面來看,這種“確認”對話框很像是一個“警告”對話框漏麦。但二者的主要區(qū)別在于“確認”對話框除了顯示OK按鈕外客税,還會顯示一個 Cancel(“取消”)按鈕况褪,兩個按鈕可以讓用戶決定是否執(zhí)行給定的操作。

為了確定用戶是單擊了OK還是Cancel更耻,可以檢查 confirm() 方法返回的布爾值:true 表示單擊了OK测垛,false 表示單擊了Cancel或單擊了右上角的 X 按鈕。確認對話框的典型用法如下秧均。

if (confirm("Are you sure?")) {
    alert("I'm so glad you're sure! ");
} else {
    alert("I'm sorry to hear you're not sure.");
}

最后一種對話框是通過調用 prompt() 方法生成的食侮,這是一個“提示”框,用于提示用戶輸入一些文本目胡。提示框中除了顯示 OK 和 Cancel 按鈕之外锯七,還會顯示一個文本輸入域,以供用戶在其中輸入內容讶隐。prompt() 方法接受兩個參數(shù):要顯示給用戶的文本提示和文本輸入域的默認值(可以是一個空字符串)起胰。

如果用戶單擊了 OK 按鈕久又,則 promp() 返回文本輸入域的值巫延;如果用戶單擊了 Cancel 或沒有單擊 OK 而是通過其他方式關閉了對話框,則該方法返回 null地消。下面是一個例子炉峰。

var result = prompt("What is your name? ", "");
if (result !== null) {
    alert("Welcome, " + result);
}

綜上所述,這些系統(tǒng)對話框很適合向用戶顯示消息并請用戶作出決定脉执。由于不涉及 HTML疼阔、CSS 或 JavaScript,因此它們是增強 Web 應用程序的一種便捷方式半夷。

location 對象

location 對象提供了與當前窗口中加載的文檔有關的信息婆廊,還提供了一些導航功能。事實上巫橄,location 對象是很特別的一個對象淘邻,因為它既是 window 對象的屬性,也是 document 對象的屬性湘换;換句話說宾舅,window.locationdocument.location 引用的是同一個對象。location 對象的用處不只表現(xiàn)在它保存著當前文檔的信息彩倚,還表現(xiàn)在它將 URL 解析為獨立的片段筹我,讓開發(fā)人員可以通過不同的屬性訪問這些片段。下表列出了 location 對象的所有屬性帆离。

屬性名 例子 說明
hash "#contents" 返回 URL 中的 hash(#號后跟零或多個字符)蔬蕊,如果 URL 中不包含散列,則返回空字符串
host "shijiajie.com:80" 返回服務器名稱和端口號(如果有)
hostname "shijiajie.com" 返回不帶端口號的服務器名稱
href "http:/shijiajie.com" 返回當前加載頁面的完整URL哥谷。而 location 對象的 toString() 方法也返回這個值
pathname "/WileyCDA/" 返回URL中的目錄和(或)文件名
port "8080" 返回 URL 中指定的端口號岸夯。如果 URL 中不包含端口號概而,則這個屬性返回空字符串
protocol "http:" 返回頁面使用的協(xié)議。通常是 http: 或 https:
search "?q=javascript" 返回URL的查詢字符串囱修。這個字符串以問號開頭

查詢字符串參數(shù)

雖然通過上面的屬性可以訪問到 location 對象的大多數(shù)信息赎瑰,但其中訪問URL包含的查詢字符串的屬性并不方便。盡管 location.search 返回從問號到 URL 末尾的所有內容破镰,但卻沒有辦法逐個訪問其中的每個查詢字符串參數(shù)餐曼。為此,可以像下面這樣創(chuàng)建一個函數(shù)鲜漩,用以解析查詢字符串源譬,然后返回包含所有參數(shù)的一個對象:

/*
 * 這個函數(shù)用來解析來自URL的查詢串中的name=value參數(shù)對
 * 它將name=value對存儲在一個對象的屬性中,并返回該對象
 * 這樣來使用它
 *
 * var args = urlArgs(); // 從URL中解析參數(shù)
 * var q = args.q || ""; // 如果參數(shù)定義了的話就使用參數(shù)孕似;否則使用一個默認值
 * var n = args.n ? parseInt(args.n) : 10;
 */
function urlArgs() {
    var args = {};                                  // 定義一個空對象
    var query = location.search.substring(1);       // 查找到查詢串踩娘,并去掉'? '
    var pairs = query.split("&");                   // 根據(jù)"&"符號將查詢字符串分隔開
    for (var i = 0; i < pairs.length; i++) {        // 對于每個片段
        var pos = pairs[i].indexOf('=');            // 查找"name=value"
        if (pos == -1) continue;                    // 如果沒有找到的話,就跳過
        var name = pairs[i].substring(0, pos);      // 提取name
        var value = pairs[i].substring(pos + 1);    // 提取value
        value = decodeURIComponent(value);          // 對value進行解碼
        args[name] = value;                         // 存儲為屬性
    }
    return args;                                    // 返回解析后的參數(shù)
}

位置操作

使用 location 對象可以通過很多方式來改變?yōu)g覽器的位置喉祭。首先养渴,也是最常用的方式,就是使用 assign()方法并為其傳遞一個 URL泛烙,如下所示理卑。

location.assign("http://shijiajie.com");

這樣,就可以立即打開新URL并在瀏覽器的歷史記錄中生成一條記錄蔽氨。如果是將 location.hrefwindow.location 設置為一個URL值藐唠,也會以該值調用 assign() 方法。例如鹉究,下列兩行代碼與顯式調用 assign() 方法的效果完全一樣宇立。

window.location = "http://shijiajie.com";
location.;

在這些改變?yōu)g覽器位置的方法中,最常用的是設置 location.href 屬性自赔。

另外妈嘹,修改 location 對象的其他屬性也可以改變當前加載的頁面。下面的例子展示了通過將 hash匿级、search蟋滴、hostnamepathnameport 屬性設置為新值來改變 URL痘绎。

// 假設初始 URL 為 http://shijiajie.com/about/
location.

// 將 URL 修改為 "http://shijiajie.com/about/#ds-thread"
location.hash = "#ds-thread";

// 將 URL 修改為 "http://shijiajie.com/about/?args=123"
location.search = "?args=123";

// 將 URL 修改為 "https://segmentfault.com/"
location.hostname = "segmentfault.com";

// 將 URL 修改為 "http://segmentfault.com/u/stone0090/"
location.pathname = "u/stone0090";

// 將 URL 修改為 "https://segmentfault.com:8080/"
location.port = 8080;

當通過上述任何一種方式修改URL之后津函,瀏覽器的歷史記錄中就會生成一條新記錄,因此用戶通過單擊“后退”按鈕都會導航到前一個頁面孤页。要禁用這種行為尔苦,可以使用 replace() 方法。這個方法只接受一個參數(shù),即要導航到的 URL允坚;結果雖然會導致瀏覽器位置改變魂那,但不會在歷史記錄中生成新記錄。在調用 replace() 方法之后稠项,用戶不能回到前一個頁面涯雅,來看下面的例子:

<!DOCTYPE html>
<html>
<head>
    <title>You won't be able to get back here</title>
</head>
    <body>
    <p>Enjoy this page for a second, because you won't be coming back here.</p>
    <script type="text/javascript">
        setTimeout(function () {
            location.replace("http://shijiajie.com/");
        }, 1000);
    </script>
</body>
</html>

如果將這個頁面加載到瀏覽器中,瀏覽器就會在1秒鐘后重新定向到 shijiajie.com展运。然后活逆,“后退”按鈕將處于禁用狀態(tài),如果不重新輸入完整的 URL拗胜,則無法返回示例頁面蔗候。

與位置有關的最后一個方法是 reload(),作用是重新加載當前顯示的頁面埂软。如果調用 reload() 時不傳遞任何參數(shù)锈遥,頁面就會以最有效的方式重新加載。也就是說勘畔,如果頁面自上次請求以來并沒有改變過所灸,頁面就會從瀏覽器緩存中重新加載。如果要強制從服務器重新加載咖杂,則需要像下面這樣為該方法傳遞參數(shù) true庆寺。

location.reload();        // 重新加載(有可能從緩存中加載)
location.reload(true);    // 重新加載(從服務器重新加載)

位于 reload() 調用之后的代碼可能會也可能不會執(zhí)行,這要取決于網(wǎng)絡延遲或系統(tǒng)資源等因素诉字。為此,最好將 reload() 放在代碼的最后一行知纷。

history 對象

history 對象保存著用戶上網(wǎng)的歷史記錄壤圃,從窗口被打開的那一刻算起。因為 historywindow 對象的屬性琅轧,因此每個瀏覽器窗口伍绳、每個標簽頁乃至每個框架,都有自己的 history 對象與特定的 window 對象關聯(lián)乍桂。出于安全方面的考慮冲杀,開發(fā)人員無法得知用戶瀏覽過的 URL。不過睹酌,借由用戶訪問過的頁面列表权谁,同樣可以在不知道實際 URL 的情況下實現(xiàn)后退和前進。

使用 go() 方法可以在用戶的歷史記錄中任意跳轉憋沿,可以向后也可以向前旺芽。這個方法接受一個參數(shù),表示向后或向前跳轉的頁面數(shù)的一個整數(shù)值。負數(shù)表示向后跳轉(類似于單擊瀏覽器的“后退”按鈕)采章,正數(shù)表示向前跳轉(類似于單擊瀏覽器的“前進”按鈕)运嗜。來看下面的例子。

// 后退一頁
history.go(-1);

// 前進一頁
history.go(1);

// 前進兩頁
history.go(2);

也可以給 go() 方法傳遞一個字符串參數(shù)悯舟,此時瀏覽器會跳轉到歷史記錄中包含該字符串的第一個位置——可能后退担租,也可能前進,具體要看哪個位置最近抵怎。如果歷史記錄中不包含該字符串翩活,那么這個方法什么也不做,例如:

// 跳轉到最近的 shijiajie.com 頁面
history.go("shijiajie.com");

另外便贵,還可以使用兩個簡寫方法 back()forward() 來代替 go()菠镇。顧名思義,這兩個方法可以模仿瀏覽器的“后退”和“前進”按鈕承璃。

// 后退一頁
history.back();

// 前進一頁
history.forward();

除了上述幾個方法外利耍,history 對象還有一個 length 屬性,保存著歷史記錄的數(shù)量盔粹。這個數(shù)量包括所有歷史記錄隘梨,即所有向后和向前的記錄。對于加載到窗口舷嗡、標簽頁或框架中的第一個頁面而言轴猎,history.length 等于0。通過像下面這樣測試該屬性的值进萄,可以確定用戶是否一開始就打開了你的頁面捻脖。

if (history.length == 0){
    //這應該是用戶打開窗口后的第一個頁面
}

雖然 history 并不常用,但在創(chuàng)建自定義的“后退”和“前進”按鈕中鼠,以及檢測當前頁面是不是用戶歷史記錄中的第一個頁面時可婶,還是必須使用它。

小結

BOM(瀏覽器對象模型)以 window 對象為依托援雇,表示瀏覽器窗口以及頁面可見區(qū)域矛渴。同時,window 對象還是 ECMAScript 中的 Global 對象惫搏,因而所有全局變量和函數(shù)都是它的屬性具温,且所有原生的構造函數(shù)及其他函數(shù)也都存在于它的命名空間下。本章討論了下列 BOM 的組成部分筐赔。

  • 在使用框架時铣猩,每個框架都有自己的 window 對象以及所有原生構造函數(shù)及其他函數(shù)的副本。每個框架都保存在 frames 集合中川陆,可以通過位置或通過名稱來訪問剂习。
  • 有一些窗口指針蛮位,可以用來引用其他框架,包括父框架鳞绕。
  • top 對象始終指向最外圍的框架失仁,也就是整個瀏覽器窗口。
  • parent 對象表示包含當前框架的框架们何,而 self 對象則回指 window萄焦。
  • 使用 location 對象可以通過編程方式來訪問瀏覽器的導航系統(tǒng)。設置相應的屬性冤竹,可以逐段或整體性地修改瀏覽器的 URL拂封。
  • 調用 replace() 方法可以導航到一個新 URL,同時該 URL 會替換瀏覽器歷史記錄中當前顯示的頁面鹦蠕。
  • navigator 對象提供了與瀏覽器有關的信息冒签。到底提供哪些信息,很大程度上取決于用戶的瀏覽器钟病;不過萧恕,也有一些公共的屬性(如 userAgent)存在于所有瀏覽器中。

BOM中還有兩個對象:screenhistory肠阱,但它們的功能有限票唆。screen 對象中保存著與客戶端顯示器有關的信息,這些信息一般只用于站點分析屹徘。history 對象為訪問瀏覽器的歷史記錄開了一個小縫隙走趋,開發(fā)人員可以據(jù)此判斷歷史記錄的數(shù)量,也可以在歷史記錄中向后或向前導航到任意頁面噪伊。

關卡

// 挑戰(zhàn)一
setTimeout(function () {
    console.log("1");
}, 0)
console.log("2");   // ???
// 挑戰(zhàn)二
for (var i = 0;i<5;i++) {
    setTimeout(function () {
        console.log(i);     // ???
    }, 0)
};
// 挑戰(zhàn)三
var a = 1;
var obj = {
    a : 2,
    b : function(){
        setTimeout(function () {
            console.log(this.a);
        }, 0)
    }
}
obj.b();    // ???
// 挑戰(zhàn)四
var a = 1;
var obj = {
    a : 2,
    b : function(){
        setTimeout(function () {
            console.log(this.a);
        }.call(this), 0);
    }
}
obj.b();    // ???

更多

關注微信公眾號「劼哥舍」回復「答案」簿煌,獲取關卡詳解。
關注 https://github.com/stone0090/javascript-lessons酥宴,獲取最新動態(tài)啦吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拙寡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳水,老刑警劉巖肆糕,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異在孝,居然都是意外死亡诚啃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門私沮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來始赎,“玉大人,你說我怎么就攤上這事≡於猓” “怎么了魔招?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長五辽。 經(jīng)常有香客問我办斑,道長,這世上最難降的妖魔是什么杆逗? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任乡翅,我火速辦了婚禮,結果婚禮上罪郊,老公的妹妹穿的比我還像新娘蠕蚜。我一直安慰自己,他們只是感情好悔橄,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布靶累。 她就那樣靜靜地躺著,像睡著了一般橄维。 火紅的嫁衣襯著肌膚如雪尺铣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天争舞,我揣著相機與錄音凛忿,去河邊找鬼。 笑死竞川,一個胖子當著我的面吹牛店溢,可吹牛的內容都是我干的。 我是一名探鬼主播委乌,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼床牧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遭贸?” 一聲冷哼從身側響起戈咳,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壕吹,沒想到半個月后著蛙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡耳贬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年踏堡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咒劲。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡顷蟆,死狀恐怖诫隅,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情帐偎,我是刑警寧澤逐纬,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站肮街,受9級特大地震影響风题,放射性物質發(fā)生泄漏。R本人自食惡果不足惜嫉父,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一沛硅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绕辖,春花似錦摇肌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至树碱,卻和暖如春肯适,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背成榜。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工框舔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赎婚。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓刘绣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挣输。 傳聞我的和親對象是個殘疾皇子纬凤,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容