讀書筆記(javascript 高級程序設計)

一. 數據類型:javascript

1. undefined: 未聲明和未初始化的變量,typeof 操做符返回的結果都是 undefined;(建議未初始化的變量進行顯式賦值,這樣當 typeof 返回 undefined 時就知道是未聲明瞭,幫助定位問題)php

2. null:建議,將即將保存但還未真正保存對象的變量,賦值爲 null;html

3. number: 保存浮點數須要的內存空間是保存整數值的兩倍,所以 ECMAScript 會不失時機的將浮點數值轉換爲整數值;前端

4. NaN: isNaN() 用於肯定其參數是否「不是數值」;eg: isNaN("blue") —— true (由於不是數值);java

附: typeof 能夠判斷的類型:undefined/boolean/string/number/function;node

5. parseInt(): 第二個參數能夠指定進制,使參數按照指定進制轉換,如不指定進制,則按照十進制轉換;android

 parseFloat() 只解析十進制值,他沒有第二個參數指定基數的用法;ios

6. 轉換爲字符串: toString() 和 String();(null 和 undefined 沒有 toString() 方法)web

String() 可將任何類型的值轉換爲字符串; 轉換規則以下:算法

若是值有 toString() 方法,則調用該方法(沒有參數)並返回相應的結果 —— 若是值是 null,則返回 「null」 —— 若是值是「undefined」, 則返回「undefined」;

7. 前 ++ 和後 ++:

前++和前--:eg:var age = 28; --age;//28(--age至關於 age = age - 1;age與(--age)總體的值也會變)

後++和後--:eg:

var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2; //22 (age的值減1,(age--)總體的值不會變)
var num4 = num1 + num2; //21

8. 位運算符: 能夠提升性能(詳見收藏);

9. == 和 !=: 比較 null 與 undefined 時,不能將它倆轉換爲任何值;

10. switch語句:switch語句在比較值時使用的是全等操做符,所以不會發生類型轉換(如,「10」 不等於 10);

 二. 變量、做用域和內存問題:

1. 函數傳參:

參數只能按值傳遞:當參數爲基本數據類型時,直接進行復制操做;當參數爲引用數據類型時,將引用地址進行復制給形參,若是在函數內部從新對引用類型的參數進行賦值,此時修改的引用變量參數爲局部變量,這個局部的引用對象會在函數執行完畢後當即被銷燬;(傳參是複製,不能理解爲是實參替換形參);函數參數也被看成變量來對待,所以其訪問規則與執行環境中的其餘變量相同(局部變量,做用域鏈)。

基本類型值得傳遞如同基本類型變量得複製同樣,而引用類型值的傳遞,則如同引用類型變量的複製同樣。在向參數傳遞基本類型的值時,被傳遞的值會被複制給一個局部變量(即命名參數,或者用ES的概念來講,就是 arguments 的一個元素)。在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變化會反映在函數的內部。

 2. 執行環境及做用域:

延長做用域鏈:雖然執行環境的類型總共只有兩種——全局和局部(函數),但仍是有其餘辦法來延長做用域鏈。這麼說是由於有些語句能夠在做用域鏈的前端臨時增長一個變量對象,該變量對象會在代碼執行後被移除。在兩種狀況下會發生這種現象。具體來講,就是當執行流進入下列任何一個語句時,做用域鏈就會獲得加長:try-catch 語句的 catch 塊;with 語句。

這兩個語句都會在做用域鏈的前端添加一個變量對象。對 with 語句來講,會將指定的對象添加到做用域鏈中。對 catch 語句來講,會建立一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。

3. 垃圾回收機制:

  • 標記清除」是目前主流的垃圾收集算法,這種算法的思想是給當前不使用的值加上標記,而後再回收其內存。
  • 解除變量的引用不只有助於消除循環引用現象,並且對垃圾收集也有好處。爲了確保有效地回收內存,應該及時解除再也不使用的全局對象、全局對象屬性以及循環引用變量的引用。 (一旦數據再也不有用,最好經過將其值設置爲 null 來釋放其引用——這個作法叫作解除引用dereferencing)。這一作法適用於大多數全局變量和全局對象的屬性。局部變量會在它們離開執行環境時自動被解除引用

 三. 引用類型

1. instanceof 操做符的問題在於,它假定只有一個全局執行環境。若是網頁中包含多個框架,那實際上就存在兩個以上不一樣的全局執行環境,從而存在兩個以上不一樣版本的 Array 構造函數。若是你從一個框架向另外一個框架傳入一個數組,那麼傳入的數組與在第二個框架中原生建立的數組分別具備各自不一樣的構造函數。
爲了解決這個問題, ECMAScript 5 新增了 Array.isArray()方法。

if (Array.isArray(value)){
    //對數組執行某些操做
}

2. 調用數組的 toString()方法會返回由數組中每一個值的字符串形式拼接而成的一個以逗號分隔的字符串。而調用 valueOf()返回的仍是數組。

注:若是數組中的某一項的值是 null 或 undefined,那麼該值在 join()、toLocalString()、toString() 和 valueOf() 方法返回的結果中以空字符串表示。

3. Date 類型的 valueOf()方法,則根本不返回字符串,而是返回日期的毫秒錶示。所以,能夠方便使用比較操做符(小於或大於)來比較日期值。

4. 解析器在向執行環境中加載數據時,對函數聲明和函數表達式並不是一視同仁。解析器會率先讀取函數聲明,並使其在執行任何代碼以前可用(能夠訪問);至於函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解釋執行。

5. ECMAScript 5 也規範化了另外一個函數對象的屬性: caller這個屬性中保存着調用當前函數的函數的引用,若是是在全局做用域中調用當前函數,它的值爲 null

function outer(){
    inner();
}
function inner(){
    alert(inner.caller);
}
outer();

以上代碼會致使警告框中顯示 outer() 函數的源代碼。由於 outer() 調用了 inter(),因此 inner.caller 就指向 outer()。爲了實現更鬆散的耦合,也能夠經過 arguments.callee.caller 來訪問相同的信息。

6. ECMAScript 5 中, prototype 屬性是不可枚舉的,所以使用 for-in 沒法發現。

7. 在沒有給函數明確指定this 值的狀況下(不管是經過將函數添加爲對象的方法,仍是經過調用 call()apply()), this 值等於 Global 對象。而像這樣經過簡單地返回 this 來取得 Global 對象,在任何執行環境下都是可行的。

var global = function(){
    return this;
}();

 四. 面向對象:

1. 要建立一個構造函數的新實例,必須使用 new 操做符。以這種方式調用構造函數實際上會經歷如下 個步驟:
(1) 建立一個新對象;
(2) 將構造函數的做用域賦給新對象(所以 this 就指向了這個新對象);
(3) 執行構造函數中的代碼(爲這個新對象添加屬性);
(4) 返回新對象。

2. 在使用 for-in 循環時,返回的是全部可以經過對象訪問的、可枚舉的( enumerated)屬性,其中既包括存在於實例中的屬性,也包括存在於原型中的屬性。屏蔽了原型中不可枚舉屬性(即將[[Enumerable]]標記爲 false 的屬性)的實例屬性也會在 for-in 循環中返回,由於根據規定,全部開發人員定義的屬性都是可枚舉的。

