JavaScript概述.pdf

第1章 JavaScript概述javascript

第2章 使用JavaScriptphp

第3章 語法、關鍵保留字及變量css

第4章 數據類型html

第5章 運算符前端

第6章 流程控制語句java

第7章 函數node

//沒有參數的函數
function box(){
    alert('只有函數被調用,我纔會被執行');
}

box();

//帶參數的函數
function box(name, age){
    alert('你的姓名:'+name+',年齡:'+age);
}

box('小明',25);

//帶return 返回值
function box(){
    return '我被返回了!';
}

alert(box());

function box(name, age){
    return '你的名字:'+name+',年齡:'+age;
}

alert('小明',28);
//把函數的返回值賦給一個變量,而後經過變量進行操做。
function box(num1, num2){
    return num1*num2;
}

var num = box(10,5);
alert(num);

//退出當前函數
function box(num){
    if(num<5) return num;
    return 100;
}
alert(box(10));

//arguments對象

function box(){
    return arguments[0]+'|'+arguments[1];
}

alert(box(1,2,3,4,5,6));

//arguments對象的length屬性能夠獲得參數的數量。
function box(){

    return arguments.length;
}

alert(box(1,2,3,4,5,6));

//實現一個加法運算,將全部傳進來的數字累加,而數字的個數又不肯定。
function box(){
    var sum = 0;
    if(arguments.length ==0) return sum;
    for (var i =0; i <=arguments.length; i++) {
        sum = sum + arguments[i];
    }
    return sum;
}

alert(box(5,9,12));

//ECMAScript中的函數,沒有像其餘高級語言那種函數重載功能。
function box(num){
    return num +100;
}

function box(num) {
    return num+200;
}
alert(box(50));
View Code

第8章 對象和數組程序員

//object類型

//使用new運算符建立Object

var box = new Object();
box.name = '小明';
box.age = 28;

//new關鍵字能夠省略
var box = object();

//使用字面量方式建立Object
var box = {
    name : '小明';
    age:28
};

//屬性字段也能夠使用字符串星矢
var box = {
    'name' : '小明';
    'age' : 28;
};

//使用字面量及傳統複製方式
var box = {};
box.name = '小明';
box.age =28;

//兩種屬性輸出方式
alert(box.age);
alert(box['age']);

//PS:在使用字面量聲明Object對象時,不會調用Object()構造函數(Firefox除外)。

//給對象建立方法
var box = {
    run : function(){
        return '運行';
    }
}

alert(box.run());

//使用delete刪除對象屬性
delete box.name;

//字面量也是向函數傳遞大量可選參數的首選方式。

function box(obj){
    if(obj.name != undefined) alert(obj.name);
    if(obj.age != undefined) alert(obj.age);
}

box({
    name : '小明';
    age :25
});


/*Array類型*/

//使用new關鍵字建立數組
var box = new Array();
var box = new Array(10);
var box = new Array('小明',28,'學生','中國');

//以上三種方法,能夠省略new關鍵字
var box = Array();

//使用字面量方式建立數組
var box = [];
var box = ['小明',28,'學生','中國'];
var box = [1,2,]//禁止這麼作,IE會識別3個元素
var box = [,,,,,,];//一樣,IE的會有識別問題 

//PS:和Object同樣,字面量的寫法不會調用Array()構造函數。(Firefox除外)。

//使用索引下標來讀取數組的值
alert(box[2]);
box[2] = '學生';
box[4] = '計算機';

//使用length屬性獲取數組元素量
alert(box.length);
box.length = 10;
box[box.length] = 'JS技術';//經過length給數組增長一個元素

//建立一個稍微複雜一點的數組
var box = [
    //第一個元素是一個對象
        {
            name : '小明',
            age :28,
            run: function(){
                return 'runing';
            }
        },
        //第二個元素是數組
        ['馬雲','李彥宏', newe Object()],

        '江蘇',    //第三個元素是字符串
        25+25,    //第四個元素是數值
        newArray(1,2,3) //第五個元素是數組


];

alert(box);

//PS:數組最多可包含4294967295個元素,超出即會發生異常。

/*對象中方法*/


//轉換方法 對象或數組都具備toLocaleString()、toString()和valueOf()方法。
var box=['小明',28,'計算機編程']; //字面量數組
alert(box); //隱式調用了toString()
alert(box.toString()); //和valueOf()返回一致
alert(box.toLocaleString()); //返回值和上面兩種一致

//默認狀況下,數組字符串都會以逗號隔開。若是使用join()方法,則能夠使用不一樣的分隔符來構建這個字符串。

var box=['小明', 28,'計算機編程'];
alert(box.join('|')); //小明|28|計算機編程

//棧方法

//ECMAScript爲數組專門提供了push()和pop()方法。

/*push()方法能夠接收任意數量的參數,把它們逐個添加到數組的末尾,並返回修改後數
組的長度。而pop()方法則從數組末尾移除最後一個元素,減小數組的length值,而後返回
移除的元素。*/

var box=['小明', 28,'計算機編程']; //字面量聲明
alert(box.push('中國')); //數組末尾添加一個元素,而且返回長度
alert(box); //查看數組
box.pop(); //移除數組末尾元素,並返回移除的元素
alert(box); //查看元素

//隊列方法

/*棧方法是後進先出,而列隊方法就是先進先出。列隊在數組的末端添加元素,從數組的
前端移除元素。經過push()向數組末端添加一個元素,而後經過shift()方法從數組前端移除
一個元素。*/

var box=['小明', 28,'計算機編程']; //字面量聲明
alert(box.push('中國')); //數組末尾添加一個元素,而且返回長度
alert(box); //查看數組
alert(box.shift()); //移除數組開頭元素,並返回移除的元素
alert(box); //查看數組

/*ECMAScript還爲數組提供了一個unshift()方法,它和shift()方法的功能徹底相反。
unshift()方法爲數組的前端添加一個元素。*/
var box=['小明', 28,'計算機編程']; //字面量聲明
alert(box.unshift('中國','江蘇')); //數組開頭添加兩個元素
alert(box); //查看數組
alert(box.pop()); //移除數組末尾元素,並返回移除的元素
alert(box); //查看數組

//PS:IE瀏覽器對unshift()方法老是返回undefined而不是數組的新長度。

//重排序方法

//數組中已經存在兩個能夠直接用來排序的方法:reverse()和sort()。

//reverse() 逆向排序
var box=[1,2,3,4,5]; //數組
alert(box.reverse()); //逆向排序方法,返回排序後的數組
alert(box); //源數組也被逆向排序了,說明是引用
//sort() 從小到大排序
var box=[4,1,7,3,9,2]; //數組
alert(box.sort()); //從小到大排序,返回排序後的數組
alert(box); //源數組也被從小到大排序了

//操做方法

/*ECMAScript爲操做已經包含在數組中的元素提供了不少方法。concat()方法能夠基於當
前數組建立一個新數組。slice()方法能夠基於當前數組獲取指定區域元素並建立一個新數組。
splice()主要用途是向數組的中部插入元素。*/

var box=['小明', 28,'中國']; //當前數組
var box2=box.concat('計算機編程'); //建立新數組,並添加新元素
alert(box2); //輸出新數組
alert(box); //當前數組沒有任何變化
var box=['小明', 28,'中國']; //當前數組
var box2=box.slice(1); //box.slice(1,3),2-4之間的元素
alert(box2); //28,中國
alert(box); //當前數組

//splice中的刪除功能:
var box=['小明', 28,'中國']; //當前數組
var box2=box.splice(0,2); //截取前兩個元素
alert(box2); //返回截取的元素
alert(box); //當前數組被截取的元素被刪除

//splice中的插入功能:
var box=['小明', 28,'中國']; //當前數組
var box2=box.splice(1,0,'計算機編程','江蘇'); //沒有截取,但插入了兩條
alert(box2); //在第2個位置插入兩條
alert(box); //輸出

//splice中的替換功能:
var box=['小明', 28,'中國']; //當前數組
var box2=box.splice(1,1,100); //截取了第2條,替換成100
alert(box2); //輸出截取的28
alert(box); //輸出數組
View Code

 

第9章 時間與日期ajax

//建立一個日期對象,使用new運算符和Date構造方法(構造函數)便可
var box = new Date();

//在調用Date構造方法而不傳遞參數的狀況下,新建的對象自動獲取當前的時間和日期。
alert(box); //不一樣瀏覽器顯示不一樣

//ECMAScript提供了兩個方法,Date.parse()和Date.UTC()。
alert(Date.parse('6/13/2011')); //1307894400000

//若是想輸出指定的日期,那麼把Date.parse()傳入Date構造方法裏。
var box = new Date(Date.parse('6/13/2011'));//MonJun13201100:00:00GMT+0800
var box = new Date('6/13/2011'); //直接傳入,Date.parse()後臺被調用

//PS:Date對象及其在不一樣瀏覽器中的實現有許多奇怪的行爲。

//Date.UTC()方法一樣也返回表示日期的毫秒數,但它與Date.parse()在構建值時使用不一樣的信息。

alert(Date.UTC(2011,11)); //1322697600000

//若是Date.UTC()參數傳遞錯誤,那麼就會出現負值或者NaN等非法信息。
alert(Date.UTC()); //負值或者NaN

//若是要輸出指定日期,那麼直接把Date.UTC()傳入Date構造方法裏便可。
var box = new Date(Date.UTC(2011,11,5,15,13,16));


/*通用的方法*/

//與其餘類型同樣,Date類型也重寫了toLocaleString()、toString()和valueOf()方法;但這
//些方法返回值與其餘類型中的方法不一樣。
var box = new Date(Date.UTC(2011,11,5,15,13,16));
alert('toString:'+box.toString());
alert('toLocaleString:'+box.toLocaleString()); //按本地格式輸出

//PS:這兩個方法在不一樣瀏覽器顯示的效果又不同,但不用擔憂,這兩個方法只是在
//調試比較有用,在顯示時間和日期上,沒什麼價值。valueOf()方法顯示毫秒數。


//日期格式化方法
var box = new Date();
alert(box.toDateString()); //以特定的格式顯示星期幾、月、日和年
alert(box.toTimeString()); //以特定的格式顯示時、分、秒和時區
alert(box.toLocaleDateString()); //以特定地區格式顯示星期幾、月、日和年
alert(box.toLocaleTimeString()); //以特定地區格式顯示時、分、秒和時區
alert(box.toUTCString()); //以特定的格式顯示完整的UTC日期。

//組件方法
/*組件方法,是爲咱們單獨獲取你想要的各類時間/日期而提供的方法。須要注意的時候,
這些方法中,有帶UTC的,有不帶UTC的。UTC日期指的是在沒有時區誤差的狀況下的
日期值。*/

alert(box.getTime()); //獲取日期的毫秒數,和valueOf()返回一致
alert(box.setTime(100)); //以毫秒數設置日期,會改變整個日期
alert(box.getFullYear()); //獲取四位年份
alert(box.setFullYear(2012)); //設置四位年份,返回的是毫秒數
alert(box.getMonth()); //獲取月份,沒指定月份,從0開始算起
alert(box.setMonth(11)); //設置月份
alert(box.getDate()); //獲取日期
alert(box.setDate(8)); //設置日期,返回毫秒數
alert(box.getDay()); //返回星期幾,0表示星期日,6表示星期六
alert(box.setDay(2)); //設置星期幾
alert(box.getHours()); //返回時
alert(box.setHours(12)); //設置時
alert(box.getMinutes()); //返回分鐘
alert(box.setMinutes(22)); //設置分鐘
alert(box.getSeconds()); //返回秒數
alert(box.setSeconds(44)); //設置秒數
alert(box.getMilliseconds()); //返回毫秒數
alert(box.setMilliseconds()); //設置毫秒數
alert(box.getTimezoneOffset()); //返回本地時間和UTC時間相差的分鐘數

//PS:以上方法除了getTimezoneOffset(),其餘都具備UTC功能,例如setDate()及getDate()
//獲取星期幾,那麼就會有setUTCDate()及getUTCDate()。表示世界協調時間。
View Code

 

第10章 正則表達式正則表達式

第11章 Function類型

//使用Function構造函數
var box = new Function ('num1','num2', 'return num1+num2'); //不推薦

//做爲值的函數,ECMAScript中的函數名自己就是變量,因此函數也能夠做爲值來使用。
Function box(sumFunction, num)  {
    return sumFunction(num);
}

Function sum(num) {
    return num +10;
}

var result = box(sum ,10);

//函數內部屬性

/*在函數內部,有兩個特殊的對象:arguments和this。arguments是一個類數組對象,包
含着傳入函數中的全部參數,主要用途是保存函數參數。但這個對象還有一個名叫callee的
屬性,該屬性是一個指針,指向擁有這個arguments對象的函數。*/

Function box(num) {
    if(num <=1){
        return 1;
    }else {
        return num*box(num-1); //一個簡單的的遞歸
    }
}


/*對於階乘函數通常要用到遞歸算法,因此函數內部必定會調用自身;若是函數名不改變
是沒有問題的,但一旦改變函數名,內部的自身調用須要逐一修改。爲了解決這個問題,我
們能夠使用arguments.callee來代替。*/

Function box(num) {
    if(num <=1){
        return 1;
    }else{
        return num * arguments.callee(num-1);//使用callee來執行自身
    }
}

/*函數內部另外一個特殊對象是this,其行爲與Java和C#中的this大體類似。換句話說,
this引用的是函數據以執行操做的對象,或者說函數調用語句所處的那個做用域。PS:當在
全局做用域中調用函數時,this對象引用的就是window。*/

//便於理解的改寫例子
window.color = '紅色的'; //全局的,或者varcolor='紅色的';也行
alert(this.color);       //打印全局的color

var box = {
    color:'藍色的',                //局部的color
    sayColor:function(){
        alert(this.color);    //此時的this只能box裏的color
    }
};


box.sayColor();            //打印局部的color
alert(this.color);        //仍是全局的

//函數屬性和方法
/*ECMAScript中的函數是對象,所以函數也有屬性和方法。每一個函數都包含兩個屬性:
length和prototype。其中,length屬性表示函數但願接收的命名參數的個數。*/

function box(name, age){
    alert(name + age);
}

alert(box.length);

//對於prototype屬性,它是保存全部實例方法的真正所在,也就是原型。
/*prototype下有兩個方法:apply()和call(),每一個函數都
包含這兩個非繼承而來的方法。這兩個方法的用途都在特定的做用域中調用函數,實際上等
於設置函數體內this對象的值。*/

Function box(num1 ,num2){
    return num1 + num2;
}

function sayBox(num1, num2){
    return box.apply(this,[num1, num2]);//this表示做用域,這裏是window
                                        //[]表示box所須要的參數
}

function sayBox2(num1, num2){
    return box.apply(this, arguments);  //arguments對象表示box所須要的參數
}

alert(sayBox(10,10));            //20
alert(sayBox2(10,10));            //20


/*call()方法於apply()方法相同,他們的區別僅僅在於接收參數的方式不一樣。對於call()方
法而言,第一個參數是做用域,沒有變化,變化只是其他的參數都是直接傳遞給函數的。*/

function box(num1, num2){
    return num1 + num2;
}

function callBox(num1, num2){
    return box.call(this, num1, num2);  //和apply區別在於後面的傳參
}

alert(callBox(10,10));


/*事實上,傳遞參數並非apply()和call()方法真正的用武之地;它們常用的地方是
可以擴展函數賴以運行的做用域。*/

var color = '紅色的';            //或者window.color='紅色的';也行

var box - {
    color :'藍色的'
};

function sayColor(){
    alert(this.color);
}

sayColor();                //做用域在window
sayColor.call(this);    //做用域在window
sayColor.call(window);    //做用域在window
sayColor.call(box);        //做用域在box,對象冒充    


/*這個例子是以前做用域理解的例子修改而成,咱們能夠發現當咱們使用call(box)方法的
時候,sayColor()方法的運行環境已經變成了box對象裏了。
使用call()或者apply()來擴充做用域的最大好處,就是對象不須要與方法發生任何耦合
關係(耦合,就是互相關聯的意思,擴展和維護會發生連鎖反應)。也就是說,box對象和
sayColor()方法之間不會有多餘的關聯操做,好比box.sayColor=sayColor;*/
View Code

 

第12章 變量、做用域及內存

/*在變量複製方面,基本類型和引用類型也有所不一樣。基本類型複製的是值自己,而引用
類型複製的是地址。*/

var box='Lee'; //在棧內存生成一個box'Lee'
var box2=box; //在棧內存再生成一個box2'Lee'

//ps:box2是雖然是box1的一個副本,但從圖示能夠看出,它是徹底獨立的。也就是說,兩個變量分別操做時互不影響。

var box = new Object();    //建立一個引用類型

box.name = 'Lee';        //新增一個屬性

var box2 = box;            //把引用地址賦值給box2

//ps:在引用類型中,box2其實就是box,由於他們指向的是同一個對象。若是這個對象中的
//name屬性被修改了,box2.name和box.name輸出的值都會被相應修改掉了。


/*傳遞參數
ECMAScript中全部函數的參數都是按值傳遞的,言下之意就是說,參數不會按引用傳
遞,雖然變量有基本類型和引用類型之分。*/

function box(num){ //按值傳遞,傳遞的參數是基本類型
    num+=10; //這裏的num是局部變量,全局無效
    retur nnum;
}
var num=50;
var result = box(num);
alert(result);         //60
alert(num);         //50
function box(num){ //按值傳遞,傳遞的參數是基本類型
num+=10;             //這裏的num是局部變量,全局無效
return num;
}
var num=50;
var result=box(num);
alert(result); //60
alert(num); //50

/*PS:以上的代碼中,傳遞的參數是一個基本類型的值。而函數裏的num是一個局部變
量,和外面的num沒有任何聯繫。*/


//下面給出一個參數做爲引用類型的例子。

function box(obj) {                //按值傳遞,傳遞的參數是引用類型
    obj.name = 'Lee';
}

var p=new Object();
box(p);
alert(p.name);

/*PS:若是存在按引用傳遞的話,那麼函數裏的那個變量將會是全局變量,在外部也可
以訪問。好比PHP中,必須在參數前面加上&符號表示按引用傳遞。而ECMAScript沒有這
些,只能是局部變量。能夠在PHP中瞭解一下。
PS:因此按引用傳遞和傳遞引用類型是兩個不一樣的概念。*/

function box(obj) {
    obj.name = 'lee';
    var obj = new Object();        //函數內部又建立了一個對象
    obj.name = 'Mr.';            //並無替換掉原來的obj
}

//最後得出結論,ECMAScript函數的參數都將是局部變量,也就是說,沒有按引用傳遞。


//檢測類型

//要檢測一個變量的類型,咱們能夠經過typeof運算符來判別。諸如:
var box='Lee';
alert(typeofbox); //string


//這時咱們應該採用instanceof運算符來查看。
var box=[1,2,3];
alert(box instanceof Array); //是不是數組
var box2={};
alert(box2 instanceof Object); //是不是對象
var box3=/g/;
alert(box3 instanceof RegExp); //是不是正則表達式
var box4=newString('Lee');
alert(box4 instanceof String); //是不是字符串對象

//PS:當使用instanceof檢查基本類型的值時,它會返回false。

//5.執行環境及做用域

//在Web瀏覽器中,全局執行環境被認爲是window對象。

var box='blue'; //聲明一個全局變量
function setBox(){
alert(box); //全局變量能夠在函數裏訪問
}
setBox(); //執行函數
//全局的變量和函數,都是window對象的屬性和方法。
var box='blue';
function setBox(){
alert(window.box); //全局變量即window的屬性
}
window.setBox(); //全局函數即window的方法


/*PS:當執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和
函數定義也隨之銷燬。若是是全局環境下,須要程序執行完畢,或者網頁被關閉纔會銷燬。
PS:每一個執行環境都有一個與之關聯的變量對象,就比如全局的window能夠調用變量
和屬性同樣。局部的環境也有一個相似window的變量對象,環境中定義的全部變量和函數
都保存在這個對象中。(咱們沒法訪問這個變量對象,但解析器會處理數據時後臺使用它)*/

//函數裏的局部做用域裏的變量替換全局變量,但做用域僅限在函數體內這個局部環境。
var box='blue';
function setBox(){
    varbox='red'; //這裏是局部變量,出來就不認識了
    alert(box);
}
setBox();
alert(box);

//經過傳參,能夠替換函數體內的局部變量,但做用域僅限在函數體內這個局部環境。
var box='blue';
function setBox(box){ //經過傳參,替換了全局變量
    alert(box);
}
setBox('red');
alert(box);

//函數體內還包含着函數,只有這個函數才能夠訪問內一層的函數。

var box='blue';
function setBox(){
    function setColor(){
        var b='orange';
        alert(box);
        alert(b);
}
    setColor(); //setColor()的執行環境在setBox()內
}
setBox();

/*PS:每一個函數被調用時都會建立本身的執行環境。當執行到這個函數時,函數的環境
就會被推到環境棧中去執行,而執行後又在環境棧中彈出(退出),把控制權交給上一級的執
行環境。
PS:當代碼在一個環境中執行時,就會造成一種叫作做用域鏈的東西。它的用途是保
證對執行環境中有訪問權限的變量和函數進行有序訪問。做用域鏈的前端,就是執行環境的
變量對象。*/

//6.沒有塊級做用域

//塊級做用域表示諸如if語句等有花括號封閉的代碼塊,因此,支持條件判斷來定義變量。
if(true){ //if語句代碼塊沒有局部做用域
    var box='Lee';
}
alert(box);
//for循環語句也是如此
for(var i=0;i<10;i++){ //沒有局部做用域
    var box='Lee';
}
alert(i);
alert(box);

//var關鍵字在函數裏的區別
function box(num1,num2){
    var sum=num1+num2; //若是去掉var就是全局變量了
    return sum;
}
alert(box(10,10));
alert(sum); //報錯

/*PS:很是不建議不使用var就初始化變量,由於這種方法會致使各類意外發生。因此初
始化變量的時候必定要加上var。*/

//通常肯定變量都是經過搜索來肯定該標識符實際表明什麼。

var box='blue';
function getBox(){
    return box; //表明全局box
} //若是加上函數體內加上varbox='red'
alert(getBox()); //那麼最後返回值就是red
//PS:變量查詢中,訪問局部變量要比全局變量更快,由於不須要向上搜索做用域鏈。


//一旦數據再也不有用,那麼將其設置爲null來釋放引用,這個作法叫作解除引用。
//這一作法適用於大多數全局變量和全局對象。
var o={
    name:'Lee'
};
o=null; //解除對象引用,等待垃圾收集器回收
View Code

 

第13章 基本包裝類型

/*爲了便於操做基本類型值,ECMAScript提供了3個特殊的引用類型:Boolean、Number
和String。這些類型與其餘引用類型類似,但同時也具備與各自的基本類型相應的特殊行爲。*/


//基本包裝類型概述
var box='Mr. Lee';         //定義一個字符串
var box2=box.substring(2); //截掉字符串前兩位
alert(box2);     //輸出新字符串


/*變量box是一個字符串類型,而box.substring(2)又說明它是一個對象(PS:只有對象才
會調用方法),最後把處理結果賦值給box2。'Mr. Lee'是一個字符串類型的值,按道理它不
應該是對象,不該該會有本身的方法,好比:*/
alert('Mr.Lee'.substring(2)); //直接經過值來調用方法

//字面量寫法:

var box = 'Mr.Lee';  //字面量
box.name = 'Lee';    //無效屬性
box.age = function(){ //無效方法
    return 100;
};

alert(box); //Mr.Lee
alert(box.substring(2)); //.Lee
alert(typeof box); //string
alert(box.name); //undefined
alert(box.age()); //錯誤

//new運算符寫法
var box=new String('Mr.Lee'); //new運算符
box.name='Lee'; //有效屬性
box.age=function(){ //有效方法
return 100;
};

alert(box); //Mr.Lee
alert(box.substring(2)); //.Lee
alert(typeof box); //object
alert(box.name); //Lee
alert(box.age()); //100

/*無論字面量形式仍是new運算符形式,均可以使用它的內置方法。而且
Boolean和Number特性與String相同,三種類型能夠成爲基本包裝類型。*/

/*PS:在使用new運算符建立以上三種類型的對象時,能夠給本身添加屬性和方法,但
咱們建議不要這樣使用,由於這樣會致使根本分不清究竟是基本類型值仍是引用類型值。*/

//Boolean類型
//Boolean類型沒有特定的屬性或者方法。


//Number類型

//Number類型有一些靜態屬性(直接經過Number調用的屬性,而無須new運算符)和方法。

/*
    Number靜態屬性:
    MAX_VALUE:表示最大數
    MIN_VALUE:表示最小值
    NaN:非數值
    NEGATIVE_INFINITY:負無窮大,溢出返回該值
    POSITIVE_INFINITY:無窮大,溢出返回該值
    prototype:原型,用於增長新屬性和方法

*/


/*
    方法:
    toString(): 將數值轉化爲字符串,而且能夠轉換進制
    toLocaleString() ::根據本地數字格式轉換爲字符串
    toFixed() :將數字保留小數點後指定位數並轉化爲字符串
    toExponential():將數字以指數形式表示,保留小數點後指定位數並轉化爲字符串
    toPrecision():指數形式或點形式表述數,保留小數點後面指定位數並轉化爲字符串

*/
var box=1000.789;
alert(box.toString()); //轉換爲字符串,傳參能夠轉換進制
alert(box.toLocaleString()); //本地形式,1,000.789
alert(box.toFixed(2)); //小數點保留,1000.78
alert(box.toExponential()); //指數形式,傳參會保留小數點
alert(box.toPrecision(3)); //指數或點形式,傳參保留小數點


//String類型

//String類型包含了三個屬性和大量的可用內置方法。

/*
    String對象屬性
    length :返回字符串的字符長度
    constructor :返回建立String對象的函數
    prototype: 經過添加屬性和方法擴展字符串定義
*/

/*
String也包含對象的通用方法,好比valueOf()、toLocaleString()和toString()方法,但這
些方法都返回字符串的基本值。
*/

/*
    字符方法
    charAt(n)     返回指定索引位置的字符
    charCodeAt(n)     以Unicode編碼形式返回指定索引位置的字符

*/
var box='Mr.Lee';
alert(box.charAt(1)); //r
alert(box.charCodeAt(1)); //114
alert(box[1]); //r,經過數組方式截取

//PS:box[1]在IE瀏覽器會顯示undefined,因此使用時要慎重。

