瘋狂Html+CSS+JS 中JS總結

0 總結

本書的JSjavascript

  1. 第一章有講語法有挺多常見的坑點和原理解釋很不錯
  2. 第二章DOM編程講述了挺多API
  3. 第三章事件處理機制其實對事件中的this關鍵字和事件傳播順序講4. 解還不錯
  4. 第四章WebStorage本地存儲例子鮮明
  5. 第五章Worker應付複雜的js操做
  6. 第六章客戶端通訊WebSocket挺有用,能夠實現用戶與用戶在瀏覽器中互動

1. JavaScript語法

1.1 執行js代碼html

javascript:alert(‘執行js’);//通常放在超連接中,用戶點擊即執行,
<script>alert("執行js")</script>前端

1.2 變量賦值java

var a = 1;//顯式
a =1; //隱式

1.3 全局變量與局部變量node

...
var scope = "全局變量";
function test(){
    alert(scope); // undefiend
    var scope = "局部變量";
    alert(scope); // 局部變量
}

由於全局變量被局部變量覆蓋了.雖然局部變量的scope還沒賦值,可是已經在方法裏」佔」上位置了.
但若是把局部變量的var刪了,就會先輸出全局變量後輸出局部變量,由於沒有var在方法裏給局部變量」佔」位置;git

1.4 浮點數github

var a =.333
var b = a * 5;
alert(b);

得出的結果是 1.66499999999999999
因此在js中判斷浮點數是否相等 建議判斷二者的差值是否小於一個足夠的數(例如0.0000000000001)web

1.5 字符串正則表達式

js中沒有字符類型變量 「」與’‘一致編程

var s ="abcdefg"

//b = "def"
b = s.slice(3, -1);

1.6 字符串的正則表達式方法中

  1. match()返回匹配的字符串(數組或null),可加/g進行全局匹配多個
  2. search()返回匹配的索引值 單個

1.7 undefined和null

null == undefined //true

null === undefined //false

undefined 是沒設值
null則是設定了爲null值

1.8 運算符

//逗號運算符 取最右返回值
a = (b =5 , c = 7 , d =56) //a =56

a = void(b =5 , c = 7 , d =56) //a = undefined

1.9 typeof和instanceof
typeof 用來得到 實例類型 :

typeof("123"); //string

instanceof 判斷變量是否爲某類的實例

var a = [4,5];
alert(a instanceof Array); //true

1.10 語句
拋出異常

throw new Error("用戶自定義異常"); //通常用於終止程序和返回錯誤提示是個不錯的選擇;

try{

}catch(e){
    alert(e.message); // "用戶自定義異常"
}

for in

//這回輸出瀏覽器的全部屬性,作瀏覽器兼容以前能夠考慮看看.
for( prop_name in navigator){
    document.wrti(prop_name + " : " + navigator[propname]);
}

跳出命名for

outer: 
for(...){
    for(...){
        ...
        continue outer;
    }
}

1.11 函數

js 容許先調用函數 再定義函數

1.11.1 定義匿名函數

var show_name = function(name){
    alert(name);
}

show_name("K"); //K

這樣的好處是什麼,若是直接定義function 它實際上也是建立了一個對象

1.11.2 函數既對象

var hello = function(){...};

hello instanceof Function //true;

hello instanceof Object //true;

alert(heelo) //輸出函數源代碼

1.11.3 調用函數方式的不一樣

  1. 直接調用函數 返回return的值或void
  2. new 函數 獲得的都是對象 - -…….

1.11.4 this關鍵字.

  1. 在函數中使用this.變量 該變量就是函數的實例變量,而非局部變量,不管它在哪裏.
  2. 函數可依附在類中.如沒指定 則依附在winodw對象中
var hello =function(){...}

window.hello();

var p = {
    wark: function(){...}
}
p.wark();

1.11.5 函數中的變量有三種

function Person(){
    //局部變量 只能在函數裏訪問
    var id ;

    //實例屬性 經過對象.訪問
    this.age ;

    //類屬性 經過Patient.name訪問 與static相似
    Person.name ;
}

