1. jQuery的選擇器
基本選擇器、層次選擇器、過濾選擇器和表單選擇器
基本:id選擇器、class選擇器、標籤選擇器、複合選擇器和“*”選擇器
層次:$("div p") 選取<div>裏所有的<p>元素
$("div>p") 選取<div>裏所有標籤爲<p>子元素
$(".class_1+div")選取屬性class爲“class_1”的下一個<div>同輩元素。
過濾:$("div:first") 選取所有<div>元素中第一個<div>元素。
$(":focus") 選取當前獲取焦點的元素。
$("div:has(p)") 選取含有<p>元素的<div>元素。
$("div[title=text]") 選取屬性title爲“text”的<div>元素。
表單:$(":input") 選取所有<input>、<textarea>、<select>和<button>元素。$(":radio") 選取所有的單選框。
$(":checkbox") 選取所有的複選框。
2. 已知ID的Input輸入框,希望獲取這個輸入框的輸入值
document.getElementById(“ID”).value
3. 希望獲取到頁面中所有的checkbox怎麼做?
var domList = document.getElementsByTagName(‘input’)
var checkBoxList = [];
var len = domList.length; //緩存到局部變量
while (len--) { //使用while的效率會比for循環更高
if (domList[len].type == ‘checkbox’) {
checkBoxList.push(domList[len]);
} }
4. 設置一個已知ID的DIV的html內容爲xxxx,字體顏色設置爲黑色
var dom = document.getElementById(“ID”);
dom.innerHTML = “xxxx”
dom.style.color= “#000”
5. 請你談談Cookie的弊端?
1.`Cookie`數量和長度的限制。每個domain最多隻能有20條cookie,每個cookie長度不能超過4KB,否則會被截掉。
2.安全性問題。如果cookie被人攔截了,就可以取得所有的session信息。即使加密也與事無補,因爲攔截者並不需要知道cookie的意義,他只要原樣轉發cookie就可以達到目的了。
3.有些狀態不可能保存在客戶端。例如,爲了防止重複提交表單,我們需要在服務器端保存一個計數器。如果我們把這個計數器保存在客戶端,那麼它起不到任何作用。
6. 正則表達式驗證郵箱,電話號碼
驗證郵箱:re=/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/
驗證電話號碼:區號+號碼,區號以0開頭,3位或4位;號碼由7位或8位數字組成;區號與號碼之間可以無連接符,也可以“-”連接: re =/^0\d{2,3}-?\d{7,8}$/;
7.當一個DOM節點被點擊時候,我們希望能夠執行一個函數,應該怎麼做?
(1)直接在DOM裏綁定事件:<divοnclick=”test()”></div>
(2)在JS裏通過onclick綁定:xxx.onclick = test
(3)通過事件添加進行綁定:btn.addEventListener(“click”,function(){
alert(his.id);
},false); //最後的參數是true,是在捕獲階段調用,false則是在冒泡階段調用
IE事件處理程序: btn.attachEvent(“onclick”,function(){
alert(“clicked”); } );
8、 JavaScript的事件流模型都有什麼?
“事件冒泡”:事件開始由最具體的元素接受,然後逐級向上傳播
“事件捕捉”:事件由最不具體的節點先接收,然後逐級向下,一直到最具體的
“DOM事件流”:三個階段:事件捕捉,目標階段,事件冒泡
9.跨瀏覽器的事件綁定和解綁程序
addHandler:function(element,type,handler){
if(element.addEventListener){ //removeEventListener
element.addEventListener(type,handler,false);
}elseif(element.attachEvent){ //detachEvent
element.attachEvent(“on”+type,handler);
}else{
element[“on”+type]=handler; //element[“on”+type]=null;
}
}
10.IE和DOM事件流的區別
執行順序不一樣,參數不一樣,事件加不加on,4.this指向問題
11.jquery的綁定事件有幾種方式,請舉例說明其優缺點。
jQuery中提供了四種事件監聽方式,分別是bind、live、delegate、on,對應的解除監聽的函數分別是unbind、die、undelegate、off。
(1).bind()是最直接的綁定方法,會綁定事件類型和處理函數到DOM element上
$( "#members li a").bind("click", function( e ) {} );
會綁定到所有的a元素上,不會綁定到它執行完後動態添加的那些元素上
(2)live() 用到了事件委託來處理事件綁定,會綁定事件到所選擇的元素的根元素上,也就是document元素上。所有冒泡上來的事件都可以用這個handler來處理。
$( "#members li a").live("click", function( e ) {} );
不需要再每個元素上綁定事件,只在document上綁定一次。動態添加的元素依然可以觸發早先綁定的事件。停止冒泡是沒有用的,當DOM樹很深時,會有效率的問題。
(3) delegate() 指定元素的綁定位置
$( "#members" ).delegate("lia", "click", function( e ) {} );
可以選擇事件綁定到哪個元素上,可以用在動態添加的元素上
(4)on() 其實.bind(), .live(), .delegate()都是通過.on()來實現的,.unbind(), .die(), .undelegate(),也是一樣的都是通過.off()來實現的
$("#members li a" ).on( "click",function( e ) {} );
12.get和post的區別是什麼?
(1) get向服務器查詢某些信息,post向服務器發送應保存的數據
(2) get把數據添加到所指的URL中,之和字段一一對應,URL中可以看到。post放在HTML中的HEADER內傳送到URL中,用戶看不到
(3) get可以傳送的數據量小,post可以傳送的數據量大
(4) get的安全性低,post的安全性高
13.瀏覽器渲染
DOM:瀏覽器將HTML解析成樹形結構,即DOM.
CSSDOM:將css解析成樹形結構,即CSSDOM。
Render Tree:DOM 和 CSSDOM合併後生成Render Tree。
Layout:計算Render Tree每個節點的具體位置。
Painting:將layout後的節點內容呈現在屏幕上;
遇到外部的css文件和圖片,瀏覽器會另外發出一個請求,來獲取css文件和相應的圖片,這個請求是異步的,並不會影響html文件。如果遇到JavaScript文件,html文件會掛起渲染的線程,等待javascript加載完畢後,html文件再繼續渲染。
Repaint——(重繪)是在一個元素的外觀被改變,但沒有改變佈局的情況下發生。如果只是改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性,將只會引起瀏覽器repaint。
Reflow——(迴流):瀏覽器發現某個部分發生了點變化影響了佈局,需要倒回去重新渲染,這個回退的過程就叫回流。意味着元件的幾何尺寸變了,我們需要重新驗證並計算 Render Tree。
14.javascript的本地對象,內置對象和宿主對象
本地對象爲array object regexp等可以new實例化,ECMA定義好的對象,是引用類型。
內置對象是本地對象的一種,只有global 和Math
宿主爲瀏覽器自帶的document,window 等,所有的BOM 和DOM對象。
15.JavaScript是一門什麼樣的語言,它有哪些特點?
JavaScript 是一種腳本語言,不需要編譯就可以由解釋器直接運行;變量鬆散定義,屬於弱類型語言;面向對象的。
16.柵格化
把寬度爲“W”的頁面分割成n個網格單元“a”,每個單元與單元之間的間隙設爲“i”,此時我們把“a+i”定義“A”。他們之間的關係如下:
W =(a×n)+(n-1)*i 由於a+i=A,
可得:(A×n)– i = W
17.閉包的原理和應用
閉包就是能夠讀取其他函數內部變量的函數。
它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。
1)由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,
在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。
閉包的用途:(1)匿名自執行函數
如果變量不加上var關鍵字,則會默認添加到全局對象屬性上去,這樣可能造成別的函數誤用這些變量,造成全局對象過於龐大,影響訪問速度。此外,也會有的函數只需執行一次,內部的變量無需維護
vardatamodel={
table:[],
tree:{}
};
(function(dm){
})(datamodel);
創建了一個匿名的函數,並立即執行它,由於外部無法引用它內部的變量,因此在執行完後很快就會被釋放,關鍵是這種機制不會污染全局對象。
(2)有一個很耗時的函數對象,每次調用都會花費很長時間,就需要把計算的值存儲起來,當調用的時候,首先在緩存中查找,找不到,則進行計算。(閉包不會釋放外部的引用,從而使函數內部的值可以保留)
(3)實現封裝,person之外的地方無法訪問其內部變量的值,通過閉包的形式訪問
varperson=function(){
var name=”default”;
return{
getName:function(){
return name;
},
setName:function (newName){
name=newName;
}
}
}();
print(person.name); // 直接訪問,結果爲undefined
print(person.getName()); //default
person.setName(“MIKE”);
print(person.getName()); //MIKE
(4)實現面向對象中的對象
functionPerson(){
var name ="default";
return {
getName :function(){
return name;
},
setName :function(newName){
name = newName;
}
}
};
varjohn = Person();
print(john.getName()); //default
john.setName("john");
print(john.getName()); //john
varjack = Person();
print(jack.getName()); //default
jack.setName("jack");
print(jack.getName()); //jack
//john和jack都可以稱爲是Person這個類的實例,因爲這兩個實例對name這個成員的訪問是獨立的,互不影響的。
functionlazy_sum(arr) {
var sum = function () {
returnarr.reduce(function (x, y) {
return x + y; //在函數lazy_sum中又定義了函數sum,
}); //並且,內部函數sum可以引用外部函數lazy_sum的參數和局部變量,
} //當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中
return sum;
}
varf = lazy_sum([1, 2, 3, 4, 5]); // 調用lazy_sum返回的不是求和結果,而是求和函數
f(); //15 調用函數f時,纔是真正的計算結果
函數在其定義內部引用了局部變量arr,所以,當一個函數返回了一個函數後,其內部的局部變量還被新函數引用
functioncount() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
varresults = count();
varf1 = results[0]; //16
varf2 = results[1]; //16
varf3 = results[2]; //16
返回的函數引用了變量i,但它並非立刻執行。等到3個函數都返回時,它們所引用的變量i已經變成了4,因此最終結果爲16。
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
如果一定要引用循環變量怎麼辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:
functioncount() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
varresults = count();
varf1 = results[0];
varf2 = results[1];
varf3 = results[2];
f1();// 1
f2();// 4
f3();// 9
閉包的可以封裝一個私有變量:
functioncreate_counter(initial) {
var x = initial || 0;
return {
inc: function() {
x += 1;
return x;
}
}
}
varc1 = create_counter();
c1.inc();// 1
c1.inc();// 2
c1.inc();// 3
varc2 = create_counter(10);
c2.inc();// 11
c2.inc();// 12
c2.inc();// 13
在返回的對象中,實現了一個閉包,該閉包攜帶了局部變量x,並且,從外部代碼根本無法訪問到變量x。
18.cookie,localStroage,sessionStroage
共同點:都是保存在瀏覽器端,且同源的。
區別:cookie數據始終在同源的http請求中攜帶(即使不需要),即cookie在瀏覽器和服務器間來回傳遞。而sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存。cookie數據還有路徑(path)的概念,可以限制cookie只屬於某個路徑下。存儲大小限制也不同,cookie數據不能超過4k,同時因爲每次http請求都會攜帶cookie,所以cookie只適合保存很小的數據,如會話標識。sessionStorage和localStorage 雖然也有存儲大小的限制,但比cookie大得多,可以達到5M或更大。數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉前有效,自然也就不可能持久保持;localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;
cookie只在設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關閉。
作用域不同:sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個頁面;localStorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
sessionStorage用於本地存儲一個會話(session)中的數據,這些數據只有在同一個會話中的頁面才能訪問並且當會話結束後數據也隨之銷燬。
因此sessionStorage不是一種持久化的本地存儲,僅僅是會話級別的存儲。而localStorage用於持久化的本地存儲,除非主動刪除數據,否則數據是永遠不會過期的。
19.prototype與__proto__
prototype是函數的內置屬性,__proto__是對象的內置屬性。
每一個函數都有一個prototype(原型)屬性, 可以返回對象的原型對象的引用。prototype是通過調用構造函數來創建的那個對象的原型(屬性)對象。
函數(對象)有prototype屬性->對應了一個原型對象->每個原型對象都有一個constructor屬性->包含一個指向prototype屬性所在函數的指針
prototype:每一個函數對象都有一個顯示的prototype屬性,它代表了對象的原型
__proto__:內部原型(IE6/7/8/9不支持),每個對象都有一個名爲__proto__的內部隱藏屬性,指向於它所對應的原型對象,
20.簡述同步和異步的區別
同步是阻塞模式,異步是非阻塞模式。
同步就是指一個進程在執行某個請求的時候,若該請求需要一段時間才能返回信息,那麼這個進程將會一直等待下去,直到收到返回信息才繼續執行下去;
異步是指進程不需要一直等下去,而是繼續執行下面的操作,不管其他進程的狀態。當有消息返回時系統會通知進程進行處理,這樣可以提高執行的效率
21.怎樣添加、移除、移動、複製、創建和查找節點?
1)創建新節點
createDocumentFragment() //創建一個DOM片段
createElement() //創建一個具體的元素
createTextNode() //創建一個文本節點
2)添加、移除、替換、插入
appendChild() //添加 removeChild() //移除
replaceChild() //替換 insertBefore()//插入
3)查找
getElementsByTagName() //通過標籤名稱
getElementsByName() //通過元素的Name屬性的值
getElementById()//通過元素Id,唯一性
22.實現一個函數clone,可以對JavaScript中的5種主要的數據類型(包括Number、String、Object、Array、Boolean)進行值複製。
Object.prototype.clone = function(){
var o =this.constructor === Array ? [] : {};
for(var e in this){
o[e] = typeofthis[e] ==="object" ? this[e].clone() : this[e];
}
return o;
}
functionclone(Obj){
var buf;
if(Obj instanceOf Array){
buf=[];
var i=Obj.length;
while(i--){
buf[i]=clone(obj[i]);
}
return buf;
}else if(ObjinstanceOf Object){
buf={};
for(var k in Obj){ buf[k]=clone(obj[k]);}
return buf;
} else { returnObj;//普通變量 }
}
23.在Javascript中什麼是僞數組?如何將僞數組轉化爲標準數組?
僞數組(類數組):無法直接調用數組方法或期望length屬性有什麼特殊的行爲,但仍可以對真正數組遍歷方法來遍歷它們。典型的是函數的argument參數,還有像調用getElementsByTagName,document.childNodes之類的,它們都返回NodeList對象都屬於僞數組。可以使用Array.prototype.slice.call(fakeArray)將數組轉化爲真正的Array對象。
24.Javascript中callee和caller的作用?
caller是返回一個對函數的引用,該函數調用了當前函數;
callee是返回正在被執行的function函數,也就是所指定的function對象的正文。
25.統計字符串中字母個數或統計最多字母數。
var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
if(!json[str.charAt(i)]){
json[str.charAt(i)]= 1;
}else{
json[str.charAt(i)]++;
}
};
var iMax = 0;
var iIndex = '';
for(var i in json){
if(json[i]>iMax){
iMax =json[i];
iIndex= i;
}
}
alert('出現次數最多的是:'+iIndex+'出現'+iMax+'次');
26.字符串反轉,如將 '12345678'變成 '87654321'
/思路:先將字符串轉換爲數組 split(),利用數組的反序函數reverse()顛倒數組,再利用 jion() 轉換爲字符串
var str = '12345678';
str =str.split('').reverse().join('');
27.將數字 12345678轉化成 RMB形式如: 12,345,678
//思路:先將數字轉爲字符, str= str + '' ;
//利用反轉函數,每三位字符加一個 ','最後一位不加; re()是自定義的反轉函數,最後再反轉回去!
for(var i = 1; i <= re(str).length; i++){
tmp += re(str)[i -1];
if(i % 3 == 0&& i !=re(str).length){
tmp += ',';
}
}
28.BOM對象有哪些,列舉window對象?
1、window對象 ,是JS的最頂層對象,其他的BOM對象都是window對象的屬性;
2、document對象,文檔對象;
3、location對象,瀏覽器當前URL信息;
4、navigator對象,瀏覽器本身信息;
5、screen對象,客戶端屏幕信息;
6、history對象,瀏覽器訪問歷史信息;
29.一次完整的HTTP事務是怎樣的一個過程?
基本流程:
a. 域名解析
b. 發起TCP的3次握手
c. 建立TCP連接後發起http請求
d. 服務器端響應http請求,瀏覽器得到html代碼
e. 瀏覽器解析html代碼,並請求html代碼中的資源
f. 瀏覽器對頁面進行渲染呈現給用戶
30.js繼承的幾種方式
(1)原型鏈:利用繼承讓一個引用類型繼承另一個引用類型的屬性和方法,本質就是重寫原型對象
每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象內部的指針
SubType.prototype=newSuperType();//創建父類的實例,並將實例賦值給子類的原型
(2)借用構造函數,子類型的構造函數內部調用超類型的構造函數
functionSubType(){
SuperType.call(this); //繼承SuperType
}
(3)組合繼承,使用原型鏈實現對原型屬性和方法的繼承,借用構造函數實現對實例屬性的繼承。調用兩次超類型的構造函數,一次是在創建子類型的時候,另一次是在子類型構造函數的內部
functionSuperType(name){ this.name=name; }
SuperType.prototype.sayName=function(){
alert(this.name); }
functionSubType(name,age){
SuperType.call(this,name);
this.age=age;
}
(4)原型式繼承:基於已有的對象創建新的對象
function object(o){
functionF(){} //先定義臨時性構造函數
F.prototype=o; //傳入的對象作爲這個構造函數的原型
return new F(); //返回臨時類型的實例
}
var person={
name:”MIKE”;
friends:[“A”,”B”,”C”];
};
varanotherPerson=object(person);
anotherPerson.name=”greg”;
anotherPerson.friedns.push(“D”);
alert(person.friends); //A B C D
person.friends不僅是person所有,而且會被anotherperson共享,相當於創建了person對象的兩個副本。
ES5規範了Object.create()原型式繼承,這個方法接收兩個參數,一是用作新對象原型的對象和一個爲新對象定義額外屬性的對象。
(5)寄生式繼承: 創建一個僅用於封裝繼承過程的函數,該函數內部以某種方式增強對象
functioncreateAnother(original){ //傳入的參數將要作爲新對象基礎的對象
var clone=object(original);
clone.sayHi=function(){ //爲clone對象添加一個新方法
alert(“hi”);
}
return clone;
}
varperson={
name:”MIKE”;
friends:[“A”,”B”,”C”]
};
varanotherPerson=createAnother(person);
anotherPerson.sayHi(); //新對象具有person的所有屬性和方法,還有自己的sayHi
(6)寄生組合式繼承
functioninheritPrototype(subtype,superType){
var prototype=object(superType.prototype); //創建對象,創建超類型的一個副本
prototype.constructor=SubType; //增強對象,重寫失去的constructor屬性
SubType.prototype=prototype; //新創建對象的副本賦值給子類型的原型
}
inheritPrototype(SubType,SuperType);
31.寫一個function,清除字符串前後的空格。
if (!String.prototype.trim) {
String.prototype.trim= function() {
returnthis.replace(/^\s+/,"").replace(/\s+$/,"");
}
}
32.事件類型
UI事件:load,unload,select,resize,scroll
焦點事件:blur, focus
鼠標事件:click, dbclick, mousedown, mouseleave
滾輪事件:mousewheel
文本事件:textinput
鍵盤事件: keydown, keypress, keyup
33.js的淺拷貝和深拷貝
淺複製:淺複製是複製引用,複製後的引用都是指向同一個對象的實例,彼此之間的操作會互相影響
深複製:深複製不是簡單的複製引用,而是在堆中重新分配內存,並且把源對象實例的所有屬性都進行新建複製,以保證深複製的對象的引用圖不包含任何原有對象或對象圖上的任何對象,複製後的對象與原來的對象是完全隔離的
(1)數組的淺拷貝和深拷貝
1)vararr=["One","Two","Three"]; var arrto = arr;
如果只是將數組的名字賦值給其他變量,改變其中一個,也會改變另一個。這種方式就是淺拷貝。
2)js的slice方法:array對象的slice函數,返回一個數組的一段。(仍爲數組)
var arr=["One","Two","Three"];
var arrtoo = arr.slice(0); //從0開始,默認的結束時到最後
arrtoo[1] = "set Map";
document.writeln(arr + "<br/>");//Export:數組的原始值:One,Two,Three
document.writeln(arrtoo + "<br/>");//Export:數組的新值:One,set Map,Three
3)concat()方法用於連接兩個或多個數組。該方法不會改變現有的數組,而僅僅會返回被連接數組的一個副本。
var arr=["One","Two","Three"];
var arrtooo = arr.concat();
arrtooo[1] = "set Map To";
document.writeln(arr); // One,Two,Three
document.writeln(arrtooo); // One,set Map To,Three
4)functiondeepCopy(arry1,arry2){
for(var i =0,l=arry1.length;i<l;i++){
arry2[i]=arry1[i];
} }
(2)對象的淺拷貝和深拷貝
1)JSON對象parse方法可以將JSON字符串反序列化成JS對象,stringify方法可以將JS對象序列化成JSON字符串,藉助這兩個方法,也可以實現對象的深複製。
varsource = {
name:"source",
child:{
name:"child"
}
}
vartarget = JSON.parse(JSON.stringify(source));
target.name= "target"; //改變target的name屬性
console.log(source.name); //source
console.log(target.name); //target
target.child.name= "target child"; //改變target的child
console.log(source.child.name); //child
console.log(target.child.name); //target child
2)把對象的屬性遍歷一遍,賦給一個新的對象。
vardeepCopy= function(source) {
var result={};
for (var key insource) {
result[key] = typeofsource[key]===’object’?deepCopy(source[key]): source[key];
} return result; }
34.Ajax工作原理,同步和異步
Ajax就是通過JavaScript創建XMLHttpRequest對象,再由JavaScript調用XMLHttpRequest對象的方法完成異步通信;然後,再由JavaScript通過DOM的屬性和方法,完成頁面的不完全刷新。
AJAX全稱爲“Asynchronous JavaScript and XML”(異步JavaScript和XML)
由事件觸發,創建一個XMLHttpRequest對象,把HTTP方法(Get/Post)和目標URL以及請求返回後的回調函數設置到XMLHttpRequest對象,通過XMLHttpRequest向服務器發送請求,請求發送後繼續響應用戶的界面交互,只有等到請求真正從服務器返回的時候才調用callback()函數,對響應數據進行處理。
同步:腳本會停留並等待服務器發送回覆然後再繼續
異步:腳本允許頁面繼續其進程並處理可能的回覆
(1)XMLHttpRequest簡介
XMLHttpRequest,是我們得以實現異步通訊的的根本。
用XMLHttpRequest進行異步通訊,首先必須用JavaScript創建一個XMLHttpRequest對象實例。創建XMLHttpRequest對象實例的代碼清單如下所示:
var xmlHttp;
functioncreateXMLHttpRequest(){
if(window.ActiveXObject){
xmlHttp =newActiveXObject("Microsoft.XMLHTTP");
} elseif(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}
}
(2)利用XMLHttpRequest對象發送簡單請求
1) 創建XMLHttpRequest對象實例。
2) 設定XMLHttpRequest對象的回調函數,利用onreadystatechange屬性。
3) 設定請求屬性:設定HTTP方法(GET或POST);設定目標URL。利用open()方法。
4) 將請求發送給服務器。利用send()方法。
(3)利用DOM對服務器響應進行處理
前面已經設置了回調函數,回調函數正是用來處理服務器響應信息的。在服務器對我們的請求信息作出響應後,我們就得實現頁面的無縫更新(就是無閃爍的更新信息)。通過DOM,我們可以把頁面上的數據和結構抽象成一個樹型表示,進而可以通過DOM中定義的屬性和方法對文檔進行操作,如遍歷、編輯等。這樣,服務器相應信息就可以通過DOM的方法和屬性,動態的更新到頁面的相應節點。從而使用戶感覺不到刷新過程的存在,提高了交互性。
(4)實例
實例包含兩個文件:Request.htm和Response.xml。通過Request.htm向服務器發送請求,而Response.xml模仿了從服務器返回的響應。兩個文件清單如下:
<!--Request.htm----------------------------------------------------------->
<html>
<head>
<title>Ajax應用實例</title>
<scripttype="text/javaScript">
var xmlHttp;
varrequestType="";
functioncreateXMLHttpRequest(){
if(window.ActiveXObject){
xmlHttp =newActiveXObject("Microsoft.XMLHTTP");
}
else if(window.XMLHttpRequest){
xmlHttp = newXMLHttpRequest();
}
}
functionstartRequest(theRequestType){
requestType= theRequestType;
createXMLHttpRequest();
xmlHttp.onreadystatechange=handleStateChange;
xmlHttp.open("GET","Response.xml",true);
xmlHttp.send(null);
}
functionmyCallback(){
if(xmlHttp.readyState==4){
if(xmlHttp.status==200){
if(requestType=="all")
listAll();
elseif(requestType=="north")
listNorth();
}
}
}
(1).AJAX的優點
<1>.無刷新更新數據。
AJAX最大優點就是能在不刷新整個頁面的前提下與服務器通信維護數據。這使得Web應用程序更爲迅捷地響應用戶交互,並避免了在網絡上發送那些沒有改變的信息,減少用戶等待時間,帶來非常好的用戶體驗。
<2>.異步與服務器通信。
AJAX使用異步方式與服務器通信,不需要打斷用戶的操作,具有更加迅速的響應能力。優化了Browser和Server之間的溝通,減少不必要的數據傳輸、時間及降低網絡上數據流量。
<3>.前端和後端負載平衡。
AJAX可以把以前一些服務器負擔的工作轉嫁到客戶端,利用客戶端閒置的能力來處理,減輕服務器和帶寬的負擔,節約空間和寬帶租用成本。並且減輕服務器的負擔,AJAX的原則是“按需取數據”,可以最大程度的減少冗餘請求和響應對服務器造成的負擔,提升站點性能。
<4>.基於標準被廣泛支持。
<5>.界面與應用分離。
Ajax使WEB中的界面與應用分離(也可以說是數據與呈現分離),有利於分工合作、減少非技術人員對頁面的修改造成的WEB應用程序錯誤、提高效率、也更加適用於現在的發佈系統。
(2).AJAX的缺點
<1>.AJAX幹掉了Back和History功能,即對瀏覽器機制的破壞。
在動態更新頁面的情況下,用戶無法回到前一個頁面狀態,因爲瀏覽器僅能記憶歷史記錄中的靜態頁面。一個被完整讀入的頁面與一個已經被動態修改過的頁面之間的差別非常微妙;用戶通常會希望單擊後退按鈕能夠取消他們的前一次操作,但是在Ajax應用程序中,這將無法實現。
<2>.AJAX的安全問題。
Ajax技術就如同對企業數據建立了一個直接通道。這使得開發者在不經意間會暴露比以前更多的數據和服務器邏輯。Ajax的邏輯可以對客戶端的安全掃描技術隱藏起來,允許黑客從遠端服務器上建立新的攻擊。還有Ajax也難以避免一些已知的安全弱點,諸如跨站點腳步攻擊、SQL注入攻擊和基於Credentials的安全漏洞等等。
<3>.對搜索引擎支持較弱。
對搜索引擎的支持比較弱。如果使用不當,AJAX會增大網絡數據的流量,從而降低整個系統的性能。
<4>.破壞程序的異常處理機制。
<5>.違背URL和資源定位的初衷。
<6>.AJAX不能很好支持移動設備。
<7>.客戶端過肥,太多客戶端代碼造成開發上的成本。編寫複雜、容易出錯;冗餘代碼比較多,破壞了Web的原有標準。
5.AJAX注意點及適用和不適用場景
(1).注意點
Ajax開發時,網絡延遲——即用戶發出請求到服務器發出響應之間的間隔——需要慎重考慮。不給予用戶明確的迴應,沒有恰當的預讀數據,或者對XMLHttpRequest的不恰當處理,都會使用戶感到延遲,這是用戶不希望看到的,也是他們無法理解的。通常的解決方案是,使用一個可視化的組件來告訴用戶系統正在進行後臺操作並且正在讀取數據和內容。
(2).Ajax適用場景
<1>.表單驅動的交互<2>.深層次的樹的導航<3>.快速的用戶與用戶間的交流響應
<4>.類似投票、yes/no等無關痛癢的場景<5>.對數據進行過濾和操縱相關數據的場景
<6>.普通的文本輸入提示和自動完成的場景
(3).Ajax不適用場景
<1>.部分簡單的表單<2>.搜索<3>.基本的導航<4>.替換大量的文本<5>.對呈現的操縱
35.Javascript異步編程的4種方法
當線程中沒有執行任何同步代碼的前提下才會執行異步代碼,setTimeout是異步代碼,所以setTimeout只能等js空閒纔會執行。
一、 回調函數
function f1(callback){ //假設f1是一個很耗時的函數,把f2寫成f1的回調函數
setTimeout(function () {
// f1的任務代碼
callback();
}, 1000); }
執行代碼就變成下面這樣: f1(f2);
採用這種方式,我們把同步操作變成了異步操作,f1不會堵塞程序運行,相當於先執行程序的主要邏輯,將耗時的操作推遲執行。回調函數的優點是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務只能指定一個回調函數。
二、 事件監聽
採用事件驅動模式。任務的執行不取決於代碼的順序,而取決於某個事件是否發生。
首先,爲f1綁定一個事件: f1.on('done',f2);
當f1發生done事件,就執行f2。然後,對f1進行改寫:
function f1(){
setTimeout(function () {
// f1的任務代碼
f1.trigger('done');
}, 1000); }
f1.trigger('done')表示,執行完成後,立即觸發done事件,從而開始執行f2。
這種方法的優點是比較容易理解,可以綁定多個事件,每個事件可以指定多個回調函數,而且可以"去耦合"(Decoupling),有利於實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。
三、 發佈/訂閱
我們假定,存在一個"信號中心",某個任務執行完成,就向信號中心"發佈"(publish)一個信號,其他任務可以向信號中心"訂閱"(subscribe)這個信號,從而知道什麼時候自己可以開始執行。這就叫做"發佈/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。
首先,f2向"信號中心"jQuery訂閱"done"信號: jQuery.subscribe("done",f2);
然後,f1進行如下改寫:
function f1(){
setTimeout(function () {
// f1的任務代碼
jQuery.publish("done");
}, 1000); }
jQuery.publish("done")的意思是,f1執行完成後,"信號中心"jQuery發佈"done"信號,從而引發f2的執行。
此外,f2完成執行後,也可以取消訂閱(unsubscribe)。
jQuery.unsubscribe("done", f2);
這種方法的性質與"事件監聽"類似,但是明顯優於後者。因爲我們可以通過查看"消息中心",瞭解存在多少信號、每個信號有多少訂閱者,從而監控程序的運行。
四、Promises對象
每一個異步任務返回一個Promise對象,該對象有一個then方法,允許指定回調函數。比如,f1的回調函數f2,可以寫成: f1().then(f2);
f1要進行如下改寫(這裏使用的是jQuery的實現):
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任務代碼
dfd.resolve();
}, 500);
return dfd.promise;
}
這樣寫的優點在於,回調函數變成了鏈式寫法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以實現許多強大的功能。
比如,指定多個回調函數: f1().then(f2).then(f3);
再比如,指定發生錯誤時的回調函數: f1().then(f2).fail(f3);
而且,它還有一個前面三種方法都沒有的好處:如果一個任務已經完成,再添加回調函數,該回調函數會立即執行。所以,你不用擔心是否錯過了某個事件或信號。這種方法的缺點就是編寫和理解,都相對比較難。
36.圖片輪播
<script>
window.οnlοad=function() {
var list =document.getElementById("list");
var liList =document.getElementsByTagName("li"); //所有圖片
var len = liList.length; //個數
var liwidth =liList[0].clientWidth; //每張圖片的寬度
var totalWidth = (len - 1) * liwidth* (-1); //圖片總寬度
var varyLeft = list.offsetLeft;//ul初始left值
var speed = 3; //每次移動距離
function move() {
if (varyLeft < totalWidth){//左移完最後一張後,瞬間切換到第二張a,第二張a和最後一張a'相同
list.style.left ="-300px";
varyLeft = -300;
}
varyLeft -= speed;//每次移動
list.style.left = varyLeft +"px";
}
var timer = setInterval(move,30);//每個40毫秒左移一次
}
</script>