/*
    字符串操做方法
    concat(str1...str2)     將字符串參數串聯到調用該方法的字符串
    slice(n,m)     返回字符串n到m之間位置的字符串
    substring(n,m) 同上
    substr(n,m) 返回字符串n開始的m個字符串

*/


va rbox='Mr.Lee';
alert(box.concat('is', ' Teacher', '!')); //Mr.LeeisTeacher!
alert(box.slice(3)); //Lee
alert(box.slice(3,5)); //Le
alert(box.substring(3)); //Lee
alert(box.substring(3,5)); //Le
alert(box.substr(3)); //Lee
alert(box.substr(3,5)); //Lee
var box='Mr.Lee';
alert(box.slice(-3)); //Lee,6+(-3)=3位開始
alert(box.substring(-3)); //Mr.Lee 負數返回所有
alert(box.substr(-3)); //Lee,6+(-3)=3位開始
var box='Mr.Lee';
alert(box.slice(3,-1)); //Le6+(-1)=5,(3,5)
alert(box.substring(3,-1)); //Mr. 第二參爲負,直接轉0,
//而且方法會把較小的數字提早,(0,3)
alert(box.substr(3,-1)); //'' 第二參數爲負,直接轉0,(3,0)

/*
PS:IE的JavaScript實如今處理向substr()方法傳遞負值的狀況下存在問題,它會返回
原始字符串,使用時要切記。
*/

/*
    字符串位置方法
    indexOf(str,n) 從n開始搜索的第一個str,並將搜索的索引值返回
    lastIndexOf(str,n) 從n開始搜索的最後一個str,並將搜索的索引值返回
*/
var box='Mr.Lee isLee';
alert(box.indexOf('L')); //3
alert(box.indexOf('L',5)); //10
alert(box.lastIndexOf('L')); //10
alert(box.lastIndexOf('L',5)); //3,從指定的位置向前搜索


//PS:若是沒有找到想要的字符串,則返回-1。

//示例:找出所有的L
var box='Mr.Lee isLee'; //包含兩個L的字符串
var boxarr=[]; //存放L位置的數組
varpos=box.indexOf('L'); //先獲取第一個L的位置
while(pos>-1){ //若是位置大於-1,說明還存在L
    boxarr.push(pos); //添加到數組
    pos=box.indexOf('L',pos+1); //重新賦值pos目前的位置
}
alert(boxarr); //輸出

/*
    大小寫轉換方法
    toLowerCase(str) 將字符串所有轉換爲小寫
    toUpperCase(str) 將字符串所有轉換爲大寫
    toLocaleLowerCase(str) 將字符串所有轉換爲小寫,而且本地化
    toLocaleupperCase(str) 將字符串所有轉換爲大寫,而且本地化
*/
var box='Mr.Lee isLee';
alert(box.toLowerCase()); //所有小寫
alert(box.toUpperCase()); //所有大寫
alert(box.toLocaleLowerCase()); //
alert(box.toLocaleUpperCase()); //

//PS:只有幾種語言(如土耳其語)具備地方特有的大小寫本地性,通常來講,是否本
//地化效果都是一致的。


/*
    字符串的模式匹配方法
    match(pattern) 返回pattern中的子串或null
    replace(pattern,replacement) 用replacement替換pattern
    search(pattern) 返回字符串中pattern開始位置
    split(pattern) 返回字符串按指定pattern拆分的數組
*/

var box='Mr.Lee isLee';
alert(box.match('L')); //找到L,返回L不然返回null
alert(box.search('L')); //找到L的位置,和indexOf類型
alert(box.replace('L','Q')); //把L替換成Q
alert(box.split('')); //以空格分割成字符串
/*
    其餘方法
    fromCharCode(ascii) 靜態方法,輸出Ascii碼對應值
    localeCompare(str1,str2) 比較兩個字符串,並返回相應的值
*/

alert(String.fromCharCode(76)); //L,輸出Ascii碼對應值

/*
    localeCompare(str1,str2)方法詳解:比較兩個字符串並返回如下值中的一個;
    1.若是字符串在字母表中應該排在字符串參數以前,則返回一個負數。(多數-1)
    2.若是字符串等於字符串參數,則返回0。
    3.若是字符串在自附表中應該排在字符串參數以後,則返回一個正數。(多數1)
*/

var box='Lee';
alert(box.localeCompare('apple')); //1
alert(box.localeCompare('Lee')); //0
alert(box.localeCompare('zoo')); //-1

/*
    HTML方法
    anchor(name)     <aname="name">str</a>
    big()             <big>str</big>
    blink()             <blink>str</blink>
    bold()             <b>Str</b>
    fixed()             <tt>Str</tt>
    fontcolor(color) <fontcolor="color">str</font>
    fontsize(size)     <fontsize="size">str</font>
    link(URL)         <ahref="URL">str</a>
    small()             <small>str</small>
    strike()         <strike>str</strike>
    italics()         <i>italics</i>
    sub()             <sub>str</sub>
    sup()             <sup>str</sup>
以上是經過JS生成一個html標籤,根據經驗,沒什麼太大用處,作個瞭解。
*/

varbox='Lee'; //
alert(box.link('http://www.yc60.com')); //超連接
View Code

 

第14章 內置對象

/*ECMA-262對內置對象的定義是:「由ECMAScript實現提供的、不依賴宿主環境的對
象,這些對象在ECMAScript程序執行以前就已經存在了。」意思就是說,開發人員沒必要顯
示地實例化內置對象;由於它們已經實例化了。ECMA-262只定義了兩個內置對象:Global
和Math。*/

//Global對象
/*Global(全局)對象是ECMAScript中一個特別的對象,由於這個對象是不存在的。在
ECMAScript中不屬於任何其餘對象的屬性和方法,都屬於它的屬性和方法。因此,事實上,
並不存在全局變量和全局函數;全部在全局做用域定義的變量和函數,都是Global對象的
屬性和方法。
PS:由於ECMAScript沒有定義怎麼調用Global對象,因此,Global.屬性或者Global.
方法()都是無效的。(Web瀏覽器將Global做爲window對象的一部分加以實現)*/

//1.URI編碼方法

    /*
        URI編碼能夠對連接進行編碼,以便發送給瀏覽器。它們採用特殊的UTF-8編碼替換
        全部無效字符,從而讓瀏覽器可以接受和理解。
        encodeURI()不會對自己屬於URI的特殊字符進行編碼,例如冒號、正斜槓、問號和#
        號;而encodeURIComponent()則會對它發現的任何非標準字符進行編碼
    */

var box='//Lee 李';
alert(encodeURI(box)); //只編碼了中文
varbox='//Lee李';
alert(encodeURIComponent(box)); //特殊字符和中文編碼了

// /PS:由於encodeURIComponent()編碼比encodeURI()編碼來的更加完全,通常來講
//encodeURIComponent()使用頻率要高一些。

//使用了URI編碼事後,還能夠進行解碼,經過decodeURI()和decodeURIComponent()
//來進行解碼
var box='//Lee 李';
alert(decodeURI(encodeURI(box))); //還原

varbox='//Lee 李';
alert(decodeURIComponent(encodeURIComponent(box))); //還原

/*PS:URI方法如上所述的四種,用於代替已經被ECMA-262第3版廢棄的escape()和
unescape()方法。URI方法可以編碼全部的Unicode字符,而原來的只能正確地編碼ASCII
字符。因此建議不要再使用escape()和unescape()方法。*/

//eval()方法
/*eval()方法主要擔當一個字符串解析器的做用,他只接受一個參數,而這個參數就是要
執行的JavaScript代碼的字符串。*/

eval('varbox=100'); //解析了字符串代碼
alert(box);
eval('alert(100)'); //同上
eval('functionbox(){return123}'); //函數也能夠
alert(box());

/*eval()方法的功能很是強大,但也很是危險。所以使用的時候必須極爲謹慎。特別是在
用戶輸入數據的狀況下,很是有可能致使程序的安全性,好比代碼注入等等。*/

//Global對象屬性    
//Global對象包含了一些屬性:undefined、NaN、Object、Array、Function等等。
alert(Array); //返回構造函數
//window對象

//以前已經說明,Global沒有辦法直接訪問,而Web瀏覽器能夠使用window對象來實現一全局訪問。
alert(window.Array); //同上

/*-----------------------------------------------------------------------------*/

//Math對象

//ECMAScript還爲保存數學公式和信息提供了一個對象,即Math對象。


/*
    1.Math對象的屬性
    Math對象包含的屬性大都是數學計算中可能會用到的一些特殊值。
    Math.E 天然對數的底數,即常量e的值
    Math.LN10 10的天然對數
    Math.LN2 2的天然對數
    Math.LOG2E 以2爲底e的對數
    Math.LOG10E 以10爲底e的對數
    Math.PI ∏的值
    Math.SQRT1_2 1/2的平方根
    Math.SQRT2 2的平方根
*/
alert(Math.E);
alert(Math.LN10);
alert(Math.LN2);
alert(Math.LOG2E);
alert(Math.LOG10E);
alert(Math.PI);
alert(Math.SQRT1_2);
alert(Math.SQRT2);

//min()和max()方法
//Math.min()用於肯定一組數值中的最小值。Math.max()用於肯定一組數值中的最大值。

alert(Math.min(2,4,3,6,3,8,0,1,3)); //最小值
alert(Math.max(4,7,8,3,1,9,6,0,3,2)); //最大值

//舍入方法
    /*
        Math.ceil()執行向上舍入,即它老是將數值向上舍入爲最接近的整數;
        Math.floor()執行向下舍入,即它老是將數值向下舍入爲最接近的整數;
        Math.round()執行標準舍入,即它老是將數值四捨五入爲最接近的整數;
    */

alert(Math.ceil(25.9)); //26
alert(Math.ceil(25.5)); //26
alert(Math.ceil(25.1)); //26
alert(Math.floor(25.9)); //25
alert(Math.floor(25.5)); //25
alert(Math.floor(25.1)); //25
alert(Math.round(25.9)); //26
alert(Math.round(25.5)); //26
alert(Math.round(25.1)); //25

//random()方法

/*
    Math.random()方法返回介於0到1之間一個隨機數,不包括0和1。若是想大於這個範
圍的話,能夠套用一下公式:
值=Math.floor(Math.random()*總數+第一個值)
*/
alert(Math.floor(Math.random()*10+1)); //隨機產生1-10之間的任意數

for(var i=0;i<10;i++){
    document.write(Math.floor(Math.random()*10+5)); //5-14之間的任意數
    document.write('<br/>');
}
//爲了更加方便的傳遞想要範圍,能夠寫成函數:
function selectFrom(lower,upper){
    varsum=upper-lower+1; //總數-第一個數+1
    returnMath.floor(Math.random()*sum+lower);
}

for(var i=0;i<10;i++){
    document.write(selectFrom(5,10)); //直接傳遞範圍便可
    document.write('<br/>');
}


/*
    其餘方法

    Math.abs(num) 返回num的絕對值
    Math.exp(num) 返回Math.E的num次冪
    Math.log(num) 返回num的天然對數
    Math.pow(num,power) 返回num的power次冪
    Math.sqrt(num) 返回num的平方根
    Math.acos(x) 返回x的反餘弦值
    Math.asin(x) 返回x的反正弦值
    Math.atan(x) 返回x的反正切值
    Math.atan2(y,x) 返回y/x的反正切值
    Math.cos(x) 返回x的餘弦值
    Math.sin(x) 返回x的正弦值
    Math.tan(x) 返回x的正切值
*/
View Code

 

第15章 面向對象與原型

//建立對象

var box=new Object();     //建立一個Object對象
box.name='Lee';         //建立一個name屬性並賦值
box.age=100;         //建立一個age屬性並賦值
box.run=function(){ //建立一個run()方法並返回值
    return this.name+this.age+'運行中...';
};
alert(box.run()); //輸出屬性和方法的值

/*上面建立了一個對象,而且建立屬性和方法,在run()方法裏的this,就是表明box對象
自己。*/

var box2=box; //獲得box的引用
box2.name='Jack'; //直接改變了name屬性

alert(box2.run()); //用box.run()發現name也改變了
var box2= new Object();
box2.name='Jack';
box2.age=200;
box2.run=function(){
    return this.name+this.age+'運行中...';
};
alert(box2.run()); //這樣才避免和box混淆,從而保持獨立

/*爲了解決多個相似對象聲明的問題,咱們能夠使用一種叫作工廠模式的方法,這種方法
就是爲了解決實例化對象產生大量重複的問題。*/

function createObject(name,age){ //集中實例化的函數
    varobj=newObject();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
    return this.name+this.age+'運行中...';
        };
    returnobj;
}
var box1=createObject('Lee',100); //第一個實例
var box2=createObject('Jack',200); //第二個實例
alert(box1.run());
alert(box2.run()); //保持獨立

/*工廠模式解決了重複實例化的問題,但還有一個問題,那就是識別問題,由於根本沒法
搞清楚他們究竟是哪一個對象的實例。*/

alert(typeof box1); //Object
alert(box1 instanceof Object); //true

/*ECMAScript中能夠採用構造函數(構造方法)可用來建立特定的對象。類型於Object對
象。*/

function Box(name,age){ //構造函數模式
    this.name=name;
    this.age=age;
    this.run=function(){
    return this.name+this.age+'運行中...';
};
}
var box1=new Box('Lee',100); //newBox()便可
var box2=new Box('Jack',200);
alert(box1.run());
alert(box1 instanceof Box); //很清晰的識別他從屬於Box

/*
使用構造函數的方法,即解決了重複實例化的問題,又解決了對象識別的問題,但問題
是,這裏並無newObject(),爲何能夠實例化Box(),這個是哪裏來的呢?
使用了構造函數的方法,和使用工廠模式的方法他們不一樣之處以下:
1.構造函數方法沒有顯示的建立對象(newObject());
2.直接將屬性和方法賦值給this對象;
3.沒有renturn語句。
*/


/*
構造函數的方法有一些規範:
1.函數名和實例化構造名相同且大寫,(PS:非強制,但這麼寫有助於區分構造函數和
普通函數);
2.經過構造函數建立對象,必須使用new運算符。
既然經過構造函數能夠建立對象,那麼這個對象是哪裏來的,newObject()在什麼地方
執行了?執行的過程以下:
1.當使用了構造函數,而且new構造函數(),那麼就後臺執行了newObject();
2.將構造函數的做用域給新對象,(即newObject()建立出的對象),而函數體內的this就
表明newObject()出來的對象。
3.執行構造函數內的代碼;
4.返回新對象(後臺直接返回)。
關於this的使用,this其實就是表明當前做用域對象的引用。若是在全局範圍this就代
表window對象,若是在構造函數體內,就表明當前的構造函數所聲明的對象。
*/

var box = 2;
alert(this.box); //全局,表明window

/*構造函數和普通函數的惟一區別,就是他們調用的方式不一樣。只不過,構造函數也是函
數,必須用new運算符來調用,不然就是普通函數。*/

var box = new Box('Lee',100); //構造模式調用
alert(box.run());
Box('Lee',20); //普通模式調用,無效
var o= new Object();
Box.call(o,'Jack', 200) //對象冒充調用
alert(o.run());

//探討構造函數內部的方法(或函數)的問題,首先看下兩個實例化後的屬性或方法是否相等。

var box1 = new Box('Lee',100); //傳遞一致
var box2 = new Box('Lee',100); //同上


alert(box1.name == box2.name); //true,屬性的值相等
alert(box1.run == box2.run); //false,方法其實也是一種引用地址
alert(box1.run() == box2.run()); //true,方法的值相等,由於傳參一致

/*能夠把構造函數裏的方法(或函數)用newFunction()方法來代替,獲得同樣的效果,更加
證實,他們最終判斷的是引用地址,惟一性。*/

function Box(name,age){ //newFunction()惟一性
    this.name=name;
    this.age=age;
    this.run =new Function("return this.name+this.age+'運行中...'");
}

/*咱們能夠經過構造函數外面綁定同一個函數的方法來保證引用地址的一致性,但這種作
法沒什麼必要,只是加深學習瞭解:*/

function Box(name,age){
    this.name=name;
    this.age=age;
    this.run=run;
}

function run(){ //經過外面調用,保證引用地址一致
    returnt his.name+this.age+'運行中...';
}

/*雖然使用了全局的函數run()來解決了保證引用地址一致的問題,但這種方式又帶來了
一個新的問題,全局中的this在對象調用的時候是Box自己,而看成普通函數調用的時候,
this又表明window。*/

/*-----------------------------------------------------------------------*/

//原型

/*咱們建立的每一個函數都有一個prototype(原型)屬性,這個屬性是一個對象,它的用途是
包含能夠由特定類型的全部實例共享的屬性和方法。邏輯上能夠這麼理解:prototype經過
調用構造函數而建立的那個對象的原型對象。使用原型的好處可讓全部對象實例共享它所
包含的屬性和方法。也就是說,沒必要在構造函數中定義對象信息,而是能夠直接將這些信息
添加到原型中。*/

function Box(){} //聲明一個構造函數
    Box.prototype.name='Lee'; //在原型裏添加屬性
    Box.prototype.age=100;
    Box.prototype.run=function(){ //在原型裏添加方法
    return this.name+this.age+'運行中...';
};


//比較一下原型內的方法地址是否一致:

var box1 = new Box();
var box2 = new Box();
alert(box1.run == box2.run); //true,方法的引用地址保持一致

/*在原型模式聲明中,多了兩個屬性,這兩個屬性都是建立對象時自動生成的。__proto__
屬性是實例指向原型對象的一個指針,它的做用就是指向構造函數的原型屬性constructor。
經過這兩個屬性,就能夠訪問到原型裏的屬性和方法了。
PS:IE瀏覽器在腳本訪問__proto__會不能識別,火狐和谷歌瀏覽器及其餘某些瀏覽器
均能識別。雖然能夠輸出,但沒法獲取內部信息。*/

alert(box1.__proto__); //[objectObject]

//判斷一個對象是否指向了該構造函數的原型對象,能夠使用isPrototypeOf()方法來測試。

alert(Box.prototype.isPrototypeOf(box)); //只要實例化對象,即都會指向

/*原型模式的執行流程:
1.先查找構造函數實例裏的屬性或方法,若是有,馬上返回;
2.若是構造函數實例裏沒有,則去它的原型對象裏找,若是有,就返回;
雖然咱們能夠經過對象實例訪問保存在原型中的值,但卻不能訪問經過對象實例重寫原
型中的值。*/

var box1 = new Box();
alert(box1.name); //Lee,原型裏的值
box1.name='Jack';
alert(box.1name); //Jack,就近原則,
var box2 = new Box();
alert(box2.name); //Lee,原型裏的值,沒有被box1修改

//若是想要box1也能在後面繼續訪問到原型裏的值,能夠把構造函數裏的屬性刪除便可,
//具體以下:

delete box1.name; //刪除屬性
alert(box1.name);

//如何判斷屬性是在構造函數的實例裏,仍是在原型裏?能夠使用hasOwnProperty()函數
//來驗證:

alert(box.hasOwnProperty('name')); //實例裏有返回true,不然返回false


//in操做符會在經過對象可以訪問給定屬性時返回true,不管該屬性存在於實例中仍是原
//型中。

alert('name'inbox); //true,存在實例中或原型中

//咱們能夠經過hasOwnProperty()方法檢測屬性是否存在實例中,也能夠經過in來判斷
//實例或原型中是否存在屬性。那麼結合這兩種方法,能夠判斷原型中是否存在屬性。

function isProperty(object,property){ //判斷原型中是否存在屬性
    return !object.hasOwnProperty(property)&&(propertyinobject);
}
var box = new Box();
alert(isProperty(box,'name')) //true,若是原型有

//爲了讓屬性和方法更好的體現封裝的效果,而且減小沒必要要的輸入,原型的建立能夠使
//用字面量的方式:

function Box(){};
    Box.prototype={ //使用字面量的方式
    name:'Lee',
    age:100,
    run:function(){
    return this.name+this.age+'運行中...';
}
};

/*使用構造函數建立原型對象和使用字面量建立對象在使用上基本相同,但仍是有一些區
別,字面量建立的方式使用constructor屬性不會指向實例,而會指向Object,構造函數建立
的方式則相反。*/

var box = newBox();
alert(box instanceof Box);
alert(box instanceof Object);
alert(box.constructor==Box); //字面量方式,返回false,不然,true
alert(box.constructor==Object); //字面量方式,返回true,不然,false

// /若是想讓字面量方式的constructor指向實例對象,那麼能夠這麼作:

Box.prototype={
    constructor:Box, //直接強制指向便可
};

/*PS:字面量方式爲何constructor會指向Object?由於Box.prototype={};這種寫法其實
就是建立了一個新對象。而每建立一個函數,就會同時建立它prototype,這個對象也會自
動獲取constructor屬性。因此,新對象的constructor重寫了Box原來的constructor,所以會
指向新對象,那個新對象沒有指定構造函數,那麼就默認爲Object。*/

//原型的聲明是有前後順序的,因此,重寫的原型會切斷以前的原型。

function Box(){};
Box.prototype={ //原型被重寫了
    constructor:Box,
    name:'Lee',
    age:100,
    run:function(){
    return this.name+this.age+'運行中...';
    }
};

Box.prototype={
    age=200
};
var box = new Box(); //在這裏聲明
alert(box.run()); //box只是最初聲明的原型

//原型對象不只僅能夠在自定義對象的狀況下使用,而ECMAScript內置的引用類型均可
//以使用這種方式,而且內置的引用類型自己也使用了原型。

alert(Array.prototype.sort); //sort就是Array類型的原型方法
alert(String.prototype.substring); //substring就是String類型的原型方
String.prototype.addstring=function(){ //給String類型添加一個方法
    return this+',被添加了!'; //this表明調用的字符串
};
alert('Lee'.addstring()); //使用這個方法


/*PS:儘管給原生的內置引用類型添加方法使用起來特別方便,但咱們不推薦使用這種
方法。由於它可能會致使命名衝突,不利於代碼維護。*/

/*原型模式建立對象也有本身的缺點,它省略了構造函數傳參初始化這一過程,帶來的缺
點就是初始化的值都是一致的。而原型最大的缺點就是它最大的優勢,那就是共享。
原型中全部屬性是被不少實例共享的,共享對於函數很是合適,對於包含基本值的屬性
也還能夠。但若是屬性包含引用類型,就存在必定的問題:*/

function Box(){};
Box.prototype={
    constructor:Box,
    name:'Lee',
    age:100,
    family:['父親', '母親', '妹妹'], //添加了一個數組屬性
    run:function(){
    return this.name+this.age+this.family;
    }
};
var box1 = new Box();

box1.family.push('哥哥'); //在實例中添加'哥哥'
alert(box1.run());
var box2 = new Box();
alert(box2.run()); //共享帶來的麻煩,也有'哥哥'了

/*PS:數據共享的緣故,致使不少開發者放棄使用原型,由於每次實例化出的數據須要
保留本身的特性,而不能共享。*/

//爲了解決構造傳參和共享問題,能夠組合構造函數+原型模式:

function Box(name,age){ //不共享的使用構造函數
    this.name=name;
    this.age=age;
    this.family=['父親', '母親', '妹妹'];
};

Box.prototype={ //共享的使用原型模式
    constructor:Box,
    run:function(){
    return this.name+this.age+this.family;
    }
};


//PS:這種混合模式很好的解決了傳參和引用共享的大難題。是建立對象比較好的方法。

/*原型模式,無論你是否調用了原型中的共享方法,它都會初始化原型中的方法,而且在
聲明一個對象時,構造函數+原型部分讓人感受又很怪異,最好就是把構造函數和原型封裝
到一塊兒。爲了解決這個問題,咱們能夠使用動態原型模式。*/

function Box(name,age){ //將全部信息封裝到函數體內
    this.name=name;
    this.age=age;
    if(typeofthis.run!='function') { //僅在第一次調用的初始化
        Box.prototype.run=function(){
        return this.name+this.age+'運行中...';
        };
    }
}
var box = new Box('Lee',100);
alert(box.run());

/*當第一次調用構造函數時,run()方法發現不存在,而後初始化原型。當第二次調用,就
不會初始化,而且第二次建立新對象,原型也不會再初始化了。這樣及獲得了封裝,又實現
了原型方法共享,而且屬性都保持獨立。*/

if(typeof this.run!='function') {
    alert('第一次初始化'); //測試用
    Box.prototype.run=function(){
    return this.name+this.age+'運行中...';
};
}
var box = new Box('Lee',100); //第一次建立對象
alert(box.run()); //第一次調用
alert(box.run()); //第二次調用
var box2 =new Box('Jack',200); //第二次建立對象
alert(box2.run());
alert(box2.run());

/*PS:使用動態原型模式,要注意一點,不能夠再使用字面量的方式重寫原型,由於會
切斷實例和新原型之間的聯繫。
以上講解了各類方式對象建立的方法,若是這幾種方式都不能知足需求,能夠使用一開
始那種模式:寄生構造函數。*/

function Box(name,age){
    var obj = new Object();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
    return this.name+this.age+'運行中...';
    };
    return obj;
}

/*寄生構造函數,其實就是工廠模式+構造函數模式。這種模式比較通用,但不能肯定對
象關係,因此,在能夠使用以前所說的模式時,不建議使用此模式。
在什麼狀況下使用寄生構造函數比較合適呢?假設要建立一個具備額外方法的引用類
型。因爲以前說明不建議直接String.prototype.addstring,能夠經過寄生構造的方式添加。*/

function myString(string){
    var str = new String(string);
    str.addstring=function(){
    return this+',被添加了!';
    };
    return str;
}
var box=new myString('Lee'); //比直接在引用原型添加要繁瑣好多
alert(box.addstring());

/*在一些安全的環境中,好比禁止使用this和new,這裏的this是構造函數裏不使用this,
這裏的new是在外部實例化構造函數時不使用new。這種建立方式叫作穩妥構造函數。*/

function Box(name,age){
    var obj = new Object();
    obj.run=function(){
    return name+age+'運行中...'; //直接打印參數便可
    };
    return obj;
}
var box=Box('Lee',100); //直接調用函數
alert(box.run());
//PS:穩妥構造函數和寄生相似。

// /繼承

/*繼承是面向對象中一個比較核心的概念。其餘正統面嚮對象語言都會用兩種方式實現繼
承:一個是接口實現,一個是繼承。而ECMAScript只支持繼承,不支持接口實現,而實現
繼承的方式依靠原型鏈完成。*/