1.11.6 js是一種動態語言,能隨時給對象增長屬性和方法

function Student(){ };

var student =new Student();
//動態增長name屬性
student.name = 'K';
alert(sutdent.name) //K

Student.age =22 ;
alert(Student.age); //22 類屬性也是能夠動態添加的

1.11.7 調用函數的三種方式

  1. 直接調用
windows.alert();
 //or
 alert();
  1. call()調用
    做用:動態地傳入一個函數引用
var each = function(array,fn){
     for(var index in arrary){
         //null表示以window爲調用者fn函數
         fn.call(null,index,arrary[index]);
     }
 }

 each([4,20,3] , function(index ,ele){
     alert("第 " + index "個元素是 : " + ele);
 });

call()調用函數語法爲:函數引用.call(調用者,參數1,參數2...)
直接調用函數語法爲:調用者.函數(參數1,參數2 ...) = 函數.call(調用者,參數1,參數2 ...)

  1. apply()調用
    apply和()call()基本類似,區別以下:

  2. 經過call()調用函數時,括號必須詳細列出每一個參數

  3. 經過apply()動態地調用函數時,能夠在括號中以arguments來表明全部參數

var myfun = function (a , b){
     alert(a + "  " +b);
 }

 myfun.call(window ,12 ,23); //12 23;

 myfun.apply(window ,[20 , 39]); //20 39

 var example = function (num1 ,num2){
     //直接調用arguments表明調用者(example,this表明example)時的傳入的全部參數
     myfun.apply(this,arguments); 
 }
 example(20,40) //20 40

1.11.8 函數的獨立性
在函數A中能夠定義函數B,可是函數B仍是獨立於函數A

function Person(name){
    this.name = name;

    this.info = function(){
        alert(this.name);
    }
}   

var person =new Person('K');
person.info(); //K
var name = 'K_window';

//因爲window爲調用者 ,this.name訪問的是window.name
p.info.call(window); //K_window

來爽一發貓學狗叫?.

function Dog(name,bark){
    this.name = name;
    this.bark = bark;
    this.info =function(){
        alert(this.name + "  " + this.bark);
    }
}

function Cat(name){
    this.name =name;
}   

var dog = new Dog("K","汪汪!");
var cat = new Cat("K_2");
dog.info.call(cat); //K_2 undefined

1.11.9 參數傳遞方式

和JAVA同樣 都是值傳遞拉~.
基本類型

function change(arg){
    arg =10 ;
    alert(arg);
}
var x = 5;
alert(x); //5
change(x); //10
alert(x); //5

複合類型

function change(person){
    person.age = 10;
    alert(person.age);
    person = null;
}

var person = {age : 5};
alert(person.age); //5
change(person); //10
alert(person.age); // 10
alert(person); // []object Object]

複合類型的傳遞爲值傳遞,原person和參數person指向同一javascript對象,因此當改變age的時候,是在改變javascript對象的age,可是參數person賦值爲null,原person並沒有改變.

1.11.10 空參數

function text(person){
    alert( typeof parson);
}

text(); //undefined

因此對於弱類型,方法重載是無做用的,由於js會把空參數當作undefined傳遞進去;
同名函數,後面出現的會覆蓋前面的,不管參數個數是多少.

1.11.11 對象和關聯數組

javascript和Map有點相似,當key爲對象,value爲函數,該該函數就是對象的方法,當key爲對象,value爲基本類型,則該基本類型爲對象的屬性.(以上爲便於理解,切勿細琢)因此訪問屬性時,能夠obj.propName也能夠obj[propName].
但有時候咱們只能用obj[propName],由於.propName不能把propName當作變量處理,而是把他當成’propName’字符串

function Person(name){
    this.name =name;
    this.info = function(){
        alert(K);
    }
}

var person = new Person("K");
//遍歷person屬性
for (propName in person){
    alert(p[propName]);//alet K info源代碼 假如此處用p.propName則undefined,由於Person無'propName'變量.
}

1.11.12 繼承和prototype