3. 組合使用構造函數模式和原型模式:

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function(){
    alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

在這個例子中,實例屬性都是在構造函數中定義的,而由全部實例共享的屬性 constructor 和方法 sayName()則是在原型中定義的。而修改了 person1.friends(向其中添加一個新字符串),並不會影響到 person2.friends,由於它們分別引用了不一樣的數組。這種構造函數與原型混成的模式,是目前在 ECMAScript 中使用最普遍、認同度最高的一種建立自定義類型的方法。能夠說,這是用來定義引用類型的一種默認模式。

4. 動態原型模式:

經過檢查某個應該存在的方法是否有效,來決定是否須要初始化原型。

function Person(name, age, job){
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
    //方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}

這裏只在 sayName()方法不存在的狀況下,纔會將它添加到原型中。這段代碼只會在初次調用構造函數時纔會執行。此後,原型已經完成初始化,不須要再作什麼修改了。不過要記住,這裏對原型所作的修改,可以當即在全部實例中獲得反映。所以,這種方法確實可
以說很是完美。其中, if 語句檢查的能夠是初始化以後應該存在的任何屬性或方法——沒必要用一大堆if 語句檢查每一個屬性和每一個方法;只要檢查其中一個便可。對於採用這種模式建立的對象,還可使用 instanceof 操做符肯定它的類型。

5. 寄生構造函數模式:

function SpecialArray(){
    //建立數組
    var values = new Array();
    //添加值
    values.push.apply(values, arguments);
    //添加方法
    values.toPipedString = function(){
        return this.join("|");
    };
    //返回數組
    return values;
}
var colors = new SpecialArray("red", "blue", "green");
alert(colors.toPipedString()); //"red|blue|green"

關於寄生構造函數模式,有一點須要說明:首先,返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係;也就是說,構造函數返回的對象與在構造函數外部建立的對象沒有什麼不一樣。爲此,不能依賴 instanceof 操做符來肯定對象類型。因爲存在上述問題,咱們建議在可使用其餘模式的狀況下,不要使用這種模式。

6. 穩妥構造函數模式:一是新建立對象的實例方法不引用 this;二是不使用 new 操做符調用構造函數。

function Person(name, age, job){
    //建立要返回的對象
    var o = new Object();
    //能夠在這裏定義私有變量和函數
    //添加方法
    o.sayName = function(){
        alert(name);
    };
    //返回對象
    return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

變量 friend 中保存的是一個穩妥對象,而除了調用 sayName()方法外,沒有別的方式能夠訪問其數據成員。即便有其餘代碼會給這個對象添加方法或數據成員,但也不可能有別的辦法訪問傳入到構造函數中的原始數據。

與寄生構造函數模式相似,instanceof 操做符對這種對象也沒有意義。

7. 在經過原型鏈實現繼承時,不能使用對象字面量建立原型方法,這樣作就會重寫原型鏈。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}
//繼承了 SuperType
SubType.prototype = new SuperType();
//使用字面量添加新方法,會致使上一行代碼無效
SubType.prototype = {
    getSubValue : function (){
        return this.subproperty;
    },
    someOtherMethod : function (){
        return false;
    }
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

 五. 函數表達式:

1. 後臺的每一個執行環境都有一個表示變量的對象——變量對象。全局環境的變量對象始終存在,而像compare()函數這樣的局部環境的變量對象,則只在函數執行的過程當中存在。在建立 compare()函數時,會建立一個預先包含全局變量對象的做用域鏈,這個做用域鏈被保存在內部的[[Scope]]屬性中。當調用 compare()函數時,會爲函數建立一個執行環境,而後經過複製函數的[[Scope]]屬性中的對象構建起執行環境的做用域鏈。此後,又有一個活動對象(在此做爲變量對象使用)被建立並被推入執行環境做用域鏈的前端。對於這個例子中 compare()函數的執行環境而言,其做用域鏈中包含兩個變量對象:本地活動對象和全局變量對象。顯然,做用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。

2. 匿名函數的執行環境具備全局性,所以其 this 對象一般指向 window;

3. 

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        alert(id);
    };
    element = null;
}

在上面的代碼中,經過把 element.id 的一個副本保存在一個變量中,而且在閉包中引用該變量消除了循環引用。但僅僅作到這一步,仍是不能解決內存泄漏的問題。必需要記住:閉包會引用包含函數的整個活動對象,而其中包含着 element。即便閉包不直接引用 element,包含函數的活動對象中也仍然會保存一個引用。所以,有必要把 element 變量設置爲 null。這樣就可以解除對 DOM 對象的引用,順利地減小其引用數,確保正常回收其佔用的內存。

4. 遞歸函數應該始終使用 arguments.callee 來遞歸地調用自身,不要使用函數名——函數名可能會發生變化。

注:這兩章精讀;

六. BOM:

1. 全局變量不能經過 delete 操做符刪除,而直接在 window 對象上的定義的屬性能夠。 由於使用 var 語句添加的 window 屬性有一個名爲[[Configurable]]的特性,這個特性的值被設置爲false,所以這樣定義的屬性不能夠經過delete 操做符刪除。

2. BOM 的核心對象是 window,它表示瀏覽器的一個實例。在瀏覽器中, window 對象有雙重角色,它既是經過 JavaScript 訪問瀏覽器窗口的一個接口,又是 ECMAScript 規定的 Global 對象。這意味着在網頁中定義的任何一個對象、變量和函數,都以 window 做爲其 Global 對象,所以有權訪問parseInt()等方法。

3. top \ parent:

(1)top:該變動永遠指分割窗口最高層次的瀏覽器窗口。若是計劃從分割窗口的最高層次開始執行命令,就能夠用top變量。 top 對象始終指向最高(最外)層的框架,也就是瀏覽器窗口。使用它能夠確保在一個框架中正確地訪問另外一個框架。由於對於在一個框架中編寫的任何代碼來講,其中的 window 對象指向的都是那個框架的特定實例,而非最高層的框架。
(2)opener:opener用於在window.open的頁面引用執行該window.open方法的的頁面的對象。例如:A頁面經過window.open()方法彈出了B頁面,在B頁面中就能夠經過opener來引用A頁面,這樣就能夠經過這個對象來對A頁面進行操做。 
(3)parent:parent用於在iframe,frame中生成的子頁面中訪問父頁面的對象。例如:A頁面中有一個iframe或frame,那麼iframe 或frame中的頁面就能夠經過parent對象來引用A頁面中的對象。這樣就能夠獲取或返回值到A頁面中。parent(父)對象始終指向當前框架的
直接上層框架。在某些狀況下, parent 有可能等於 top;但在沒有框架的狀況下, parent 必定等於 top(此時它們都等於 window)。

(4)self:指的是當前窗口。它始終指向 window;實際上, self window 對象能夠互換使用。引入 self 對象的目的只是爲了與 top parent 對象對應起來,所以它不格外包含其餘值。

  parent與opener的區別:

  parent指父窗口,在FRAMESET中,FRAME的PARENT就是FRAMESET窗口。 
  opener指用WINDOW.OPEN等方式建立的新窗口對應的原窗口。 
  parent是相對於框架來講父窗口對象
  opener是針對於用window.open打開的窗口來講的父窗口,前提是window.open打開的纔有

  document.parentWindow.menthod()調用父頁面的方法

  附:Window對象、Parent對象、Frame對象、Document對象和Form對象的階層關係:Window對象→Parent對象→Frame對象→Document對象→Form對象,以下: parent.frame1.document.forms[0].elements[0].value;

4. 窗口位置:

IESafariOpera Chrome 都提供了 screenLeft screenTop 屬性,分別用於表示窗口相對於屏幕左邊和上邊的位置。

Firefox 則在screenX screenY 屬性中提供相同的窗口位置信息, Safari Chrome 也同時支持這兩個屬性。

最終結果,就是沒法在跨瀏覽器的條件下取得窗口左邊和上邊的精確座標值。使用 moveTo() moveBy()方法卻是有可能將窗口精確地移動到一個新位置。這兩個方法都接收兩個參數,其中 moveTo()接收的是新位置的 x y 座標值,而 moveBy()接收的是在水平和垂直方向上移動的像素數。

5. window.open():使用 window.open()方法既能夠導航到一個特定的 URL,也能夠打開一個新的瀏覽器窗口。這個方法能夠接收 4 個參數:要加載的 URL、窗口目標、一個特性字符串以及一個表示新頁面是否取代瀏覽器歷史記錄中當前加載頁面的布爾值。一般只須傳遞第一個參數,最後一個參數只在不打開新窗口的狀況下使用。 

6. 調用 close()方法能夠關閉新打開的窗口,可是,這個方法僅適用於經過 window.open()打開的彈出窗口。對於瀏覽器的主窗口,若是沒有獲得用戶的容許是不能關閉它的。不過,彈出窗口卻是能夠調用 top.close()在不經用戶容許的狀況下關閉本身。 

7. 若是是瀏覽器擴展或其餘程序阻止的彈出窗口,那麼 window.open()一般會拋出一個錯誤。所以,要想準確地檢測出彈出窗口是否被屏蔽,必須在檢測返回值的同時,將對 window.open()的調用封裝在一個 try-catch 塊中,以下所示:

var blocked = false;
try {
    var wroxWin = window.open("http://www.wrox.com", "_blank");
    if (wroxWin == null){
        blocked = true;
    }
} catch (ex){
    blocked = true;
}
if (blocked){
    alert("The popup was blocked!");
}

8. setTimeout()的第二參數告訴 JavaScript 再過多長時間把當前任務添加到隊列中。若是隊列是空的,那麼添加的代碼會當即執行;若是隊列不是空的,那麼它就要等前面的代碼執行完了之後再執行。

9. window.location document.location 引用的是同一個對象。

10. 檢測瀏覽器中是否安裝了特定的插件是一種最多見的檢測例程。對於非 IE 瀏覽器,可使用 navigator 的 plugins 數組來達到這個目的。該數組中的每一項都包含下列屬性:
name:插件的名字。
description:插件的描述。
filename:插件的文件名。
length:插件所處理的 MIME 類型數量。

每一個插件對象自己也是一個 MimeType 對象的數組,這些對象能夠經過方括號語法來訪問。每一個 MimeType 對象有4個屬性:包含 MimeType 類型描述的 description、回指插件對象的 enabledPlugin、表示與 MIME 類型對應的文件擴展名的字符串 suffixes(以逗號分隔)和表示完整 MIME 類型字符串的 type。

10. 調用 replace() 方法能夠導航到一個新 URL,同時該 URL 會替換瀏覽器歷史紀錄中當前顯示的頁面。

11. BOM 中還有兩個對象: screen history,但它們的功能有限。 screen 對象中保存着與客戶端顯示器有關的信息,這些信息通常只用於站點分析。 history 對象爲訪問瀏覽器的歷史記錄開了一個小縫隙,開發人員能夠據此判斷歷史記錄的數量,也能夠在歷史記錄中向後或向前導航到任意頁面。

七. 客戶端檢測:

1. 能力檢測:

//做者: Peter Michaux
function isHostMethod(object, property) {
    var t = typeof object[property];
    return t=='function' ||
        (!!(t=='object' && object[property])) ||
        t=='unknown';
}

目前使用 isHostMethod()方法仍是比較可靠的,由於它考慮到了瀏覽器的怪異行爲。不過也要注意,宿主對象沒有義務保持目前的實現方式不變,也不必定會模仿已有宿主對象的行爲。因此,這個函數——以及其餘相似函數,都不能百分之百地保證永遠可靠。做爲開發人員,必須對本身要使用某個功能的風險做出理性的估計。

2. 用戶代理檢測:

如下是完整的用戶代理字符串檢測腳本,包括檢測呈現引擎、平臺、 Windows 操做系統、移動設備和遊戲系統。

var client = function(){
//呈現引擎
var engine = {
    ie: 0,
    gecko: 0,
    webkit: 0,
    khtml: 0,
    opera: 0,
    //完整的版本號
    ver: null
};
//瀏覽器
var browser = {
    //主要瀏覽器
    ie: 0,
    firefox: 0,
    safari: 0,
    konq: 0,
    opera: 0,
    chrome: 0,
    //具體的版本號
    ver: null
};
//平臺、設備和操做系統
var system = {
    win: false,
    mac: false,
    x11: false,
    //移動設備
    iphone: false,
    ipod: false,
    ipad: false,
    ios: false,
    android: false,
    nokiaN: false,
    winMobile: false,
    //遊戲系統
    wii: false,
    ps: false
};
//檢測呈現引擎和瀏覽器
var ua = navigator.userAgent;
    if (window.opera){
        engine.ver = browser.ver = window.opera.version();
        engine.opera = browser.opera = parseFloat(engine.ver);
    } else if (/AppleWebKit\/(\S+)/.test(ua)){
        engine.ver = RegExp["$1"];
        engine.webkit = parseFloat(engine.ver);
        //肯定是 Chrome 仍是 Safari
        if (/Chrome\/(\S+)/.test(ua)){
            browser.ver = RegExp["$1"];
            browser.chrome = parseFloat(browser.ver);
        } else if (/Version\/(\S+)/.test(ua)){
            browser.ver = RegExp["$1"];
            browser.safari = parseFloat(browser.ver);
        } else {
        //近似地肯定版本號
        var safariVersion = 1;
        if (engine.webkit < 100){
            safariVersion = 1;
        } else if (engine.webkit < 312){
            safariVersion = 1.2;
        } else if (engine.webkit < 412){
            safariVersion = 1.3;
        } else {
            safariVersion = 2;
        }
        browser.safari = browser.ver = safariVersion;
    }
} else if (/KHTML\/(\S+)/.test(ua) ||   /Konqueror\/([^;]+)/.test(ua)){
    engine.ver = browser.ver = RegExp["$1"];
    engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
    engine.ver = RegExp["$1"];
    engine.gecko = parseFloat(engine.ver);
    //肯定是否是 Firefox
    if (/Firefox\/(\S+)/.test(ua)){
        browser.ver = RegExp["$1"];
        browser.firefox = parseFloat(browser.ver);
    }
} else if (/MSIE ([^;]+)/.test(ua)){
    engine.ver = browser.ver = RegExp["$1"];
    engine.ie = browser.ie = parseFloat(engine.ver);
}
//檢測瀏覽器
browser.ie = engine.ie;
browser.opera = engine.opera;
//檢測平臺
var p = navigator.platform;
system.win = p.indexOf("Win") == 0;
system.mac = p.indexOf("Mac") == 0;
system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
//檢測 Windows 操做系統
if (system.win){
    if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)){
        if (RegExp["$1"] == "NT"){
            switch(RegExp["$2"]){
                case "5.0":
                    system.win = "2000";
                    break;
                case "5.1":
                    system.win = "XP";
                    break;
                case "6.0":
                    system.win = "Vista";
                    break;
                case "6.1":
                    system.win = "7";
                    break;
                default:
                    system.win = "NT";
                    break;
            }
        } else if (RegExp["$1"] == "9x"){
            system.win = "ME";
        } else {
            system.win = RegExp["$1"];
        }
    }
}
//移動設備
system.iphone = ua.indexOf("iPhone") > -1;
system.ipod = ua.indexOf("iPod") > -1;
system.ipad = ua.indexOf("iPad") > -1;
system.nokiaN = ua.indexOf("NokiaN") > -1;
//windows mobile
if (system.win == "CE"){
    system.winMobile = system.win;
} else if (system.win == "Ph"){
    if(/Windows Phone OS (\d+.\d+)/.test(ua)){;
        system.win = "Phone";
        system.winMobile = parseFloat(RegExp["$1"]);
    }
}
//檢測 iOS 版本
if (system.mac && ua.indexOf("Mobile") > -1){
    if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)){
        system.ios = parseFloat(RegExp.$1.replace("_", "."));
    } else {
        system.ios = 2; //不能真正檢測出來,因此只能猜想
    }
}
//檢測 Android 版本
if (/Android (\d+\.\d+)/.test(ua)){
    system.android = parseFloat(RegExp.$1);
}
//遊戲系統
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
//返回這些對象
return {
    engine: engine,
    browser: browser,
    system: system
};
}();

 注:用戶代理檢測是客戶端檢測的最後一個選擇。只要可能,都應該優先採用能力檢測和怪癖檢測。