function Box(){ //Box構造
    this.name='Lee';
}
function Desk(){ //Desk構造
    this.age=100;
}
Desk.prototype = new Box(); //Desc繼承了Box,經過原型,造成鏈條
var desk = new Desk();
alert(desk.age);
alert(desk.name); //獲得被繼承的屬性
function Table(){ //Table構造
    this.level='AAAAA';
}

Table.prototype = new Desk(); //繼續原型鏈繼承
var table = new Table();
alert(table.name); //繼承了Box和Desk

/*若是要實例化table,那麼Desk實例中有age=100,原型中增長相同的屬性age=200,
最後結果是多少呢?*/

Desk.prototype.age=200; //實例和原型中均包含age

/*PS:以上原型鏈繼承還缺乏一環,那就是Obejct,全部的構造函數都繼承自Obejct。而
繼承Object是自動完成的,並不須要程序員手動繼承。
通過繼承後的實例,他們的從屬關係會怎樣呢?*/

alert(table instanceof Object); //true
alert(desk instanceof Table); //false,desk是table的超類
alert(table instanceof Desk); //true
alert(table instanceof Box); //true

/*在JavaScript裏,被繼承的函數稱爲超類型(父類,基類也行,其餘語言叫法),繼承的
函數稱爲子類型(子類,派生類)。繼承也有以前問題,好比字面量重寫原型會中斷關係,使
用引用類型的原型,而且子類型還沒法給超類型傳遞參數。
爲了解決引用共享和超類型沒法傳參的問題,咱們採用一種叫借用構造函數的技術,或
者成爲對象冒充(僞造對象、經典繼承)的技術來解決這兩種問題。*/

function Box(age){
    this.name=['Lee','Jack', 'Hello']
    this.age=age;
}
function Desk(age){
Box.call(this,age); //對象冒充,給超類型傳參
}

var desk = new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push('AAA'); //添加的新數據,只給desk
alert(desk.name);

//借用構造函數雖然解決了剛纔兩種問題,但沒有原型,複用則無從談起。因此,咱們需
//要原型鏈+借用構造函數的模式,這種模式成爲組合繼承。

function Box(age){
    this.name=['Lee','Jack', 'Hello']
    this.age=age;
}
Box.prototype.run=function(){
return this.name+this.age;
};
function Desk(age){
    Box.call(this,age); //對象冒充
}
Desk.prototype=new Box(); //原型鏈繼承
var desk = new Desk(100);
alert(desk.run());

/*還有一種繼承模式叫作:原型式繼承;這種繼承藉助原型並基於已有的對象建立新對象,
同時還沒必要所以建立自定義類型。*/

function obj(o){ //傳遞一個字面量函數
    function F(){} //建立一個構造函數
    F.prototype=o; //把字面量函數賦值給構造函數的原型
    return new F(); //最終返回出實例化的構造函數
}
var box={ //字面量對象
    name:'Lee',
    arr:['哥哥','妹妹','姐姐']
};
var box1 = obj(box); //傳遞
alert(box1.name);

box1.name='Jack';
alert(box1.name);
alert(box1.arr);
box1.arr.push('父母');
alert(box1.arr);
varbox2=obj(box); //傳遞
alert(box2.name);
alert(box2.arr); //引用類型共享了


//寄生式繼承把原型式+工廠模式結合而來,目的是爲了封裝建立對象的過程。


function create(o){ //封裝建立過程
    var f = obj(o);
    f.run=function(){
    return this.arr; //一樣,會共享引用
    };
    return f;
}

/*組合式繼承是JavaScript最經常使用的繼承模式;但,組合式繼承也有一點小問題,就是超
類型在使用過程當中會被調用兩次:一次是建立子類型的時候,另外一次是在子類型構造函數的
內部。*/

function Box(name){
    this.name=name;
    this.arr=['哥哥','妹妹','父母'];
}
Box.prototype.run=function(){
return this.name;
};
function Desk(name,age){
    Box.call(this,name); //第二次調用Box
    this.age=age;
}
Desk.prototype = new Box(); //第一次調用Box

// /以上代碼是以前的組合繼承,那麼寄生組合繼承,解決了兩次調用的問題。

function obj(o){
    function F(){}
    F.prototype=o;
    return new F();
}

function create(box,desk){
var f = obj(box.prototype);
f.constructor=desk;
desk.prototype=f;
}
function Box(name){
    this.name=name;
    this.arr=['哥哥','妹妹','父母'];
}
Box.prototype.run=function(){
    return this.name;
};

function Desk(name,age){
    Box.call(this,name);
    this.age=age;
}
inPrototype(Box,Desk); //經過這裏實現繼承
var desk = new Desk('Lee',100);
desk.arr.push('姐姐');
alert(desk.arr);
alert(desk.run()); //只共享了方法
var desk2 = new Desk('Jack',200);
alert(desk2.arr); //引用問題解決
View Code

 

第16章 匿名函數和閉包

//匿名函數就是沒有名字的函數,閉包是可訪問一個函數做用域裏變量的函數。

//匿名函數

//普通函數
function box(){ //函數名是box
    return 'Lee';
}

//匿名函數
function (){ //匿名函數,會報錯
    return 'Lee';
}

//經過表達式自我執行
(function box(){ //封裝成表達式
    alert('Lee');
})(); //()表示執行函數,而且傳參

//把匿名函數賦值給變量
var box = function(){ //將匿名函數賦給變量
    return'Lee';
};
alert(box()); //調用方式和函數調用類似

//函數裏的匿名函數
function box(){
    return function(){ //函數裏的匿名函數,產生閉包
    return'Lee';
    }
}
alert(box()()); //調用匿名函數


//閉包
/*閉包是指有權訪問另外一個函數做用域中的變量的函數,建立閉包的常見的方式,就是在
一個函數內部建立另外一個函數,經過另外一個函數訪問這個函數的局部變量。*/

//經過閉包能夠返回局部變量

function box(){
    var user = 'Lee';
    return function(){ //經過匿名函數返回box()局部變量
    return user;
};
}
alert(box()()); //經過box()()來直接調用匿名函數返回值
var b = box();
alert(b()); //另外一種調用匿名函數返回值

/*使用閉包有一個優勢,也是它的缺點:就是能夠把局部變量駐留在內存中,能夠避免使
用全局變量。(全局變量污染致使應用程序不可預測性,每一個模塊均可調用必將引來災難,
因此推薦使用私有的,封裝的局部變量)。*/

//經過全局變量來累加
var age = 100; //全局變量
function box(){
    age++; //模塊級能夠調用全局變量,進行累加
}
box(); //執行函數,累加了
alert(age); //輸出全局變量
//經過局部變量沒法實現累加
function box(){
    var age=100;
    age++; //累加
    return age;
}
alert(box()); //101
alert(box()); //101,沒法實現,由於又被初始化了

//經過閉包能夠實現局部變量的累加
function box(){
    var age=100;
    return function(){
    age++;
    return age;
    }
}
var b = box(); //得到函數
alert(b()); //調用匿名函數
alert(b()); //第二次調用匿名函數,實現累加

/*PS:因爲閉包裏做用域返回的局部變量資源不會被馬上銷燬回收,因此可能會佔用更
多的內存。過分使用閉包會致使性能降低,建議在很是有必要的時候才使用閉包。*/

//做用域鏈的機制致使一個問題,在循環中裏的匿名函數取得的任何變量都是最後一個值。

//循環裏包含匿名函數
function box(){
    var arr=[];
    for(var i=0;i<5;i++){
    arr[i]=function(){
    return i;
    };
    }
    return arr;
}
var b = box(); //獲得函數數組
alert(b.length); //獲得函數集合長度
for(var i=0;i<b.length;i++){
alert(b[i]()); //輸出每一個函數的值,都是最後一個值
}

/*上面的例子輸出的結果都是5,也就是循環後獲得的最大的i值。由於b[i]調用的是匿
名函數,匿名函數並無自我執行,等到調用的時候,box()已執行完畢,i早已變成5,所
以最終的結果就是5個5。*/

//循環裏包含匿名函數-改1,自我執行匿名函數

function box(){
    var arr=[];
    for(var i=0;i<5;i++){
    arr[i]=(function(num){ //自我執行
    return num;
    })(i); //而且傳參
    }
    returnarr;
}


var b = box();
for(var i=0;i<b.length;i++){
    alert(b[i]);
}

/*改1中,咱們讓匿名函數進行自我執行,致使最終返回給a[i]的是數組而不是函數了。
最終致使b[0]-b[4]中保留了0,1,2,3,4的值。*/

//循環裏包含匿名函數-改2,匿名函數下再作個匿名函數

function box(){
    var arr=[];
    for(vari=0;i<5;i++){
    arr[i]=(function(num){
    return function(){ //直接返回值,改2變成返回函數
    return num; //原理和改1同樣
        }
    })(i);
    }
    return arr;
}
var b = box();
for(var i=0;i<b.length;i++){
alert(b[i]()); //這裏經過b[i]()函數調用便可
}

/*改1和改2中,咱們經過匿名函數自我執行,當即把結果賦值給a[i]。每個i,是調
用方經過按值傳遞的,因此最終返回的都是指定的遞增的i。而不是box()函數裏的i。*/

/*關於this對象
在閉包中使用this對象也可能會致使一些問題,this對象是在運行時基於函數的執行環
境綁定的,若是this在全局範圍就是window,若是在對象內部就指向這個對象。而閉包卻
在運行時指向window的,由於閉包並不屬於這個對象的屬性或方法。*/

var user='The Window';
var obj={
    user:'The Object',
    getUserFunction:function(){
    return function(){ //閉包不屬於obj,裏面的this指向window
    return this.user;

        };
    }
};

alert(obj.getUserFunction()()); //Thewindow

//能夠強制指向某個對象
alert(obj.getUserFunction().call(obj)); //TheObject

//也能夠從上一個做用域中獲得對象
getUserFunction:function(){
    var that=this; //從對象的方法裏得對象
    return function(){
    return that.user;
    };
}

/*內存泄漏
因爲IE的JScript對象和DOM對象使用不一樣的垃圾收集方式,所以閉包在IE中會致使
一些問題。就是內存泄漏的問題,也就是沒法銷燬駐留在內存中的元素。如下代碼有兩個知
識點尚未學習到,一個是DOM,一個是事件。*/

function box(){
    var oDiv = document.getElementById('oDiv'); //oDiv用完以後一直駐留在內存
    oDiv.onclick=function(){
    alert(oDiv.innerHTML); //這裏用oDiv致使內存泄漏
    };
}
box();

//那麼在最後應該將oDiv解除引用來避免內存泄漏。
function box(){
    var oDiv=document.getElementById('oDiv');
    var text=oDiv.innerHTML;
    oDiv.onclick=function(){
    alert(text);
};
oDiv=null; //解除引用
}

//PS:若是並無使用解除引用,那麼須要等到瀏覽器關閉才得以釋放。

//模仿塊級做用域
//JavaScript沒有塊級做用域的概念。
function box(count){
    for(vari=0;i<count;i++){}
    alert(i); //i不會由於離開了for塊就失效
}
box(2);
function box(count){
for(vari=0;i<count;i++){}
vari; //就算從新聲明,也不會前面的值
alert(i);
}
box(2);

/*以上兩個例子,說明JavaScript沒有塊級語句的做用域,if(){}for(){}等沒有做用域,
若是有,出了這個範圍i就應該被銷燬了。就算從新聲明同一個變量也不會改變它的值。
JavaScript不會提醒你是否屢次聲明瞭同一個變量;遇到這種狀況,它只會對後續的聲
明視而不見(若是初始化了,固然還會執行的)。使用模仿塊級做用域可避免這個問題。*/

//模仿塊級做用域(私有做用域)
(function(){
//這裏是塊級做用域
})();
//使用塊級做用域(私有做用域)改寫
function box(count){
(function(){
for( var i=0; i<count; i++){}
    })();
    alert(i); //報錯,沒法訪問
}
box(2);

/*使用了塊級做用域(私有做用域)後,匿名函數中定義的任何變量,都會在執行結束時被
銷燬。這種技術常常在全局做用域中被用在函數外部,從而限制向全局做用域中添加過多的
變量和函數。通常來講,咱們都應該儘量少向全局做用域中添加變量和函數。在大型項目
中,多人開發的時候,過多的全局變量和函數很容易致使命名衝突,引發災難性的後果。如
果採用塊級做用域(私有做用域),每一個開發者既能夠使用本身的變量,又沒必要擔憂搞亂全局
做用域。*/

(function(){
    var box=[1,2,3,4];
    alert(box); //box出來就不認識了
})();

/*在全局做用域中使用塊級做用域能夠減小閉包占用的內存問題,由於沒有指向匿名函數
的引用。只要函數執行完畢,就能夠當即銷燬其做用域鏈了。*/

/*私有變量
JavaScript沒有私有屬性的概念;全部的對象屬性都是公有的。不過,卻有一個私有變
量的概念。任何在函數中定義的變量,均可以認爲是私有變量,由於不能在函數的外部訪問
這些變量。*/

function box(){
    var age=100; //私有變量,外部沒法訪問
}

//而經過函數內部建立一個閉包,那麼閉包經過本身的做用域鏈也能夠訪問這些變量。而
//利用這一點,能夠建立用於訪問私有變量的公有方法。

function Box(){
    var age=100; //私有變量
    function run(){ //私有函數
    return'運行中...';
}
this.get=function(){ //對外公共的特權方法
return age+run();
};
}
var box = new Box();
alert(box.get());

//能夠經過構造方法傳參來訪問私有變量。

function Person(value){
    var user = value; //這句其實能夠省略
    this.getUser=function(){
    return user;
    };
    this.setUser=function(value){
    user =v alue;
    };
}

/*可是對象的方法,在屢次調用的時候,會屢次建立。能夠使用靜態私有變量來避免這個
問題。
靜態私有變量
經過塊級做用域(私有做用域)中定義私有變量或函數,一樣能夠建立對外公共的特權方
法。*/


(function(){
    varage=100;
    functionrun(){
    return'運行中...';
    }
    Box=function(){}; //構造方法
    Box.prototype.go=function(){ //原型方法
    returnage+run();
    };
})();

var box = new Box();
alert(box.go());

/*上面的對象聲明,採用的是Box=function(){}而不是functionBox(){}由於若是用後
面這種,就變成私有函數了,沒法在全局訪問到了,因此使用了前面這種。*/

(function(){
    var user='';
    Person = function(value){
    user = value;
    };
    Person.prototype.getUser=function(){
    return user;
    };
    Person.prototype.setUser=function(value){
    user=value;
    }
})();

/*使用了prototype致使方法共享了,而user也就變成靜態屬性了。(所謂靜態屬性,即共
享於不一樣對象中的屬性)。
模塊模式
以前採用的都是構造函數的方式來建立私有變量和特權方法。那麼對象字面量方式就採
用模塊模式來建立。*/

var box={ //字面量對象,也是單例對象
    age:100, //這是公有屬性,將要改爲私有
    run:function(){ //這時公有函數,將要改爲私有
    return'運行中...';
    };
};

//私有化變量和函數:

var box = function(){
    var age=100;
    function run(){
    return'運行中...';
}
    return{ //直接返回對象
    go:function(){
    return age+run();
    }
    };
}();

//上面的直接返回對象的例子,也能夠這麼寫:

var box = function(){
    var age=100;
    function run(){
    return'運行中...';
    }
    var obj= { //建立字面量對象
    go:function(){
    return age+run();
    }
    };
    return obj; //返回這個對象
}();

/*字面量的對象聲明,其實在設計模式中能夠看做是一種單例模式,所謂單例模式,就是
永遠保持對象的一個實例。
加強的模塊模式,這種模式適合返回自定義對象,也就是構造函數。*/

function Desk(){};
var box = function(){
    var age=100;
    function run(){
    return'運行中...';
}
var desk = new Desk(); //能夠實例化特定的對象
desk.go=function(){
return age + run();
};
return desk;
}(); 
alert(box.go());
View Code

 

第17章 BOM

//BOM也叫瀏覽器對象模型,它提供了不少對象,用於訪問瀏覽器的功能。
//BOM自己是沒有標準的或者尚未哪一個組織去標準它。

//window對象

/*BOM的核心對象是window,它表示瀏覽器的一個實例。window對象處於JavaScript結
構的最頂層,對於每一個打開的窗口,系統都會自動爲其定義window對象。*/

/*1.對象的屬性和方法
window對象有一系列的屬性,這些屬性自己也是對象。*/

/*    
    window對象的屬性
    
    closed 當窗口關閉時爲真
    defaultStatus 窗口底部狀態欄顯示的默認狀態消息
    document 窗口中當前顯示的文檔對象
    frames 窗口中的框架對象數組
    history 保存有窗口最近加載的URL
    length 窗口中的框架數
    location 當前窗口的URL
    name 窗口名
    offscreenBuffering 用於繪製新窗口內容並在完成後複製已存在的內容,控制屏幕更新
    opener 打開當前窗口的窗口
    parent 指向包含另外一個窗口的窗口(由框架使用)
    screen 顯示屏幕相關信息,如高度、寬度(以像素爲單位)
    self 指示當前窗口。
    status 描述由用戶交互致使的狀態欄的臨時消息
    top 包含特定窗口的最頂層窗口(由框架使用)
    window 指示當前窗口,與self等效
*/

/*
    window對象的方法
    alert(text) 建立一個警告對話框,顯示一條信息
    blur() 將焦點從窗口移除
    clearInterval(interval) 清除以前設置的定時器間隔
    clearTimeOut(timer) 清除以前設置的超時
    close() 關閉窗口
    confirm() 建立一個須要用戶確認的對話框
    focus() 將焦點移至窗口
    open(url,name,[options]) 打開一個新窗口並返回新window對象
    prompt(text,defaultInput) 建立一個對話框要求用戶輸入信息
    scroll(x,y) 在窗口中滾動到一個像素點的位置
    setInterval(expression,milliseconds)通過指定時間間隔計算一個表達式
    setInterval(function,millisenconds,[arguments])通過指定時間間隔後調用一個函數
    setTimeout(expression,milliseconds)在定時器超事後計算一個表達式
    setTimeout(expression,milliseconds,[arguments])在定時器超過期後計算一個函數
    print() 調出打印對話框
    find() 調出查找對話框

*/

/*window下的屬性和方法,能夠使用window.屬性、window.方法()或者直接屬性、方法()
的方式調用。例如:window.alert()和alert()是一個意思。*/

/*2.系統對話框
瀏覽器經過alert()、confirm()和prompt()方法能夠調用系統對話框向用戶顯示信息。系
統對話框與瀏覽器中顯示的網頁沒有關係,也不包含HTML。*/

//彈出警告
alert('Lee'); //直接彈出警告


//肯定和取消
confirm('請肯定或者取消'); //這裏按哪一個都無效
if(confirm('請肯定或者取消')) { //confirm自己有返回值
        alert('您按了肯定!'); //按肯定返回true
    }else{
        alert('您按了取消!'); //按取消返回false
}

//輸入提示框
var num = prompt('請輸入一個數字', 0); //兩個參數,一個提示,一個值
alert(num); //返回值能夠獲得

//調出打印及查找對話框
print(); //打印
find(); //查找

defaultStatus='狀態欄默認文本'; //瀏覽器底部狀態欄初始默認值
status='狀態欄文本'; //瀏覽器底部狀態欄設置值

/*3.新建窗口
使用window.open()方法能夠導航到一個特定的URL,也能夠打開一個新的瀏覽器窗口。
它能夠接受四個參數:1.要加載的URL;2.窗口的名稱或窗口目標;3.一個特性字符串;4.
一個表示新頁面是否取代瀏覽器記錄中當前加載頁面的布爾值。*/
open('http://www.baidu.com'); //新建頁面並打開百度
open('http://www.baidu.com','baidu'); //新建頁面並命名窗口並打開百度
open('http://www.baidu.com','_parent'); //在本頁窗口打開百度,_blank是新建

/*PS:不命名會每次打開新窗口,命名的第一次打開新窗口,以後在這個窗口中加載。
窗口目標是提供頁面的打開的方式,好比本頁面,仍是新建。*/

/*
    第三字符串參數
    設置         值                     說明
    width         數值         新窗口的寬度。不能小於100
    height         數值         新窗口的高度。不能小於100
    top         數值         新窗口的Y座標。不能是負值
    left         數值         新窗口的X座標。不能是負值
    location     yes或no     是否在瀏覽器窗口中顯示地址欄。不一樣瀏覽器默認值不一樣
    menubar     yes或no     是否在瀏覽器窗口顯示菜單欄。默認爲no
    resizable     yes或no     是否能夠經過拖動瀏覽器窗口的邊框改變大小。默認爲no
    scrollbars     yes或no     若是內容在頁面中顯示不下,是否容許滾動。默認爲no
    status         yes或no     是否在瀏覽器窗口中顯示狀態欄。默認爲no
    toolbar     yes或no     是否在瀏覽器窗口中顯示工具欄。默認爲no
    fullscreen    yes或no     瀏覽器窗口是否最大化,僅限IE
*/
//第三參數字符串
open('http://www.baidu.com','baidu','width=400,height=400,top=200,left=200,toolbar=yes');

//open自己返回window對象
var box = open();
box.alert(''); //能夠指定彈出的窗口執行alert();

//子窗口操做父窗口
document.onclick=function(){
    opener.document.write('子窗口讓我輸出的!');
}

/*3.窗口的位置和大小
用來肯定和修改window對象位置的屬性和方法有不少。IE、Safari、Opera和Chrome
都提供了screenLeft和screenTop屬性,分別用於表示窗口相對於屏幕左邊和上邊的位置。
Firefox則在screenX和screenY屬性中提供相同的窗口位置信息,Safari和Chrome也同時
支持這兩個屬性。*/

//肯定窗口的位置,IE支持
alert(screenLeft); //IE支持
alert(typeof screenLeft); //IE顯示number,不支持的顯示undefined

//肯定窗口的位置,Firefox支持
alert(screenX); //Firefox支持
alert(typeof screenX); //Firefox顯示number,不支持的同上

/*PS:screenX屬性IE瀏覽器不認識,直接alert(screenX),screenX會看成一個爲聲明的
變量,致使不執行。那麼必須將它將至爲window屬性才能顯示爲初始化變量應有的值,所
以應該寫成:alert(window.screenX)。*/

//跨瀏覽器的方法
var leftX=(typeof screenLeft=='number') ? screenLeft : screenX;
var topY=(typeof screenTop=='number') ? screenTop : screenY;

/*窗口頁面大小,Firefox、Safari、Opera和Chrome均爲此提供了4個屬性:innerWidth
和innerHeight,返回瀏覽器窗口自己的尺寸;outerWidth和outerHeight,返回瀏覽器窗口本
身及邊框的尺寸。*/

alert(innerWidth); //頁面長度
alert(innerHeight); //頁面高度
alert(outerWidth); //頁面長度+邊框
alert(outerHeight); //頁面高度+邊框

/*
    PS:在Chrome中,innerWidth=outerWidth、innerHeight=outerHeight;
    PS:IE沒有提供當前瀏覽器窗口尺寸的屬性;不過,在後面的DOM課程中有提供相
    關的方法。
    在IE以及Firefox、Safari、Opera和Chrome中,document.documentElement.clientWidth
    和document.documentElement.clientHeight中保存了頁面窗口的信息。
    PS:在IE6中,這些屬性必須在標準模式下才有效;若是是怪異模式,就必須經過
    document.body.clientWidth和document.body.clientHeight取得相同的信息。
*/


//若是是Firefox瀏覽器,直接使用innerWidth和innerHeight

var width=window.innerWidth; //這裏要加window,由於IE會無效
var height=window.innerHeight;
if(typeof width!='number') { //若是是IE,就使用document
    if(document.compatMode=='CSS1Compat') {
        width=document.documentElement.clientWidth;
        height=document.documentElement.clientHeight;
    }else{
        width=document.body.clientWidth; //非標準模式使用body
        height=document.body.clientHeight;
    }
}

/*PS:以上方法能夠經過不一樣瀏覽器取得各自的瀏覽器窗口頁面可視部分的大小。
document.compatMode能夠肯定頁面是否處於標準模式,若是返回CSS1Compat即標準模式。*/

//調整瀏覽器位置
moveTo(0,0); //IE有效,移動到0,0座標
moveBy(10,10); //IE有效,向下和右分別移動10像素
//調整瀏覽器大小
resizeTo(200,200); //IE有效,調正大小
resizeBy(200,200); //IE有效,擴展收縮大小
//PS:因爲此類方法被瀏覽器禁用較多,用處不大。

/*
4.間歇調用和超時調用
JavaScript是單線程語言,但它容許經過設置超時值和間歇時間值來調度代碼在特定的
時刻執行。前者在指定的時間事後執行代碼,然後者則是每隔指定的時間就執行一次代碼。
超時調用須要使用window對象的setTimeout()方法,它接受兩個參數:要執行的代碼
和毫秒數的超時時間。
*/
setTimeout("alert('Lee')",1000); //不建議直接使用字符串

function box(){
    alert('Lee');
}

setTimeout(box,1000); //直接傳入函數名便可
setTimeout(function(){ //推薦作法
    alert('Lee');
},1000);
//PS:直接使用函數傳入的方法,擴展性好,性能更佳。

/*調用setTimeout()以後,該方法會返回一個數值ID,表示超時調用。這個超時調用的ID
是計劃執行代碼的惟一標識符,能夠經過它來取消超時調用。
要取消還沒有執行的超時調用計劃,能夠調用clearTimeout()方法並將相應的超時調用ID
做爲參數傳遞給它。*/

var box = setTimeout(function(){ //把超時調用的ID複製給box
    alert('Lee');
},1000);

clearTimeout(box); //把ID傳入,取消超時調用

/*間歇調用與超時調用相似,只不過它會按照指定的時間間隔重複執行代碼,直至間歇調
用被取消或者頁面被卸載。設置間歇調用的方法是setInterval(),它接受的參數與setTimeout()
相同:要執行的代碼和每次執行以前須要等待的毫秒數。*/

setInterval(function(){ //重複不停執行
    alert('Lee');
},1000);


/*取消間歇調用方法和取消超時調用相似,使用clearInterval()方法。但取消間歇調用的重
要性要遠遠高於取消超時調用,由於在不加干涉的狀況下,間歇調用將會一直執行到頁面關
閉。*/

var box=setInterval(function(){ //獲取間歇調用的ID
    alert('Lee');
},1000);
clearInterval(box); //取消間歇調用