在一個類(函數)中定義一個函數會致使

  1. 性能低下:每次new一個類 都會生成一個函數
  2. 函數中若引用類的局部變量會產生閉包 致使局部變量一直存在
function Person(){
     var local = "局部變量"
     this.info = function(){
         //產生閉包
         alert(local);
     }
 }

 var person = new Person();
 person.ifno(); // 局部變量

解決方案:prototype
增長了prototype屬性的類可視爲繼承了原先的類(僞繼承)

function Person(){...}

var person = new Person();

//person.wark(); 程序報錯wark不存在
Person.prototype.wark = function(){...}

person.wark(); //ok

在prototype以前實例化的類會具備wark方法嗎? 有的,由於prototype這樣並不會產生一個新的類,而是直接動態的往Person里加函數.

判斷某方法是否繼承了該對象

該對象.prototype['某方法'] != undefined;

var test = new 該對象();
test.某方法 !=undefined ;

1.12 建立對象三種方式

//(單身汪別說我不教你)

  1. new關鍵字調用構造器建立對象
function Person(name){...}
 var person_1 = new Person();
 var person_2 = new Person('K'); //js不存在方法重載,空參數undefined頂替
  1. 使用Object直接建立對象
var my_obj = new Object();
 my_ojb.name = 'K';
 my_obj.handsome = function(){...}

 function text(){...}
 my_obj.text = text;//不要添加(),否則會認爲是調用方法
  1. JSON建立對象
ver person = {
     name : 'K',
     school : ['ChangAn','TianJin'],
     girl_friends :[
         {
             name : 'Haski',
             age : 11
         },
         {
             name : 'Samoyed',
             age : '8'
         }
     ]
 }

 alert(person.girl_friends[0].name); //Haski

2 DOM編程

DOM操做其實JQuery已經作得很好了,這裏簡單補充一下原生JS的知識
HTML文檔中只有一個根節點

2.1 訪問HTML元素

  1. id getElementById(‘id’); or getElementsByName(‘name’);
  2. 根據節點關係
Node parentNode: 返回父節點
 Node previousSibling: 返回前一個兄弟節點
 Node nextSibling: 飯後後一個兄弟節點
 Node[] childNodes 返回當前節點的全部節點
 Node[] getElementsByTagName('標籤名稱'): 返回當前節點具備制定標籤的子節點
 //注意ol標籤的子標籤IE8和其餘瀏覽器不同(其餘瀏覽器會把ol下的li和其後面的空白分別當成2個節點,IE8則不會)

2.2 增長HTML函數

  1. document.createElement(「標籤名」);
  2. 複製節點 var node =ul.firstChild.nextSibling.cloneNode(boolean),boolean爲true時,複製全部全部後代節點.false則僅複製當前節點.clone了節點之後還要找一個節點添加進去.

2.3 添加節點

  1. appendChild(Node);添加爲當前節點的最後一個子節點
  2. inserBefore(newNode,refNode);在refNode前添加newNode
  3. replaceChild(newChild,oldChild);替換節點
  4. 增長select選項 new Option(text,value,defaultSelected,selected);

2.4 刪除節點

  1. removeChild(oldNode);

2.5 window對象

  1. 返回上一個頁面: back()
  2. window.href: 當前url
  3. window.width: 屏幕橫向分辨率
  4. window.height: 屏幕縱向分辨率
  5. 遍歷window.screen,包含因此屏幕屬性
for(var propName in window.screen){
     alert(propName+":" +screent[propname]);
 }
  1. cofrim(‘標題’); 能彈出是否確認的提示框
  2. prompt(‘標題’); 能彈出一個文本輸入框輸入.
  3. 定時器:setInterVal,clearInterval()
var timer;
 var cur = new Date().getTime();
 var setTime = function(){
     document.getElementById("tm").innerHTML = new Date().toLocationString();
     if(new Date().getTime- cur > 60 *1000){
         clearInterval(timer);
     }
 }
 //每1S執行一次,執行了60次就暫停
 timer = window.setInterval("setTime()",1000);