在決定使用哪一種客戶端檢測方法時,通常應優先考慮使用能力檢測。怪癖檢測是肯定應該如何處理代碼的第二選擇。而用戶代理檢測則是客戶端檢測的最後一種方案,由於這種方法對用戶代理字符串具備很強的依賴性。

八. DOM:

1. JavaScript 經過 Document 類型表示文檔。在瀏覽器中, document 對象是 HTMLDocument(繼承自 Document 類型)的一個實例,表示整個 HTML 頁面。並且, document 對象是 window 對象的一個屬性,所以能夠將其做爲全局對象來訪問。 Document 節點具備下列特徵:
nodeType 的值爲 9
nodeName 的值爲"#document"
nodeValue 的值爲 null
parentNode 的值爲 null
ownerDocument 的值爲 null
其子節點多是一個 DocumentType(最多一個)、 Element(最多一個)、 ProcessingInstruction Comment

2. 除了 Document 類型以外, Element 類型就要算是 Web 編程中最經常使用的類型了。 Element 類型用於表現 XML HTML 元素,提供了對元素標籤名、子節點及特性的訪問。 Element 節點具備如下特徵:
nodeType 的值爲 1
nodeName 的值爲元素的標籤名;
nodeValue 的值爲 null
parentNode 多是 Document Element
其子節點多是 ElementTextCommentProcessingInstructionCDATASection 或 EntityReference

要訪問元素的標籤名,可使用 nodeName 屬性,也可使用 tagName 屬性;這兩個屬性會返回相同的值(使用後者主要是爲了清晰起見)。

3. 文本節點由 Text 類型表示,包含的是能夠照字面解釋的純文本內容。純文本中能夠包含轉義後的 HTML 字符,但不能包含 HTML 代碼。 Text 節點具備如下特徵:
nodeType 的值爲 3
nodeName 的值爲"#text"
nodeValue 的值爲節點所包含的文本;
parentNode 是一個 Element
不支持(沒有)子節點。能夠經過 nodeValue 屬性或 data 屬性訪問 Text 節點中包含的文本,這兩個屬性中包含的值相同。對 nodeValue 的修改也會經過 data 反映出來,反之亦然。使用下列方法能夠操做節點中的文本。
appendData(text):將 text 添加到節點的末尾。
deleteData(offset, count):從 offset 指定的位置開始刪除 count 個字符。
insertData(offset, text):在 offset 指定的位置插入 text
replaceData(offset, count, text):用 text 替換從 offset 指定的位置開始到 offsetcount 爲止處的文本。
splitText(offset):從 offset 指定的位置將當前文本節點分紅兩個文本節點。
substringData(offset, count):提取從 offset 指定的位置開始到 offset+count 爲止處的字符串。

除了這些方法以外,文本節點還有一個 length 屬性,保存着節點中字符的數目。並且,nodeValue.length data.length 中也保存着一樣的值。

4. 理解 DOM 的關鍵,就是理解 DOM 對性能的影響。 DOM 操做每每是 JavaScript 程序中開銷最大的部分,而因訪問 NodeList 致使的問題爲最多。 NodeList 對象都是「動態的」,這就意味着每次訪問NodeList 對象,都會運行一次查詢。有鑑於此,最好的辦法就是儘可能減小 DOM 操做。

5. 假設咱們想爲這個<ul>元素添加 3 個列表項。若是逐個地添加列表項,將會致使瀏覽器反覆渲染(呈現)新信息。爲避免這個問題,可使用一個文檔片斷來保存建立的列表項,而後再一次性將它們添加到文檔中。

6. document.hasFocus()方法 :經過檢測文檔是否得到了焦點,能夠知道用戶是否是正在與頁面交互。

var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true

 7. HTML5 規定能夠爲元素添加非標準的屬性,但要添加前綴 data-,目的是爲元素提供與渲染無關的信息,或者提供語義信息。這些屬性能夠任意添加、隨便命名,只要以 data-開頭便可。

<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>

添加了自定義屬性以後,能夠經過元素的 dataset 屬性來訪問自定義屬性的值。 dataset 屬性的值是 DOMStringMap 的一個實例,也就是一個名值對兒的映射。在這個映射中,每一個 data-name 形式的屬性都會有一個對應的屬性,只不過屬性名沒有 data-前綴(好比,自定義屬性是 data-myname,那映射中對應的屬性就是 myname)。仍是看一個例子吧。

//本例中使用的方法僅用於演示
var div = document.getElementById("myDiv");
//取得自定義屬性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;
//設置值
div.dataset.appId = 23456;
div.dataset.myname = "Michael";
//有沒有"myname"值呢?
if (div.dataset.myname){
    alert("Hello, " + div.dataset.myname);
}

若是須要給元素添加一些不可見的數據以便進行其餘處理,那就要用到自定義數據屬性。在跟蹤連接或混搭應用中,經過自定義數據屬性能方便地知道點擊來自頁面中的哪一個部分。

8. scrollIntoView()scrollIntoViewIfNeeded()的做用對象是元素的容器,而 scrollByLines()scrollByPages()影響的則是元素自身。

//將頁面主體滾動 5 行
document.body.scrollByLines(5);

//在當前元素不可見的時候,讓它進入瀏覽器的視口
document.images[0].scrollIntoViewIfNeeded();

