一,高级函数
1,安全的类型检测
safari中tepeof操作符对正则表达式返回function
instanceof存在多个全局作用域(包含多个frame)时,frame中定义的变量不能再其他的frame中使用
在任何值上调用Object原生的toString()方法,返回一个[object NativeConstructorName]格式的字符串
每个类的内部都有一个[[class]]属性,指定了上述字符串中的构造函数名
检测是否为原生函数
function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
检测是否为正则表达式
function isRegExp(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}
对于IE中以COM对象形式实现的任何函数,以上都会返回false
开发人员定义的任何构造函数都将返回[object Object];用于区别原生与非原生的Javascript对象
2,作用域安全的构造函数
当使用new操作符时,构造函数内用到的this对象会指向新创建的对象实例
当没有使用new操作符,直接使用构造函数如Person(),this会映射到全局对象window上,导致错误对象属性的意外增加
创建一个作用域安全的构造函数
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); } }
此时锁定了构造函数的环境,如果不使用原型链会破坏使用这个构造函数窃取模式的继承
使用构造函数窃取结合使用原型链或者寄生组合可以解决这个问题
function Polygon(sides){ if(this instanceof Polygon){ this.sides = sides; this.getArea = function(){}; }else{ return new Polygon(); } } 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();
3,惰性载入函数
表示函数执行的分支仅会发生一次,if语句
1)在函数被调用时再处理函数
function createXHR(){ if(typeof XMLHttpRequest != "undefined"){ createXHR = function(){ return new XMLHttpRequest(); }; }else if(typeof AcitveXObject != "undefined"){ createXHR = function(){ if(typeof argument.callee.activeXString != "String"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len; for(i=0,len=versions.length;i<len;i++){ new ActiveXObject(versions[i]); argument.callee.activeXString = versions[i]; break; }catch(ex){ //skip } } return new ActiveXObject(argument.callee.activeXString); }; }else{ createXHR = function(){ throw new Error("NO XHR object available"); }; } return createXHR(); }
2)在声明函数时指定适当的函数,第一次调用函数时就不会损失性能,在代码首次加载时会损失性能
创建匿名,自执行的函数
var createXHR = (function(){ if(typeof XMLHttpRequest != "undefined"){ return function(){ return new XMLHttpRequest(); }; }else if(typeof ActiveXObject != "undefined"){ return function(){ if(typeof argument.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len; for(i=0,len=versions.length;i<len;i++){ try{ new ActiveXObject(versions[i]); argument.callee.activeXString = versions[i]; break; }catch(ex){ //skip } } } return new ActiveXObject(argument.callee.activeXString); }; }else{ return function(){ throw new Error("NO XHR object available") }; } })();
4,函数绑定
创建一个函数,可以在特定的this环境中以指定参数调用另一个函数,常常和回调函数与事件处理程序一起使用,将函数作为变量传递的同时保留代码执行环境
将函数绑定到指定环境的函数,这个函数叫做bind(),接受一个函数和一个环境,返回一个在给定环境中调用给定函数的函数,所有参数原封不动的传递过去
function bind(fn,context){
return function(){
return fn.apply(context,argument);
};
}
argument是内部函数的而非bind()函数的
使用自定义的bind()函数,
var handler = {
message:"Event handlers",
handleClick:function(event){alert(this.message + ":" + event.type);}
};
var btn = document.getElementById("my-btn");
EventUtil.addhandler(btn,"click",bind(handler.hangleClick,handler));
原生的bind()方法,在ECMAscript中定义,传人作为this值的对象
EventUtil.addhandler(btn,"click",handler.handleClick.bind(handler));
5,函数的柯里化
用于创建已经设置好了一个或多个参数的函数,基本方法,使用一个闭包返回一个函数,返回的函数还需要设置传人的参数
创建步骤,调用另一个函数并为它传人要柯里化的函数和必要的参数
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)) var curriedAdd = curry(ass,6,10); alert(curredAdd());
函数柯里化可以作为函数绑定的一部分包含在其中,构造出更为复杂的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); }; } 除了event对象额外给事件处理程序传递参数时, var handler = { message:"Event Handler"; handleClick:function(name,event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn,"click",bind(handler.handleClick,handler,"my-btn")); EventUtil.addHandler(btn,"click",handler.handleClick.bind(handler,"my-btn"));
二,防篡改对象
1,不可扩展对象
var person = {name:"Nicholas"};
Object.preventExtensions(person);
阻止对象的扩展,不能再添加属性和方法,可以修改删除已有成员
Object.isExtensible(person);确定person是否可以扩展
2,密封的对象
密封对象不可扩展,已有成员的[[Configurable]]特性被修改为false,不能删除属性和方法,属性值可以修改
Object.seal(person);密封对象
Object.isSeal(person);确定person是否被密封
3,冻结的对象
冻结的对象既不可扩展,又是密封的,而且对象数据属性[[Writable]]特性被设置为false,定义[[Set]]函数,访问器属性仍然是可写的
Object.freeze(person);冻结对象
Object.isFrozen(person);确定对象是否冻结
三,高级定时器
1,重复的定时器
1)某些间隔会被跳过,2)多个定时器的代码执行之间的间隔可能会比预期小
避免上述问题,使用链式setTimeout()调用
setTimeout(function(){ setTimeout(argument.callee,interval); },interval); setTimeout(function(){ var div = document.getElementById("myDiv"); left = parseInt(div.style.left) + 5; div.style.left = left + "px"; if(left < 200){ setTimeout(argument.callee,50); } },50);
2,Yielding Processes
长时间运行脚本的制约,如果代码运行超过特定的时间或者特定语句数量,会弹出对话框
原因有二,过长的,过深嵌套的函数调用或者是进行大量处理的循环,
当发现某个循环占用了大量时间,可是使用定时器分割这个循环,叫做数组分块
基本模式
setTimeout(function(){ //取出下一个条目并处理 var item = array.shift(); process(item); //若还有条目,再设置另一个定时器 if(array.length>0){ setTimeout(arguments.callee,100); } },100);
实现数组分块,使用下列函数
function chunk(array,process,context){ setTimeout(function(){ var item = array.shift(); process.call(context,item); if(array.length > 0){ setTimeout(arguments.callee,100); } },100); }
3,函数节流
思想是某些代码不可以在没有时间间断的情况连续重复执行,第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码
第二次调用该函数时,它会清除前一次的定时器并设置另一个
基本形式
var processor = { timeoutID:null; //实际进行处理的方法 performProcessing:function(){ //实际执行的代码 }, //初始化处理调用的方法 process:function(){ clearTimeout(this.timeoutID); var that = this; this.timeoutID = setTimeout(function(){ that.performProcessing(); },100); } }; processor.process();
化简模式
function throttle(method,context){
clearTimeout(method.tid);
method.tid = setTimeout(function(){
method.call(context);
},100);
}
resize事件,window.onresize = function(){};
function resizeDIv(){
var div = document.getElementById("myDIv");
div.style.height = div.offsetWidth + "px";
}
window.onresize = function(){throttle(resizeDIv);};
四,自定义事件
自定义事件的基本模式
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](evnet); } } }, 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(handlersp[i] === handler){ break; } } handlers.splice(i,1); } }, };
使用自定义事件
function handleMessage(event){
alert("Message received:" + event.message);
}
var target = new EventTarget();
target.addHandler("message",handleMessage);
target.fire({type:"message",message:"hellod"});
target.removeHandler("message",handleMessage);
target.fire({type:"message",message:"hello"});
其他对象可以继承EventTarget对象
function Person(name,age){
EventTarget.call(this);
this.name = name;
this.age = age;
}
inheritProtetype(Person,EventTarget);
Person.prototype.say=function(message){
this.fire({type:"message",message:message});
};
使用方法
function handleMessage(event){
alert(event.target.name + "say" + event.target);
}
var person = new Person("Nicholas",29);
person.addHandler("message",handleMessage);
person.say("Hi,there");
五,拖放
var DragDrop = function(){ var dragging = null; 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}; break; case "mousemove": if(dragging != null){ //指定位置 dragging.style.left = event.clientX + "px"; dragging.style.top = event.clientY + "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.addHandler(document,"mousedown",handleEvent); EventUtil.addHandler(document,"mousemove",handleEvent); EventUtil.addHandler(document,"mouseup",handleEvent); }, }; }();
1,修缮拖动功能
var DragDrop = function(){ var dragging = null, diffX = 0;, 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){ dragging.style.left = (event.clientX-fiffX) + "px"; dragging.style.top = (event.clientY - fiffY) + "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.addHandler(document,"mousedown",handleEvent); EventUtil.addHandler(document,"mousemove",handleEvent); EventUtil.addHandler(document,"mouseup",handleEvent); } }; }();
2,添加自定义事件
var DragDrop = function(){ var dragdrop = new EventTarget(), dragging = null, diffX = 0;, 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; //触发dragstart dragdrop.fire({type:"dragstart",target:dragging,x:event.clientX,y:event.clientY}); } break; case "mousemove": if(dragging != null){ dragging.style.left = (event.clientX-fiffX) + "px"; dragging.style.top = (event.clientY - fiffY) + "px"; //触发自定义事件 dragdrop.fire({type:"drag",target:dragging,x:event.clientX,y:clientY}); } break; case "mouseup": //触发fragend事件 dragdrop.fire({type:"dragend",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.addHandler(document,"mousedown",handleEvent); EventUtil.addHandler(document,"mousemove",handleEvent); EventUtil.addHandler(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/>Dragged " + event.target.id + " to("+event.x+","+event.y+")"; }); DragDrop.addhandler("dragend",function(event){ var status = document.getElementById("status"); status.innerHTML += "<br/> Dropped" + event.target.id + "at ("+event.x+","+event.y+")"; });