高级技巧
高级函数
安全的类型检测
由于原生数组的构造函数名与全局作用域无关,因此使用toString就能保证返回一致的值。
function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]" ;
}
var isNativeJSON = window.JSON&&Object.prototype.toString.call(JSON) == "[object JSON]“
作用域安全的构造函数
自定义对象使用构造函数,当没有使用new操作符来调用构造函数时,由于该this对象是在运行时绑定的,所有直接调用构造函数,this会映射到全局对象window上,导致错误对象属性意外增加。这样一来,构造函数实例的属性就被加到window上,可能造成对属性的覆盖,导致错位出现。
所有可以使用一些方法,构造作用域安全的构造函数:
function Person(name,agr,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() {
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);
惰性载入函数
如果if语句不必每次执行,那么代码可以运行地更快一些。这里可以使用惰性载入。
惰性载入表示函数执行的分支仅会发生一次。
实现惰性载入:
- 函数被调用时再处理函数。
- 在第一次调用时,该函数会被覆盖为另一个按合适方式执行的函数,对原函数的调用都不用再经过执行的分支。
- 第二种方式是在声明函数时就指定适当的函数。 创建一个匿名、自执行的函数,用以确定应该使用哪个函数。
var createXHR = (function () {
if(typeof XMLHttpRequest != "undefined"){
return function(){
return new XMLHttpRequest();
};
}else if (typeof ActiveXObject != "undefined"){
return function(){
if(typeof arguments.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]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
}else{
return function(){
throw new Error("No XHR object available")
}
}
})();
函数绑定
创建一个函数,可以在特定的this环境中用以指定参数调用另一个函数。
var handle = {
message : "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn,"click", function(event){
handle.handleClick(event);
});
还可以使用bind()方法代替闭包,进行绑定this或其他对象
防篡改对象
不可扩展对象
使用Object.preventExtendions()方法可以阻止给对象添加方法和属性。
密封的对象
密封对象不可扩展,而且成员的[[Configurable]]特性将被设置为false。意味着不能删除属性和方法。
冻结的对象
冻结的对象既不可扩展,又是密封的,而且对象数据属性的[[Writable]]特性会被设置为false。
可以使用Object.freeze()来冻结对象。
高级定时器
重复的定时器
使用setInserval()创建的定时器可能导致定时器代码在代码再次被添加到队列之前还没有完成执行,定时器代码被连续运行好几次。
这种重复定时器的规则有2点问题:
- 某些间隔会被跳过
- 多个定时器的代码执行之间的间隔可能比预期的小
为了避免这两个问题,可以使用链式setTimeout()
setTimeout(function){
setTimeout(arguments.callee,interval);
},interval);
上面调用了setTimeout,每次函数执行的时候会创建一个新的定时器,第二个定时器调用arguments.callee获取对当前执行的函数的引用,并为其设置另一个定时器。这样,可以在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。
拖放
点击某个对象,并按住鼠标按钮不放,将鼠标移动到另一个区域,然后释放鼠标按钮将对象放在这里。
所有创建一个绝对定位的元素,使其可以用鼠标移动。也就是鼠标拖尾
鼠标拖尾就是一个或多个图片在页面上跟着鼠标指针移动,设置一个onmousemove事件处理程序,总时将指定元素移动到鼠标指针的位置
EventUitl.addHandler(document, "mousemove", function(event){
var myDiv = document.getElementById("myDiv");
myDiv.style.left = event.clientX + "px:"
myDiv.style.top = event.clientY + "px";
});
实现拖动界面
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.indoxOf("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")
}
}
}
完善拖动功能
拖动会出现的问题:,元素的左上角总是和指针在一起。为了改善这个问题,也就是说当拖动元素时,用户点击的那一点就是指针应该保持的位置
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.indoxOf("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-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.addHandler(document,"mousedown",handleEvent);
EventUtil.addHandler(document,"mousemove",handleEvent);
EventUtil.addHandler(document,"mouseup")
}
}
}
添加自定义事件
需要应用起来,还有指定什么时候开始拖动,监视拖动开始,正在拖动和已经结束的事件。
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.indoxOf("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){
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:"dragend",target:dragging,x:event.clientX,y:event.clientY});
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")
}
}
}
DragDrop.addHandler("dragstart",function (event) {
var status = document.getElementById("status");
status.innerHTML = "Started" + event.target.id;
})
DragDrop.addHandler("drag",function (event) {
var status = document.getElementById("status");
status.innerHTML += "Dragged" + event.target.id;
})
DragDrop.addHandler("dragend",function (event) {
var status = document.getElementById("status");
status.innerHTML = "Dropped" + event.target.id;
})