//將頁面主體往回滾動 1 頁
document.body.scrollByPages(-1);

 9. 在不肯定某個給定的 CSS 屬性擁有什麼默認值的狀況下,就可使用這個方法。只要移除相應的屬性,就能夠爲元素應用默認值。

如:
myDiv.style.removeProperty("border");

 10. DOM2 級遍歷和範圍」模塊定義了兩個用於輔助完成順序遍歷 DOM 結構的類型: NodeIterator TreeWalker這兩個類型可以基於給定的起點對 DOM 結構執行深度優先( depth-first)的遍歷操做。 DOM 遍歷是深度優先的 DOM 結構遍歷,也就是說,移動的方向至少有兩個(取決於使用的遍歷類型)。遍歷以給定節點爲根,不可能向上超出 DOM 樹的根節點。如下面的 HTML 頁面爲例。

<!DOCTYPE html>
<html>
    <head>
        <title>Example</title>
    </head>
    <body>
        <p><b>Hello</b> world!</p>
    </body>
</html>    

何節點均可以做爲遍歷的根節點。 若是假設<body>元素爲根節點,那麼遍歷的第一步就是訪問<p>元素,而後再訪問同爲<body>元素後代的兩個文本節點。不過,此次遍歷永遠不會到達<html><head>元素,也不會到達不屬於<body>元素子樹的任何節點。而以 document 爲根節點的遍歷則能夠訪問到文檔中的所有節點。

(1)NodeIterator 類型是二者中比較簡單的一個,可使用 document.createNodeIterator() 方法建立它的新實例。 NodeIterator 類型的兩個主要方法是 nextNode()previousNode()。顧名思義,在深度優先的 DOM 子樹遍歷中, nextNode()方法用於向前前進一步,而 previousNode()用於向後後退一步。在剛剛建立的 NodeIterator 對象中,有一個內部指針指向根節點。

var div = document.getElementById("div1");
var filter = function(node){
    return node.tagName.toLowerCase() == "li" ?
        NodeFilter.FILTER_ACCEPT :
        NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while (node !== null) {
    alert(node.tagName); //輸出標籤名
    node = iterator.nextNode();
}

(2)TreeWalker NodeIterator 的一個更高級的版本。建立 TreeWalker 對象要使用 document.createTreeWalker()方法。除了包括 nextNode() previousNode() 在內的相同的功能以外,這個類型還提供了下列用於在不一樣方向上遍歷 DOM 結構的方法。
parentNode():遍歷到當前節點的父節點;
firstChild():遍歷到當前節點的第一個子節點;
lastChild():遍歷到當前節點的最後一個子節點;
nextSibling():遍歷到當前節點的下一個同輩節點;
previousSibling():遍歷到當前節點的上一個同輩節點。

九. 事件:

1. 事件流描述的是從頁面中接收事件的順序:事件捕獲和事件冒泡。

2. 在事件處理程序中經過 this 訪問元素的任何屬性和方法(包括默認屬性及自定義屬性)。以這種方式添加的事件處理程序會在事件流的冒泡階段被處理。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id); //"myBtn"
    alert(this.className);  // class名
};

 3. 能夠刪除經過 DOM0 級方法指定的事件處理程序,只要像下面這樣將事件處理程序屬性的值設置爲 null 便可:

btn.onclick = null; //刪除事件處理程序

若是你使用 HTML 指定事件處理程序,那麼 onclick 屬性的值就是一個包含着在同名 HTML 特性中指定的代碼的函數。而將相應的屬性設置爲 null,也能夠刪除以這種方式指定的事件處理程序。

4. 只有在事件處理程序執行期間, event 對象纔會存在;一旦事件處理程序執行完成, event 對象就會被銷燬。 (事件處理程序即觸發事件時執行的函數方法這一過程)

5. UI 事件:

(1) abort:在用戶中止下載過程時,若是嵌入的內容沒有加載完,則在<object>元素上面觸發。

(2) unload 事件:這個事件在文檔被徹底卸載後觸發。只要用戶從一個頁面切換到另外一個頁面,就會發生 unload 事件。而利用這個事件最多的狀況是清除引用,以免內存泄漏。

(3) resize:推薦使用 

EventUtil.addHandler(window, "resize", function(event){
    alert("Resized");
}); 

 關於什麼時候會觸發 resize 事件,不一樣瀏覽器有不一樣的機制。 IESafariChrome Opera 會在瀏覽器窗口變化了 1 像素時就觸發 resize 事件,而後隨着變化不斷重複觸發。 Firefox 則只會在用戶中止調整窗口大小時纔會觸發 resize 事件。因爲存在這個差異,應該注意不要在這個事件的處理程序中加入大計算量的代碼,由於這些代碼有可能被頻繁執行,從而致使瀏覽器反應明顯變慢。瀏覽器窗口最小化或最大化時也會觸發 resize 事件。

6. 鼠標事件:

觸摸設備:

iOS Android 設備的實現很是特別,由於這些設備沒有鼠標。在面向 iPhone iPod 中的 Safari 開發時,要記住如下幾點。
不支持 dblclick 事件。雙擊瀏覽器窗口會放大畫面,並且沒有辦法改變該行爲。
輕擊可單擊元素會觸發 mousemove 事件。若是此操做會致使內容變化,將再也不有其餘事件發生;若是屏幕沒有所以變化,那麼會依次發生 mousedownmouseup click 事件。輕擊不可單擊的元素不會觸發任何事件。可單擊的元素是指那些單擊可產生默認操做的元素(如連接),或者那些已經被指定了 onclick 事件處理程序的元素。
mousemove 事件也會觸發 mouseover mouseout 事件。
兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發 mousewheel scroll 事件。

7. beforeunload 事件:
這個事件的意圖是將控制權交給用戶。顯示的消息會告知用戶頁面行將被卸載(正由於如此纔會顯示這個消息),詢問用戶是否真的要關閉頁面,仍是但願繼續留下來。

爲了顯示這個彈出對話框,必須將 event.returnValue 的值設置爲要顯示給用戶的字符串(對IE Fiefox 而言),同時做爲函數的值返回(對 Safari Chrome 而言)

EventUtil.addHandler(window, "beforeunload", function(event){
    event = EventUtil.getEvent(event);
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message;
    return message;
});

 8. 觸摸事件:touchend 事件發生時, touches 集合中就沒有任何 Touch 對象了,由於不存在活動的觸摸操做;此時,就必須轉而使用 changeTouchs 集合。

在觸摸屏幕上的元素時,這些事件(包括鼠標事件)發生的順序以下:

(1) touchstart
(2) mouseover
(3) mousemove(一次)
(4) mousedown
(5) mouseup
(6) click
(7) touchend


9. 手勢事件:

當兩個手指觸摸屏幕時就會產生手勢,手勢一般會改變顯示項的大小,或者旋轉顯示項。有三個手勢事件:
gesturestart:當一個手指已經按在屏幕上而另外一個手指又觸摸屏幕時觸發。
gesturechange:當觸摸屏幕的任何一個手指的位置發生變化時觸發。
gestureend:當任何一個手指從屏幕上面移開時觸發。

每一個手勢事件的 event 對象都包含着標準的鼠標事件屬性: bubblescancelableviewclientXclientYscreenXscreenYdetailaltKeyshiftKeyctrlKey metaKey。此外,還包含兩個額外的屬性: rotation scale。其中, rotation 屬性表示手指變化引發的旋轉角度,負值表示逆時針旋轉,正值表示順時針旋轉(該值從 0 開始)。而 scale 屬性表示兩個手指間距離的變化狀況(例如向內收縮會縮短距離);這個值從 1 開始,並隨距離拉大而增加,隨距離縮短而減少。

 10. 事件委託:事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。 使用事件委託,只需在DOM 樹中儘可能最高的層次上添加一個事件處理程序。

這種技術須要佔用的內存更少。全部用到按鈕的事件(多數鼠標事件和鍵盤事件)都適合採用事件委託技術。

var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "click", function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch(target.id){
        case "doSomething":
            document.title = "I changed the document's title";
            break;
        case "goSomewhere":
            location.href = "http://www.wrox.com";
            break;
        case "sayHi":
            alert("hi");
            break;
    }
});

 11. 內存與性能

(1)事件委託

(2)移除事件處理程序:每當將事件處理程序指定給元素時,運行中的瀏覽器代碼與支持頁面交互的 JavaScript 代碼之間就會創建一個鏈接。這種鏈接越多,頁面執行起來就越慢。如前所述,能夠採用事件委託技術,限制創建的鏈接數量。

<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    //先執行某些操做
    btn.onclick = null; //移除事件處理程序
        document.getElementById("myDiv").innerHTML = "Processing...";
    };
</script>

注意,在事件處理程序中刪除按鈕也能阻止事件冒泡。目標元素在文檔中是事件冒泡的前提。

採用事件委託也有助於解決這個問題。若是事先知道未來有可能使用 innerHTML 替換掉頁面中的某一部分,那麼就能夠不直接把事件處理程序添加到該部分的元素中。而經過把事件處理程序指定給較高層次的元素,一樣可以處理該區域中的事件。

最好的作法是在頁面卸載以前,先經過 onunload 事件處理程序移除全部事件處理程序。在此,事件委託技術再次表現出它的優點——須要跟蹤的事件處理程序越少,移除它們就越容易。

12. 模擬事件:模擬事件就如同瀏覽器建立的事件同樣.

(1)能夠在 document 對象上使用 createEvent()方法建立 event 對象。這個方法接收一個參數,即表示要建立的事件類型的字符串。