//但上面的代碼是沒有意義的,咱們須要一個能設置5秒的定時器,須要以下代碼:
var num=0; //設置起始秒
var max=5; //設置最終秒
setInterval(function(){ //間歇調用
num++; //遞增num
if(num==max){ //若是獲得5秒
    clearInterval(this); //取消間歇調用,this表示方法自己
    alert('5秒後彈窗!');
    }
},1000); //1秒


/*通常認爲,使用超時調用來模擬間歇調用是一種最佳模式。在開發環境下,不多使用真
正的間歇調用,由於須要根據狀況來取消ID,而且可能形成同步的一些問題,咱們建議不
使用間歇調用,而去使用超時調用。*/

var num=0;
var max=5;
function box(){
    num++;
        if(num==max){
        alert('5秒後結束!');
    }else{
        setTimeout(box,1000);
    }
}
setTimeout(box,1000); //執行定時器

/*PS:在使用超時調用時,不必跟蹤超時調用ID,由於每次執行代碼以後,若是再也不
設置另外一次超時調用,調用就會自行中止。*/


/*location對象
location是BOM對象之一,它提供了與當前窗口中加載的文檔有關的信息,還提供了
一些導航功能。事實上,location對象是window對象的屬性,也是document對象的屬性;
因此window.location和document.location等效。*/


alert(location); //獲取當前的URL

/*
location對象的屬性


屬性                         描述的URL內容
hash                 若是該部分存在,表示錨點部分
host                 主機名:端口號
hostname             主機名
href                 整個URL
pathname             路徑名
port                 端口號
protocol            協議部分
search                 查詢字符串

*/

/*
location對象的方法

方法                     功能
assign()         跳轉到指定頁面,與href等效
reload()         重載當前URL
repalce()         用新的URL替換當前頁面
*/

location.hash='#1'; //設置#後的字符串,並跳轉
alert(location.hash); //獲取#後的字符串

location.port=8888; //設置端口號,並跳轉
alert(location.port); //獲取當前端口號,

location.hostname='Lee'; //設置主機名,並跳轉
alert(location.hostname); //獲取當前主機名,

location.pathname='Lee'; //設置當前路徑,並跳轉
alert(location.pathname); //獲取當前路徑,

location.protocal='ftp:'; //設置協議,沒有跳轉
alert(location.protocol); //獲取當前協議

location.search='?id=5'; //設置?後的字符串,並跳轉
alert(location.search); //獲取?後的字符串

location.href='http://www.baidu.com'; //設置跳轉的URL,並跳轉
alert(location.href); //獲取當前的URL

/*在Web開發中,咱們常常須要獲取諸如?id=5&search=ok這種類型的URL的鍵值對,
那麼經過location,咱們能夠寫一個函數,來一一獲取。*/


function getArgs(){
//建立一個存放鍵值對的數組
var args=[];
//去除?號
var qs=location.search.length>0?location.search.substring(1):'';
//按&字符串拆分數組

    var items=qs.split('&');
    var item=null,name=null,value=null;
    //遍歷
    for(var i =0;i<items.length;i++){
    item=items[i].split('=');
    name=item[0];
    value=item[1];
    //把鍵值對存放到數組中去
    args[name]=value;
    }
    return args;
}
var args=getArgs();
alert(args['id']);
alert(args['search']);
location.assign('http://www.baidu.com'); //跳轉到指定的URL
location.reload(); //最有效的從新加載,有可能從緩存加載
location.reload(true); //強制加載,從服務器源頭從新加載
location.replace('http://www.baidu.com'); //能夠避免產生跳轉前的歷史記錄


/*三.history對象
history對象是window對象的屬性,它保存着用戶上網的記錄,從窗口被打開的那一刻
算起。*/


/*
        history對象的屬性
    屬性         描述URL中的哪部分
    length         history對象中的記錄數
*/

/*
    history對象的方法


方法                         功能
back()             前往瀏覽器歷史條目前一個URL,相似後退
forward()         前往瀏覽器歷史條目下一個URL,相似前進
go(num)         瀏覽器在history對象中向前或向後

*/


function back(){ //跳轉到前一個URL
    history.back();
}


function forward(){ //跳轉到下一個URL
    history.forward();
}
function go(num){ //跳轉指定歷史記錄的URL
    history.go(num);
}
//PS:能夠經過判斷history.length==0,獲得是否有歷史記錄。
View Code

 

第18章 瀏覽器檢測

第19章 DOM基礎

/*DOM(DocumentObjectModel)即文檔對象模型,針對HTML和XML文檔的API(應
用程序接口)。DOM描繪了一個層次化的節點樹,運行開發人員添加、移除和修改頁面的
某一部分。DOM脫胎於Netscape及微軟公司創始的DHTML(動態HTML),但如今它已
經成爲表現和操做頁面標記的真正跨平臺、語言中立的方式。*/

/*一.DOM介紹
DOM中的三個字母,D(文檔)能夠理解爲整個Web加載的網頁文檔;O(對象)可
以理解爲相似window對象之類的東西,能夠調用屬性和方法,這裏咱們說的是document
對象;M(模型)能夠理解爲網頁文檔的樹型結構。
DOM有三個等級,分別是DOM一、DOM二、DOM3,而且DOM1在1998年10月成爲
W3C標準。DOM1所支持的瀏覽器包括IE6+、Firefox、Safari、Chrome和Opera1.7+。
PS:IE中的全部DOM對象都是以COM對象的形式實現的,這意味着IE中的DOM
可能會和其餘瀏覽器有必定的差別。
1.節點
加載HTML頁面時,Web瀏覽器生成一個樹型結構,用來表示頁面內部結構。DOM將
這種樹型結構理解爲由節點組成。*/
View Code

/*從上圖的樹型結構,咱們理解幾個概念,html標籤沒有父輩,沒有兄弟,因此html標
籤爲根標籤。head標籤是html子標籤,meta和title標籤之間是兄弟關係。若是把每一個標籤*/

/*看成一個節點的話,那麼這些節點組合成了一棵節點樹。
PS:後面咱們常常把標籤稱做爲元素,是一個意思。
2.節點種類:元素節點、文本節點、屬性節點。
*/
View Code

/*二.查找元素
W3C提供了比較方便簡單的定位節點的方法和屬性,以便咱們快速的對節點進行操做。
分別爲:getElementById()、getElementsByTagName()、getElementsByName()、getAttribute()、
setAttribute()和removeAttribute()。*/

/*元素節點方法
    方法                     說明
getElementById()         獲取特定ID元素的節點
getElementsByTagName()    獲取相同元素的節點列表
getElementsByName()     獲取相同名稱的節點列表
getAttribute()             獲取特定元素節點屬性的值
setAttribute()             設置特定元素節點屬性的值
removeAttribute()         移除特定元素節點屬性
*/

/*1.getElementById()方法
getElementById()方法,接受一個參數:獲取元素的ID。若是找到相應的元素則返回該
元素的HTMLDivElement對象,若是不存在,則返回null。*/

document.getElementById('box'); //獲取id爲box的元素節點

/*PS:上面的例子,默認狀況返回null,這無關是否存在id="box"的標籤,而是執行順序
問題。解決方法,1.把script調用標籤移到html末尾便可;2.使用onload事件來處理JS,等
待html加載完畢再加載onload事件裏的JS。*/

window.onload=function(){ //預加載html後執行
    document.getElementById('box');
};

/*PS:id表示一個元素節點的惟一性,不能同時給兩個或以上的元素節點建立同一個命
名的id。某些低版本的瀏覽器會沒法識別getElementById()方法,好比IE5.0-,這時須要作
一些判斷,能夠結合上章的瀏覽器檢測來操做。*/

if(document.getElementById){ //判斷是否支持getElementById
    alert('當前瀏覽器支持getElementById');
}

//當咱們經過getElementById()獲取到特定元素節點時,這個節點對象就被咱們獲取到了,
//而經過這個節點對象,咱們能夠訪問它的一系列屬性。


/*元素節點屬性
    屬性     說明
tagName 獲取元素節點的標籤名
innerHTML 獲取元素節點裏的內容,非W3CDOM規範*/
document.getElementById('box').tagName; //DIV
document.getElementById('box').innerHTML; //測試Div

/*
HTML屬性的屬性
屬性             說明
id             元素節點的id名稱
title         元素節點的title屬性值
style         CSS內聯樣式屬性值
className     CSS元素的類
*/
document.getElementById('box').id; //獲取id
document.getElementById('box').id='person'; //設置id

document.getElementById('box').title; //獲取title
document.getElementById('box').title='標題' //設置title

document.getElementById('box').style; //獲取CSSStyleDeclaration對象
document.getElementById('box').style.color; //獲取style對象中color的值

document.getElementById('box').style.color='red'; //設置style對象中color的值
document.getElementById('box').className; //獲取class

document.getElementById('box').className='box'; //設置class
alert(document.getElementById('box').bbb); //獲取自定義屬性的值,非IE不支持

//2.getElementsByTagName()方法

/*getElementsByTagName()方法將返回一個對象數組HTMLCollection(NodeList),這個數
組保存着全部相同元素名的節點列表。*/

document.getElementsByTagName('*'); //獲取全部元素

//PS:IE瀏覽器在使用通配符的時候,會把文檔最開始的html的規範聲明看成第一個元素節點。
document.getElementsByTagName('li'); //獲取全部li元素,返回數組
document.getElementsByTagName('li')[0]; //獲取第一個li元素,HTMLLIElement
document.getElementsByTagName('li').item(0) //獲取第一個li元素,HTMLLIElement
document.getElementsByTagName('li').length; //獲取全部li元素的數目


/*PS:無論是getElementById仍是getElementsByTagName,在傳遞參數的時候,並非
全部瀏覽器都必須區分大小寫,爲了防止沒必要要的錯誤和麻煩,咱們必須堅持養成區分大小
寫的習慣。*/

/*3.getElementsByName()方法
getElementsByName()方法能夠獲取相同名稱(name)的元素,返回一個對象數組
HTMLCollection(NodeList)。*/

document.getElementsByName('add') //獲取input元素
document.getElementsByName('add')[0].value //獲取input元素的value值
document.getElementsByName('add')[0].checked//獲取input元素的checked值

/*PS:對於並非HTML合法的屬性,那麼在JS獲取的兼容性上也會存在差別,IE瀏
覽器支持自己合法的name屬性,而不合法的就會出現不兼容的問題。*/

/*4.getAttribute()方法
getAttribute()方法將獲取元素中某個屬性的值。它和直接使用.屬性獲取屬性值的方法有
必定區別。*/

document.getElementById('box').getAttribute('id');//獲取元素的id值
document.getElementById('box').id; //獲取元素的id值
document.getElementById('box').getAttribute('mydiv');//獲取元素的自定義屬性值
document.getElementById('box').mydiv //獲取元素的自定義屬性值,非IE不支持
document.getElementById('box').getAttribute('class');//獲取元素的class值,IE不支持
document.getElementById('box').getAttribute('className');//非IE不支持

/*PS:HTML通用屬性style和onclick,IE7更低的版本style返回一個對象,onclick返回
一個函數式。雖然IE8已經修復這個bug,但爲了更好的兼容,開發人員只有儘量避免使
用getAttribute()訪問HTML屬性了,或者碰到特殊的屬性獲取作特殊的兼容處理。*/

/*5.setAttribute()方法
setAttribute()方法將設置元素中某個屬性和值。它須要接受兩個參數:屬性名和值。若是
屬性自己已存在,那麼就會被覆蓋。*/

document.getElementById('box').setAttribute('align','center');//設置屬性和值
document.getElementById('box').setAttribute('bbb','ccc');//設置自定義的屬性和值

/*PS:在IE7及更低的版本中,使用setAttribute()方法設置class和style屬性是沒有效果
的,雖然IE8解決了這個bug,但仍是不建議使用。*/

//6.removeAttribute()方法
removeAttribute() //能夠移除HTML屬性。
document.getElementById('box').removeAttribute('style');//移除屬性

//PS:IE6及更低版本不支持removeAttribute()方法。
/*三.DOM節點
1.node節點屬性
節點能夠分爲元素節點、屬性節點和文本節點,而這些節點又有三個很是有用的屬性,
分別爲:nodeName、nodeType和nodeValue。*/

/*
信息節點屬性
節點類型     nodeName     nodeType     nodeValue
元素         元素名稱         1             null
屬性         屬性名稱         2             屬性值
文本         #text            3             文本內容(不包含html)

*/

document.getElementById('box').nodeType; //1,元素節點
View Code

 

第20章 動態加載腳本和樣式

//一.元素位置
//這節課補充一個DOM的方法:getBoundingClientRect()。
/*這個方法返回一個矩形對象,包含四個屬性:left、top、right
和bottom。分別表示元素各邊與頁面上邊和左邊的距離。*/

var box = document.getElementById('box'); //獲取元素
alert(box.getBoundingClientRect().top); //元素上邊距離頁面上邊的距離
alert(box.getBoundingClientRect().right); //元素右邊距離頁面左邊的距離
alert(box.getBoundingClientRect().bottom); //元素下邊距離頁面上邊的距離
alert(box.getBoundingClientRect().left); //元素左邊距離頁面左邊的距離

/*PS:IE、Firefox3+、Opera9.五、Chrome、Safari支持,在IE中,默認座標從(2,2)開始計
算,致使最終距離比其餘瀏覽器多出兩個像素,咱們須要作個兼容。*/

/*PS:IE、Firefox3+、Opera9.五、Chrome、Safari支持,在IE中,默認座標從(2,2)開始計
算,致使最終距離比其餘瀏覽器多出兩個像素,咱們須要作個兼容。*/

document.documentElement.clientTop; //非IE爲0,IE爲2
document.documentElement.clientLeft; //非IE爲0,IE爲2

function getRect(element){
    var rect=element.getBoundingClientRect();
    var top=document.documentElement.clientTop;
    var left=document.documentElement.clientLeft;
    return{
    top:rect.top-top,
    bottom:rect.bottom-top,
    left:rect.left-left,
    right:rect.right-left
    }
}

//PS:分別加上外邊據、內邊距、邊框和滾動條,用於測試全部瀏覽器是否一致。


/*
    二.動態腳本
當網站需求變大,腳本的需求也逐步變大。咱們就不得不引入太多的JS腳本而下降了
整站的性能,因此就出現了動態腳本的概念,在適時的時候加載相應的腳本。
好比:咱們想在須要檢測瀏覽器的時候,再引入檢測文件。
*/

var flag=true; //設置true再加載
    if(flag){
    loadScript('browserdetect.js'); //設置加載的js
}

function loadScript(url){
    var script=document.createElement('script');
    script.type='text/javascript';
    script.src=url;
    //document.head.appendChild(script); //document.head表示<head>
    document.getElementsByTagName('head')[0].appendChild(script);
}


//PS:document.head調用,IE不支持,會報錯!

//動態執行js
var script=document.createElement('script');
script.type='text/javascript';
vartext=document.createTextNode("alert('Lee')"); //IE瀏覽器報錯
script.appendChild(text);
document.getElementsByTagName('head')[0].appendChild(script);

/*PS:IE瀏覽器認爲script是特殊元素,不能在訪問子節點。爲了兼容,能夠使用text
屬性來代替。*/

script.text="alert('')"; //IE能夠支持了。

/*PS:固然,若是不支持text,那麼就能夠針對不一樣的瀏覽器特性來使用不一樣的方法。這
裏就忽略寫法了。*/

/*三.動態樣式
爲了動態的加載樣式表,好比切換網站皮膚。樣式表有兩種方式進行加載,一種是<link>
標籤,一種是<style>標籤。*/

//動態執行link
var flag=true;
if(flag){
    loadStyles('basic.css');
}
function loadStyles(url){
    var link=document.createElement('link');
    link.rel='stylesheet';
    link.type='text/css';
    link.href=url;
    document.getElementsByTagName('head')[0].appendChild(link);
}

//動態執行style
var flag=true;
if(flag){
    var style=document.createElement('style');
    style.type='text/css';
    //var box=document.createTextNode(#box{background:red}');IE不支持
    //style.appendChild(box);
    document.getElementsByTagName('head')[0].appendChild(style);
    insertRule(document.styleSheets[0],'#box', 'background:red', 0);
}

function insertRule(sheet,selectorText,cssText,position){
    //若是是非IE
    if(sheet.insertRule){
        sheet.insertRule(selectorText+"{"+cssText+"}",position);
    //若是是IE
    }elseif(sheet.addRule){
        sheet.addRule(selectorText,cssText,position);
    }
}
View Code

 

第24章 事件

  ①。事件入門

/*JavaScript事件是由訪問Web頁面的用戶引發的一系列操做,例如:用戶點擊。當用戶
執行某些操做的時候,再去執行一系列代碼。*/

/*一.事件介紹
事件通常是用於瀏覽器和用戶操做進行交互。最先是IE和NetscapeNavigator中出現,
做爲分擔服務器端運算負載的一種手段。直到幾乎全部的瀏覽器都支持事件處理。而DOM2
級規範開始嘗試以一種複合邏輯的方式標準化DOM事件。IE九、Firefox、Opera、Safari和
Chrome全都已經實現了「DOM2級事件」模塊的核心部分。IE8以前瀏覽器仍然使用其專
有事件模型。
JavaScript有三種事件模型:內聯模型、腳本模型和DOM2模型。*/

/*二.內聯模型
這種模型是最傳統接單的一種處理事件的方法。在內聯模型中,事件處理函數是HTML
標籤的一個屬性,用於處理指定事件。雖然內聯在早期使用較多,但它是和HTML混寫的,
並無與HTML分離。*/

//在HTML中把事件處理函數做爲屬性執行JS代碼
<input type="button" value="按鈕" onclick="alert('Lee');" /> //注意單雙引號
//在HTML中把事件處理函數做爲屬性執行JS函數
<input type="button" value="按鈕" onclick="box();" /> //執行JS的函數
//PS:函數不得放到window.onload裏面,這樣就看不見了。


/*三.腳本模型
因爲內聯模型違反了HTML與JavaScript代碼層次分離的原則。爲了解決這個問題,我
們能夠在JavaScript中處理事件。這種處理方式就是腳本模型。*/

var input=document.getElementsByTagName('input')[0]; //獲得input對象
input.onclick=function(){ //匿名函數執行
alert('Lee');
};

/*PS:經過匿名函數,能夠直接觸發對應的代碼。也能夠經過指定的函數名賦值的方式
來執行函數(賦值的函數名不要跟着括號)。*/

input.onclick=box; //把函數名賦值給事件處理函數

//四.事件處理函數
//JavaScript能夠處理的事件類型爲:鼠標事件、鍵盤事件、HTML事件。

/*
    JavaScript事件處理函數及其使用列表
事件處理函數            影響的元素                             什麼時候發生
onabort                     圖像                         當圖像加載被中斷時
onblur             窗口、框架、全部表單對象                 當焦點從對象上移開時
onchange         輸入框,選擇框和文本區域                當改變一個元素的值且失去焦點時
onclick         連接、按鈕、表單對象、圖像映射區域        當用戶單擊對象時
ondblclick         連接、按鈕、表單對象                     當用戶雙擊對象時
ondragdrop         窗口                                     當用戶將一個對象拖放到瀏覽器窗口時
onError         腳本                                     當腳本中發生語法錯誤時
onfocus         窗口、框架、全部表單對象                 當單擊鼠標或者將鼠標移動聚焦到窗口或框架時
onkeydown         文檔、圖像、連接、表單                     當按鍵被按下時
onkeypress         文檔、圖像、連接、表單                     當按鍵被按下而後鬆開時
onkeyup         文檔、圖像、連接、表單                     當按鍵被鬆開時
onload             主題、框架集、圖像                         文檔或圖像加載後
onunload         主體、框架集                             文檔或框架集卸載後
onmouseout         連接                                     當圖標移除連接時
onmouseover     連接                                     當鼠標移到連接時
onmove             窗口                                     當瀏覽器窗口移動時
onreset         表單復位按鈕                             單擊表單的reset按鈕
onresize         窗口                                     當選擇一個表單對象時
onselect         表單元素                                 當選擇一個表單對象時
onsubmit         表單                                     當發送表格到服務器時
*/

/*PS:全部的事件處理函數都會都有兩個部分組成,on+事件名稱,例如click事件的事件
處理函數就是:onclick。在這裏,咱們主要談論腳本模型的方式來構建事件,違反分離原
則的內聯模式,咱們忽略掉。*/

/*對於每個事件,它都有本身的觸發範圍和方式,若是超出了觸發範圍和方式,事件處
理將失效。*/

//1.鼠標事件,頁面全部元素均可觸發
//click:當用戶單擊鼠標按鈕或按下回車鍵時觸發。
input.onclick=function(){
    alert('Lee');
};

//dblclick:當用戶雙擊主鼠標按鈕時觸發。
input.ondblclick=function(){
    alert('Lee');
};

//mousedown:當用戶按下了鼠標還未彈起時觸發。
input.onmousedown=function(){
    alert('Lee');
};


//mouseup:當用戶釋放鼠標按鈕時觸發。
input.onmouseup=function(){
    alert('Lee');
};
//mouseover:當鼠標移到某個元素上方時觸發。
input.onmouseover=function(){
    alert('Lee');
};
//mouseout:當鼠標移出某個元素上方時觸發。
input.onmouseout=function(){
    alert('Lee');
};

//mousemove:當鼠標指針在元素上移動時觸發。
input.onmousemove=function(){
    alert('Lee');
};
//2.鍵盤事件
//keydown:當用戶按下鍵盤上任意鍵觸發,若是按住不放,會重複觸發。

onkeydown=function(){
    alert('Lee');
};


//keypress:當用戶按下鍵盤上的字符鍵觸發,若是按住不放,會重複觸發。
onkeypress=function(){
    alert('Lee');
};
//keyup:當用戶釋放鍵盤上的鍵觸發。
onkeyup=function(){
    alert('Lee');
};

//3.HTML事件
//load:當頁面徹底加載後在window上面觸發,或當框架集加載完畢後在框架集上觸發。
window.onload=function(){
    alert('Lee');
};
//unload:當頁面徹底卸載後在window上面觸發,或當框架集卸載後在框架集上觸發。
window.onunload=function(){
    alert('Lee');
};

//select:當用戶選擇文本框(input或textarea)中的一個或多個字符觸發。
input.onselect=function(){
    alert('Lee');
};
//change:當文本框(input或textarea)內容改變且失去焦點後觸發。
input.onchange=function(){
    alert('Lee');
};
//focus:當頁面或者元素得到焦點時在window及相關元素上面觸發。
input.onfocus=function(){
    alert('Lee');
};

//blur:當頁面或元素失去焦點時在window及相關元素上觸發。
input.onblur=function(){
    alert('Lee');
};


//submit:當用戶點擊提交按鈕在<form>元素上觸發。
form.onsubmit=function(){
    alert('Lee');
};
//reset:當用戶點擊重置按鈕在<form>元素上觸發。
form.onreset=function(){
    alert('Lee');
};
//resize:當窗口或框架的大小變化時在window或框架上觸發。
window.onresize=function(){
    alert('Lee');
};

//scroll:當用戶滾動帶滾動條的元素時觸發。
window.onscroll=function(){
    alert('Lee');
};
View Code

 

  ②。事件對象

/*JavaScript事件的一個重要方面是它們擁有一些相對一致的特色,能夠給你的開發提供
更多的強大功能。最方便和強大的就是事件對象,他們能夠幫你處理鼠標事件和鍵盤敲擊方
面的狀況,此外還能夠修改通常事件的捕獲/冒泡流的函數。*/

/*一.事件對象
事件處理函數的一個標準特性是,以某些方式訪問的事件對象包含有關於當前事件的上
下文信息。
事件處理三部分組成:對象.事件處理函數=函數。例如:單擊文檔任意處。*/
document.onclick=function(){
    alert('Lee');
};

/*PS:以上程序的名詞解釋:click表示一個事件類型,單擊。onclick表示一個事件處理
函數或綁定對象的屬性(或者叫事件監聽器、偵聽器)。document表示一個綁定的對象,用於
觸發某個元素區域。function()匿名函數是被執行的函數,用於觸發後執行。*/

//除了用匿名函數的方法做爲被執行的函數,也能夠設置成獨立的函數。
document.onclick=box; //直接賦值函數名便可,無須括號
    function box(){
    alert('Lee');
}

/*this關鍵字和上下文
在面向對象那章咱們瞭解到:在一個對象裏,因爲做用域的關係,this表明着離它最近
對象。*/

var input=document.getElementsByTagName('input')[0];
input.onclick=function(){
    alert(this.value); //HTMLInputElement,this表示input對象
};

/*從上面的拆分,咱們並無發現本章的重點:事件對象。那麼事件對象是什麼?它在哪
裏呢?當觸發某個事件時,會產生一個事件對象,這個對象包含着全部與事件有關的信息。
包括致使事件的元素、事件的類型、以及其它與特定事件相關的信息。
事件對象,咱們通常稱做爲event對象,這個對象是瀏覽器經過函數把這個對象做爲參
數傳遞過來的。那麼首先,咱們就必須驗證一下,在執行函數中沒有傳遞參數,是否能夠得
到隱藏的參數。*/

function box(){ //普通空參函數
    alert(arguments.length); //0,沒有獲得任何傳遞的參數
}
input.onclick=function(){ //事件綁定的執行函數
    alert(arguments.length); //1,獲得一個隱藏參數
};

/*經過上面兩組函數中,咱們發現,經過事件綁定的執行函數是能夠獲得一個隱藏參數的。
說明,瀏覽器會自動分配一個參數,這個參數其實就是event對象。*/

input.onclick=function(){
alert(arguments[0]); //MouseEvent,鼠標事件對象
};


//上面這種作法比較累,那麼比較簡單的作法是,直接經過接收參數來獲得便可。
input.onclick=function(evt){ //接受event對象,名稱不必定非要event
alert(evt); //MouseEvent,鼠標事件對象
};

//直接接收event對象,是W3C的作法,IE不支持,IE本身定義了一個event對象,直
//接在window.event獲取便可。
input.onclick=function(evt){
    vare=evt|| window.event; //實現跨瀏覽器兼容獲取event對象
    alert(e);
};

/*二.鼠標事件
鼠標事件是Web上面最經常使用的一類事件,畢竟鼠標仍是最主要的定位設備。那麼經過
事件對象能夠獲取到鼠標按鈕信息和屏幕座標獲取等。
1.鼠標按鈕
只有在主鼠標按鈕被單擊時(常規通常是鼠標左鍵)纔會觸發click事件,所以檢測按鈕
的信息並非必要的。但對於mousedown和mouseup事件來講,則在其event對象存在一
個button屬性,表示按下或釋放按鈕。*/

