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>