(2)建立了 event 對象以後,還須要使用與事件有關的信息對其進行初始化。每種類型的 event 對象都有一個特殊的方法,爲它傳入適當的數據就能夠初始化該 event 對象。不一樣類型的這個方法的名字也不相同,具體要取決於 createEvent()中使用的參數。

(3)模擬事件的最後一步就是觸發事件。這一步須要使用 dispatchEvent()方法,全部支持事件的DOM 節點都支持這個方法。調用 dispatchEvent()方法時,須要傳入一個參數,即表示要觸發事件的 event 對象。

// 模擬鼠標事件
var
btn = document.getElementById("myBtn"); //建立事件對象 var event = document.createEvent("MouseEvents"); //初始化事件對象 event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); //觸發事件 btn.dispatchEvent(event);

鍵盤事件:KeyboardEvent

var textbox = document.getElementById("myTextbox"),
event;
//以 DOM3 級方式建立事件對象
if (document.implementation.hasFeature("KeyboardEvents", "3.0")){
    event = document.createEvent("KeyboardEvent");
    //初始化事件對象
    event.initKeyboardEvent("keydown", true, true, document.defaultView, "a",0, "Shift", 0);
}
//觸發事件
textbox.dispatchEvent(event);

 十. 表單:

1. 只要表單中存在 type 爲 submit 或 image的 button 或 input 任何一種按鈕,那麼在相應表單控件擁有焦點的狀況下,按回車鍵就能夠提交該表單。( textarea 是一個例外,在文本區中回車會換行)若是表單裏沒有提交按鈕,按回車鍵不會提交表單。

阻止這個事件的默認行爲就能夠取消表單提交。

var form = document.getElementById("myForm");
EventUtil.addHandler(form, "submit", function(event){
    //取得事件對象
    event = EventUtil.getEvent(event);
    //阻止默認事件
    EventUtil.preventDefault(event);
});

在如下面調用 submit()方法的形式提交表單時,不會觸發 submit 事件,所以要記得在調用此方法以前先驗證表單數據。

var form = document.getElementById("myForm");
//提交表單
form.submit();

 2. 每一個表單字段都有兩個方法: focus()blur() 。

3.  除了支持鼠標、鍵盤、更改和 HTML 事件以外,全部表單字段都支持下列 3 個事件。
blur:當前字段失去焦點時觸發。
change:對於<input><textarea>元素,在它們失去焦點且 value 值改變時觸發;對於<select>元素,在其選項改變時觸發。
focus:當前字段得到焦點時觸發。

當用戶改變了當前字段的焦點,或者咱們調用了 blur()focus()方法時,均可以觸發 blur 和 focus 事件。

4. 選擇文本:文本框都支持 select()方法,這個方法用於選擇文本框中的全部文本。 —— select() 方法對應的,是一個 select 事件。在選擇了文本框中的文本時,就會觸發 select 事件。

5. 操做剪切板:

下列就是 6 個剪貼板事件。
beforecopy:在發生複製操做前觸發。
copy:在發生複製操做時觸發。
beforecut:在發生剪切操做前觸發。
cut:在發生剪切操做時觸發。
beforepaste:在發生粘貼操做前觸發。
paste:在發生粘貼操做時觸發
在實際的事件發生以前,經過 beforecopybeforecut beforepaste 事件能夠在向剪貼板發送數據,或者從剪貼板取得數據以前修改數據。不過,取消這些事件並不會取消對剪貼板的操做——只有取消 copycut paste 事件,才能阻止相應操做發生。

要訪問剪貼板中的數據,可使用 clipboardData 對象:在 IE 中,這個對象是 window 對象的屬性;而在 Firefox 4+Safari Chrome 中,這個對象是相應 event 對象的屬性。可是,在 FirefoxSafari Chorme 中,只有在處理剪貼板事件期間 clipboardData 對象纔有效,這是爲了防止對剪貼板的未受權訪問;在 IE 中,則能夠隨時訪問 clipboardData 對象。爲了確保跨瀏覽器兼容性,最好只在發生剪貼板事件期間使用這個對象。

這個 clipboardData 對象有三個方法: getData()setData()clearData()。 其中, getData() 用於從剪貼板中取得數據,它接受一個參數,即要取得的數據的格式。在 IE 中,有兩種數據格式: "text""URL"。在 FirefoxSafari Chrome 中,這個參數是一種 MIME 類型;不過,能夠用"text"表明"text/plain"setData()方法的第一個參數也是數據類型,第二個參數是要放在剪貼板中的文本。

var EventUtil = {
    //省略的代碼
    getClipboardText: function(event){
        var clipboardData = (event.clipboardData ||    window.clipboardData);
        return clipboardData.getData("text");
    },
    //省略的代碼
    setClipboardText: function(event, value){
        if (event.clipboardData){
            return event.clipboardData.setData("text/plain", value);
        } else if (window.clipboardData){
            return window.clipboardData.setData("text", value);
        }
    },
    //省略的代碼
};

 6. 自動切換焦點:

(function(){
    function tabForward(event){
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if (target.value.length == target.maxLength){
            var form = target.form;
            for (var i=0, len=form.elements.length; i < len; i++) {
                if (form.elements[i] == target) {
                    if (form.elements[i+1]){
                        form.elements[i+1].focus();
                    }
                    return;
                }
            }
        }
    }
    var textbox1 = document.getElementById("txtTel1");
    var textbox2 = document.getElementById("txtTel2");
    var textbox3 = document.getElementById("txtTel3");
    EventUtil.addHandler(textbox1, "keyup", tabForward);
    EventUtil.addHandler(textbox2, "keyup", tabForward);
    EventUtil.addHandler(textbox3, "keyup", tabForward);
})();    

 7. 下拉列表select:

移動和重排選項:

使用 DOM appendChild()方法,就能夠將第一個選擇框中的選項直接移動到第二個選擇框中。咱們知道,若是爲 appendChild()方法傳入一個文檔中已有的元素,那麼就會先從該元素的父節點中移除它,再把它添加到指定的位置。

移動選項與移除選項有一個共同之處,即會重置每個選項的 index 屬性。
要將選擇框中的某一項移動到特定位置,最合適的 DOM 方法就是 insertBefore()appendChild()方法只適用於將選項添加到選擇框的最後。

8. 富文本:

(1) 要讓文檔中的 iframe 能夠編輯,必需要將 designMode 設置爲"on",但只有在頁面徹底加載以後才能設置這個屬性。

<iframe name="edit" height="100" width="200"></iframe>

<script>
window.addEventListener('load', function() {
  frames['edit'].document.designMode = 'on';
});
</script>

 (2) 另外一種編輯富文本內容的方式是使用名爲 contenteditable 的特殊屬性,能夠把 contenteditable 屬性應用給頁面中的任何元素,而後用戶當即就能夠編輯該元素。

<div class="editable" id="richedit" contenteditable></div>

 (3) 與富文本編輯器交互的主要方式,就是使用 document.execCommand()能夠爲 document.execCommand()方法傳遞 3 個參數:要執行的命令名稱、表示瀏覽器是否應該爲當前命令提供用戶界面的一個布爾值和執行命令必須的一個值(若是不須要值,則傳遞 null)。

十一. HTML5:

1. 跨文檔消息傳送簡稱爲 XDM,指的是在來自不一樣域的頁面間傳遞消息。例如, www.wrox.com 域中的頁面與位於一個內嵌框架中的 p2p.wrox.com 域中的頁面通訊。

XDM 的核心是 postMessage()方法。在 HTML5 規範中,除了 XDM 部分以外的其餘部分也會提到這個方法名,但都是爲了同一個目的:向另外一個地方傳遞數據。對於 XDM 而言, 另外一個地方指的是包含在當前頁面中的<iframe>元素,或者由當前頁面彈出的窗口。
postMessage()方法接收兩個參數:一條消息和一個表示消息接收方來自哪一個域的字符串。第二個參數對保障安全通訊很是重要,能夠防止瀏覽器把消息發送到不安全的地方。

//注意:全部支持 XDM 的瀏覽器也支持 iframe 的 contentWindow 屬性
var iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");

最後一行代碼嘗試向內嵌框架中發送一條消息,並指定框架中的文檔必須來源於"http://www.wrox.com"域。若是來源匹配,消息會傳遞到內嵌框架中;不然, postMessage()什麼也不作。

接收到 XDM 消息時,會觸發 window 對象的 message 事件。這個事件是以異步形式觸發的,所以從發送消息到接收消息(觸發接收窗口的 message 事件)可能要通過一段時間的延遲。觸發 message 事件後,傳遞給 onmessage 處理程序的事件對象包含如下三方面的重要信息。
data:做爲 postMessage()第一個參數傳入的字符串數據。
origin:發送消息的文檔所在的域,例如"http://www.wrox.com"。 

source:發送消息的文檔的 window 對象的代理。這個代理對象主要用於在發送上一條消息的窗口中調用 postMessage()方法。若是發送消息的窗口來自同一個域,那這個對象就是 window

接收到消息後驗證發送窗口的來源是相當重要的。就像給 postMessage()方法指定第二個參數,以確保瀏覽器不會把消息發送給未知頁面同樣,在 onmessage 處理程序中檢測消息來源能夠確保傳入的消息來自已知的頁面。基本的檢測模式以下。

EventUtil.addHandler(window, "message", function(event){
    //確保發送消息的域是已知的域
    if (event.origin == "http://www.wrox.com"){
        //處理接收到的數據
        processMessage(event.data);
        //可選:向來源窗口發送回執
        event.source.postMessage("Received!", "http://p2p.wrox.com");
    }
});