2.6 navigator和地理位置

navigator漢堡瀏覽器全部信息,遍歷循環獲取信息

for(var propName in window.navigator){
    alert(propName + ":" + window.navigator[propName]);
}

HTML5新增geolocation屬性
Geolocation提供的方法

  1. getCurrentPosition(onSuccess,onError,options)
  2. int watchCurrentPostion(OnSuccess,onError,options),週期性調用getCurrentPosition,返回的int表明這個」監聽器」的ID,用來clearWatch(watchID)取消監聽
  3. clearWatch(watchID),用於取消watchCurrentPosition

上面的前兩個方法的options參數是一個對象,可包含3個變量

  1. enabelHighAccuracy(是否制定高精度地理位置)
  2. tiemout 設置超時時長
  3. maximumAge,設置緩存時間
    例子:
var geoHandler = function(position){
    var geoMsg = "用戶地址位置是 : <br/>"
    geoMsg += "timestamp屬性爲 :" + position.timestamp + "<br/>"//獲取位置的時間
    var cords =position.coords;
    for(var prop in coords ){
        geoMsg += prop + ": " + coords[prop] +"<br/>"//經緯度,移動速度等
    }
    document.writeln(geoMsg);
}

var errorHandler = function(error){
    var errMsg = {
        1: '用戶拒絕了位置服務'
        2: '沒法獲取地址位置信息'
        3: '獲取地理位置信息超時'
    };
    alert(error[error.code]);
}

navigator.geolocation.getCurrentPosition(geoHandler
, errorHandler
, {
    enableHighAccuracy:true,
    maximuAge:1000
});

2.7 HTML5新增瀏覽器分析

實現該功能主要經過performance對象
其中的(PerformanceTiming)timing屬性包含加載時間相關的屬性
另外(PerformanceNavigation)navigation,主要屬性有
type :

TYPE_NAVIGATE(數值爲0): 超連接/輸入url
TYPE_RELOAD(1): 從新加載方式,diaoyonglocation.reload()等
TYPE_BACK_FORWARD(2): 經過瀏覽器的前進方式
TYPE_RESERVED(255): 未知方式

3 事件處理機制

3.1 常見事件

  1. onabort: 圖片加載終端
  2. onblur: 失去焦點
  3. onchange: 表單域更改
  4. onclick: 點擊
  5. onerror: 圖片加載出錯
  6. onfocus: 得到焦點
  7. onkeydown: 按下鼠標
  8. onkeypress: 當焦點在當前元素上,單擊鍵盤某個鍵觸發
  9. onkeyup: 當焦點在當前元素上,鬆開某個鍵觸發
  10. onload: 某個對象加載完畢,適用於img,oframe,body
  11. onunload: 當某個對象從窗口下卸載觸發,適用於img,oframe,body
  12. onmousedown: 焦點停留在當前元素上,按下鼠標觸發
  13. onmousemore: 當焦點在當前元素上,鼠標移動到該元素
  14. onmouseout: 鼠標移出當前元素觸發
  15. onmouseover: 鼠標移動到該元素觸發
  16. onmouseup: 焦點在當前元素,鬆開鼠標時觸發
  17. onreset: 重置表單時觸發
  18. onsubmit: 表單提交觸發

3.2 事件處理和this

p.info = function(){
    alert(this.name);
}
document.getElementById("bt").onclick = p.info//this指向'bt'控件
document.getElementById("bt").onclick = new function (){ p.info();} //this老是指向p

注意表單設置id爲x和name爲y時候,至關於表單建立了x屬性和y屬性,因此id和name不能是關鍵字submit等

3.3 DOM

建立監聽事件

objectTarget.addEventListener(「eventType」,handler,capture),第一個參數表示綁定的事件,如click、keypress之類的,第二個制定綁定的函數,第3個位boolean,true表示監聽捕獲階段,false表示監聽冒泡階段
objectTarget.removeEventListener(「eventType」,handler,captureFlag): 刪除綁定事件