/*
    非IE(W3C)中的button屬性
    值                 說明
    0         表示主鼠標按鈕(常規通常是鼠標左鍵)
    1         表示中間的鼠標按鈕(鼠標滾輪按鈕)
    2         表示次鼠標按鈕(常規通常是鼠標右鍵)

*/

/*
    IE中的button屬性
值                     說明
0                 表示沒有按下按鈕
1                 表示主鼠標按鈕(常規通常是鼠標左鍵)
2                 表示次鼠標按鈕(常規通常是鼠標右鍵)
3                 表示同時按下了主、次鼠標按鈕
4                 表示按下了中間的鼠標按鈕
5                 表示同時按下了主鼠標按鈕和中間的鼠標按鈕
6                 表示同時按下了次鼠標按鈕和中間的鼠標按鈕
7                 表示同時按下了三個鼠標按鈕    
*/

/*
    PS:在絕大部分狀況下,咱們最多隻使用主次中三個單擊鍵,IE給出的其餘組合鍵一
般沒法使用上。因此,咱們只須要作上這三種兼容便可。
*/


function getButton(evt){ //跨瀏覽器左中右鍵單擊相應
    var e=evt|| window.event;
    if(evt){ //Chrome瀏覽器支持W3C和IE
        return e.button; //要注意判斷順序
    }else if(window.event){
        switch(e.button){
        case 1:
        return 0;
        case 4:
        return 1;
        case 2:
        return 2;
        }
    }
}


document.onmouseup=function(evt){ //調用
    if(getButton(evt)==0){
        alert('按下了左鍵!');
    }elseif(getButton(evt)==1){
        alert('按下了中鍵!');
    }elseif(getButton(evt)==2){
        alert('按下了右鍵!' );
    }
};


/*2.可視區及屏幕座標
事件對象提供了兩組來獲取瀏覽器座標的屬性,一組是頁面可視區左邊,另外一組是屏幕
座標。*/

/*
    座標屬性
屬性             說明
clientX     可視區X座標,距離左邊框的位置
clientY     可視區Y座標,距離上邊框的位置
screenX     屏幕區X座標,距離左屏幕的位置
screenY     屏幕區Y座標,距離上屏幕的位置
*/
document.onclick=function(evt){
    var e=evt|| window.event;
    alert(e.clientX+',' +e.clientY);
    alert(e.screenX+',' +e.screenY);
};


/*3.修改鍵
有時,咱們須要經過鍵盤上的某些鍵來配合鼠標來觸發一些特殊的事件。這些鍵爲:
Shfit、Ctrl、Alt和Meat(Windows中就是Windows鍵,蘋果機中是Cmd鍵),它們常常被用
來修改鼠標事件和行爲,因此叫修改鍵。*/
/*
    修改鍵屬性
屬性             說明
shiftKey     判斷是否按下了Shfit鍵
ctrlKey     判斷是否按下了ctrlKey鍵
altKey         判斷是否按下了alt鍵
metaKey     判斷是否按下了windows鍵,IE不支持
*/

function getKey(evt){
    var e=evt|| window.event;
    var keys=[];
    if(e.shiftKey) keys.push('shift'); //給數組添加元素
    if(e.ctrlKey) keys.push('ctrl');
    if(e.altKey) keys.push('alt');
    return keys;
}

document.onclick=function(evt){
    alert(getKey(evt));
};

/*
    三.鍵盤事件
用戶在使用鍵盤時會觸發鍵盤事件。「DOM2級事件」最初規定了鍵盤事件,結果又刪
除了相應的內容。最終仍是使用最初的鍵盤事件,不過IE9已經率先支持「DOM3」級鍵盤
事件。
1.鍵碼
在發生keydown和keyup事件時,event對象的keyCode屬性中會包含一個代碼,與鍵
盤上一個特定的鍵對應。對數字字母字符集,keyCode屬性的值與ASCII碼中對應小寫字母
或數字的編碼相同。字母中大小寫不影響。

*/

document.onkeydown=function(evt){
    alert(evt.keyCode); //按任意鍵,獲得相應的keyCode
};

/*
不一樣的瀏覽器在keydown和keyup事件中,會有一些特殊的狀況:
在Firefox和Opera中,分號鍵時keyCode值爲59,也就是ASCII中分號的編碼;而IE
和Safari返回186,即鍵盤中按鍵的鍵碼。
PS:其餘一些特殊狀況因爲瀏覽器版本太老和市場份額過低,這裏不作補充。
2.字符編碼
Firefox、Chrome和Safari的event對象都支持一個charCode屬性,這個屬性只有在發
生keypress事件時才包含值,並且這個值是按下的那個鍵所表明字符的ASCII編碼。此時
的keyCode一般等於0或者也可能等於所按鍵的編碼。IE和Opera則是在keyCode中保存
字符的ASCII編碼。
*/

function getCharCode(evt){
    var e=evt|| window.event;
    if(typeof e.charCode=='number') {
    return e.charCode;
    }else{
    return e.keyCode;
    }
}

//PS:能夠使用String.fromCharCode()將ASCII編碼轉換成實際的字符。

/*
keyCode和charCode區別以下:好比當按下「a鍵(重視是小寫的字母)時,
在Firefox中會得到
keydown:keyCodeis65 charCodeis0
keyup: keyCodeis65charCodeis0
keypress:keyCodeis0 charCodeis97
在IE中會得到
keydown:keyCodeis65 charCodeisundefined
keyup: keyCodeis65 charCodeisundefined
keypress:keyCodeis97 charCodeisundefined
而當按下shift鍵時,在Firefox中會得到
keydown:keyCodeis16 charCodeis0
keyup:keyCodeis16 charCodeis0
在IE中會得到
keydown:keyCodeis16 charCodeisundefined
keyup:keyCodeis16 charCodeisundefined
keypress:不會得到任何的charCode值,由於按shift並沒輸入任何的字符,而且也不
會觸發keypress事務

*/

/*
    PS:在keydown事務裏面,事務包含了keyCode–用戶按下的按鍵的物理編碼。
在keypress裏,keyCode包含了字符編碼,即默示字符的ASCII碼。如許的情勢實用於
全部的瀏覽器–除了火狐,它在keypress事務中的keyCode返回值爲0。
*/
/*
四.W3C與IE
在標準的DOM事件中,event對象包含與建立它的特定事件有關的屬性和方法。觸發
的事件類型不同,可用的屬性和方法也不同。
*/


/*
                            W3C中event對象的屬性和方法
屬性/方法                 類型             讀/寫             說明
bubbles                 Boolean         只讀        代表事件是否冒泡
cancelable                 Boolean         只讀         代表是否能夠取消事件的默認行爲
currentTarget             Element         只讀        其事件處理程序當前正在處理事件的那個元素
detail                     Integer         只讀         與事件相關的細節信息
eventPhase                 Integer         只讀        調用事件處理程序的階段:1表示捕獲階段,2表示「處理目標」,3表示冒泡階段
preventDefault()         Function         只讀        取消事件的默認行爲。若是cancelabel是true,則能夠使用這個方法
stopPropagation()         Function         只讀        取消事件的進一步捕獲或冒泡。若是bubbles爲true,則能夠使用這個方法
target                     Element         只讀         事件的目標
type                     String             只讀         被觸發的事件的類型
view                     AbstractView     只讀        與事件關聯的抽象視圖。等同於發生事件的window對象
cancelBubble             Boolean         讀/寫       默認值爲false,但將其設置爲true就能夠取消事件冒泡
returnValue             Boolean         讀/寫       默認值爲true,但將其設置爲false就能夠取消事件的默認行爲
srcElement                Element         只讀         事件的目標
type                     String             只讀         被觸發的事件類型
*/

/*在這裏,咱們只看全部瀏覽器都兼容的屬性或方法。首先第一個咱們瞭解一下W3C中
的target和IE中的srcElement,都表示事件的目標。*/

function getTarget(evt){
    var e=evt|| window.event;
    return e.target|| e.srcElement; //兼容獲得事件目標DOM對象
}
document.onclick=function(evt){
    var target=getTarget(evt);
    alert(target);
};

/*
事件流
事件流是描述的從頁面接受事件的順序,當幾個都具備事件的元素層疊在一塊兒的時候,
那麼你點擊其中一個元素,並非只有當前被點擊的元素會觸發事件,而層疊在你點擊範圍
的全部元素都會觸發事件。事件流包括兩種模式:冒泡和捕獲。
事件冒泡,是從裏往外逐個觸發。事件捕獲,是從外往裏逐個觸發。那麼現代的瀏覽器
默認狀況下都是冒泡模型,而捕獲模式則是早期的Netscape默認狀況。而如今的瀏覽器要
使用DOM2級模型的事件綁定機制才能手動定義事件流模式。
*/

document.onclick=function(){
    alert('我是document');
};
document.documentElement.onclick=function(){
    alert('我是html');
};
document.body.onclick=function(){
    alert('我是body');
};
document.getElementById('box').onclick=function(){
    alert('我是div');
};
document.getElementsByTagName('input')[0].onclick=function(){
    alert('我是input');
};
//在阻止冒泡的過程當中,W3C和IE採用的不一樣的方法,那麼咱們必須作一下兼容。
function stopPro(evt){
    vare=evt|| window.event;
    window.event?e.cancelBubble=true:e.stopPropagation();
}
View Code

  ③。事件綁定及深刻

/*
    事件綁定分爲兩種:一種是傳統事件綁定(內聯模型,腳本模型),一種是現代事件綁定
(DOM2級模型)。現代事件綁定在傳統綁定上提供了更強大更方便的功能。
*/

/*
一.傳統事件綁定的問題
傳統事件綁定有內聯模型和腳本模型,內聯模型咱們不作討論,基本不多去用。先來看
一下腳本模型,腳本模型將一個函數賦值給一個事件處理函數。
*/

var box=document.getElementById('box'); //獲取元素
box.onclick=function(){ //元素點擊觸發事件
    alert('Lee');
};


//問題一:一個事件處理函數觸發兩次事件
window.onload=function(){ //第一組程序項目或第一個JS文件
    alert('Lee');
};
window.onload=function(){ //第二組程序項目或第二個JS文件
    alert('Mr.Lee');
};

//當兩組程序或兩個JS文件同時執行的時候,後面一個會把前面一個徹底覆蓋掉。致使
//前面的window.onload徹底失效了。

//解決覆蓋問題,咱們能夠這樣去解決:
window.onload=function(){ //第一個要執行的事件,會被覆蓋
    alert('Lee');
};
if(typeof window.onload=='function') { //判斷以前是否有window.onload
    var saved=null; //建立一個保存器
    saved=window.onload; //把以前的window.onload保存起來
}

window.onload=function(){ //最終一個要執行事件
    if(saved)saved(); //執行以前一個事件
    alert('Mr.Lee'); //執行本事件的代碼
};

//問題二:事件切換器
box.onclick=toBlue; //第一次執行boBlue()
function toRed(){
    this.className='red';
    this.onclick=toBlue; //第三次執行toBlue(),而後來回切換
}

function toBlue(){
    this.className='blue';
    this.onclick=toRed; //第二次執行toRed()
}

//這個切換器在擴展的時候,會出現一些問題:
//1.若是增長一個執行函數,那麼會被覆蓋
box.onclick=toAlert; //被增長的函數
box.onclick=toBlue; //toAlert被覆蓋了
//2.若是解決覆蓋問題,就必須包含同時執行,但又出新問題
box.onclick=function(){ //包含進去,但可讀性下降
    toAlert(); //第一次不會被覆蓋,但第二次又被覆蓋
    toBlue.call(this); //還必須把this傳遞到切換器裏
};

/*
    綜上的三個問題:覆蓋問題、可讀性問題、this傳遞問題。咱們來建立一個自定義的事
件處理函數,來解決以上三個問題。
*/

function addEvent(obj,type,fn){ //取代傳統事件處理函數
    var saved=null; //保存每次觸發的事件處理函數
    if(typeof obj['on'+type]=='function') { //判斷是否是事件
        saved=obj['on'+type]; //若是有,保存起來
    }
    obj['on'+type]=function(){ //而後執行
        if(saved)saved(); //執行上一個
        fn.call(this); //執行函數,把this傳遞過去
    };
}

addEvent(window,'load', function(){ //執行到了

    alert('Lee');
});
addEvent(window,'load', function(){ //執行到了
    alert('Mr.Lee');
});


/*
    PS:以上編寫的自定義事件處理函數,還有一個問題沒有處理,就是兩個相同函數名
的函數誤註冊了兩次或屢次,那麼應該把多餘的屏蔽掉。那,咱們就須要把事件處理函數進
行遍歷,若是有一樣名稱的函數名就不添加便可。(這裏就不作了)
*/

addEvent(window,'load', init); //註冊第一次
addEvent(window,'load', init); //註冊第二次,應該忽略
function init(){
    alert('Lee');
}


//用自定義事件函數註冊到切換器上查看效果:
addEvent(window,'load', function(){
    var box=document.getElementById('box');
    addEvent(box,'click', toBlue);
});
function toRed(){
    this.className='red';
    addEvent(this,'click', toBlue);
}
functiontoBlue(){
    this.className='blue';
    addEvent(this,'click', toRed);
}

/*PS:當你單擊不少不少次切換後,瀏覽器直接卡死,或者彈出一個錯誤:toomuch
recursion(太多的遞歸)。主要的緣由是,每次切換事件的時候,都保存下來,沒有把無用的
移除,致使越積越多,最後卡死。*/

function removeEvent(obj,type){
    if(obj['on']+type)obj['on'+type]=null; //刪除事件處理函數
}

/*
    以上的刪除事件處理函數只不過是一刀切的刪除了,這樣雖然解決了卡死和太多遞歸的
問題。但其餘的事件處理函數也一併被刪除了,致使最後得不到本身想要的結果。若是想要
只刪除指定的函數中的事件處理函數,那就須要遍歷,查找。(這裏就不作了)
*/

/*
    二.W3C事件處理函數
「DOM2級事件」定義了兩個方法,用於添加事件和刪除事件處理程序的操做:
addEventListener()和removeEventListener()。全部DOM節點中都包含這兩個方法,而且它
們都接受3個參數;事件名、函數、冒泡或捕獲的布爾值(true表示捕獲,false表示冒泡)。
*/

window.addEventListener('load',function(){
    alert('Lee');
},false);
window.addEventListener('load',function(){
    alert('Mr.Lee');
},false);

/*PS:W3C的現代事件綁定比咱們自定義的好處就是:1.不須要自定義了;2.能夠屏蔽相
同的函數;3.能夠設置冒泡和捕獲。*/

window.addEventListener('load',init,false); //第一次執行了
window.addEventListener('load',init,false); //第二次被屏蔽了
function init(){
    alert('Lee');
}


//事件切換器
window.addEventListener('load',function(){
    var box=document.getElementById('box');
    box.addEventListener('click',function(){ //不會被誤刪
    alert('Lee');
},false);
box.addEventListener('click',toBlue,false); //引入切換也不會太多遞歸卡死
},false);
function toRed(){
    this.className='red';
    this.removeEventListener('click',toRed,false);
    this.addEventListener('click',toBlue,false);
}

function toBlue(){
    this.className='blue';
    this.removeEventListener('click',toBlue,false);
    this.addEventListener('click',toRed,false);
}

/*
    設置冒泡和捕獲階段
以前咱們上一章瞭解了事件冒泡,即從裏到外觸發。咱們也能夠經過event對象來阻止
某一階段的冒泡。那麼W3C現代事件綁定能夠設置冒泡和捕獲。
*/

document.addEventListener('click',function(){
    alert('document');
},true); //把布爾值設置成true,則爲捕獲
box.addEventListener('click',function(){
    alert('Lee');
},true); //把布爾值設置成false,則爲冒泡

/*三.IE事件處理函數
IE實現了與DOM中相似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受
相同的參數:事件名稱和函數。
在使用這兩組函數的時候,先把區別說一下:1.IE不支持捕獲,只支持冒泡;2.IE添加
事件不能屏蔽重複的函數;3.IE中的this指向的是window而不是DOM對象。4.在傳統事
件上,IE是沒法接受到event對象的,但使用了attchEvent()卻能夠,但有些區別。*/

window.attachEvent('onload',function(){
    var box=document.getElementById('box');
    box.attachEvent('onclick',toBlue);
});
function toRed(){
    var that=window.event.srcElement;
    that.className='red';
    that.detachEvent('onclick',toRed);
    that.attachEvent('onclick',toBlue);
}
function toBlue(){
    var that=window.event.srcElement;
    that.className='blue';
    that.detachEvent('onclick',toBlue);
    that.attachEvent('onclick',toRed);
}

/*
    PS:IE不支持捕獲,無解。IE不能屏蔽,須要單獨擴展或者自定義事件處理。IE不能
傳遞this,能夠call過去。
*/
window.attachEvent('onload',function(){
    var box=document.getElementById('box');
    box.attachEvent('onclick',function(){
    alert(this===window); //this指向的window
    });
});
window.attachEvent('onload',function(){
var box=document.getElementById('box');
    box.attachEvent('onclick',function(){
    toBlue.call(box); //把this直接call過去
    });
});
function toThis(){
   alert(this.tagName);
}

/*在傳統綁定上,IE是沒法像W3C那樣經過傳參接受event對象,但若是使用了
attachEvent()卻能夠。*/

box.onclick=function(evt){
    alert(evt); //undefined
}
box.attachEvent('onclick',function(evt){
    alert(evt); //object
    alert(evt.type); //click
});
box.attachEvent('onclick',function(evt){
    alert(evt.srcElement===box); //true
    alert(window.event.srcElement===box); //true
});

//最後,爲了讓IE和W3C能夠兼容這個事件切換器,咱們能夠寫成以下方式:
function addEvent(obj,type,fn){ //添加事件兼容
    if(obj.addEventListener){
    obj.addEventListener(type,fn);
    }else if(obj.attachEvent){
    obj.attachEvent('on'+type,fn);
    }
}
function removeEvent(obj,type,fn){ //移除事件兼容
    if(obj.removeEventListener){
    obj.removeEventListener(type,fn);
    }else if(obj.detachEvent){
    obj.detachEvent('on'+type,fn);
    }
}

function getTarget(evt){ //獲得事件目標
    if(evt.target){
        return evt.target;
    }else if(window.event.srcElement){
        return window.event.srcElement;
    }
}
/*PS:調用忽略,IE兼容的事件,若是要傳遞this,改爲call便可。
PS:IE中的事件綁定函數attachEvent()和detachEvent()可能在實踐中不去使用,有幾個
緣由:1.IE9就將全面支持W3C中的事件綁定函數;2.IE的事件綁定函數沒法傳遞this;3.IE
的事件綁定函數不支持捕獲;4.同一個函數註冊綁定後,沒有屏蔽掉;5.有內存泄漏的問題。
至於怎麼替代,咱們將在之後的項目課程中探討。*/

/*四.事件對象的其餘補充
在W3C提供了一個屬性:relatedTarget;這個屬性能夠在mouseover和mouseout事件
中獲取從哪裏移入和從哪裏移出的DOM對象。*/

box.onmouseover=function(evt){ //鼠標移入box
    alert(evt.relatedTarget); //獲取移入box最近的那個元素對象
} //span
box.onmouseout=function(evt){ //鼠標移出box
    alert(evt.relatedTarget); //獲取移出box最近的那個元素對象
} //span

//IE提供了兩組分別用於移入移出的屬性:fromElement和toElement,分別對應mouseover和mouseout。

box.onmouseover=function(evt){ //鼠標移入box
    alert(window.event.fromElement.tagName);//獲取移入box最近的那個元素對象span
}
box.onmouseout=function(evt){ //鼠標移入box
    alert(window.event.toElement.tagName); //獲取移入box最近的那個元素對象span
}

//PS:fromElement和toElement若是分別對應相反的鼠標事件,沒有任何意義。
//剩下要作的就是跨瀏覽器兼容操做:

function getTarget(evt){
    var e=evt|| window.event; //獲得事件對象
    if(e.srcElement){ //若是支持srcElement,表示IE
    if(e.type=='mouseover') { //若是是over
    return e.fromElement; //就使用from
    }elseif(e.type=='mouseout') { //若是是out
    return e.toElement; //就使用to
    }
    }else if(e.relatedTarget){ //若是支持relatedTarget,表示W3C
    return e.relatedTarget;
}
}

/*有時咱們須要阻止事件的默認行爲,好比:一個超連接的默認行爲就點擊而後跳轉到指
定的頁面。那麼阻止默認行爲就能夠屏蔽跳轉的這種操做,而實現自定義操做。
取消事件默認行爲還有一種不規範的作法,就是返回false。*/

link.onclick=function(){
    alert('Lee');
    return false; //直接給個假,就不會跳轉了。
};

/*PS:雖然returnfalse;能夠實現這個功能,但有漏洞;第一:必須寫到最後,這樣致使
中間的代碼執行後,有可能執行不到returnfalse;第二:returnfalse寫到最前那麼以後的自
定義操做就失效了。因此,最好的方法應該是在最前面就阻止默認行爲,而且後面還能執行
代碼。*/

link.onclick=function(evt){
    evt.preventDefault(); //W3C,阻止默認行爲,放哪裏均可以
    alert('Lee');
};
link.onclick=function(evt){ //IE,阻止默認行爲
    window.event.returnValue=false;
    alert('Lee');
};

//跨瀏覽器兼容
function preDef(evt){
    var e=evt|| window.event;
    if(e.preventDefault){
    e.preventDefault();
    }else{
    e.returnValue=false;
    }
}

/*上下文菜單事件:contextmenu,當咱們右擊網頁的時候,會自動出現windows自帶的
菜單。那麼咱們能夠使用contextmenu事件來修改咱們指定的菜單,但前提是把右擊的默認
行爲取消掉。*/

addEvent(window,'load', function(){
    var text=document.getElementById('text');
    addEvent(text,'contextmenu', function(evt){
        var e=evt|| window.event;
        preDef(e);
        var menu=document.getElementById('menu');
        menu.style.left=e.clientX+'px';
        menu.style.top=e.clientY+'px';
        menu.style.visibility='visible';
        addEvent(document,'click', function(){
        document.getElementById('myMenu').style.visibility='hidden';
    });
    });
});

/*
PS:contextmenu事件很經常使用,這直接致使瀏覽器兼容性較爲穩定。
卸載前事件:beforeunload,這個事件能夠幫助在離開本頁的時候給出相應的提示,「離
開」或者「返回」操做。

*/
addEvent(window,'beforeunload', function(evt){
    preDef(evt);
});

//鼠標滾輪(mousewheel)和DOMMouseScroll,用於獲取鼠標上下滾輪的距離。
addEvent(document,'mousewheel', function(evt){ //非火狐
    alert(getWD(evt));
});
addEvent(document,'DOMMouseScroll', function(evt){ //火狐
    alert(getWD(evt));
});
function getWD(evt){
    var e=evt|| window.event;
    if(e.wheelDelta){
    return e.wheelDelta;
    }else if(e.detail){
    return-evt.detail*30; //保持計算的統一
    }
}

/*
    PS:經過瀏覽器檢測能夠肯定火狐只執行DOMMouseScroll。
DOMContentLoaded事件和readystatechange事件,有關DOM加載方面的事件,關於這
兩個事件的內容很是多且繁雜,咱們先點明在這裏,在項目課程中使用的時候詳細討論。
*/
View Code

 

第25章 表單處理

/*
    爲了分擔服務器處理表單的壓力,JavaScript提供了一些解決方案,從而大大打破了處
處依賴服務器的局面。
*/

/*一.表單介紹
在HTML中,表單是由<form>元素來表示的,而在JavaScript中,表單對應的則是
HTMLFormElement類型。HTMLFormElement繼承了HTMLElement,所以它擁有HTML元
素具備的默認屬性,而且還獨有本身的屬性和方法:*/

/*
        HTMLFormElement屬性和方法

    屬性或方法                     說明
    acceptCharset             服務器可以處理的字符集
    action                     接受請求的URL
    elements                 表單中全部控件的集合
    enctype                 請求的編碼類型
    length                     表單中控件的數量
    name                     表單的名稱
    target                     用於發送請求和接受響應的窗口名稱
    reset()                 將全部表單重置
    submit()                 提交表單

*/

//獲取表單<form>對象的方法有不少種,以下:
document.getElementById('myForm'); //使用ID獲取<form>元素
document.getElementsByTagName('form')[0]; //使用獲取第一個元素方式獲取
document.forms[0]; //使用forms的數字下標獲取元素
document.forms['yourForm']; //使用forms的名稱下標獲取元素
document.yourForm; //使用name名稱直接獲取元素

/*PS:最後一種方法使用name名稱直接獲取元素,已經不推薦使用,這是向下兼容的早
期用法。問題頗多,好比有兩個相同名稱的,變成數組;並且這種方式之後有可能會不兼容。*/

/*提交表單
經過事件對象,能夠阻止submit的默認行爲,submit事件的默認行爲就是攜帶數據跳
轉到指定頁面。*/

addEvent(fm,'submit', function(evt){
    preDef(evt);
});

/*咱們能夠能夠使用submit()方法來自定義觸發submit事件,也就是說,並不必定非要點
擊submit按鈕才能提交。*/

if(e.ctrlKey&&e.keyCode==13)fm.submit(); //判斷按住了ctrl和enter鍵觸發

//PS:在表單中儘可能避免使用name="submit"或id="submit"等命名,這會和submit()方法
//發生衝突致使沒法提交。

/*提交數據最大的問題就是重複提交表單。由於各類緣由,當一條數據提交到服務器的時
候會出現延遲等長時間沒反映,致使用戶不停的點擊提交,從而使得重複提交了不少相同的
請求,或形成錯誤、或寫入數據庫多條相同信息。*/

addEvent(fm,'submit', function(evt){ //模擬延遲
    preDef(evt);
    setTimeout(function(){
    fm.submit();
    },3000);
});

/*有兩種方法能夠解決這種問題:第一種就是提交以後,馬上禁用點擊按鈕;第二種就是
提交以後取消後續的表單提交操做。*/

document.getElementById('sub').disabled=true; //將按鈕禁用
var flag=false //設置一個監聽變量
if(flag==true)return //若是存在返回退出事件
flag=true; //不然肯定是第一次,設置爲true

//PS:在某些瀏覽器,F5只能起到緩存刷新的效果,有可能獲取不到真正的源頭更新的
//數據。那麼使用ctrl+F5就能夠把源頭給刷出來。