event.source 大多數狀況下只是 window 對象的代理,並不是實際的 window 對象。換句話說,不能經過這個代理對象訪問 window 對象的其餘任何信息。記住,只經過這個代理調用postMessage()就好,這個方法永遠存在,永遠能夠調用 。

2. 拖拽

(1)拖動某元素時,將依次觸發dragstart、drag、dragend:

按下鼠標鍵並開始移動鼠標時,會在被拖放的元素上觸發 dragstart 事件。此時光標變成不能放符號(圓環中有一條反斜線),表示不能把元素放到本身上面。拖動開始時,能夠經過 ondragstart 事件處理程序來運行 JavaScript 代碼。觸發 dragstart 事件後,隨即會觸發 drag 事件,並且在元素被拖動期間會持續觸發該事件。這個事件與 mousemove 事件類似,在鼠標移動過程當中, mousemove 事件也會持續發生。 當拖動中止時(不管是把元素放到了有效的放置目標,仍是放到了無效的放置目標上),會觸發 dragend 事件。上述三個事件的目標都是被拖動的元素。默認狀況下,瀏覽器不會在拖動期間改變被拖動元素的外觀,但你能夠本身修改。不過,大多數瀏覽器會爲正被拖動的元素建立一個半透明的副本,這個副本始終跟隨着光標移動。

(2)當某個元素被拖動到一個有效的放置目標上時,會依次發生:dragenter、dragover、dragleave drop:

只要有元素被拖動到放置目標上,就會觸發 dragenter 事件(相似於 mouseover 事件)。緊隨其後的是 dragover 事件,並且在被拖動的元素還在放置目標的範圍內移動時,就會持續觸發該事件。若是元素被拖出了放置目標, dragover 事件再也不發生,但會觸發 dragleave 事件(相似於 mouseout 事件)。若是元素被放到了放置目標中,則會觸發 drop 事件而不是 dragleave 事件。上述三個事件的目標都是做爲放置目標的元素。

(3)當遇到不可拖拽到的目標時:

var droptarget = document.getElementById("droptarget");
EventUtil.addHandler(droptarget, "dragover", function(event){
    EventUtil.preventDefault(event);
});
EventUtil.addHandler(droptarget, "dragenter", function(event){
    EventUtil.preventDefault(event);
});

// 在 Firefox 3.5+中,放置事件的默認行爲是打開被放到放置目標上的 URL。爲了讓 Firefox 支持正常的拖放,還要取消 drop 事件的默認行爲,阻止它打開 URL:
EventUtil.addHandler(droptarget, "drop", function(event){
    EventUtil.preventDefault(event);
});

(4)數據交換:dataTransfer對象。

dataTransfer 對象有兩個主要方法: getData()setData()。不難想象, getData()能夠取得由 setData()保存的值。 setData()方法的第一個參數,也是 getData()方法惟一的一個參數,是一個字符串,表示保存的數據類型,取值爲"text""URL"。

//設置和接收文本數據
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");
//設置和接收 URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");

IE只定義了"text""URL"兩種有效的數據類型,而HTML5則對此加以擴展,容許指定各類 MIME 類型。考慮到向後兼容, HTML5 也支持"text""URL",但這兩種類型會被映射爲"text/plain""text/uri-list"

實際上, dataTransfer 對象能夠爲每種 MIME 類型都保存一個值。換句話說,同時在這個對象中保存一段文本和一個 URL 不會有任何問題。不過,保存在 dataTransfer 對象中的數據只能在 drop 事件處理程序中讀取。若是在 ondrop 處理程序中沒有讀到數據,那就是 dataTransfer 對象已經被銷燬,數據也丟失了。

在拖動文本框中的文本時,瀏覽器會調用 setData()方法,將拖動的文本以"text"格式保存在 dataTransfer 對象中。

(5)拖拽屬性:dataTransfer 對象的兩個屬性: dropEffect 和 effectAllowed

dropEffect:

"none":不能把拖動的元素放在這裏。這是除文本框以外全部元素的默認值。
"move":應該把拖動的元素移動到放置目標。
"copy":應該把拖動的元素複製到放置目標。
"link":表示放置目標會打開拖動的元素(但拖動的元素必須是一個連接,有 URL)。

dropEffect 屬性只有搭配 effectAllowed 屬性纔有用。 effectAllowed 屬性表示容許拖動元素的哪一種 dropEffecteffectAllowed 屬性:

"uninitialized":沒有給被拖動的元素設置任何放置行爲。
"none":被拖動的元素不能有任何行爲。
"copy":只容許值爲"copy"dropEffect
"link":只容許值爲"link"dropEffect
"move":只容許值爲"move"dropEffect
"copyLink":容許值爲"copy""link"dropEffect
"copyMove":容許值爲"copy""move"dropEffect
"linkMove":容許值爲"link""move"dropEffect
"all":容許任意 dropEffect
必須在 ondragstart 事件處理程序中設置 effectAllowed 屬性。
(6)可拖動:draggable

<!-- 讓這個圖像不能夠拖動 -->
<img src="smile.gif" draggable="false" alt="Smiley face">
<!-- 讓這個元素能夠拖動 -->
<div draggable="true">...</div>

(7)其餘:

HTML5 規範規定 dataTransfer 對象還應該包含下列方法和屬性。
addElement(element):爲拖動操做添加一個元素。添加這個元素隻影響數據(即增長做爲拖動源而響應回調的對象),不會影響拖動操做時頁面元素的外觀。在寫做本書時, 只有 Firefox 3.5+實現了這個方法。
clearData(format):清除以特定格式保存的數據。實現這個方法的瀏覽器有 IEFireforx 3.5+Chrome Safari 4+
setDragImage(element, x, y):指定一幅圖像,當拖動發生時,顯示在光標下方。這個方法接收的三個參數分別是要顯示的 HTML 元素和光標在圖像中的 xy 座標。其中, HTML 元素能夠是一幅圖像,也能夠是其餘元素。是圖像則顯示圖像,是其餘元素則顯示渲染後的元素。實現這個方法的瀏覽器有 Firefox 3.5+Safari 4+Chrome
types:當前保存的數據類型。這是一個相似數組的集合,以"text"這樣的字符串形式保存着數據類型。實現這個屬性的瀏覽器有 IE10+Firefox 3.5+Chrome

 3. 音視頻:

<audio>元素還有一個原生的 JavaScript 構造函數 Audio,能夠在任什麼時候候播放音頻。從同爲 DOM 元素的角度看, Audio Image 很類似,但 Audio 不用像 Image 那樣必須插入到文檔中。只要建立一個新實例,並傳入音頻源文件便可。

var audio = new Audio("sound.mp3");
EventUtil.addHandler(audio, "canplaythrough", function(event){
    audio.play();
});

建立新的 Audio 實例便可開始下載指定的文件。下載完成後,調用 play()就能夠播放音頻。在 iOS 中,調用 play()時會彈出一個對話框,獲得用戶的許可後才能播放聲音。若是想在一段音頻播放後再播放另外一段音頻,必須在 onfinish 事件處理程序中調用 play()方法。

4. 歷史狀態管理

經過 hashchange 事件,能夠知道 URL 的參數何時發生了變化,即何時該有所反應。而經過狀態管理 API,可以在不加載新頁面的狀況下改變瀏覽器的 URL 。爲此 ,須要使用history.pushState()方法,該方法能夠接收三個參數:狀態對象、新狀態的標題和可選的相對 URL。

history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html");

執行 pushState()方法後,新的狀態信息就會被加入歷史狀態棧,而瀏覽器地址欄也會變成新的相對 URL。可是,瀏覽器並不會真的向服務器發送請求,即便狀態改變以後查詢 location.href 也會返回與地址欄中相同的地址。

按下「後退」按鈕,會觸發 window 對象的 popstate 事件popstate 事件的事件對象有一個 state 屬性,這個屬性就包含着當初以第一個參數傳遞給 pushState() 的狀態對象。

EventUtil.addHandler(window, "popstate", function(event){
    var state = event.state;
    if (state){ //第一個頁面加載時 state 爲空
        processState(state);
    }
});

十二. 錯誤處理與調試:

1. 若是提供 finally 子句,則 catch 子句就成了可選的( catch finally 有一個便可)。只要代碼中包含 finally 子句,那麼不管 try 仍是 catch 語句塊中的 return 語句都將被忽略。

function testFinally(){
    try {
        return 2;
    } catch (error){
        return 1;
    } finally {
        return 0;
    }
}

好比上例:調用這個函數只能返回 0。若是把 finally 子句拿掉,這個函數將返回 2

2. 錯誤類型(ECMA-262定義了 7 個):

Error:主要是瀏覽器拋出的;
EvalError:會在使用 eval()函數而發生異常時被拋出。
RangeError:會在數值超出相應範圍時觸發。
ReferenceError:在找不到對象的狀況下,會發生 ReferenceError(這種狀況下,會直接致使人所共知的"object expected"瀏覽器錯誤)。一般,在訪問不存在的變量時,就會發生這種錯誤。
SyntaxError:當咱們把語法錯誤的 JavaScript 字符串傳入 eval()函數時,就會致使此類錯誤。
TypeError:在變量中保存着意外的類型時,或者在訪問不存在的方法時,都會致使這種錯誤。歸根結底仍是因爲在執行特定於類型的操做時,變量的類型並不符合要求所致。
URIError:在使用 encodeURI()decodeURI(),而 URI 格式不正確時,就會致使 URIError 錯誤。這種錯誤也不多見,由於前面說的這兩個函數的容錯性很是高。

var obj = x; //在 x 並未聲明的狀況下拋出 ReferenceError

eval("a ++ b"); //拋出 SyntaxError