捕獲狀態階段的綁定事件先執行,事件冒泡狀態階段的綁定事件後執行. 捕獲狀態階段從外往內觸發,事件冒泡狀態階段從內往外觸發.
綁定例子

var got_click = function (event){
    for ( event_one in event){
        alert(event_one + "  : " + event[event_one]);
    }
}   

document.getElementByID("test").addEventListener("cilck",got_click,true);

阻止事件傳播

event.stopPropagation();

取消事件的默認行爲,如跳轉頁面等,但不會阻止事件傳播.

event.preventDefault();

3.3.1轉發事件
DOM提供了dispathEvent方法用於事件轉發,該方法屬於Node
target.dispathEvent(Event event),將event轉發到target上
DOM的dispath()必須轉發人工合成的Event事件
document.createEvent(String type),tpy參數用於指定事件類型,eg:普通事件Events,UI事件UIEvents,鼠標事件:MouseEvents
初始化事件

initEvent(具體參數...)

initUIEvent(具體參數...)

intMouseEvent(具體參數...)

//例子

<input id="bt1">
<input id="bt2">
...

var rd =function(evt){
    alert("事件冒泡階段: " + evt.currentTarget.value +"被點擊了");
    var e =document.createEvent("Evnets");
    e.initEvent("click",true,false);//true表示是否支持冒泡,false表示是否有默認行爲
    document.getElementById("bn2").dispathEvent(e);
}
var go_click = function (evt){
    alert("事件冒泡階段: " + evt.currentTarget.value);
}

document.getElementById("bn1").addEventListener("click",rd,false);

document.getElementById("bn2").addEventListener("click",go_click,false);;

//點解按鈕一結果
alert(事件冒泡階段: 按鈕一被點擊了);
alert(事件冒泡階段:按鈕2);

點擊按鈕1,按鈕執行了前面按鈕一被點擊了提示語句後,將點擊事件轉給了按鈕2,按鈕2執行自身的點擊事件.

4 本地存儲與離線應用

4.1 Web Storage
使用理由之一Cookie的侷限性:

  1. Cookie大小被限制爲4KB

  2. Cookie會包含在每次HTTP請求中

  3. Cookie網絡傳輸未加密(除非整個應用都使用SSL)
    Web Storage分兩種
    Session Storage: 生命週期與用戶Session一致(用戶Session是指:用戶從訪問網址到離開網址/關閉瀏覽器)
    Local Storage: 保存在用戶的磁盤中,失效的方式爲用戶/程序顯示刪除.
    Web Storage的方法有

  4. length: 返回key-value對數

  5. key(index): 返回第index個key

  6. getItem(key): 獲取key對應的value

  7. set(key,value): 設置key-value

  8. removeItem(key): 刪除key-value

  9. clear(): 清除全部key-value

Web Storage包含在window對象中
當value爲對象時,建議用JSON存儲

4.2 構建離線應用

  1. 在html標籤中修改
//代表該頁使用index.manifest文件
 <html manifest="index.manifest">
  1. index.mainfest文件
CACHE MANIFEST
 //第一行必須爲上述字符
 //指定版本號
 #version 1
 //本地緩存資源
 CACHE
 inedx.html
 logo.jpg
 //不緩存的資源
 NETWORK
 *
 //前者表示在線狀態使用的資源,後者表明離線狀態使用的資源
 FALLBACK
 test.js offline.js
  1. Tomcat爲例,天津映射文件
<!--conf的web.xml根元素中增長MIME映射-->
 <mine-mapping>
     <extension>manifest</extension>
     <mine-type>text/cache-mainfest</mime-type>
 </mime-mapping>

啓動應用後,頁面可刷新(即便離線狀態),並使用離線時候的資源

4.2.1 判斷在線狀態

navigator.onLine屬性: true表示在線
online/offline事件: 當在線/離線狀態切換時,body上的online/offine事件會被觸發,沿着document.body、document和window冒泡

window.addEventListener("offline",function(){
    alert("離線狀態")
},true);
if(navigator.onLine){
    alert("在線");
}

4.2.2 applicationCache對象