/*重置表單
用戶點擊重置按鈕時,表單會被初始化。雖然這個按鈕還得以保留,但目前的Web已
經不多去使用了。由於用戶已經填寫好各類數據,不當心點了重置就會所有清空,用戶體驗
極差。
有兩種方法調用reset事件,第一個就是直接type="reset"便可;第二個就是使用fm.reset()
方法調用便可。*/

<input type="reset" value="重置"/> //不須要JS代碼便可實現
addEvent(document,'click',function(){
    fm.reset(); //使用JS方法實現重置
});
addEvent(fm,'reset',function(){ //獲取重置按鈕
//
});

/*表單字段
若是想訪問表單元素,能夠使用以前章節講到的DOM方法訪問。但使用原生的DOM
訪問雖然比較通用,但不是很便利。表單處理中,咱們建議使用HTMLDOM,它有本身的
elements屬性,該屬性是表單中全部元素的集合。*/

fm.elements[0]; //獲取第一個表單字段元素
fm.elements['user']; //獲取name是user的表單字段元素
fm.elements.length; //獲取全部表單字段的數量
//若是多個表單字段都使用同一個name,那麼就會返回該name的NodeList表單列表。
fm.elements['sex']; //獲取相同name表單字段列表

/*PS:咱們是經過fm.elements[0]來獲取第一個表單字段的,但也能夠使用fm[0]直接訪問
第一個字段。由於fm[0]訪問方式是爲了向下兼容的,因此,咱們建議你們使用elements屬
性來獲取。
共有的表單字段屬性
除了<fieldset>元素以外,全部表單字段都擁有相同的一組屬性。因爲<input>類型能夠
表示多種表單字段,所以有些屬性只適用於某些字段。如下羅列出共有的屬性:*/

/*
屬性或方法                     說明
disabled         布爾值,表示當前字段是否被禁用
form             指向當前字段所屬表單的指針,只讀
name             當前字段的名稱
readOnly         布爾值,表示當前字段是否只讀
tabIndex         表示當前字段的切換
type             當前字段的類型
value             當前字段的值
*/

/*
    這些屬性其實就是HTML表單裏的屬性,在XHTML課程中已經詳細講解過,這裏不
一個個贅述,重點看幾個最經常使用的。
*/

fm.elements[0].value; //獲取和設置value
fm.elements[0].form==fm; //查看當前字段所屬表單
fm.elements[0].disabled=true; //禁用當前字段
fm.elements[0].type='checkbox'; //修改字段類型,極不推薦

/*除了<fieldset>字段以外,全部表單字段都有type屬性。對於<input>元素,這個值等於
HTML屬性的type值。對於非<input>元素,這個type的屬性值以下:*/

/*
元素說明                     HTML標籤                         type屬性的值
單選列表                 <select>...</select>                 select-one
多選列表                 <selectmultiple>...</select>         select-multiple
自定義按鈕                 <button>...</button>                 button
自定義非提交按鈕         <buttontype="button">...</button>     button
自定義重置按鈕             <buttontype="reset">...</button>     reset
自定義提交按鈕             <buttontype="submit">...</button>     submit
*/

/*PS:<input>和<button>元素的type屬性是能夠動態修改的,而<select>元素的type屬性
則是隻讀的。(在沒必要要的狀況下,建議不修改type)。
共有的表單字段方法
每一個表單字段都有兩個方法:foucs()和blur()。*/

/*方法 說明
focus() 將焦點定位到表單字段裏
blur() 從元素中將焦點移走*/

fm.elements[0].focus(); //將焦點移入
fm.elements[0].blur(); //將焦點移出

/*
共有的表單字段事件
表單共有的字段事件有如下三種:
*/

/*
事件名                     說明
blur                 當字段失去焦點時觸發
change                對於<input>和<textarea>元素,在改變value並失去焦點時觸發;對於<select>元素,在改變選項時觸發
focus                 當前字段獲取焦點時觸發
*/

addEvent(textField,'focus', function(){ //緩存blur和change再測試一下
    alert('Lee');
});

/*
PS:關於blur和change事件的關係,並無嚴格的規定。在某些瀏覽器中,blur事件
會先於change事件發生;而在其餘瀏覽器中,則剛好相反。
*/

/*
二.文本框腳本
在HTML中,有兩種方式來表現文本框:一種是單行文本框<inputtype="text">,一種
是多行文本框<textarea>。雖然<input>在字面上有value值,而<textarea>卻沒有,但經過都
能夠經過value獲取他們的值。
*/

var textField=fm.elements[0];
var areaField=fm.elements[1];
alert(textField.value+',' +areaField.value); //獲得value值

/*
    PS:使用表單的value是最推薦使用的,它是HTMLDOM中的屬性,不建議使用標準
DOM的方法。也就是說不要使用getAttribute()獲取value值。緣由很簡單,對value屬性的
修改,不必定會反映在DOM中。
除了value值,還有一個屬性對應的是defaultValue,能夠獲得本來的value值,不會因
爲值的改變而變化。
*/

alert(textField.defaultValue); //獲得最初的value值

//選擇文本
//使用select()方法,能夠將文本框裏的文本選中,而且將焦點設置到文本框中。
textField.select(); //選中文本框中的文本

/*選擇部分文本
在使用文本框內容的時候,咱們有時要直接選定部分文本,這個行爲尚未標準。Firefox
的解決方案是:setSelectionRange()方法。這個方法接受兩個參數:索引和長度。*/

textField.setSelectionRange(0,1); //選擇第一個字符
textField.focus(); //焦點移入
textField.setSelectionRange(0,textField.value.length); //選擇所有
textField.focus(); //焦點移入

/*除了IE,其餘瀏覽器都支持這種寫法(IE9+支持),那麼IE想要選擇部分文本,能夠使
用IE的範圍操做。*/

var range=textField.createTextRange(); //建立一個文本範圍對象
range.collapse(true); //將指針移到起點
range.moveStart('character',0); //移動起點,character表示逐字移動
range.moveEnd('character',1); //移動終點,同上
range.select(); //焦點選定

/*PS:關於IE範圍的詳細講解,咱們將在從此的課程中繼續討論。而且W3C也有本身
的範圍。*/

//選擇部分文本實現跨瀏覽器兼容
function selectText(text,start,stop){
    if(text.setSelectionRange){
        text.setSelectionRange(start,stop);
        text.focus();
    }elseif(text.createTextRange){
    var range=text.createTextRange();
    range.collapse(true);
    range.moveStart('character',start);
    range.moveEnd('character',stop-start); //IE用終點減去起點獲得字符數
    range.select();
    }
}

//使用select事件,能夠選中文本框文本後觸發。
    addEvent(textField,'select', function(){
    alert(this.value); //IE事件須要傳遞this才能夠這麼寫
});

/*取得選擇的文本
若是咱們想要取得選擇的那個文本,就必須使用一些手段。目前位置,沒有任何規範解
決這個問題。Firefox爲文本框提供了兩個屬性:selectionStart和selectionEnd。*/

addEvent(textField,'select', function(){
alert(this.value.substring(this.selectionStart,this.selectionEnd));
});

/*除了IE,其餘瀏覽器均支持這兩個屬性(IE9+已支持)。IE不支持,而提供了另外一個方
案:selection對象,屬於document。這個對象保存着用戶在整個文檔範圍內選擇的文本信息。
致使咱們須要作瀏覽器兼容。*/

function getSelectText(text){
    if(typeof text.selectionStart=='number') { //非IE
    return text.value.substring(text.selectionStart,text.selectionEnd);
    }else if(document.selection){ //IE
    return document.selection.createRange().text; //獲取IE選擇的文本
    }
}

/*
PS:有一個最大的問題,就是IE在觸發select事件的時候,在選擇一個字符後當即觸
發,而其餘瀏覽器是選擇想要的字符釋放鼠標鍵後才觸發。因此,若是使用alert()的話,導
致跨瀏覽器的不兼容。咱們沒有辦法讓瀏覽器行爲保持統一,但能夠經過不去使用alert()來
解決。
*/

addEvent(textField,'select', function(){
    //alert(getSelectText(this)); //致使用戶行爲結果不一致
    document.getElementById('box').innerHTML=getSelectText(this);
});

/*過濾輸入
爲了使文本框輸入指定的字符,咱們必須對輸入進的字符進行驗證。有一種作法是判斷
字符是否合法,這是提交後操做的。那麼咱們還能夠在提交前限制某些字符,還過濾輸入。*/

addEvent(areaField,'keypress', function(evt){
    var e=evt|| window.event;
    var charCode=getCharCode(evt); //獲得字符編碼
    if(!/\d/.test(String.fromCharCode(charCode))&&charCode>8){ //條件阻止默認
    preDef(evt);
    }
});

/*
    PS:前半段條件判斷只有數字才能夠輸入,致使常規按鍵,好比光標鍵、退格鍵、刪
除鍵等沒法使用。部分瀏覽器好比Firfox,須要解放這些鍵,而非字符觸發的編碼均爲0;
在Safari3以前的瀏覽器,也會被阻止,而它對應的字符編碼所有爲8,因此最後就加上
charCode>8的判斷便可。
PS:固然,這種過濾仍是比較脆落的,咱們還但願可以阻止裁剪、複製、粘貼和中文
字符輸入操做才能真正屏蔽掉這些。
若是要阻止裁剪、複製和粘貼,那麼咱們能夠在剪貼板相關的事件上進行處理,
JavaScript提供了六組剪貼板相關的事件:
*/

/*
    事件名             說明
    copy           在發生複製操做時觸發
    cut         在發生裁剪操做時觸發
    paste         在發生粘貼操做時觸發
    beforecopy     在發生複製操做前觸發
    beforecut     在發生裁剪操做前觸發
    beforepaste 在發生粘貼操做前觸發
*/

/*
因爲剪貼板沒有標準,致使不一樣的瀏覽器有不一樣的解釋。Safari、Chrome和Firefox中,
凡是before前綴的事件,都須要在特定條件下觸發。而IE則會在操做時以前觸發帶before
前綴的事件。
若是咱們想要禁用裁剪、複製、粘貼,那麼只要阻止默認行爲便可。
*/
addEvent(areaField,'cut', function(evt){ //阻止裁剪
    preDef(evt);
});
addEvent(areaField,'copy', function(evt){ //阻止複製
    preDef(evt);
});
addEvent(areaField,'paste', function(evt){ //阻止粘貼
    preDef(evt);
});

/*當咱們裁剪和複製的時候,咱們能夠訪問剪貼板裏的內容,但問題是FireFox,Opera
瀏覽器不支持訪問剪貼板。而且,不一樣的瀏覽器也有本身不一樣的理解。因此,這裏咱們就不
在贅述。
最後一個問題影響到可能會影響輸入的因素就是:輸入法。咱們知道,中文輸入法,它的
原理是在輸入法面板上先存儲文本,按下回車就寫入英文文本,按下空格就寫入中文文本。
有一種解決方案是經過CSS來禁止調出輸入法:*/
style="ime-mode:disabled" //CSS直接編寫
areaField.style.imeMode='disabled'; //或在JS裏設置也能夠

/*PS:但咱們也發先,Chrome瀏覽器卻沒法禁止輸入法調出。因此,爲了解決谷歌瀏覽
器的問題,最好還要使用正則驗證已輸入的文本。*/

addEvent(areaField,'keyup', function(evt){ //keyup彈起的時候
    this.value=this.value.replace(/[^\d]/g,''); //把非數字都替換成空
});

/*
自動切換焦點
爲了增長表單字段的易用性,不少字段在知足必定條件時(好比長度),就會自動切換到
下一個字段上繼續填寫。
*/
<input type="text" name="user1" maxlength="1"/> //只能寫1個
<input type="text" name="user2" maxlength="2"/> //只能寫2個
<input type="text" name="user3" maxlength="3"/> //只能寫3個

function tabForward(evt){
    var e=evt|| window.event;
    var target=getTarget(evt);
    //判斷當前長度是否和指定長度一致
    if(target.value.length==target.maxLength){
    //遍歷全部字段
    for(vari=0;i<fm.elements.length;i++){
    //找到當前字段
    if(fm.elements[i]==target){
    //就把焦點移入下一個
    fm.elements[i+1].focus();
    //中途返回
    return;
    }
    }
    }
}

/*三.選擇框腳本
選擇框是經過<select>和<option>元素建立的,除了通用的一些屬性和方法外,
HTMLSelectElement類型還提供了以下的屬性和方法:*/

/*
            HTMLSelectElement對象
屬性/方法                 說明
add(new,rel)         插入新元素,並指定位置
multiple             布爾值,是否容許多項選擇
options             <option>元素的HTMLColletion集合
remove(index)         移除給定位置的選項
selectedIndex         基於0的選中項的索引,若是沒有選中項,則值爲-1
size                 選擇框中可見的行數
*/

/*在DOM中,每一個<option>元素都有一個HTMLOptionElement對象,以便訪問數據,這
個對象有以下一些屬性:*/

/*
                    HTMLOptionElement對象
屬性             說明
index             當前選項在options集合中的索引
label             當前選項的標籤
selected          布爾值,表示當前選項是否被選中
text             選項的文本
value             選項的值
*/

var city=fm.elements['city']; //HTMLSelectElement
alert(city.options); //HTMLOptionsCollection
alert(city.options[0]); //HTMLOptionElement
alert(city.type); //select-one

/*
    PS:選擇框裏的type屬性有多是:select-one,也有多是:select-multiple,這取決
於HTML代碼中有沒有multiple屬性。
*/
alert(city.options[0].firstChild.nodeValue); //上海t,獲取text值,不推薦的作法
alert(city.options[0].getAttribute('value')); //上海v,獲取value值,不推薦的作法
alert(city.options[0].text); //上海t,獲取text值,推薦
alert(city.options[0].value); //上海v,獲取value值,推薦

/*PS:操做select時,最好使用HTMLDOM,由於全部瀏覽器兼容的很好。而若是使用
標準DOM,會由於不一樣的瀏覽器致使不一樣的結果。
PS:當選項沒有value值的時候,IE會返回空字符串,其餘瀏覽器會返回text值。
選擇選項
對於只能選擇一項的選擇框,使用selectedIndex屬性最爲簡單。*/
addEvent(city,'change', function(){
    alert(this.selectedIndex); //獲得當前選項的索引,從0開始
    alert(this.options[this.selectedIndex].text); //獲得當前選項的text值
    alert(this.options[this.selectedIndex].value); //獲得當前選項的value值
});

//PS:若是是多項選擇,他始終返回的是第一個項。

city.selectedIndex=1; //設置selectedIndex能夠定位某個索引

//經過option的屬性(布爾值),也能夠設置某個索引,設置爲true便可。
city.options[0].selected=true; //設置第一個索引

/*而selected和selectedIndex在用途上最大的區別是,selected是返回的布爾值,因此一
般用於判斷上;而selectedIndex是數值,通常用於設置和獲取。*/
addEvent(city,'change', function(){
if(this.options[2].selected==true){ //判斷第三個選項是否被選定
    alert('選擇正確!');
}
});
//添加選項
//如需動態的添加選項咱們有兩種方案:DOM和Option構造函數。
var option=document.createElement('option');
option.appendChild(document.createTextNode('北京t'));
option.setAttribute('value','北京v')
city.appendChild(option);
//使用Option構造函數建立:
var option=newOption('北京t','北京v');
city.appendChild(option); //IE出現bug
//使用add()方法來添加選項:
var option=newOption('北京t','北京v');
city.add(option,0); //0,表示添加到第一位

/*PS:在DOM規定,add()中兩個參數是必須的,若是不肯定索引,那麼第二個參數設
置null便可,即默認移入最後一個選項。但這是IE中規定第二個參數是可選的,因此設置
null表示放入不存在的位置,致使失蹤,爲了兼容性,咱們傳遞undefined便可兼容。*/

city.add(option,null); //IE不顯示了
city.add(option,undefined); //兼容了

//移除選項
//有三種方式能夠移除某一個選項:DOM移除、remove()方法移除和null移除。
city.removeChild(city.options[0]); //DOM移除
city.remove(0); //remove()移除,推薦
city.options[0]=null; //null移除

//PS:當第一項移除後,下面的項,往上頂,因此不停的移除第一項,便可所有移除。

//移動選項

//若是有兩個選擇框,把第一個選擇框裏的第一項移到第二個選擇框裏,而且第一個選擇
//框裏的第一項被移除。
var city=fm.elements['city']; //第一個選擇框
var info=fm.elements['info']; //第二個選擇框
info.appendChild(city.options[0]); //移動,被自我刪除

/*排列選項
選擇框提供了一個index屬性,能夠獲得當前選項的索引值,和selectedIndex的區別是,
一個是選擇框對象的調用,一個是選項對象的調用。*/

var option1=city.options[1];
city.insertBefore(option1,city.options[option1.index-1]); //往下移動移位

//單選按鈕
//經過checked屬性來獲取單選按鈕的值。
for(var i=0;i<fm.sex.length;i++){ //循環單選按鈕
if(fm.sex[i].checked==true){ //遍歷每個找出選中的那個
    alert(fm.sex[i].value); //獲得值
    }
}

//PS:除了checked屬性以外,單選按鈕還有一個defaultChecked按鈕,它獲取的是本來
//的checked按鈕對象,而不會由於checked的改變而改變。
if(fm.sex[i].defaultChecked==true){
    alert(fm.sex[i].value);
}

//複選按鈕
//經過checked屬性來獲取複選按鈕的值。複選按鈕也具備defaultChecked屬性。
var love='';
for(vari=0;i<fm.love.length;i++){
    if(fm.love[i].checked==true){
    love+=fm.love[i].value;
    }
}
alert(love);
View Code

 

第26章 錯誤處理與調試

/*JavaScript在錯誤處理調試上一直是它的軟肋,若是腳本出錯,給出的提示常常也讓人
摸不着頭腦。ECMAScript第3版爲了解決這個問題引入了try...catch和throw語句以及一些
錯誤類型,讓開發人員更加適時的處理錯誤。*/

/*一.瀏覽器錯誤報告
隨着瀏覽器的不斷升級,JavaScript代碼的調試能力也逐漸變強。IE、Firefox、Safari、
Chrome和Opera等瀏覽器,都具有報告JavaScript錯誤的機制。只不過,瀏覽器通常面向
的是普通用戶,默認狀況下會隱藏此類信息。
IE:在默認狀況下,左下角會出現錯誤報告,雙擊這個圖標,能夠看到錯誤消息對話框。
若是開啓禁止腳本調試,那麼出錯的時候,會彈出錯誤調試框。設置方法爲:工具->Internet
Options選項->高級->禁用腳本調試,取消勾選便可。
Firefox:在默認狀況下,錯誤不會經過瀏覽器給出提示。但在後臺的錯誤控制檯能夠查
看。查看方法爲:工具->[Web開發者]->Web控制檯|錯誤控制檯。除了瀏覽器自帶的,開發
人員爲Firefox提供了一個強大的插件:Firebug。它不但能夠提示錯誤,還能夠調試JavaScript
和CSS、DOM、網絡連接錯誤等。
Safari:在默認狀況下,錯誤不會經過瀏覽器給出提示。因此,咱們須要開啓它。查看
方法爲:顯示菜單欄->編輯->偏好設置->高級->在菜單欄中顯示開發->顯示Web檢查器|顯示
錯誤控制器。
Opera:在默認狀況下,錯誤會被隱藏起來。打開錯誤記錄的方式爲:顯示菜單欄->查
看->開發者工具->錯誤控制檯。
Chrome:在默認狀況下,錯誤會被隱藏起來。打開錯誤記錄的方法爲:工具->JavaScript
控制檯。*/

/*二.錯誤處理
良好的錯誤處理機制能夠及時的提醒用戶,知道發生了什麼事,而不會惶恐不安。爲此,
做爲開發人員,咱們必須理解在處理JavaScript錯誤的時候,都有哪些手段和工具能夠利用。
try-catch語句
ECMA262第3版引入了try-catch語句,做爲JavaScript中處理異常的一種標準方式。*/

try{ //嘗試着執行try包含的代碼
    window.abcdefg(); //不存在的方法
}catch(e){ //若是有錯誤,執行catch,e是異常對象
    alert('發生錯誤啦,錯誤信息爲:' +e); //直接打印調用toString()方法
}


//在e對象中,ECMA-262還規定了兩個屬性:message和name,分別打印出信息和名稱。
alert('錯誤名稱:' +e.name);
alert('錯誤名稱:' +e.message);

/*PS:Opera9以前的版本不支持這個屬性。而且IE提供了和message徹底相同的
description屬性、還添加了number屬性提示內部錯誤數量。Firefox提供了fileName(文件名)、
lineNumber(錯誤行號)和stack(棧跟蹤信息)。Safari添加了line(行號)、sourceId(內部錯誤代
碼)和sourceURL(內部錯誤URL)。因此,要跨瀏覽器使用,那麼最好只使用通用的message。
finally子句
finally語句做爲try-catch的可選語句,無論是否發生異常處理,都會執行。而且無論try
或是catch裏包含return語句,也不會阻止finally執行。*/

try{
    window.abcdefg();
}catch(e){
    alert('發生錯誤啦,錯誤信息爲:' +e.stack);
}finally{ //老是會被執行
    alert('我都會執行!');
}

/*
PS:finally的做用通常是爲了防止出現異常後,沒法往下再執行的備用。也就是說,如
果有一些清理操做,那麼出現異常後,就執行不到清理操做,那麼能夠把這些清理操做放到
finally裏便可。
錯誤類型
執行代碼時可能會發生的錯誤有不少種。每種錯誤都有對應的錯誤類型,ECMA-262
定義了7種錯誤類型:
1.Error
2.EvalError
3.RangeError
4.ReferenceError
5.SyntaxError
6.TypeError
7.URIError
其中,Error是基類型(其餘六種類型的父類型),其餘類型繼承自它。Error類型不多見,
通常由瀏覽器拋出的。這個基類型主要用於開發人員拋出自定義錯誤。
PS:拋出的意思,就是當前錯誤沒法處理,丟給另一我的,好比丟給一個錯誤對象。
*/
new Array(-5); //拋出RangeError(範圍)
/*錯誤信息爲:RangeError:invalidarraylength(無效的數組的長度)
PS:RangeError錯誤通常在數值超出相應範圍時觸發*/

var box=a; //拋出ReferenceError(引用)
/*錯誤信息爲:ReferenceError:aisnotdefined(a是沒有定義的)
PS:ReferenceError一般訪問不存在的變量產生這種錯誤*/

a $ b; //拋出SyntaxError(語法)
//錯誤信息爲:SyntaxError:missing;beforestatement(失蹤;語句以前)
//PS:SyntaxError一般是語法錯誤致使的
new 10; //拋出TypeError(類型)
//錯誤信息爲:TypeError:10isnotaconstructor(10不是一個構造函數)
//PS:TypeError一般是類型不匹配致使的

/*
    PS:EvalError類型表示全局函數eval()的使用方式與定義的不一樣時拋出,但實際上並不
能產生這個錯誤,因此實際上碰到的可能性不大。
PS:在使用encodeURI()和decodeURI()時,若是URI格式不正確時,會致使URIError
錯誤。但由於URI的兼容性很是強,致使這種錯誤幾乎見不到。
*/
alert(encodeURI('李炎恢'));

//利用不一樣的錯誤類型,能夠更加恰當的給出錯誤信息或處理。
try{
    new 10;
}catch(e){
if(e instanceof TypeError){ //若是是類型錯誤,那就執行這裏
    alert('發生了類型錯誤,錯誤信息爲:' +e.message);
    }else{
    alert('發生了未知錯誤!');
    }
}

/*
善用try-catch
在明明知道某個地方會產生錯誤,能夠經過修改代碼來解決的地方,是不適合用
try-catch的。或者是那種不一樣瀏覽器兼容性錯誤致使錯誤的也不太適合,由於能夠經過判斷
瀏覽器或者判斷這款瀏覽器是否存在此屬性和方法來解決。
*/
try{
    var box=document.getElementbyid('box'); //單詞大小寫錯誤,致使類型錯誤
}catch(e){ //這種狀況不必try-catch
    alert(e);
}

try{
    alert(innerWidth); //W3C支持,IE報錯
}catch(e){
    alert(document.documentElement.clientWidth); //兼容IE
}

/*
PS:常規錯誤和這種瀏覽器兼容錯誤,咱們都不建議使用try-catch。由於常規錯誤能夠
修改代碼便可解決,瀏覽器兼容錯誤,能夠經過普通if判斷便可。而且try-catch比通常語
句消耗資源更多,負擔更大。因此,在萬不得已,沒法修改代碼,不能經過普通判斷的狀況
下才去使用try-catch,好比後面的Ajax技術。
拋出錯誤
使用catch來處理錯誤信息,若是處理不了,咱們就把它拋出丟掉。拋出錯誤,其實就
是在瀏覽器顯示一個錯誤信息,只不過,錯誤信息能夠自定義,更加精確和具體。
*/

try{
    new 10;
}catch(e){
if(e instanceof TypeError){
    throw new TypeError('實例化的類型致使錯誤!'); //直接中文解釋錯誤信息
}else{
    throw new Error('拋出未知錯誤!');
}
}
//PS:IE瀏覽器只支持Error拋出的錯誤,其餘錯誤類型不支持。

//三.錯誤事件
//error事件是當某個DOM對象產生錯誤的時候觸發。
addEvent(window,'error', function(){
    alert('發生錯誤啦!')
});
new 10; //寫在後面
<img src="123.jpg" onerror="alert('圖像加載錯誤!')" />

/*
四.錯誤處理策略
因爲JavaScript錯誤均可能致使網頁沒法使用,因此什麼時候搞清楚及爲何發生錯誤相當
重要。這樣,咱們才能對此採起正確的應對方案。
常見的錯誤類型
由於JavaScript是鬆散弱類型語言,不少錯誤的產生是在運行期間的。通常來講,須要
關注3種錯誤:
1.類型轉換錯誤;2.數據類型錯誤;3.通訊錯誤,這三種錯誤通常會在特定的模式下或
者沒有對值進行充分檢查的狀況下發生。
類型轉換錯誤
在一些判斷比較的時候,好比數組比較,有相等和全等兩種:
*/
alert(1=='1'); //true
alert(1==='1'); //false
alert(1==true); //true
alert(1===true); //false

/*PS:因爲這個特性,咱們建議在這種會類型轉換的判斷,強烈推薦使用全等,以保證
判斷的正確性。*/