var o = new 10; //拋出 TypeError
alert("name" in true); //拋出 TypeError
Function.prototype.toString.call("name"); //拋出 TypeError

// 處理錯誤類型
try {
    someFunction();
} catch (error){
    if (error instanceof TypeError){
    //處理類型錯誤
    } else if (error instanceof ReferenceError){
    //處理引用錯誤
    } else {
    //處理其餘類型的錯誤
    }
}
var obj = x; //在 x 並未聲明的狀況下拋出 ReferenceError

eval("a ++ b"); //拋出 SyntaxError

var o = new 10; //拋出 TypeError
alert("name" in true); //拋出 TypeError
Function.prototype.toString.call("name"); //拋出 TypeError

// 處理錯誤類型
try {
    someFunction();
} catch (error){
    if (error instanceof TypeError){
    //處理類型錯誤
    } else if (error instanceof ReferenceError){
    //處理引用錯誤
    } else {
    //處理其餘類型的錯誤
    }
}

 3. 拋出錯誤與使用 try-catch:

關於什麼時候該拋出錯誤,而什麼時候該使用 try-catch 來捕獲它們,是一個老生常談的問題。通常來講,應用程序架構的較低層次中常常會拋出錯誤,但這個層次並不會影響當前執行的代碼,於是錯誤一般得不到真正的處理。若是你打算編寫一個要在不少應用程序中使用的 JavaScript 庫,甚至只編寫一個可能會在應用程序內部多個地方使用的輔助函數,我都強烈建議你在拋出錯誤時提供詳盡的信息。而後,便可在應用程序中捕獲並適當地處理這些錯誤。說到拋出錯誤與捕獲錯誤,咱們認爲只應該捕獲那些你確切地知道該如何處理的錯誤。捕獲錯誤的目的在於避免瀏覽器以默認方式處理它們;而拋出錯誤的目的在於提供錯誤發生具體緣由的消息。

4. 錯誤事件:

只要發生錯誤,不管是否是瀏覽器生成的,都會觸發 error 事件,並執行這個事件處理程序。而後,瀏覽器默認的機制發揮做用,像往常同樣顯示出錯誤消息。像下面這樣在事件處理程序中返回false,能夠阻止瀏覽器報告錯誤的默認行爲。

window.onerror = function(message, url, line){
    alert(message);
    return false;
};

經過返回 false,這個函數實際上就充當了整個文檔中的 try-catch 語句,能夠捕獲全部無代碼處理的運行時錯誤。這個事件處理程序是避免瀏覽器報告錯誤的最後一道防線,理想狀況下,只要可能就不該該使用它。

5. url 錯誤:

// 不正確的url
http://www.yourdomain.com/?redir=http://www.someotherdomain.com?a=b&c=d

// 針對"redir="後面的全部字符串調用 encodeURIComponent()就能夠解決這個問題
http://www.yourdomain.com/?redir=http%3A%2F%2Fwww.someotherdomain.com%3Fa%3Db%26c%3Dd

 對於查詢字符串,應該記住必需要使用 encodeURIComponent()方法。 

十三. Ajax:

1. 在接收到響應以前還能夠調用 abort()方法來取消異步請求:xhr.abort();調用這個方法後, XHR 對象會中止觸發事件,並且也再也不容許訪問任何與響應有關的對象屬性。在終止請求以後,還應該對 XHR 對象進行解引用操做。因爲內存緣由,不建議重用 XHR 對象。

2. 使用 GET 請求常常會發生的一個錯誤,就是查詢字符串的格式有問題。查詢字符串中每一個參數的名稱和值都必須使用 encodeURIComponent()進行編碼,而後才能放到 URL 的末尾;並且全部名-值對兒都必須由和號( &)分隔,以下面的例子所示。

xhr.open("get", "example.php?name1=value1&name2=value2", true);

下面這個函數能夠輔助向現有 URL 的末尾添加查詢字符串參數:

function addURLParam(url, name, value) {
    url += (url.indexOf("?") == -1 ? "?" : "&");
    url += encodeURIComponent(name) + "=" +  encodeURIComponent(value);
    return url;
}

 3. 進度事件:

4. Comet:

Comet 是一種服務器向頁面推送數據的技術。 Comet 可以讓信息近乎實時地被推送到頁面上,很是適合處理體育比賽的分數和股票報價。

有兩種實現 Comet 的方式: 長輪詢。長輪詢是傳統輪詢(也稱爲短輪詢)的一個翻版,即瀏覽器定時向服務器發送請求,看有沒有更新的數據。第二種流行的 Comet 實現是 HTTP 流。流不一樣於上述兩種輪詢,由於它在頁面的整個生命週期內只使用一個 HTTP 鏈接。具體來講,就是瀏覽器向服務器發送一個請求,而服務器保持鏈接打開,而後週期性地向瀏覽器發送數據。

5. 服務器發送事件(SSE:Server-Sent Events):

SSE API 用於建立到服務器的單向鏈接,服務器經過這個鏈接能夠發送任意數量的數據。SSE 支持短輪詢、長輪詢和 HTTP 流,並且能在斷開鏈接時自動肯定什麼時候從新鏈接。

 

6.Web Sockets : 

Web Sockets 的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。在 JavaScript 中建立了 Web Socket 以後,會有一個 HTTP 請求發送到瀏覽器以發起鏈接。在取得服務器響應後,創建的鏈接會使用 HTTP 升級從 HTTP 協議交換爲 Web Socket 協議。也就是說,使用標準的 HTTP 服務器沒法實現 Web Sockets,只有支持這種協議的專門服務器才能正常工做。

7. 安全:

爲確保經過 XHR 訪問的 URL 安全,通行的作法就是驗證發送請求者是否有權限訪問相應的資源。有下列幾種方式可供選擇:
要求以 SSL 鏈接來訪問能夠經過 XHR 請求的資源。
要求每一次請求都要附帶通過相應算法計算獲得的驗證碼。
請注意,下列措施對防範 CSRF 攻擊不起做用。
要求發送 POST 而不是 GET 請求——很容易改變。
檢查來源 URL 以肯定是否可信——來源記錄很容易僞造。
基於 cookie 信息進行驗證——一樣很容易僞造。

十四. 高級技巧:

1. 類型檢測:

instanceof 操做符在存在多個全局做用域(像一個頁面包含多個 frame)的狀況下, 可能會判斷錯誤,如:

var isArray = value instanceof Array;

以上代碼要返回 truevalue 必須是一個數組,並且還必須與 Array 構造函數在同個全局做用域中。(別忘了, Array window 的屬性。)若是 value 是在另個 frame 中定義的數組,那麼以上代碼就會返回 false

Web 開發中可以區分原生與非原生 JavaScript 對象很是重要。只有這樣才能確切知道某個對象到底有哪些功能。這個技巧能夠對任何對象給出正確的結論。

注:Object.prototpye.toString()自己也可能會被修改。本節討論的技巧假設 Object.prototpye.toString()是未被修改過的原生版本。

2. 做用域安全的構造函數:

上面這段重寫的代碼中, 一個Rectangle 實例也同時是一個Polygon 實例, 因此Polygon.call() 會照原意執行,最終爲 Rectangle 實例添加了 sides 屬性。

3. 惰性載入函數:

惰性載入表示函數執行的分支僅會發生一次。有兩種實現惰性載入的方式,第一種就是在函數被調用時再處理函數。在第一次調用的過程當中,該函數會被覆蓋爲另一個按合適方式執行的函數,這樣任何對 原 函數 的調 用 都不 用再 經 過執 行的 分 支了 。例 如 ,能夠用 下面的方式使用惰性載入重寫 createXHR()。

方式一:

 

 方式二:

 4.函數綁定:

function bind(fn, context){
    return function(){
        return fn.apply(context, arguments);
    };
}

只要是將某個函數指針以值的形式進行傳遞,同時該函數必須在特定環境中執行,被綁定函數的效用就突顯出來了。它們主要用於事件處理程序以及 setTimeout() setInterval()。然而,被綁定函數與普通函數相比有更多的開銷,它們須要更多內存,同時也由於多重函數調用稍微慢一點,因此最好只在必要時使用。

5. 函數柯里化:

curry() 函數的主要工做是將被返回函數的參數進行排序。 curry() 的第一個參數是要進行柯里化的函數,其餘參數是要傳入的值。

柯里化函數一般由如下步驟動態建立:調用另外一個函數併爲它傳入要柯里化的函數和必要參數。下面是建立柯里化函數的通用方式。

function curry(fn){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    };
}

 5. 防篡改對象:

(1) 不可擴展:Object.preventExtensions(),不能給對象添加新屬性和方法;

(2) 密封對象:Object.seal(),密封對象不可擴展,並且已有成員的 [[Configurable]] 特性將被設置爲 false,即不能刪除屬性和方法,不能使用 Object.defineProperty()把數據屬性修改成訪問器屬性。檢測:使用 Object.isSealed()方法能夠肯定對象是否被密封了。由於被密封的對象不可擴展,Object.isExtensible() 檢測密封的對象也會返回 false

(3) 凍結對象:Object.freeze(),不可擴展,密封的,對象數據屬性的 [[Writable]] 特性會被設置爲 false。檢測:Object.isFrozen() 方法用於檢測凍結對象。凍結對象既是密封的又不可擴展,用 Object.isExtensible() Object.isSealed() 檢測凍結對象將分別返回 false true

6. 高級定時器:關於定時器,指定的時間間隔表示什麼時候將定時器的代碼添加到隊列,而不是什麼時候實際執行代碼。

注:setInterval():這種重複定時器的規則有兩個問題: (1) 某些間隔會被跳過; (2) 多個定時器的代碼執行之間的間隔可能會比預期的小。替代方案以下(鏈式調用 setTimeout):