js可經過applicationCache控制離線緩存.
status屬性:

  • UNCACHE: 主機沒開啓離線功能.
  • IDLE: 空閒狀態.
  • CHECKING: 正在檢查本地manifest和服務器中manifest的差別
  • DOWNLOADING: 正在下載須要的緩存數據
  • OBSOLETE: 緩存已通過期

經常使用方法

  • void update(): 強制檢查服務器的mainfest文件是否有更新
  • void swapCache(): 更新緩存,只能在applicationCache的updateReady事件被觸發時調用.
setInterval(function(){
     applicationCache.update()
 },2000);

 applicationCache.onupdateready = function(){
     if(confirm("已從遠程服務器下載了須要的緩存,是否更新?")){
         applicationCache.swapCache();
         location.reload();
     }
 }

4.2.3 離線應用的事件與監聽

訪問html頁面過程

  • 瀏覽器請求index.html
  • 服務器返回index.html
  • 瀏覽器頁面是否制定manifest屬性,若制定,觸發checking事件,檢查服務器中的manifest文件是否存在,不存在則觸發error事件,不會制定第六部及其後續步驟
  • 瀏覽器解析index.html,請求該頁其餘資源.
  • 服務器返回因此請求
  • 瀏覽器處理mainfest文件,從新請求manifest文件中的因此頁面,包括index.html頁面,前面下載過的資源,扔會再下一遍.
  • 服務器返回因此要求被要求緩存的資源
  • 瀏覽器開始下載須要在本地緩存的資源,開始下載時觸發ondownloading事件,在下載過程當中不斷觸發onprogress事件.以便開發人員瞭解下載進度.
  • 下載完成後觸發oncache事件.緩存完成

當用戶再訪問index.html時,前面1~5徹底相同,接下來檢測mainfest文件是否有改變.

  • 沒有改變觸發onnoupdate事件,結束.
  • mainfest改變,執行第7,8部,當因此文件本地緩存下載完畢後,瀏覽器- 觸發onupdateready事件,而不會觸發oncached事件.

5 使用worker建立多線程

worker中沒法使用DOM、alert等與界面有關的操做.
使用理由:防止js阻塞主線程的js運行
WorkerAPI

  • onmessage: 獲取前臺js提交過來的數據
  • postMessage(data): 前臺js經過postMessage觸發Worker對象的onmessage事件.
  • importScripts(urls),導入多個js,importScripts(「a.js」,「b.js」);
  • sessionStorge/localStorage: 使用Worker操做Storage本地存儲
  • Worker: 建立新的Worker對象啓動嵌套線程
  • XMLHttpRequest: Worker使用XMLHttpRequest發送異步請求.
  • navigator: 與window的location屬性相似
  • location: 與window的location屬性類似
  • self: WorkerGlobalScope對象,表明當前Worker線程自身做用域.調用self的close()結束線程
  • setTimeout()/seInterval()/eval()/inNaN()/parseInt,等與界面無關的js核心函數,包括Array/Data/Math/Number/Object/String等.
  • 寫一段找出輸入start和end之間素數的線程.
    worker.js代碼
onmessage =function(event){
    var data =JSON.parse(event.data);
    var start =data.start;
    var end =data.end;
    var result ="";
    search:
    for (var n =start; n <= end :n++){
        if(n%i==0){
            continue search;
        }
        result +=(n+",");
    }
}
postMessage(result);

網頁代碼

<input name="start" ...>
<input name="end" ...>
<input type=button inclick="cal();" ...>
...
var car =function(){
    var start = parseInt(document.getElementById("start").value);
    var end = parseInt(document.getElementById("end").value);
    //建立線程
    var cal = new Worker("worker.js");
    var data ={
        "start" : srart,
        "end" : end
    };
    //發送數據
    cal.postMessage(JSON.stringify(data));
    cal.onmessage = function (evnet){
        alert(event);
    }
}

並行的兩條Worker不能互相通訊,但Wroker可嵌套.

6 客戶端通訊

WebSocket: 服務器主動推送信息/客戶端實時推送數據到服務器