var box=10; //能夠試試0
if(box){ //10自動轉換爲布爾值爲true
    alert(box);
}

/*PS:由於0會自動轉換爲false,其實0也是數值,也是有值的,不該該認爲是false,
因此咱們要判斷box是否是數值再去打印。*/

var box=0;
if(typeof box=='number') { //判斷box是number類型便可
    alert(box);
}

/*PS:typeofbox=='number'這裏也是用的相等,沒有用全等呀?緣由是typeofbox自己
返回的就是類型的字符串,右邊也是字符串,那不必驗證類型,因此相等就夠了。
數據類型錯誤
因爲JavaScript是弱類型語言,在使用變量和傳遞參數以前,不會對它們進行比較來確
保數據類型的正確。因此,這樣開發人員必須須要靠本身去檢測。*/

function getQueryString(url){ //傳遞了非字符串,致使錯誤
    var pos=url.indexOf('?');
    return pos;
}
alert(getQueryString(1));
//PS:爲了不這種錯誤的出現,咱們應該使用類型比較。
function getQueryString(url){
    if(typeofurl=='string') { //判斷了指定類型,就不會出錯了
    var pos=url.indexOf('?');
    return pos;
    }
}
alert(getQueryString(1));

//對於傳遞參數除了限制數字、字符串以外,咱們對數組也要進行限制。
function sortArray(arr){
if(arr){ //只判斷布爾值遠遠不夠
    alert(arr.sort());
    }
}

varbox=[3,5,1];
sortArray(box);

/*
PS:只用if(arr)判斷布爾值,那麼數值、字符串、對象等都會自動轉換爲true,而這些
類型調用sort()方法好比會產生錯誤,這裏提一下:空數組會自動轉換爲true而非false。
*/

function sortArray(arr){
    if(typeof arr.sort=='function') { //判斷傳遞過來arr是否有sort方法
    alert(arr.sort()); //就算這個繞過去了
    alert(arr.reverse()); //這個就又繞不過去了
    }
}
var box={ //建立一個自定義對象,添加sort方法
sort:function(){}
};
sortArray(box);

/*PS:這斷代碼本意是判斷arr是否有sort方法,由於只有數組有sort方法,從而判斷arr
是數組。但忘記了,自定義對象添加了sort方法就能夠繞過這個判斷,且arr還不是數組。*/

function sortArray(arr){
if(arr instanceof Array){ //使用instanceof判斷是Array最爲合適
    alert(arr.sort());
    }
}
var box=[3,5,1];
sortArray(box);

/*
通訊錯誤
在使用url進行參數傳遞時,常常會傳遞一些中文名的參數或URL地址,在後臺處理
時會發生轉換亂碼或錯誤,由於不一樣的瀏覽器對傳遞的參數解釋是不一樣的,因此有必要使用
編碼進行統一傳遞。
好比:?user=李炎恢&age=100
*/

var url='?user=' +encodeURIComponent('李炎恢') +'&age=100'; //編碼

/*
PS:在AJAX章節中咱們會繼續探討通訊錯誤和編碼問題。
五.調試技術
在JavaScript初期,瀏覽器並無針對JavaScript提供調試工具,因此開發人員就想出
了一套本身的調試方法,好比alert()。這個方法能夠打印你懷疑的是否獲得相應的值,或者
放在程序的某處來看看是否能執行,得知以前的代碼無誤。
*/

var num1=1;
var num2=b; //在這段先後加上alert('')調試錯誤
var result=num1+num2;
alert(result);

/*
PS:使用alert('')來調試錯誤比較麻煩,重要裁剪和粘貼alert(''),若是遺忘掉沒有刪掉
用於調試的alert('')將特別頭疼。因此,咱們如今須要更好的調試方法。
將消息記錄到控制檯
IE八、Firefox、Opera、Chrome和Safari都有JavaScript控制檯,能夠用來查看JavaScript
錯誤。對於Firefox,須要安裝Firebug,其餘瀏覽器直接使用console對象寫入消息便可。
*/

/*
        console    對象的方法
方法名                     說明
error(message)        將錯誤消息記錄到控制檯
info(message)         將信息性消息記錄到控制檯
log(message)         將通常消息記錄到控制檯
warn(message)         將警告消息記錄到控制檯
*/

console.error('錯誤!'); //紅色帶叉
console.info('信息!'); //白色帶信息號
console.log('日誌!'); //白色
console.warn('警告!'); //黃色帶感嘆號

//PS:這裏以Firefox爲標準,其餘瀏覽器會稍有差別。
var num1=1;
console.log(typeofnum1); //獲得num1的類型
var num2='b';
console.log(typeofnum2); //獲得num2的類型
var result=num1+num2;
alert(result); //結果是1b,匪夷所思

/*
PS:咱們誤把num2賦值成字符串了,其實應該是數值,致使最後的結果是1b。那麼
傳統調試就必須使用alert(typeonum1)來看看是否是數值類型,比較麻煩,由於alert()會阻
斷後面的執行,看過以後還要刪,刪完估計一下子又忘了,而後又要alert(typeofnum1)來加
深印象。若是用了console.log的話,全部要調試的變量一目瞭然,也不須要刪除,放着也
沒事。
將錯誤拋出
以前已經將結果錯誤的拋出,這裏不在贅述。
*/

if(typeof num2 !='number') thrownewError('變量必須是數值!');

/*
六.調試工具
IE八、Firefox、Chrome、Opera、Safari都自帶了本身的調試工具,而開發人員只習慣了
Firefox一種,因此不少狀況下,在Firefox開發調試,而後去其餘瀏覽器作兼容。其實Firebug
工具提供了一種Web版的調試工具:Firebuglite。
如下是網頁版直接調用調試工具的代碼:直接複製到瀏覽器網址便可。
*/

javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.doc
umentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);
E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(doc
ument,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','rele
ases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');

/*
還有一種離線版,把firebug-lite下載好,載入工具便可,致使最終工具沒法運行,其餘
瀏覽器運行無缺。雖然Web版本的FirebugLite能夠跨瀏覽器使用Firebug,但除了Firefox
原生的以外,都不支持斷點、單步調試、監視、控制檯等功能。好在,其餘瀏覽器本身的調
試器都有。
PS:Chrome瀏覽器必須在服務器端方可有效。測試也發現,只能簡單調試,若是遇到
錯誤,系統不能自動拋出錯誤給firebug-lite。
1.設置斷點
咱們能夠選擇Script(腳本),點擊要設置斷點的JS腳本處,便可設置斷點。當咱們須要
調試的時候,從斷點初開始模擬運行,發現代碼執行的流程和變化。
2.單步調試
設置完斷點後,能夠點擊單步調試,一步步看代碼執行的步驟和流程。上面有五個按鈕:
從新運行:從新單步調試
斷繼:正常執行代碼
單步進入:一步一步執行流程
單步跳過:跳到下一個函數塊
單步退出:跳出執行到內部的函數
3.監控
單擊「監控」選項卡上,能夠查看在單步進入是,全部變量值的變化。你也能夠新建監
控表達式來重點查看本身所關心的變量。
4.控制檯
顯示各類信息。以前已瞭解過。
PS:其餘瀏覽器除IE8以上都可實現以上的調試功能,你們能夠本身常識下。而咱們
主要採用Firebug進行調試而後兼容到其餘瀏覽器的作法以提升開發效率。
*/
View Code

 

第27章 Cookie與存儲

/*
隨着Web愈來愈複雜,開發者急切的須要可以本地化存儲的腳本功能。這個時候,第
一個出現的方案:cookie誕生了。cookie的意圖是:在本地的客戶端的磁盤上以很小的文件
形式保存數據。
一.Cookie
cookie也叫HTTPCookie,最初是客戶端與服務器端進行會話使用的。好比,會員登陸,
下次回訪網站時無須登陸了;或者是購物車,購買的商品沒有及時付款,過兩天發現購物車
裏還有以前的商品列表。
HTTPCookie要求服務器對任意HTTP請求發送Set-Cookie,所以,Cookie的處理原則
上須要在服務器環境下進行。固然,如今大部分瀏覽器在客戶端也能實現Cookie的生成和
獲取。(目前Chrome不能夠在客戶端操做,其餘瀏覽器都可)
cookie的組成
cookie由名/值對形式的文本組成:name=value。完整格式爲:
name=value;[expires=date];[path=path];[domain=somewhere.com];[secure]
中括號是可選,name=value是必選。
*/

document.cookie='user=' +encodeURIComponent('小明'); //編碼寫入
alert(decodeURIComponent(document.cookie)); //解碼讀取

/*
expires=date 失效時間,若是沒有聲明,則爲瀏覽器關閉後即失效。聲明瞭失效時間,
那麼時間到期後方能失效。
*/

var date=newDate(); //建立一個
date.setDate(date.getDate()+7);
document.cookie="user="+encodeURIComponent('小明') +";expires="+date;

/*
PS:能夠經過Firefox瀏覽器查看和驗證失效時間。若是要提早刪除cookie也很是簡單,
只要從新建立cookie把時間設置當前時間以前便可:date.getDate()-1或newDate(0)。
path=path訪問路徑,當設置了路徑,那麼只有設置的那個路徑文件才能夠訪問cookie。
*/

var path='/E:/%E5%A4%87%E8%AF%BE%E7%AC%94%E8%AE%B0/JS1/29/demo';
document.cookie="user="+encodeURIComponent('小明') +";path="+path;

/*
PS:爲了操做方便,我直接把路徑複製下來,而且增長了一個目錄以強調效果。
domain=domain訪問域名,用於限制只有設置的域名才能夠訪問,那麼沒有設置,會
默認限制爲建立cookie的域名。
*/

var domain='yc60.com';
document.cookie="user="+encodeURIComponent('小明') +";domain="+domain;

/*
PS:若是定義了yc60.com,那麼在這個域名下的任何網頁均可訪問,若是定義了
v.yc60.com,那麼只能在這個二級域名訪問該cookie,而主域名和其餘子域名則不能訪問。
PS:設置域名,必須在當前域名綁定的服務器上設置,若是在yc60.com服務器上隨意
設置其餘域名,則會沒法建立cookie。
secure 安全設置,指明必須經過安全的通訊通道來傳輸(HTTPS)才能獲取cookie。
*/

document.cookie="user="+encodeURIComponent('小明') +";secure";

/*PS:https安全通訊連接須要單獨配置。
JavaScript設置、讀取和刪除並非特別的直觀方便,咱們能夠封裝成函數來方便調用。*/

//建立cookie
function setCookie(name,value,expires,path,domain,secure){
    var cookieText=encodeURIComponent(name)+'=' +encodeURIComponent(value);
    if(expiresinstanceofDate) {
    cookieText+='; expires='+expires;
    }
    if(path){
    cookieText+='; expires='+expires;
    }
    if(domain){
    cookieText+='; domain='+domain;
    }
    if(secure){
    cookieText+='; secure';
    }
    document.cookie=cookieText;
}

//獲取cookie
function getCookie(name){
var cookieName=encodeURIComponent(name)+'=';
var cookieStart=document.cookie.indexOf(cookieName);
var cookieValue=null;
if(cookieStart>-1){
    var cookieEnd=document.cookie.indexOf(';',cookieStart);
    if(cookieEnd==-1){
    cookieEnd=document.cookie.length;
    }
    cookieValue=decodeURIComponent(
    document.cookie.substring(cookieStart+cookieName.length,cookieEnd));
    }
    return cookieValue;
}

//刪除cookie
function unsetCookie(name){
    document.cookie=name+"=;expires="+newDate(0);
}
//失效天數,直接傳一個天數便可
function setCookieDate(day){
    if(typeof day=='number' &&day>0){
    var date = new Date();
    date.setDate(date.getDate()+day);
    }else{
    throw new Error('傳遞的day必須是一個天數,必須比0大');
    }
    return date;
}

/*
二.cookie侷限性
cookie雖然在持久保存客戶端用戶數據提供了方便,分擔了服務器存儲的負擔。可是還
有不少侷限性的。
第一:每一個特定的域名下最多生成20個cookie(根據不一樣的瀏覽器有所區別)。
1.IE6或更低版本最多20個cookie
2.IE7和以後的版本最多能夠50個cookie。IE7最初也只能20個,以後因被升級不定後
增長了。
3.Firefox最多50個cookie
4.Opera最多30個cookie
5.Safari和Chrome沒有作硬性限制。
PS:爲了更好的兼容性,因此按照最低的要求來,也就是最多不得超過20個cookie。
當超過指定的cookie時,瀏覽器會清理掉早期的cookie。IE和Opera會清理近期最少使用
的cookie,Firefox會隨機清理cookie。
第二:cookie的最大大約爲4096字節(4k),爲了更好的兼容性,通常不能超過4095字
節便可。
第三:cookie存儲在客戶端的文本文件,因此特別重要和敏感的數據是不建議保存在
cookie的。好比銀行卡號,用戶密碼等。
三.其餘存儲
IE提供了一種存儲能夠持久化用戶數據,叫作userData,從IE5.0就開始支持。每一個數
據最多128K,每一個域名下最多1M。這個持久化數據存放在緩存中,若是緩存沒有清理,
那麼會一直存在。
*/
<div style="behavior:url(#default#userData)" id="box"></div>
addEvent(window,'load', function(){
    var box=document.getElementById('box');
    box.setAttribute('name',encodeURIComponent('小明'));
    box.expires=setCookieDate(7);
    box.save('bookinfo');
    //box.removeAttribute('name'); //刪除userDate
    //box.save('bookinfo');
    box.load('bookinfo');
    alert(decodeURIComponent(box.getAttribute('name')));
});

/*
PS:這個數據文件也是保存在cookie目錄中,只要清除cookie便可。若是指定過時日
期,則到期後自動刪除,若是沒有指定就是永久保存。
Web存儲
在比較高版本的瀏覽器,JavaScript提供了sessionStorage和globalStorage。在HTML5
中提供了localStorage來取代globalStorage。而瀏覽器最低版本爲:IE8+、Firefox3.5+、Chrome
4+和Opera10.5+。
PS:因爲這三個對瀏覽器版本要求較高,咱們就只簡單的在Firefox瞭解一下,有興趣
的能夠經過關鍵字搜索查詢。
*/

//經過方法存儲和獲取
sessionStorage.setItem('name','小明');
alert(sessionStorage.getItem('name'));
//經過屬性存儲和獲取
sessionStorage.book='小明';
alert(sessionStorage.book);
//刪除存儲
sessionStorage.removeItem('name');

/*PS:因爲localStorage代替了globalStorage,因此在Firefox、Opera和Chrome目前的最
新版本已不支持。*/

//經過方法存儲和獲取
localStorage.setItem('name','小明');
alert(localStorage.getItem('name'));
//經過屬性存儲和獲取
localStorage.book='小明';
alert(localStorage.book);
//刪除存儲
localStorage.removeItem('name');

/*PS:這三個對象都是永久保存的,保存在緩存裏,只有手工刪除或者清理瀏覽器緩存
方可失效。在容量上也有一些限制,主要看瀏覽器的差別,Firefox3+、IE8+、Opera爲5M,
Chrome和Safari爲2.5M。*/
View Code

 

第28章 XML

/*
隨着互聯網的發展,Web應用程序的豐富,開發人員愈來愈但願可以使用客戶端來操
做XML技術。而XML技術一度成爲存儲和傳輸結構化數據的標準。因此,本章就詳細探
討一下JavaScript中使用XML的技術。
對於什麼是XML,幹什麼用的,這裏就不在贅述了,在以往的XHTML或PHP課程都
有涉及到,能夠理解成一個微型的結構化的數據庫,保存一些小型數據用的。
一.IE中的XML
在統一的正式規範出來之前,瀏覽器對於XML的解決方案各不相同。DOM2級提出了
動態建立XMLDOM規範,DOM3進一步加強了XMLDOM。因此,在不一樣的瀏覽器實現
XML的處理是一件比較麻煩的事情。
1.建立XMLDOM對象
IE瀏覽器是第一個原生支持XML的瀏覽器,而它是經過ActiveX對象實現的。這個對
象,只有IE有,通常是IE9以前採用。微軟當年爲了開發人員方便的處理XML,建立了
MSXML庫,但卻沒有讓Web開發人員經過瀏覽器訪問相同的對象。
*/


var xmlDom = new ActiveXObject('MSXML2.DOMDocument');


/*
        ActiveXObject類型
XML版本字符串                             說明
Microsoft.XmlDom             最初隨同IE發佈,不建議使用
MSXML2.DOMDocument             腳本處理而更新的版本,僅在特殊狀況做爲備份用
MSXML2.DOMDocument.3.0         在JavaScript中使用,這是最低的建議版本
MSXML2.DOMDocument.4.0         腳本處理時並不可靠,使用這個版本致使安全警告
MSXML2.DOMDocument.5.0         腳本處理時並不可靠,使用這個版本致使安全警告
MSXML2.DOMDocument.6.0         腳本可以可靠處理的最新版本

*/

/*
PS:在這六個版本中微軟只推薦三種:
1.MSXML2.DOMDocument.6.0最可靠最新的版本
2.MSXML2.DOMDocument.3.0兼容性較好的版本
3.MSXML2.DOMDocument 僅針對IE5.5以前的版本
*/

/*PS:這三個版本在不一樣的windows平臺和瀏覽器下會有不一樣的支持,那麼爲了實現兼
容,咱們應該考慮這樣操做:從6.0->3.0->備用版本這條路線進行實現。*/

function createXMLDOM(){
var version=[
    'MSXML2.DOMDocument.6.0',
    'MSXML2.DOMDocument.3.0',
    'MSXML2.DOMDocument'
    ];
    for(vari=0;i<version.length;i++){
        try{
        varxmlDom=newActiveXObject(version[i]);
        returnxmlDom;
        }catch(e){
        //跳過
        }
    }
    throw new Error('您的系統或瀏覽器不支持MSXML!'); //循環後拋出錯誤
}

/*2.載入XML
若是已經獲取了XMLDOM對象,那麼能夠使用loadXML()和load()這兩個方法能夠分
別載入XML字符串或XML文件。*/

xmlDom.loadXML('<rootversion="1.0"><user>Lee</user></root>');
alert(xmlDom.xml);

/*PS:loadXML參數直接就是XML字符串,若是想效果更好,能夠添加換行符\n。.xml
屬性能夠序列化XML,獲取整個XML字符串。*/

xmlDom.load('test.xml'); //載入一個XML文件
alert(xmlDom.xml);

/*當你已經能夠加載了XML,那麼你就能夠用以前學習的DOM來獲取XML數據,好比
標籤內的某個文本。*/

var user=xmlDom.getElementsByTagName('user')[0]; //獲取<user>節點
alert(user.tagName); //獲取<user>元素標籤
alert(user.firstChild.nodeValue); //獲取<user>裏的值Lee

//DOM不僅僅能夠獲取XML節點,也能夠建立。
var email=xmlDom.createElement('email');
xmlDom.documentElement.appendChild(email);

/*3.同步及異步
load()方法是用於服務器端載入XML的,而且限制在同一臺服務器上的XML文件。那
麼在載入的時候有兩種模式:同步和異步。
所謂同步:就是在加載XML完成以前,代碼不會繼續執行,直到徹底加載了XML再
返回。好處就是簡單方便、壞處就是若是加載的數據中止響應或延遲過久,瀏覽器會一直堵
塞從而形成假死狀態。*/

xmlDom.async=false; //設置同步,false,能夠用PHP測試假死

/*所謂異步:就是在加載XML時,JavaScript會把任務丟給瀏覽器內部後臺去處理,不
會形成堵塞,但要配合readystatechange事件使用,因此,一般咱們都使用異步方式。*/

xmlDom.async=true; //設置異步,默認

/*經過異步加載,咱們發現獲取不到XML的信息。緣由是,它並無徹底加載XML就
返回了,也就是說,在瀏覽器內部加載一點,返回一點,加載一點,返回一點。這個時候,
咱們須要判斷是否徹底加載,而且能夠使用了,再進行獲取輸出。*/

/*
XMLDOM中readystatechange事件
就緒狀態             說明
1                 DOM正在加載
2                 DOM已經加載完數據
3                 DOM已經能夠使用,但某些部分還沒法訪問
4                 DOM已經徹底能夠
PS:readyState能夠獲取就緒狀態值

*/

var xmlDom=createXMLDOM();
    xmlDom.async=true; //異步,能夠不寫
xmlDom.onreadystatechange=function(){
    if(xmlDom.readyState==4){ //徹底加載了,再去獲取XML
    alert(xmlDom.xml);
    }
}
xmlDom.load('test.xml'); //放在後面重點體現異步的做用

/*PS:能夠經過readyState來了解事件的執行次數,將load()方法放到最後不會由於代碼
的順序而致使沒有加載。而且load()方法必須放在onreadystatechange以後,才能保證就緒狀
態變化時調用該事件處理程序,由於要先觸發。用PHP來測試,在瀏覽器內部執行時,是
否能操做,是否會假死。
PS:不可以使用this,不可以用IE的事件處理函數,緣由是ActiveX控件爲了預防安
全性問題。
PS:雖然能夠經過XMLDOM文檔加載XML文件,但公認的仍是XMLHttpRequest
對象比較好。這方面內容,咱們在Ajax章節詳細瞭解。
4.解析錯誤
在加載XML時,不管使用loadXML()或load()方法,都有可能遇到XML格式不正確的
狀況。爲了解決這個問題,微軟的XMLDOM提供了parseError屬性。*/

/*
        parseError屬性對象
屬性                     說明
errorCode         發生的錯誤類型的數字代號
filepos         發生錯誤文件中的位置
line             錯誤行號
linepos         遇到錯誤行號那一行上的字符的位置
reason             錯誤的解釋信息

*/

if(xmlDom.parseError==0){
    alert(xmlDom.xml);
    }else{
    throw new Error('錯誤行號:' +xmlDom.parseError.line+
        '\n 錯誤代號:' +xmlDom.parseError.errorCode+
        '\n 錯誤解釋:' +xmlDom.parseError.reason);
}

/*二.DOM2中的XML
IE能夠實現了對XML字符串或XML文件的讀取,其餘瀏覽器也各自實現了對XML
處理功能。DOM2級在document.implementaion中引入了createDocument()方法。IE九、Firefox、
Opera、Chrome和Safari都支持這個方法。*/

//1.建立XMLDOM對象
var xmlDom=document.implementation.createDocument('','root',null); //建立xmlDom
var user=xmlDom.createElement('user'); //建立user元素
xmlDom.getElementsByTagName('root')[0].appendChild(user); //添加到root下
var value=xmlDom.createTextNode('Lee'); //建立文本
xmlDom.getElementsByTagName('user')[0].appendChild(value); //添加到user下
alert(xmlDom.getElementsByTagName('root')[0].tagName);
alert(xmlDom.getElementsByTagName('user')[0].tagName);
alert(xmlDom.getElementsByTagName('user')[0].firstChild.nodeValue);

/*PS:因爲DOM2中不支持loadXML()方法,因此,沒法簡易的直接建立XML字符串。
因此,只能採用以上的作法。
PS:createDocument()方法須要傳遞三個參數,命名空間,根標籤名和文檔聲明,因爲
JavaScript管理命名空間比較困難,因此留空便可。文檔聲明通常根本用不到,直接null即
可。命名空間和文檔聲明留空,表示建立XMLDOM對象不須要命名空間和文檔聲明。
PS:命名空間的用途是防止太多的重名而進行的分類,文檔類型代表此文檔符合哪一種
規範,而這裏建立XMLDOM不須要使用這兩個參數,因此留空便可。
2.載入XML
DOM2只支持load()方法,載入一個同一臺服務器的外部XML文件。固然,DOM2也
有async屬性,來表面同步或異步,默認異步。*/

//同步狀況下
var xmlDom=document.implementation.createDocument('','root',null);
xmlDom.async=false;
xmlDom.load('test.xml');
alert(xmlDom.getElementsByTagName('user')[0].tagName);
//異步狀況下
var xmlDom=document.implementation.createDocument('','root',null);
xmlDom.async=true;
addEvent(xmlDom,'load', function(){ //異步直接用onload便可
    alert(this.getElementsByTagName('user')[0].tagName);
});
xmlDom.load('test.xml');

/*PS:無論在同步或異步來獲取load()方法只有Mozilla的Firefox才能支持,只不過新版
的Opera也是支持的,其餘瀏覽器則不支持。
3.DOMParser類型
因爲DOM2沒有loadXML()方法直接解析XML字符串,因此提供了DOMParser類型
來建立XMLDOM對象。IE九、Safari、Chrome和Opera都支持這個類型。*/

var xmlParser=newDOMParser(); //建立DOMParser對象
var xmlStr='<user>Lee</user></root>'; //XML字符串
var xmlDom=xmlParser.parseFromString(xmlStr,'text/xml'); //建立XMLDOM對象
alert(xmlDom.getElementsByTagName('user')[0].tagName); //獲取user元素標籤名

/*PS:XMLDOM對象是經過DOMParser對象中的parseFromString方法來建立的,兩個
參數:XML字符串和內容類型text/xml。
4.XMLSerializer類型
因爲DOM2沒有序列化XML的屬性,因此提供了XMLSerializer類型來幫助序列化
XML字符串。IE九、Safari、Chrome和Opera都支持這個類型。*/

var serializer=newXMLSerializer(); //建立XMLSerializer對象
var xml=serializer.serializeToString(xmlDom); //序列化XML
alert(xml);

/*5.解析錯誤
在DOM2級處理XML發生錯誤時,並無提供特有的對象來捕獲錯誤,而是直接生
成另外一個錯誤的XML文檔,經過這個文檔能夠獲取錯誤信息。*/

var errors=xmlDom.getElementsByTagName('parsererror');
if(errors.length>0){
    throw new Error('XML格式有誤:' +errors[0].textContent);
}

//PS:errors[0].firstChild.nodeValue也能夠使用errors[0].textContent來代替。

/*三.跨瀏覽器處理XML
若是要實現跨瀏覽器就要思考幾個個問題:1.load()只有IE、Firefox、Opera支持,因此
沒法跨瀏覽器;2.獲取XMLDOM對象順序問題,先判斷先進的DOM2的,而後再去判斷
落後的IE;3.針對不一樣的IE和DOM2級要使用不一樣的序列化。4.針對不一樣的報錯進行不一樣
的報錯機制。*/

//首先,咱們須要跨瀏覽器獲取XMLDOM
function getXMLDOM(xmlStr){
    var xmlDom=null;
    if(typeof window.DOMParser!='undefined') { //W3C
    xmlDom=(new DOMParser()).parseFromString(xmlStr,'text/xml');
    var errors=xmlDom.getElementsByTagName('parsererror');
    if(errors.length>0){
    throw new Error('XML解析錯誤:' +errors[0].firstChild.nodeValue);
    }
    }elseif(typeof window.ActiveXObject!='undefined') { //IE
    varversion=[
    'MSXML2.DOMDocument.6.0',
    'MSXML2.DOMDocument.3.0',
    'MSXML2.DOMDocument'
    ];
    for(var i=0;i<version.length;i++){
    try{
    xmlDom = new ActiveXObject(version[i]);
    }catch(e){
    //跳過
    }
}
xmlDom.loadXML(xmlStr);
if(xmlDom.parseError!=0){
    throw new Error('XML解析錯誤:' +xmlDom.parseError.reason);
    }
    }else{
    throw new Error('您所使用的系統或瀏覽器不支持XMLDOM!');
    }
    return xmlDom;
}

//其次,咱們還必須跨瀏覽器序列化XML
function serializeXML(xmlDom){
    var xml='';
    if(typeof XMLSerializer!='undefined') {
    xml=(new XMLSerializer()).serializeToString(xmlDom);
    }else if(typeof xmlDom.xml!='undefined') {
        xml=xmlDom.xml;
    }else{
    throw new Error('沒法解析XML!');
    }
    return xml;
}
//PS:因爲兼容性序列化過程有必定的差別,可能返回的結果字符串可能會有一些不一樣。
//至於load()加載XML文件則由於只有部分瀏覽器支持而沒法跨瀏覽器。
View Code

 

第29章 XPath

/*XPath是一種節點查找手段,對比以前使用標準DOM去查找XML中的節點方式,大
大下降了查找難度,方便開發者使用。可是,DOM3級之前的標準並無就XPath作出規
範;直到DOM3在首次推薦到標準規範行列。大部分瀏覽器實現了這個標準,IE則以本身
的方式實現了XPath。*/

/*一.IE中的XPath
在IE8及以前的瀏覽器,XPath是採用內置基於ActiveX的XMLDOM文檔對象實現的。
在每個節點上提供了兩個方法:selectSingleNode()和selectNodes()。
selectSingleNode()方法接受一個XPath模式(也就是查找路徑),找到匹配的第一個節
點並將它返回,沒有則返回null。*/

var user=xmlDom.selectSingleNode('root/user'); //獲得第一個user節點
alert(user.xml); //查看xml序列
alert(user.tagName); //節點元素名
alert(user.firstChild.nodeValue); //節點內的值

/*上下文節點:咱們經過xmlDom這個對象實例調用方法,而xmlDom這個對象實例其
實就是一個上下文節點,這個節點指針指向的是根,也就是root元素以前。那麼若是咱們
把這個指針指向user元素以前,那麼結果就會有所變化。*/

//經過xmlDom,而且使用root/user的路徑
var user=xmlDom.selectSingleNode('root/user');
alert(user.tagName); //user
//經過xmlDom.documentElement,而且使用user路徑,省去了root
var user=xmlDom.documentElement.selectSingleNode('user');
alert(user.tagName); //user
//經過xmlDom,而且使用user路徑,省去了root
var user=xmlDom.selectSingleNode('user');
alert(user.tagName); //找不到了,出錯

/*PS:xmlDom和xmlDom.documentElement都是上下文節點,主要就是定位當前路徑查
找的指針,而xmlDom對象實例的指針就是在最根上。*/

//XPath XPath經常使用語法
//經過user[n]來獲取第n+1條節點,PS:XPath實際上是按1爲起始值的
var user=xmlDom.selectSingleNode('root/user[1]');
alert(user.xml);
//經過text()獲取節點內的值
var user=xmlDom.selectSingleNode('root/user/text()');
alert(user.xml);
alert(user.nodeValue);
//經過//user表示在整個xml獲取到user節點,不關心任何層次
var user=xmlDom.selectSingleNode('//user');
alert(user.xml);
//經過root//user表示在root包含的層次下獲取到user節點,在root內不關心任何層次
var user=xmlDom.selectSingleNode('root//user');
alert(user.tagName);
//經過root/user[@id=6]表示獲取user中id=6的節點
var user=xmlDom.selectSingleNode('root/user[@id=6]');
alert(user.xml);
/*PS:更多的XPath語法,能夠參考XPath手冊或者XMLDOM手冊進行參考,這裏只
提供了最經常使用的語法。
selectSingleNode()方法是獲取單一節點,而selectNodes()方法則是獲取一個節點集合。*/

var users=xmlDom.selectNodes('root/user'); //獲取user節點集合
alert(users.length);
alert(users[1].xml);

/*二.W3C下的XPath
在DOM3級XPath規範定義的類型中,最重要的兩個類型是XPathEvaluator和
XPathResult。其中,XPathEvaluator用於在特定上下文對XPath表達式求值。*/

/*
XPathEvaluator的方法
方法                                     說明
createExpression(e,n)         將XPath表達式及命名空間轉化成XPathExpression
createNSResolver(n)         根據n命名空間建立一個新的XPathNSResolver對象
evaluate(e,c,n,t,r)         結合上下文來獲取XPath表達式的值

*/

/*W3C實現XPath查詢節點比IE來的複雜,首先第一步就是須要獲得XPathResult對象
的實例。獲得這個對象實例有兩種方法,一種是經過建立XPathEvaluator對象執行evaluate()
方法,另外一種是直接經過上下文節點對象(好比xmlDom)來執行evaluate()方法。*/

//使用XPathEvaluator對象建立XPathResult
var eva=newXPathEvaluator();
var result=eva.evaluate('root/user',xmlDom,null,
                                    XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
alert(result);
//使用上下文節點對象(xmlDom)建立XPathResult
var result=xmlDom.evaluate('root/user',xmlDom,null,
                                        XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
alert(result);

/*相對而言,第二種簡單方便一點,但evaluate方法有五個屬性:1.XPath路徑、2.上下
文節點對象、3.命名空間求解器(一般是null)、4.返回結果類型、5保存結果的XPathResult
對象(一般是null)。*/

/*對於返回的結果類型,有10中不一樣的類型
常量                                                 說明
XPathResult.ANY_TYPE                         返回符合XPath表達式類型的數據
XPathResult.ANY_UNORDERED_NODE_TYPE           返回匹配節點的節點集合,但順序可能與文檔中的節點的順序不匹配
XPathResult.BOOLEAN_TYPE                     返回布爾值
XPathResult.FIRST_ORDERED_NODE_TYPE         返回只包含一個節點的節點集合,且這個節點是在文檔中第一個匹配的節點
XPathResult.NUMBER_TYPE                     返回數字值
XPathResult.ORDERED_NODE_ITERATOR_TYPE      返回匹配節點的節點集合,順序爲節點在文檔中出現的順序。這是最經常使用到的結果類型
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE      返回節點集合快照,在文檔外捕獲節點,這樣未來對文檔的任何修改都不會影響這個節點列表
XPathResult.STRING_TYPE                     返回字符串值
XPathResult.UNORDERED_NODE_ITERATOR_TYPE    返回匹配節點的節點集合,不過順序可能不會按照節點在文檔中出現的順序排列
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE    返回節點集合快照,在文檔外捕獲節點,這樣未來對文檔的任何修改都不會影響這個節點列表*/

/*PS:上面的常量過於繁重,對於咱們只須要學習瞭解,其實也就須要兩個:1.獲取一個
單一節、2.獲取一個節點集合。*/

//1.獲取一個單一節點

var result=xmlDom.evaluate('root/user',xmlDom,null,
                            XPathResult.FIRST_ORDERED_NODE_TYPE,null);
if(result!==null){
            alert(result.singleNodeValue.tagName); //singleNodeValue屬性獲得節點對象
}
//2.獲取節點集合
var result = xmlDom.evaluate('root/user',xmlDom,null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
var nodes=[];
if(result!==null){
    while((node=result.iterateNext())!==null){
        nodes.push(node);
    }
}

/*PS:節點集合的獲取方式,是經過迭代器遍歷而來的,咱們保存到數據中就模擬出IE
類似的風格。*/

/*三.XPath跨瀏覽器兼容
若是要作W3C和IE的跨瀏覽器兼容,咱們要思考幾個問題:1.若是傳遞一個節點的下
標,IE是從0開始計算,W3C從1開始計算,能夠經過傳遞獲取下標進行增1減1的操做
來進行。2.獨有的功能放棄,爲了保證跨瀏覽器。3.只獲取單一節點和節點列表便可,基本
能夠完成全部的操做。*/

//跨瀏覽器獲取單一節點
function selectSingleNode(xmlDom,xpath){
var node=null;
    if(typeof xmlDom.evaluate!='undefined') {
        var patten=/\[(\d+)\]/g;
        var flag=xpath.match(patten);
        var num=0;
        if(flag!==null){
        num=parseInt(RegExp.$1)+1;
        xpath=xpath.replace(patten,'[' +num+']');
        }
        var result=xmlDom.evaluate(xpath,xmlDom,null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,null);
        if(result!==null){
            node=result.singleNodeValue;
        }
        }else if(typeofxmlDom.selectSingleNode!='undefined') {
        node=xmlDom.selectSingleNode(xpath);
    }
    return node;
}


//跨瀏覽器獲取節點集合
function selectNodes(xmlDom,xpath){
var nodes=[];
if(typeof xmlDom.evaluate!='undefined') {
    var patten=/\[(\d+)\]/g;
    var flag=xpath.match(patten);
    var num=0;
    if(flag!==null){
    num=parseInt(RegExp.$1)+1;
    xpath=xpath.replace(patten,'[' +num+']');
    }
    var node=null;
    var result=xmlDom.evaluate('root/user',xmlDom,null,
    XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
    if(result!==null){
    while((node=result.iterateNext())!==null){
    nodes.push(node);
    }
    }
    }elseif(typeof xmlDom.selectNodes!='undefined') {
    nodes=xmlDom.selectNodes(xpath);
    }
    return nodes;
}

/*PS:在傳遞xpath路徑時,沒有作驗證判斷是否合法,有興趣的同窗能夠自行完成。在
XML還有一個重要章節是XSLT和EX4,因爲在使用頻率的緣故,咱們暫且擱置。*/
View Code

 

第30章 JSON

/*前兩章咱們探討了XML的結構化數據,但開發人員仍是以爲這種微型的數據結構仍是
過於煩瑣、冗長。爲了解決這個問題,JSON的結構化數據出現了。JSON是JavaScript的一
個嚴格的子集,利用JavaScript中的一些模式來表示結構化數據。
一.JSON語法
JSON和XML類型,都是一種結構化的數據表示方式。因此,JSON並非JavaScript
獨有的數據格式,其餘不少語言均可以對JSON進行解析和序列化。
JSON的語法能夠表示三種類型的值:
1.簡單值:能夠在JSON中表示字符串、數值、布爾值和null。但JSON不支持JavaScript
中的特殊值undefined。
2.對象:顧名思義。
3.數組:顧名思義。
簡單值
100、"Lee" 這兩個量就是JSON的表示方法,一個是JSON數值,一個是JSON字符串。
布爾值和null也是有效的形式。但實際運用中要結合對象或數組。*/

//對象
//JavaScript對象字面量表示法:
var box={
    name:'Lee',
    age:100
};
//而JSON中的對象表示法須要加上雙引號,而且不存在賦值運算和分號:
{
"name":"Lee", //使用雙引號,不然轉換會出錯
"age":100
}
//數組
//JavaScript數組字面量表示法:
var box=[100,'Lee', true];
//而JSON中的數組表示法一樣沒有變量賦值和分號:
[100,"Lee",true]

//通常比較經常使用的一種複雜形式是數組結合對象的形式:

[
    {
        "title":"a",
        "num":1
    },
    {
        "title":"b",
        "num":2
    },
    {
        "title":"c",
        "num":3
    }
]

/*PS:通常狀況下,咱們能夠把JSON結構數據保存到一個文本文件裏,而後經過
XMLHttpRequest對象去加載它,獲得這串結構數據字符串(XMLHttpRequest對象將在Aajx
章節中詳細探討)。因此,咱們能夠模擬這種過程。
模擬加載JSON文本文件的數據,而且賦值給變量。*/

var box='[{"name" :"a","age":1},{"name":"b","age":2}]';

/*PS;上面這短代碼模擬了varbox=load('demo.json');賦值過程。由於經過load加載的文
本文件,無論內容是什麼,都必須是字符串。因此兩邊要加上雙引號。
其實JSON就是比普通數組多了兩邊的雙引號,普通數組以下:*/

var box=[{name:'a', age:1},{name:'b', age:2}];

/*二.解析和序列化
若是是載入的JSON文件,咱們須要對其進行使用,那麼就必須對JSON字符串解析成
原生的JavaScript值。固然,若是是原生的JavaScript對象或數組,也能夠轉換成JSON字
符串。
對於講JSON字符串解析爲JavaScript原生值,早期採用的是eval()函數。但這種方法
既不安全,可能會執行一些惡意代碼。*/

var box='[{"name" :"a","age":1},{"name":"b","age":2}]';
alert(box); //JSON字符串
var json=eval(box); //使用eval()函數解析
alert(json); //獲得JavaScript原生值

/*ECMAScript5對解析JSON的行爲進行規範,定義了全局對象JSON。支持這個對象的
瀏覽器有IE8+、Firefox3.5+、Safari4+、Chrome和Opera10.5+。不支持的瀏覽器也能夠經過
一個開源庫json.js來模擬執行。JSON對象提供了兩個方法,一個是將原生JavaScript值轉
換爲JSON字符串:stringify();另外一個是將JSON字符串轉換爲JavaScript原生值:parse()。*/

var box='[{"name" :"a","age":1},{"name":"b","age":2}]'; //特別注意,鍵要用雙引號
    alert(box);
var json = JSON.parse(box); //不是雙引號,會報錯
alert(json);
var box=[{name:'a', age:1},{name:'b', age:2}]; //JavaScript原生值
var json=JSON.stringify(box); //轉換成JSON字符串
alert(json); //自動雙引號

/*在序列化JSON的過程當中,stringify()方法還提供了第二個參數。第一個參數能夠是一個
數組,也能夠是一個函數,用於過濾結果。第二個參數則表示是否在JSON字符串中保留縮
進。*/

var box=[{name:'a', age:1,height:177},{name:'b', age:2,height:188}];
var json=JSON.stringify(box,['name','age'], 4);
alert(json);

/*PS:若是不須要保留縮進,則不填便可;若是不須要過濾結果,但又要保留縮進,則
講過濾結果的參數設置爲null。若是採用函數,能夠進行復雜的過濾。*/

var box=[{name:'a', age:1,height:177},{name:'b', age:2,height:188}];
var json=JSON.stringify(box,function(key,value){
    switch(key){
    case'name' :
    return'Mr. ' +value;
    case'age' :
    returnvalue+'歲';
    default:
    return value;
}
},4);
alert(json);

/*PS:保留縮進除了是普通的數字,也能夠是字符。
還有一種方法能夠自定義過濾一些數據,使用toJSON()方法,能夠將某一組對象裏指
定返回某個值。*/


var box=[{name:'a', age:1,height:177,toJSON:function(){
    return this.name;
}},{name:'b',age :2,height:188,toJSON:function(){
    return this.name;
}}];

var json=JSON.stringify(box);
alert(json);


/*PS:因而可知序列化也有執行順序,首先先執行toJSON()方法;若是應用了第二個過
濾參數,則執行這個方法;而後執行序列化過程,好比將鍵值對組成合法的JSON字符串,
好比加上雙引號。若是提供了縮進,再執行縮進操做。
解析JSON字符串方法parse()也能夠接受第二個參數,這樣能夠在還原出JavaScript值
的時候替換成本身想要的值。*/

var box='[{"name" :"a","age":1},{"name":"b","age":2}]';
var json=JSON.parse(box,function(key,value){
if(key=='name') {
    return 'Mr. ' +value;
}else{
    return value;
}
});
alert(json[0].name);
View Code

 

第31章 Ajax

 

/*2005年JesseJamesGarrett發表了一篇文章,標題爲:「Ajax:AnewApproachtoWeb
Applications」。他在這篇文章裏介紹了一種技術,用他的話說,就叫:Ajax,是Asynchronous
JavaScript+XML的簡寫。這種技術可以想服務器請求額外的數據而無須卸載頁面(即刷新),
會帶來更好的用戶體驗。一時間,席捲全球。*/

/*一.XMLHttpRequest
Ajax技術核心是XMLHttpRequest對象(簡稱XHR),這是由微軟首先引入的一個特性,
其餘瀏覽器提供商後來都提供了相同的實現。在XHR出現以前,Ajax式的通訊必須藉助一
些hack手段來實現,大多數是使用隱藏的框架或內嵌框架。
XHR的出現,提供了向服務器發送請求和解析服務器響應提供了流暢的接口。可以以
異步方式從服務器獲取更多的信息,這就意味着,用戶只要觸發某一事件,在不刷新網頁的
狀況下,更新服務器最新的數據。
雖然Ajax中的x表明的是XML,但Ajax通訊和數據格式無關,也就是說這種技術不
必定使用XML。
IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR對象,在這些瀏覽器中創
建XHR對象能夠直接實例化XMLHttpRequest便可。*/

var xhr=newXMLHttpRequest();
alert(xhr); //XMLHttpRequest

/*若是是IE6及如下,那麼咱們必須還須要使用ActiveX對象經過MSXML庫來實現。在
低版本IE瀏覽器可能會遇到三種不一樣版本的XHR對象,即MSXML2.XMLHttp、
MSXML2.XMLHttp.3.0、MSXML2.XMLHttp.6.0。咱們能夠編寫一個函數。*/

function createXHR(){
    if(typeof XMLHttpRequest!='undefined') {
    return newXMLHttpRequest();
    }else if (typeofActiveXObject!='undefined') {
    var versions=[
        'MSXML2.XMLHttp.6.0',
        'MSXML2.XMLHttp.3.0',
        'MSXML2.XMLHttp'
];
for(var i=0;i<versions.length;i++){
try{
return new ActiveXObject(version[i]);
}catch(e){
//跳過
}
}
}else{
throw new Error('您的瀏覽器不支持XHR對象!');
}
}


var xhr = new createXHR();

/*在使用XHR對象時,先必須調用open()方法,它接受三個參數:要發送的請求類型(get、
post)、請求的URL和表示是否異步。*/

xhr.open('get','demo.php', false); //對於demo.php的get請求,false同步

//PS:demo.php的代碼以下:
<?php echo Date('Y-m-dH:i:s')?> //一個時間

/*open()方法並不會真正發送請求,而只是啓動一個請求以備發送。經過send()方法進行
發送請求,send()方法接受一個參數,做爲請求主體發送的數據。若是不須要則,必須填null。
執行send()方法以後,請求就會發送到服務器上。*/

xhr.send(null); //發送請求

/*當請求發送到服務器端,收到響應後,響應的數據會自動填充XHR對象的屬性。那麼
一共有四個屬性:*/

/*屬性名                         說明
responseText             做爲響應主體被返回的文本
responseXML                若是響應主體內容類型是"text/xml"或"application/xml",則返回包含響應數據的XMLDOM文檔
status                     響應的HTTP狀態
statusText                 HTTP狀態的說明
*/
/*接受響應以後,第一步檢查status屬性,以肯定響應已經成功返回。通常而已HTTP狀
態代碼爲200做爲成功的標誌。除了成功的狀態代碼,還有一些別的:*/

/*
HTTP狀態碼                 狀態字符串                         說明
200                         OK                     服務器成功返回了頁面
400                         BadRequest             語法錯誤致使服務器不識別
401                         Unauthorized         請求須要用戶認證
404                         Notfound             指定的URL在服務器上找不到
500                     InternalServerError     服務器遇到意外錯誤,沒法完成請求
503                     ServiceUnavailable         因爲服務器過載或維護致使沒法完成請求
*/
/*
咱們判斷HTTP狀態值便可,不建議使用HTTP狀態說明,由於在跨瀏覽器的時候,可
能會不太一致。
*/
addEvent(document,'click', function(){
    var xhr=newcreateXHR();
    xhr.open('get','demo.php?rand=' +Math.random(),false); //設置了同步
    xhr.send(null);
    if(xhr.status==200){ //若是返回成功了
    alert(xhr.responseText); //調出服務器返回的數據
    }else{
    alert('數據返回失敗!狀態代碼:' +xhr.status+'狀態信息:' +xhr.statusText);
    }
});

/*以上的代碼每次點擊頁面的時候,返回的時間都是時時的,不一樣的,說明都是經過服務
器及時加載回的數據。那麼咱們也能夠測試一下在非Ajax狀況下的狀況,建立一個
demo2.php文件,使用非Ajax。*/

<script type="text/javascript"src="base.js"></script>
<script type="text/javascript">addEvent(document,'click', function(){
    alert("<?phpechoDate('Y-m-dH:i:s')?>");
});
</script>

/*
同步調用當然簡單,但使用異步調用纔是咱們真正經常使用的手段。使用異步調用的時候,
須要觸發readystatechange事件,而後檢測readyState屬性便可。這個屬性有五個值:
*/

/*
值             狀態                  說明
0             未初始化         還沒有調用open()方法
1             啓動             已經調用open()方法,但還沒有調用send()方法
2             發送             已經調用send()方法,但還沒有接受響應
3             接受             已經接受到部分響應數據
4             完成             已經接受到所有響應數據,並且能夠使用
*/


addEvent(document,'click', function(){
var xhr=new createXHR();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
    if(xhr.status==200){
        alert(xhr.responseText);
    }else{
        alert('數據返回失敗!狀態代碼:' +xhr.status+'狀態信息:'
        +xhr.statusText);
    }
    }
};
xhr.open('get','demo.php?rand=' +Math.random(),true);
xhr.send(null);
});

/*PS:使用abort()方法能夠取消異步請求,放在send()方法以前會報錯。放在responseText
以前會獲得一個空值。
二.GET與POST
在提供服務器請求的過程當中,有兩種方式,分別是:GET和POST。在Ajax使用的過
程中,GET的使用頻率要比POST高。
在瞭解這兩種請求方式前,咱們先了解一下HTTP頭部信息,包含服務器返回的響應頭
信息和客戶端發送出去的請求頭信息。咱們能夠獲取響應頭信息或者設置請求頭信息。咱們
能夠在Firefox瀏覽器的firebug查看這些信息。*/

//使用getResponseHeader()獲取單個響應頭信息
alert(xhr.getResponseHeader('Content-Type'));
//使用getAllResponseHeaders()獲取整個響應頭信息
alert(xhr.getAllResponseHeaders());
//使用setRequestHeader()設置單個請求頭信息
xhr.setRequestHeader('MyHeader','Lee'); //放在open方法以後,send方法以前
/*PS:咱們只能夠獲取服務器返回回來響應頭信息,沒法獲取向服務器提交的請求頭信
息,天然自定義的請求頭,在JavaScript端是沒法獲取到的。
GET請求
GET請求是最多見的請求類型,最經常使用於向服務器查詢某些信息。必要時,能夠將查
詢字符串參數追加到URL的末尾,以便提交給服務器。*/

xhr.open('get','demo.php?rand=' +Math.random()+'&name=Koo', true);

/*經過URL後的問號給服務器傳遞鍵值對數據,服務器接收到返回響應數據。特殊字符
傳參產生的問題能夠使用encodeURIComponent()進行編碼處理,中文字符的返回及傳參,
能夠講頁面保存和設置爲utf-8格式便可。
*/
//一個通用的URL提交函數
function addURLParam(url,name,value){
    url+=(url.indexOf('?')==-1?'?' :'&'); //判斷的url是否有已有參數
    url+=encodeURIComponent(name)+'=' +encodeURIComponent(value);
    alert(url);
    return url;
}

/*PS:當沒有encodeURIComponent()方法時,在一些特殊字符好比「&」,會出現錯誤導
致沒法獲取。
POST請求
POST請求能夠包含很是多的數據,咱們在使用表單提交的時候,不少就是使用的POST
傳輸方式。*/

xhr.open('post','demo.php', true);

//而發送POST請求的數據,不會跟在URL的尾巴上,而是經過send()方法向服務器提交數據。
xhr.send('name=Lee&age=100');

/*通常來講,向服務器發送POST請求因爲解析機制的緣由,須要進行特別的處理。由於
POST請求和Web表單提交是不一樣的,須要使用XHR來模仿表單提交。*/

xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

/*PS:從性能上來說POST請求比GET請求消耗更多一些,用相同數據比較,GET最多
比POST快兩倍。
上一節課的JSON也能夠使用Ajax來回調訪問。*/

var url='demo.json?rand=' +Math.random();
var box=JSON.parse(xhr.responseText);

/*三.封裝Ajax
由於Ajax使用起來比較麻煩,主要就是參數問題,好比到底使用GET仍是POST;到
底是使用同步仍是異步等等,咱們須要封裝一個Ajax函數,來方便咱們調用。*/

function ajax(obj){
    var xhr=new createXHR();
    obj.url=obj.url+'?rand=' +Math.random();
    obj.data=params(obj.data);
    if(obj.method==='get') obj.url=obj.url.indexOf('?')==-1?
    obj.url+'?' +obj.data:obj.url+'&' +obj.data;
    if(obj.async===true){
    xhr.onreadystatechange=function(){
        if(xhr.readyState==4)callback();
};
}

xhr.open(obj.method,obj.url,obj.async);
    if(obj.method==='post') {
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    xhr.send(obj.data);
    }else{
    xhr.send(null);
    }
    if(obj.async===false){
    callback();
    }
    function callback(){
    if(xhr.status==200){
    obj.success(xhr.responseText); //回調
    }else{
    alert('數據返回失敗!狀態代碼:' +xhr.status+',
    狀態信息:' +xhr.statusText);
    }
}
}

//調用ajax
addEvent(document,'click', function(){ //IE6須要重寫addEvent
    ajax({
        method:'get',
        url:'demo.php',
        data:{
        'name' :'Lee',
        'age' :100
    },
    success:function(text){
        alert(text);
    },
    async:true
    });
});
//名值對編碼
function params(data){
    var arr=[];
    for(variindata){
    arr.push(encodeURIComponent(i)+'=' +encodeURIComponent(data[i]));
    }
    return arr.join('&');
}

//PS:封裝Ajax並非一開始就造成以上的形態,須要通過屢次變化而成。
View Code
相關文章
相關標籤/搜索