setTimeout(function(){
    //處理中
    setTimeout(arguments.callee, interval);
}, interval);

7. Yielding Processes:

數組分塊技術:基本的思路是爲要處理的項目建立一個隊列,而後使用定時器取出下一個要處理的項目進行處理,接着再設置另外一個定時器。基本的模式以下:

function chunk(array, process, context){
    setTimeout(function(){
        var item = array.shift();
        process.call(context, item);
        if (array.length > 0){
            setTimeout(arguments.callee, 100);
        }
    }, 100);
}

在數組分塊模式中, array 變量本質上就是一個「待辦事宜」列表,它包含了要處理的項目。使用 shift() 方法能夠獲取隊列中下一個要處理的項目,而後將其傳遞給某個函數。若是在隊列中還有其餘項目,則設置另外一個定時器,並經過 arguments.callee 調用同一個匿名函數。chunk()方法接受三個參數:要處理的項目的數組,用於處理項目的函數,以及可選的運行該函數的環境。

8. 函數節流:

function throttle(method, context) {
    clearTimeout(method.tId);
    method.tId= setTimeout(function(){
        method.call(context);
    }, 100);
}

函數節流背後的基本思想是指,某些代碼不能夠在沒有間斷的狀況連續重複執行。第一次調用函數,建立一個定時器,在指定的時間間隔以後運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另外一個。若是前一個定時器已經執行過了,這個操做就沒有任何意義。然而,若是前一個定時器還沒有執行,其實就是將其替換爲一個新的定時器。目的是隻有在執行函數的請求中止了一段時間以後才執行。

9. 自定義事件:

自定義事件背後的概念是建立一個管理事件的對象,讓其餘對象監聽那些事件。實現此功能的基本模式以下:

function EventTarget(){
    this.handlers = {};
}
EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function(type, handler){
        if (typeof this.handlers[type] == "undefined"){
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire: function(event){
        if (!event.target){
            event.target = this;
        }
        if (this.handlers[event.type] instanceof Array){
            var handlers = this.handlers[event.type];
            for (var i=0, len=handlers.length; i < len; i++){
                handlers[i](event);
            }
        }
    },
    removeHandler: function(type, handler){
        if (this.handlers[type] instanceof Array){
            var handlers = this.handlers[type];
            for (var i=0, len=handlers.length; i < len; i++){
                if (handlers[i] === handler){
                    break;
                }
            }
            handlers.splice(i, 1);
        }
    }
};    

EventTarget 類型有一個單獨的屬性 handlers,用於儲存事件處理程序。還有三個方法:addHandler() , 用於註冊給定類型事件的事件處理程序; fire() ,用於觸發一個事件;removeHandler(),用於註銷某個事件類型的事件處理程序。

十五. 離線存儲:

1. navigator.onLine 屬性,這個屬性值爲 true 表示設備能上網,值爲 false 表示設備離線。

2. HTML5 還定義了兩個事件:online offline。當網絡從離線變爲在線或者從在線變爲離線時,分別觸發這兩個事件。這兩個事件在 window 對象上觸發。

EventUtil.addHandler(window, "online", function(){
    alert("Online");
});
EventUtil.addHandler(window, "offline", function(){
    alert("Offline");
});

 3. cookie:

(1)當超過單個域名限制以後還要再設置 cookie,瀏覽器就會清除之前設置的 cookieIE Opera 會刪除最近最少使用過的( LRULeast Recently Usedcookie,騰出空間給新設置的 cookieFirefox 看上去好像是隨機決定要清除哪一個 cookie,因此考慮 cookie 限制很是重要,以避免出現不可預期的後果。瀏覽器中對於 cookie 的尺寸也有限制。大多數瀏覽器都有大約 4096B(加減 1)的長度限制。

  爲了最佳的瀏覽器兼容性,最好將整個 cookie 長度限制在 4095B(含 4095)之內。尺寸限制影響到一個域下全部的 cookie,而並不是每一個 cookie 單獨限制。若是你嘗試建立超過最大尺寸限制的 cookie,那麼該 cookie 會被悄無聲息地丟掉。注意,雖然一個字符一般佔用一字節,可是多字節狀況則有不一樣。

(2)cookie 全部的 name 和 value 都需通過 URL 編碼,因此使用時也必須使用 decodeURIComponent()來解碼。
(3)因爲 js 中讀寫入 cookie 都不是很直觀,因此通常會寫一些函數來簡化 cookie 的功能:

 

 

 

 這些方法經過處理解析、構造 cookie 字符串的任務令在客戶端利用 cookie 存儲數據更加簡單。

 (4)cookie:cookie 是存放在單個 cookie 中的更小段的數據,也就是使用 cookie 值來存儲多個名稱值對兒。子 cookie 最多見的的格式以下所示:

name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5

4. storage 事件:

Storage 對象進行任何修改,都會在文檔上觸發 storage 事件。當經過屬性或 setItem() 方法保存數據,使用 delete 操做符或 removeItem()刪除數據,或者調用 clear()方法時,都會發生該事件。這個事件的 event 對象有如下屬性:

  • domain:發生變化的存儲空間的域名。
  • key:設置或者刪除的鍵名。
  • newValue:若是是設置值,則是新值;若是是刪除鍵,則是 null
  • oldValue:鍵被更改以前的值。在這四個屬性中, IE8 Firefox 只實現了 domain 屬性。在撰寫本書的時候, WebKit 尚不支持storage 事件。

如下代碼展現瞭如何偵聽 storage 事件: 

EventUtil.addHandler(document, "storage", function(event){
    alert("Storage changed for " + event.domain);
});

 5. IndexedDB:
IndexedDB 是一種相似 SQL 數據庫的結構化數據存儲機制。但它的數據不是保存在表中,而是保存在對象存儲空間中。建立對象存儲空間時,須要定義一個鍵,而後就能夠添加數據。可使用遊標在對象存儲空間中查詢特定的對象。而索引則是爲了提升查詢速度而基於特定的屬性建立的。有了以上這些選擇,就能夠在客戶端機器上使用 JavaScript 存儲大量數據了。但你必須當心,不要在客戶端存儲敏感數據,由於數據緩存不會加密。

var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB ||
window.webkitIndexedDB;

 注:

IndexedDB 的限制不少都與對 Web Storage 的相似。首先, IndexedDB 數據庫只能由同源(相同協議、域名和端口)頁面操做,所以不能跨域共享信息。換句話說, www.wrox.com p2p.wrox.com 的數據庫是徹底獨立的。其次,每一個來源的數據庫佔用的磁盤空間也有限制。 Firefox 4+目前的上限是每一個源 50MB,而Chrome 的限制是 5MB。移動設備上的 Firefox 最多容許保存 5MB,若是超過了這個配額,將會請求用戶的許可。Firefox 還有另一個限制,即不容許本地文件訪問 IndexedDBChrome 沒有這個限制。

十六. API:

1. web 性能:

Web 計時機制的核心是 window.performance 對象。對頁面的全部度量信息,包括那些規範中已經定義的和未來才能肯定的,都包含在這個對象裏面。 Web Timing 規範一開始就爲 performance 對象定義了兩個屬性。其中, performance.navigation 屬性也是一個對象,包含着與頁面導航有關的多個屬性, 以下所示。
redirectCount:頁面加載前的重定向次數。
type:數值常量,表示剛剛發生的導航類型。
performance.navigation.TYPE_NAVIGATE (0):頁面第一次加載。
performance.navigation.TYPE_RELOAD (1):頁面重載過。
performance.navigation.TYPE_BACK_FORWARD (2):頁面是經過「後退」或「前進」按鈕打開的。
另外, performance.timing 屬性也是一個對象,但這個對象的屬性都是時間戳(從軟件紀元開始通過的毫秒數),不一樣的事件會產生不一樣的時間值。

2. Web Worker:

隨着 Web 應用複雜性的與日俱增,愈來愈複雜的計算在所不免。長時間運行的 JavaScript 進程會致使瀏覽器凍結用戶界面,讓人感受屏幕「凍結」了。 Web Workers 規範經過讓 JavaScript 在後臺運行解決了這個問題。瀏覽器實現 Web Workers 規範的方式有不少種,可使用線程、後臺進程或者運行在其餘處理器核心上的進程,等等。

實例化 Worker 對象並傳入要執行的 JavaScript 文件名就能夠建立一個新的 Web Worker關於 Web Worker,最重要的是要知道它所執行的 JavaScript 代碼徹底在另外一個做用域中,與當前網
頁中的代碼不共享做用域。在 Web Worker 中,一樣有一個全局對象和其餘對象以及方法。可是, Web Worker 中的代碼不能訪問 DOM,也沒法經過任何方式影響頁面的外觀。Web Worker 中的全局對象是 worker 對象自己。也就是說,在這個特殊的全局做用域中, this self 引用的都是 worker 對象。爲便於處理數據, Web Worker 自己也是一個最小化的運行環境。

var worker = new Worker("stufftodo.js");

 關於 Web Worker,最重要的是要知道它所執行的 JavaScript 代碼徹底在另外一個做用域中,與當前網頁中的代碼不共享做用域。在 Web Worker 中,一樣有一個全局對象和其餘對象以及方法。可是, Web Worker 中的代碼不能訪問 DOM,也沒法經過任何方式影響頁面的外觀。Web Worker 中的全局對象是 worker 對象自己。也就是說,在這個特殊的全局做用域中, this 和 self 引用的都是 worker 對象。爲便於處理數據, Web Worker 自己也是一個最小化的運行環境。

相關文章
相關標籤/搜索