6.1 跨文檔通訊

window對象新增方法

  1. targetWindow.postMessage(message,targetOrigin): 該方法用戶向targetWindow中狀態的HTML發送信息,targetOrigin表示接收html的域名.
  2. onmessage: 調用方法:windows.onmessage =function(event){…}
    event中的屬性:
  • data: 數據
  • orgin: 發送消息window的源域名
  • lastEventID: 返回發送消失時間的ID
  • source: 返回發送消息的窗口

html想發送要作

  1. 獲取接收消息的window對象
  2. 調用接收消息的window對象的postMessage(any message)方法

html想接收要作

  1. 本html綁定事件window.message = function(event){…};
    跨文檔消息傳遞
//source.html
var targetWin = window.open("接收方url",'_blank','windth=400,height=300');

targetWin.onload =function(){
    targetWin.postMessage("傳輸消息","接收方域名");
}

window.onmessage =function(event){
    //忽略其餘域名發送的消息
    if(event.orgin !="指定域名"){
        return ;
    }
    alert(event.data);
}

//接收頁面.html
window.onmessage = function(event){
    //忽略其餘域名發送的消息
    if(event.orgin !="指定域名"){
        return ;
    }
    alert("接收到消息拉!"+event.data);
    event.source.postMessage("回傳消息",event.origin);
}

結果:
alert(接收到消息拉!傳輸消息);
alert(回傳消息);
注意!必定要判斷髮送方的域名!!!!!必定要判斷髮送方的域名!!!!!必定要判斷髮送方的域名!!!!!

6.2 WebSocket與服務器通訊

之前方案:

  1. 週期發送請求
  2. 頁面使用隱藏窗口與服務器長鏈接

WebSocket方法

  1. send(「數據」);向服務器發送數據.
  2. close();關閉該WebSocket.

WebSocket監聽事件

  1. onopen: 當WebSocket創建網絡鏈接觸發該.
  2. onerror: 網絡鏈接錯誤
  3. onclose: WebScokt被關閉觸發
  4. onmessage: WebSocket接收到服務器數據時

WebSocket屬性

  1. readyState
    1.1 CONNECTING(0): WebSocket正在嘗試鏈接
    1.2 OPEN(1): 已經鏈接
    1.3 CLOSING(2): 正在關閉鏈接
    1.4 CLOSED(3): 已經關閉鏈接

WebSocket與服務器通訊步驟

  1. WebSocket.Constructor(url,[DOMString protocols]);建立WebSocket對象
  2. 發送信息: WebSocket對象的send()
  3. 接收信息: WebSocket對象的onmessage屬性綁定函數;
    實現客戶端多人聊天,JAVA爲例

客戶端代碼:

var web_socket =new WebSocket("ws://域名:端口");

web_socket.onopen =function(){
    web_socket.onmessage =function(event){
        document.getElementById('show').innerHTML += event.data +"</br>"
    }
};

var sendMsg =function(val){
    var inputElement = document.getElementByID('msg');
    webSocket.send(inputElement.value);
    inputElement.value="";
}

...

服務端代碼

<article class="post" >


import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.regex.*;
import java.util.*;
import sun.misc.BASE64Encoder;

