1:高级函数
?
?
1.1 作用域安全的构造函数
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=Person("Nicholas",29,"Software Engineer"); alert(window.name); //"Nicholas" alert(window.age); //29 alert(window.job); //Software Engineer /** *因为构造函数是作为普通函数调用的,忽略了new操作符。这个问题是由this对象的晚绑定造成的,在这里this被解析成了window对象。 * window 的name属性是用于识别链接目标和框架的,所以这里对该属性的偶然覆盖可能会导致该页面上出现其他错误。 * 这个问题的解决方法就是创建一个作用域安全的构造函数。 */ function Person(name,age,job){ if(this instanceof Person){ this.name=name; this.age=age; this.job=job; }else { return new Person(name,age,job); } } var person1=Person("Nicholas",29,"Software Engineer"); alert(window.name); //"" alert(person1.name); //"Nicholas" var person2=Person("Shelby",34,"Ergonomist"); alert(person2.name); //"Shelby"; /** *如果使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。0 */ function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(width,height){ Polygon.call(this,2); this.width=width; this.height=height; this.getArea=function(){ return this.width*this.height; } } var rect=new Rectangle(5,10); alert(rect.sides); //undefined /** * 如果构造函数窃取结合使用原型链或者寄生组合则可以解决这个问题。 */ function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(width,height){ Polygon.call(this,2); this.width=width; this.height=height; this.getArea=function(){ return this.width*this.height; } } Rectangle.prototype=new Polygon(); var rect=new Rectangle(5,10); alert(rect.sides);
?
?
1.2:惰性载入函数
/** * 惰性载入函数 * * 每次调用createXHR()的时候,它都要对浏览器所支持的能力仔细检查。首先检查内置的XHR,然后测试有没有基于ActiveX的XHR, * 最后如果都没有发现的话就抛出一个错误。每次调用 该函数都是这样,即使每次调用时分去的结果都不变。 */ function createXHR(){ if(typeof XMLHttpRequest !="undefined"){ return new XMLHttpRequest(); }else if(typeof ActiveXObject != "undefined"){ if(typeof arguments.call.activeXString != "string"){ var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"]; for(var i=0,len=versions.length;i<len;i++){ try{ new ActiveXObject(version[i]); arguments.callee.activeXString=versions[i]; break; }catch(ex){ //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); }else{ throw new Error("No XHR object available."); } } /** *解决方案就是称之为惰性载入的技巧。 * 惰性载入表示函数执行的分支仅会发生1次:即函数第一次调用的时候。 在第一次调用 的过程中, * 该函数会被覆盖为另一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。 */ function createXHR() { if( typeof XMLHttpRequest != "undefined") { createXHR = function() { return new XMLHttpRequest(); } } else if( typeof ActiveXObject != "undefined") { createXHR = function() { if( typeof arguments.call.activeXString != "string") { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"]; for(var i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(version[i]); arguments.callee.activeXString = versions[i]; break; } catch(ex) { //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } } else { createXHR=function(){ throw new Error("No XHR object available."); } } }
?
?
1.3:函数绑定
/** * 函数绑定 * * 函数绑定要创建一个函数,可以在特定环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用。 * 以将函数作为变量传递的同时保留代码执行环境。 */ /** * 执行事件时,显示的是undefined。这个问题在于没有保存handler.handleClick()的环境。 */ var handler={ message:"Event handled", handleClick:function(event){ alert(this.message); } } var btn=document.getElementById("myBtn"); EventUtil.addHandler(btn,"click",handler.handleClick); /** * 使用一个闭包来修正这个问题 */ var handler={ message:"Event handled", handleClick:function(event){ alert(this.message); } }; var btn=document.getElementById("myBtn"); EventUtil.addHandler(btn,"click",function(event){ handler.handleClick(event); }) /** *以上是特定于那段代码的解决方案,创建多个闭包可能会令代码变得难于理解和调试。 * 因此,很多javascript库实现了一个可以将函数绑定到指定环境的函数。这个函数一般都叫bind(). * 一个简单的bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用 给定对象的函数, * 并且将所有参数原封不动传递过去。 */ /** *这个函数似乎简单,但其功能是非常强大的。在bind()中创建一个闭包,闭包使用apply()调用传入的函数, * 并给apply()传递context对象和参数。注意这里使用的arguments对象是内部函数的,而非bind() 的。 */ function bind(fn,context){ return function(){ return fn.apply(context,arguments); } } var handler={ message:"Event handled", handleClick:function(event){ alert(this.message); } } var btn=document.getElementById("myBtn"); EventUtil.addHandler(btn,"click",bind(handler.handleClick,handler));
?
?
?
1.4:函数柯里化
/** * 函数柯里化 * * 与函数绑定紧密相关的主题是函数柯里化(function currying ),它且于创建已经设置好一个或多个参数的函数。 * 函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。 * 两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。 */ function add(num1,num2){ return num1+num2; } function curriedAdd(num2){ return add(5,num2); } alert(add(2,3))//5 alert(curriedAdd(3))//8 /** *尽管从技术上来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念。 * 柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。 */ function curry(fn){ var args=Array.prototype.slice.call(arguments,1); return function(){ var innerArgs=Array.prototype.slice.call(arguments); var finalArgs=args.concat(innerArgs); return fn.apply(null,finalArgs); } } function add(num1,num2){ return num1+num2; } var curriedAdd=curry(add,5); alert(curriedAdd(3));//8 var curriedAdd=curry(add,5,12); alert(curriedAdd());//17 /** * 通过函数柯里化构造出更为复杂的bind()函数 */ function bind(fn,context){ var args=Array.prototype.slice.call(arguments,2); return function(){ var innerArgs=Array.prototype.slice.call(arguments); var finalArgs=args.concat(innerArgs); return fn.apply(context,finalArgs); } } var handler={ message:"Event handled", handleClick:function(name,event){ alert(this.message+":"+name+":"+event.type); } } var btn=document.getElementById("myBtn"); EventUtil.addHandler(btn,"click",bind(handler.handleClick,handler,"myBtn"));
?
?
?
2: 定时器
/** *定时器 * * 虽然人们对javascript的定时器存在普遍的误解,认为它们是线程,其实javaScript是运行于单线程的环境中的, * 而定 时器仅仅只是计划代码在未来的某个执行时间。 */
?
?
?
3:自定义事件
/** * 自定义事件 * * 自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。 */ function EventTarget(){ this.handlers={}; } EventTarget.prototype={ constructor:EventTarget, addHandler:function(type,handler){ if(typeof this.handlers[type]=="undefined"){ this.handlers[type]=[]; } this.handlers[type].push(handler); }, fire:function(event){ if(!event.target){ event.target=this; } if(this.handlers[event.type] instanceof Array){ var handlers=this.handlers[event.type]; for(var i=0,len=handlers.length;i<len;i++){ handlers[i](event); } } }, removeHandler:function(type,handler){ if(this.handlers[type] instanceof Array){ var handlers=this.handlers[type]; for(var i=0,len=handlers.length;i<len;i++){ if(handlers[i]===handler){ break; } } handlers.splice(i,1); } } } function Person(name,age){ EventTarget.call(this); this.name=name; this.age=age; } inheritPrototype(Person,EventTarget); Person.prototype.say=function(message){ this.fire({type:"message",message:message}); } function handleMessage(event){ alert(event.target.name+" say: "+event.message); } //创建新person var person=new Person("Nicholas",29); //添加一个事件处理程序 person.addHandler("message",handleMessage); //在该对象上调用1个方法,它触发消息事件 persona.say("Hi there.");
?
?
?
?
4:拖放
?
/** *拖放 */ var DragDrop=function(){ var dragging=null; var diffx=0; var diffy=0; function handleEvent(event){ //获取事件和目标 event=EventUtil.getEvent(event); var target=EventUtil.getTarget(event); //确定事件类型 switch(event.type){ case "mousedown": if(target.className.indexOf("draggable")>-1){ dragging=target; diffX=event.clientX-target.offsetLeft; diffY=event.clientY-target.offsetTop; } break; case "mousemove": if(dragging!=null){ //获取事件 event=EventUtil.getEvent(event); //指定位置 dragging.style.left=(event.clientX-diffX)+"px"; dragging.style.top=(event.clientY-diffY)+"px"; } break; case "mouseup": dragging=null; break; } } //公共接口 return{ enable:function(){ EventUtil.addHandler(document,"mousedown",handleEvent); EventUtil.addHandler(document,"mousemove",handleEvent); EventUtil.addHandler(document,"mouseup",handleEvent); }, disable:function(){ EventUtil.removeHandler(document,"mousedown",handleEvent); EventUtil.removeHandler(document,"mousemove",handleEvent); EventUtil.removeHandler(document,"mouseup",handleEvent); } } }(); /** * 上面拖放功能还不能真正应用起来,除非能知道什么时候手动开始了。从这点上看,前面的代码没有提供任何方法表示拖动开始、 * 正在拖动或者已经结束。这时,可以使用自定义事件来指示这几个事件的发生,让应用的其他部分与拖动功能进行交互。 */ var DragDrop=function(){ var dragdrop=new EventTarget(); var dragging=null; var diffx=0; var diffy=0; function handleEvent(event){ //获取事件和目标 event=EventUtil.getEvent(event); var target=EventUtil.getTarget(event); //确定事件类型 switch(event.type){ case "mousedown": if(target.className.indexOf("draggable")>-1){ dragging=target; diffX=event.clientX-target.offsetLeft; diffY=event.clientY-target.offsetTop; dragdrop.fire({type:"dragstart",target:dragging,x:event.clientX,y:event.clientY}); } break; case "mousemove": if(dragging!=null){ //获取事件 event=EventUtil.getEvent(event); //指定位置 dragging.style.left=(event.clientX-diffX)+"px"; dragging.style.top=(event.clientY-diffY)+"px"; dragdrop.fire({type:"drag",target:dragging,x:event.clientX,y:event.clientY}); } break; case "mouseup": dragdrop.fire({type:"darged",target:dragging,x:event.clientX,y:event.clientY}); dragging=null; break; } } //公共接口 dragdrop.enable=function(){ EventUtil.addHandler(document,"mousedown",handleEvent); EventUtil.addHandler(document,"mousemove",handleEvent); EventUtil.addHandler(document,"mouseup",handleEvent); }, dragdrop.disable=function(){ EventUtil.removeHandler(document,"mousedown",handleEvent); EventUtil.removeHandler(document,"mousemove",handleEvent); EventUtil.removeHandler(document,"mouseup",handleEvent); } return dragdrop; }(); DragDrop.addHandler("dragstart",function(event){ var status=document.getElementById("status"); status.innerHTML="Started dragging "+event.target.id; }); DragDrop.addHandler("drag",function(event){ var status=document.getElementById("status"); status.innerHTML+="<br/> Draged "+event.target.id+" to("+event.x+","+event.y+")"; }) DragDrop.addHandler("draged",function(event){ var status=document.getElementById("status"); status.innerHTML="<br/> Dropped "+event.target.id+" at("+event.x+","+event.y+")"; })
?