public class ChatServer
{
    // 記錄全部的客戶端Soccket
    public static List<Socket> clientSockets
        = new ArrayList<Socket>();
public ChatServer()throws IOException
{
    // 建立ServerSocket,準備接受客戶端鏈接
    ServerSocket ss = new ServerSocket(30000);
    while(true)
    {
        // 接收到客戶端鏈接
        Socket socket = ss.accept();
        // 將客戶端Socket添加到clientSockets集合中
        clientSockets.add(socket);
        // 啓動線程
        new ServerThread(socket).start();
    }
}
public static void main(String[] args)
    throws Exception{
    new ChatServer();
}
}
class ServerThread extends Thread
{
    private Socket socket;
    public ServerThread(Socket socket)
    {
        this.socket = socket;
    }
    public void run()
    {
    try
    {
        // 獲得Socket對應的輸入流
        InputStream in = socket.getInputStream();
        // 獲得Socket對應的輸出流
        OutputStream out = socket.getOutputStream();
        byte[] buff = new byte[1024];
        String req = "";
        // 讀取數據,此時創建與WebSocket的"握手"。
        int count = in.read(buff);
        // 若是讀取的數據長度大於0
        if(count > 0)
        {
            // 將讀取的數據轉化爲字符串
            req = new String(buff , 0 , count);
            System.out.println("握手請求:" + req);
            // 獲取WebSocket的key
            String secKey = getSecWebSocketKey(req);
            System.out.println("secKey = " + secKey);
            String response = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: "
                + "websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "
                    + getSecWebSocketAccept(secKey) + "\r\n\r\n";
            System.out.println("secAccept = " + getSecWebSocketAccept(secKey));
            out.write(response.getBytes());
        }
        int hasRead = 0;
        // 不斷讀取WebSocket發送過來的數據
        while((hasRead = in.read(buff)) > 0){
            System.out.println("接收的字節數:" + hasRead);
            /*
                由於WebSocket發送過來的數據遵循了必定的協議格式,
                其中第3個〜第6個字節是數據掩碼。
                從第7個字節開始纔是真正的有效數據。
                所以程序使用第3個〜第6個字節對後面的數據進行了處理
            */
            for (int i = 0 ; i < hasRead - 6 ; i++ ){
                buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);
            }
            // 得到從瀏覽器發送過來的數據
            String pushMsg = new String(buff
                , 6 , hasRead - 6 , "UTF-8");
            // 遍歷Socket集合,依次向每一個Socket發送數據
            for (Iterator<Socket> it = ChatServer.clientSockets.iterator()
                ; it.hasNext() ;)
            {
                try
                {
                    Socket s = it.next();
                    // 發送數據時,第一個字節必須與讀到的第一個字節相同
                    byte[] pushHead = new byte[2];
                    pushHead[0] = buff[0];
                    // 發送數據時,第二個字節記錄發送數據的長度
                    pushHead[1] = (byte) pushMsg.getBytes("UTF-8").length;
                    // 發送前兩個字節
                    s.getOutputStream().write(pushHead);
                    // 發送有效數據
                    s.getOutputStream().write(pushMsg.getBytes("UTF-8"));
                }
                catch (SocketException ex)
                {
                    // 若是捕捉到異常,代表該Socket已經關閉
                    // 將該Socket從Socket集合中刪除
                    it.remove();
                }
            }
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        try
        {
            // 關閉Socket
            socket.close();
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }
}
// 獲取WebSocket請求的SecKey
private String getSecWebSocketKey(String req)
{
    //構建正則表達式,獲取Sec-WebSocket-Key: 後面的內容
    Pattern p = Pattern.compile("^(Sec-WebSocket-Key:).+",
            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    Matcher m = p.matcher(req);
    if (m.find())
    {
        // 提取Sec-WebSocket-Key
        String foundstring = m.group();
        return foundstring.split(":")[1].trim();
    }
    else
    {
        return null;
    }
}
// 根據WebSocket請求的SecKey計算SecAccept
private String getSecWebSocketAccept(String key)
    throws Exception
{
    String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    key += guid;
    MessageDigest md = MessageDigest.getInstance("SHA-1");
    md.update(key.getBytes("ISO-8859-1") , 0 , key.length());
    byte[] sha1Hash = md.digest();
    BASE64Encoder encoder = new BASE64Encoder();
    return encoder.encode(sha1Hash);
}
}

[javascript](http://mzkmzk.github.io/blog/categories/javascript/)

</article>

<a class="addthis_button_facebook_like" fb:like:layout="button_count" ></a><a class="addthis_button_tweet" ></a>

【我有一個前端學習交流QQ羣:328058344 若是你在學習前端的過程當中遇到什麼問題,歡迎來個人QQ羣提問,羣裏天天還會更新一些學習資源。禁止閒聊,非喜勿進。】

相關文章
相關標籤/搜索