javascript精粹

比较函数
function compare(value1, value2) {
	if (value1 < value2) {
		return 1;
	} else if (value1 > value2) {
		return -1;
	} else {
		return 0;
	}
}

function compare(value1, value2) {
	return value2 - value1;
}

function createComparisonFunction(propertyName) {
	
	return function(object1, object2) {
		var value1 = object1[propertyName];
		var value2 = object2[propertyName];

		if(value1 < value2) {
			return -1;
		} else if (value1 > value2) {
			return 1;
		} else {
			return 0;
		}
	};
}

例如:
var data = [{name:"Zachary", age:28}, {name:"Nicholas", age:29}];

date.sort(createComparisonFunction("name"));
alert(data[0].name);

data.sort(createComparisonFunction("age"));
alert(data[0].name);

函数内部属性

arguments this

例如:
window.color = "red";
var o = {color: "blue"};

function sayColor() {
	alert(this.color);
}

sayColor();

o.sayColor = sayColor;
o.sayColor();

函数属性

length prototype caller

说明:caller是一个非标准的属性,一般在函数内部通过arguments.callee.caller使用,推荐用于调试目的。

函数方法

apply() call()

例如:
function sum(num1, num2) {
	return num1 + num2;
}

function callSum1(num1, num2) {
	return sum.apply(this, arguments);
}

function callSum2(num1, num2) {
	return sum.apply(this, [num1, num2]);
}

alert(callSum1(10, 10));
alert(callSum2(10, 10));

window.color = "red";
var o = {color: "blue"};

function sayColor() {
	alert(this.color);
}

sayColor();

sayColor.call(this);
sayColor.call(window);
sayColor.call(o);

阶乘函数

function factorial(num) {
	if (num <= 1) {
		return 1;
	} else {
		return num * factorial(num - 1);
	}
}

function factorial(num) {
	if (num <= 1) {
		return 1;
	} else {
		return num * arguments.callee(num - 1);
	}
}

例如:
var trueFactorial = factorial;

factorial = function() {
	return 0;
};

alert(trueFactorial(5));
alert(factorial(5));

随机数产生函数

值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)

function selectFrom(lowerValue, upperValue) {
	var choices = upperValue - lowerValue + 1;
	return Math.floor(Math.random() * choices + lowerValue);
}

例如:var num = selectFrom(2, 10);
alert(num);

var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"];
var color = colors[selectFrom(0, colors.length - 1)];
alert(color);

组合使用构造函数模式和原型模式

例如:
function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.friends = ["Shelby", "Court"];
}

Person.prototype = {
	constructor : Person,
	sayName : function() {
		alert(this.name);
	}
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 29, "Doctor");

person1.friedds.push("Van");
alert(person1.friends);
alert(person2.friends);
alert(person1.friends === person2.friends);
alert(person1.sayName === person2.sayName);

组合继承

例如:
function SuperType(name) {
	this.name = name;
	this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
	alert(this.name);
};

function SubType(name, age) {
	SuperType.call(this, name);

	this.age = age;
}

SubType.prototype = new SuperType();

SubType.prototype.sayAge = function() {
	alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors);
instance1.sayName();
instance1.sayAge();

var instance2 = new SubType("Greg", 27);
alert(instance2.colors);
instance2.sayName();
instance2.sayAge();

模仿块级作用域

例如:
(function() {
	
	var now = new Date();
	if(now.getMonth() == 0 && now.getDate() == 1) {
		alert("Happy new year!");
	}
})();

私有变量

例如:
function MyObject() {
	
	//函数的私有变量
	var privateVariable = 10;

	function privateFunction() {
		return false;
	}

	//特权方法
	this.publicMethod = function() {
		privateVariable++;
		return privateFunction();
	};
}

静态私有变量

(function() {
	
	//私有变量和私有函数
	var privateVariable = 10;

	function privateFunction() {
		return false;
	}

	//构造函数
	MyObject = function() {
	};

	//公有/特权方法
	MyObject.prototype.publicMethod = function() {
		privateVariable++;
		return privateFunction();
	};
})();

(function() {
	
	var name = "";

	Person = function(value) {
		name = value;
	};

	Person.prototype.getName = function() {
		return name;
	};

	Person.prototype.setName = function() {
		name = value;
	};
})();

var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"

var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"

模块模式

var singleton = {
	name : value,
	method : function() {
		//这里是方法的代码
	}
};

var singleton = function() {
	
	//私有变量和私有函数
	var privateVariable = 10;

	function privateFunction() {
		return false;
	}

	//特权/公有方法和属性
	return {
		publicProperty : true,

		publicMethod : function() {
			privateVariable++;
			return privateFunction();
		}
	};
}();

function BaseComponent() {
}

function OtherComponent() {
}

var application = function() {
	
	//私有变量和函数
	var components = new Array();

	//初始化
	components.push(new BaseComponent());

	//公有
	return {
		getComponentCount : function() {
			return components.length;
		},

		registerComponentCount : function(component) {
			if(typeof component == "object") {
				components.push(componet);
			}
		}
	};
}();

application.registerComponent(new OtherComponent());
alert(application.getComponentCount());

窗口位置

var leftPos = (typeof window.screenLeft == "number") ?
				window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ?
				window.screenTop : window.screenY;

window.moveTo(x, y);
window.moveBy(x, y);

窗口大小

var pageWidth = window.innerWidth,
	pageHeight = window.innerHeight;

if(typeof pageWidth != "number") {
	if(document.compatMode == "CSS1Compat") {
		pageWidth = document.documentElement.clientWidth;
		pageHeight = document.documentElement.clientHeight;
	} else {
		pageWidth = document.body.clientWidth;
		pageHeight = document.body.clientHeight;
	}
}

window.resizeTo();
window.resizeBy();

检查弹出窗口是否被屏蔽

var blocked = false;

try {
	var wroxWin = window.open("http://www.wrox.com", "_blank");
	if (wroxWin == null) {
		blocked = true;
} catch (ex) {
	blocked = true;
}

if(blocked) {
	alert("The popup was blocked!");
}

查询字符串参数

function getQueryStringArgs() {
	
	//取得查询字符串并去掉开头的问号
	var qs = (location.search.length > 0 ? location.search.substring(1) : "");

	//保存数据的对象
	var args = {};

	//取得每一项
	var items = qs.split("&");
	var item = null,
		name = null,
		value = null;

	//逐个将每一项添加到args对象中
	for (var i=0; i<items.length; i++) {
		item = items[i].split("=");
		name = decodeURIComponent(item[0]);
		value = decodeURIComponent(item[1]);
		args[name] = value;
	}

	return args;
}

例如:
假设查询字符串是?q=javascript&num=10
var args = getQueryStringArgs();

alert(args["q"]);
alert(args["num"]);

遍历元素的特性

function outputAttributes(element) {
	var pairs = new Array();
	for(var i=0, len=element.attributes.length; i<len; i++) {
		var attrName = element.attributes[i].nodeName;
		var attrValue = element.attributes[i].nodeValue;
		if(element.attributes[i].specified) {
			pairs.push(attrName + "=\"" + attrValue + "\"");
		}
	}
	return pairs.join(" ");
}

ie bug

if(client.browser.ie && client.browser.ie<=7) {
	var iframe = document.createElement("<iframe name=\"myframe\"></iframe>");
	var input = document.createElement("<input name=\"checkbox\">");
	var button = document.createElement("<button name=\"reset\"></button>");
	var radio1 = document.createElement("<input type=\"radio\" name=\"choice\" value=\"1\">");
	var radio2 = document.createElement("<input type=\"radio\" name=\"choice\" value=\"2\">");
	
}

通用的contains()函数

function contains(refNode, otherNode) {
	if (typeof refNode.contains == "funciton" && (!client.engine.webkit || client.engine.webkit >= 522)) {
		return refNode.contains(otherNode);
	} else if (typeof refNode.compareDocumentPosition == "function") {
		return !!(refNode.compareDocumentPosition(otherNode) & 16);
	} else {
		var node = otherNode.parentNode;
		do {
			if (node === refNode) {
				return true;
			} else {
				node = node.parentNode;
			}
		} while (node !==null);
		return false;
	}
	
}

通用innerText

function getInnerText(element) {
	return (typeof element.textContent == "string") ? element.textContent : element.innerText;
}

function setInnerText(element, text) {
	if (typeof element.textContent == "string") {
		element.textContent = text;
	} else {
		element.innerText = text;
	}
}

动态脚本

function loadScript(url) {
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = url;
	document.body.appendChild(script);
}

loadScript("client.js");

function loadScriptString(code) [
	var script = document.createElement("script");
	script.type = "text/javascript";
	try {
		script.appendChild(document.createTextNode(code));
	} catch (ex) {
		script.text = code;
	}
	document.body.appendChild(script);
}

loadScriptString("function sayHi(){alert('hi');}");

动态样式

function loadStyles(url) {
	var link = document.createElement("link");
	link.rel = "stylesheet";
	link.type = "text/css";
	link.href = url;
	var head = document.getElementByTagName("head")[0];
	head.appendChild(link);
}

loadStyles("styles.css");

function loadStyleString(css) {
	var style = document.createElement("style");
	style.type = "text/css";
	try {
		style.appendChild(document.createTextNode(css));
	} catch (ex) {
		style.styleSheet.cssText = css;
	}
	var head = document.getElementByTagName("head")[0];
	head.appendChild(style);
}

loadStyleString("body{background-color:red}");

访问框架或内嵌框架的文档对象

var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

偏移量

function getElementLeft(element) {
	var actualLeft = element.offsetLeft;
	var current = element.offsetParent;

	while (current !== null) {
		actualLeft += current.offsetLeft;
		current = current.offsetParent;
	}

	return actualLeft;
}

function getElementTop(element) {
	var actualTop = element.offsetTop;
	var current = element.offsetParent;

	while (current !== null) {
		actualTop += current.offsetTop;
		current = current.offsetParent;
	}

	return actualTop;
}

客户区大小

浏览器视口大小

function getViewport() {
	if (document.compatMode == "BackCompat") {
		return {
			width : document.body.clientWidth,
			height : document.body.clientHeight
		};
	} else {
		return {
			width : document.documentElement.clientWidth,
			height : document.documentElement.clientHeight	
		};
	}
}

文档的总高度

var docHeight = Math.max(document.documentElement.scrollHeight,
		document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth,
		document.documentElement.clientWidth);

控制滚动条

function scrollToTop(element) {
	if (element.scrollTop != 0) {
		element.scrollTop = 0;
	}
}

function getBoundingClientRect(element) {
	
	var scrollTop = document.documentElement.scrollTop;
	var scrollLeft = document.documentElement.scrollLeft;

	if (element.getBoundingClientRect) {
		if (typeof arguments.callee.offset != "number") {
			var temp = document.createElement("div");
			temp.style.cssText = "position:absolute; left:0; top:0;";
			document.body.appendChild(temp);
			arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
			document.body.removeChild(temp);
			temp = null;
		}

		var rect = element.getBoundingClientRect();
		var offset = arguments.callee.offset;

		return {
			left : rect.left + offset,
			right : rect.right + offset,
			top : rect.top + offset,
			bottom : rect.bottom + offset
		};
	} else {
		
		var actualLeft = getElementLeft(element);
		var actualTop = getElementTop(element);

		return {
			left : actualLeft - scrollLeft,
			right : actualLeft + element.offsetWidth - scrollLeft,
			top : actualTop - scrollTop,
			bottom : actualTop + element.offsetHeight - scrollTop
		};
	}
}

DOM0级事件处理程序

var btn = document.getElementById("myBtn");
btn.onclick = function() {
	alert("Clicked");
}

DOM2级事件处理程序

firefox事件处理程序

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function() {
	alert(this.id);
}, false);

btn.addEventListener("click", function() {
	alert("Hello world!");
}, false);

这里为按钮添加了两个事件处理程序,这两个事件处理程序会按照添加它们的顺序触发,因此首先会显示元素Id,其次会显示"Hello world!"消息。

var btn = document.getElementById("myBtn");
var handler = function() {
	alert(this.id);
};
btn.addEventListener("click", handler, false);

btn.removeEventListener("click", handler, false);

IE事件处理程序

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
	alert(this === window); //true
});

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
	alert("Clicked");
});
btn.attachEvent("onclick", function() {
	alert("Hello world!");
});

这里调用了两次attachEvent(),为同一个按钮添加了两个不同的事件处理程序,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序执行。

var btn = document.getElementById("myBtn");
var handler = function() {
	alert("Clicked");
};
btn.attachEvent("onclick", handler);

btn.detachEvent("onclick", handler);

跨浏览器的事件处理程序

var EventUtil = {
	
	addHandler: function(element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if (element.attachEvent) {
			element.attachEvent("on", + type, handler);
		} else {
			element["on" + type] = handler;
		}	
	},
	removeHandler: function(element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, handler, false);
		} else if (element.detachEvent) {
			element.detachEvent("on", + type, handler);
		} else {
			element["on" + type] = null;
		}	
	},
};

例如:

var btn = document.getElementById("myBtn");
var handler = function() {
	alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);

EventUtil.removeHandler(btn, "click", handler);

DOM中的事件对象

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
	alert(event.type); //"click"
};

btn.addEventListener("click", function(event) {
	alert(event.type); //"click"
}, false);

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
	alert(event.currentTarget === this); //true
	alert(event.target === this); //true
};

document.body.onclick = function(event) {
	alert(event.currentTarget === document.body); //true
	alert(this === document.body); //true
	alert(event.target === document.getElementById("myBtn")); //true
};

通过一个函数处理多个事件

var btn = document.getElementById("myBtn");
var handler = function(event) {
	switch(event.type) {
		case "click":
		alert("Click");
		break;

		case "mouseover":
		event.target.style.backgroundColor = "red";
		break;

		case "mouseout":
		event.target.style.backgroundColor = "";
		break;
	}
};

btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

跨浏览器的事件对象

var EventUtil = {
	
	addHandler: function(element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if (element.attachEvent) {
			element.attachEvent("on", + type, handler);
		} else {
			element["on" + type] = handler;
		}	
	},

	getEvent: function(event) {
		return event ? window.event;
	},

	getTarget: function(event) {
		return event.target || event.srcElement;
	},

	preventDefault: function(event) {
		if (event.preventDefault) {
			event.preventDefault();
		} else {
			event.returnValue = false;
		}
	},

	removeHandler: function(element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, handler, false);
		} else if (element.detachEvent) {
			element.detachEvent("on", + type, handler);
		} else {
			element["on" + type] = null;
		}	
	},

	stopPropagation: function(event) {
		if (event.stopPropatation) {
			event.stopPropagation();
		} else {
			event.cancelBubble = true;
		}
	},

	getRelatedTarget: function(event) {
		if (event.relatedTarget) {
			return event.relatedTarget;
		} else if (event.toElement) {
			return event.toElement;
		} else if (event.fromElement) {
			return event.fromElement;
		} else {
			return null;
		}
	}, 

	getButton: function(event) {
		if (document.implementation.hasFeature("MouseEvents", "2.0")) {
			return event.button;
		} else {
			switch(event.button) {
				case 0:
				case 1:
				case 3:
				case 5:
				case 7:
					return 0;
				case 2:
				case 6:
					return 2;
				case 4:
					return 1;
			}
		}
	}
};

事件委托

<ul id="muLinks">
	<li id="goSomeshere">Go somewhere</li>
	<li id="doSomething">Do something</li>
	<li id="sayHi">Say hi</li>
</ul>

var list = document.getElementById("myLinks");

EventUtil.addHandler(list, "click", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);

	switch(target.id){
		case "doSomething":
			document.title = "I changed the document's title";
			break;

		case "goSomewhere":
			location.href = "http"//www.wrox.com";
			break;

		case "sayHi":
			alert("hi");
			break;

	}
});

最适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress

//避免多次提交表单
EventUtil.addHandler(form, "submit", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);

	//取得提交按钮
	var btn = target.elements["submit-btn"];

	//禁用它
	btn.disabled = true;
});

focus和blur事件以某种方式改变用户界面

var textbox = document.forms[0].element[0];

EventUtil.addHandler(textbox, "focus", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);
	
	if(target.style.backgroundColor != "red"){
		target.style.backgroundColor = "yellow";
	}
});

EventUtil.addHandler(textbox, "blur", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);

	if(/[^\d]/.test(target.value)){
		target.style.backgroundColor = "red";
	} else {
		target.style.backgroundColor = " ";
	}
});

EventUti.addHandler(textbox, "change", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);

	if(/[^\d]/.test(target.value)){
		target.style.backgroundColor = "red";
	} else {
		target.style.backgroundColor = " ";
	}
});

文本框获得焦点

EventUtil.addHandler(textbox, "focus", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);

	target.select();
});

取得选择的文本

functin getSelectedText(textbox){
	if(document.selection){
		return document.selection.createRange().text;
	} else {
		return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
	}
}

选择部分文本

function selectText(textbox, startIndex, stopIndex){
	if(textbox.setSelectionRange){
		textbox.setSelectionRange(startIndex, stopIndex);
	} else if(textbox.createTextRange){
		var range = textbox.createTextRange();
		range.collapse(true);
		range.moveStart("character", startIndex);
		range.moveEnd("character", stopIndex - startIndex);
		range.select();
	}
	textbox.focus();
}

例如:

textbox.value = "Hello world";

//选择所有文本

selectText(textbox, 0, textbox.value.length); //"Hello world!"

//选择前3个字符

selectText(teextbox, 0, 3); //"Hel"

//选择第4到第6个字符

selectText(textbox, 4, 7); //"o w"

过滤输入

EventUtil.addHandler(textbox, "keypress", function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);
	var charCode = EventUtil.getCharCode(event);

	if(!/\d/.test(String.fromCharCode(charCode)) && charCode > 9 && !event.ctrlKey){
		EventUtil.preventDefault(event);
	}
});

自动切换焦点

<input type="text" name="tel1" id="txtTel1" maxlength="3">
<input type="text" name="tel2" id="txtTel2" maxlength="3">
<input type="text" name="tel3" id="txtTel3" maxlength="3">

(function(){
	
	function tabForward(event);
	var target = EventUtil.getTarget(evet);

	if(target.value.length == target.maxLength){
		var form = target.form;
		for(var i=0, len = form.elements.length; i<len; i++){
			if (form.elements[i] == target) {
				form.elements[i+1].focus();
				return;
			}
		}
	}

	var textbox1 = docuemnt.getElementById("txtTel1");
	var textbox2 = docuemnt.getElementById("txtTel2");
	var textbox3 = docuemnt.getElementById("txtTel3");

	EventUtil.addHandler(textbox1, "keyup", tabForward);
	EventUtil.addHandler(textbox2, "keyup", tabForward);
	EventUtil.addHandler(textbox3, "keyup", tabForward);
})();

添加选项

var newOption = document.createElement("option");
newOption.appendChild(document.createTextNode("Option text"));
newOption.setAttribute("value", "Option value");

selectbox.appendChild(newOption);

var newOption = new Option("Option text", "Option value");
selectbox.add(newOption, undefined);

移除选项

selectbox.removeChild(selectbox.option[0]); //移除第一个选项

selectbox.remove(0);

selectbox.option[0] = null;

function clearSelectbox(selectbox) {
	for(var i=0, len=selectbox.options.length; i<len; i++) {
		selectbox.remove(0);
	}
}

移动和重排选项

var selectbox1 = document.getElementById("selLocation1");
var selectbox2 = document.getElementById("selLocation2");

selectbox2.appendChild(selectbox1.option[0]);

var optionToMove = selectbox.options[1];
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index-1]);

var optionToMove = selectbox.options[1];
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index+2]);

表单序列化

function serialize(form) {
	var parts = new Array();
	var field = null;

	for (var i=0, len=form.elements.length; i<len; i++) {
		field = form.elements[i];

		switch(field.type) {
			case "select-one":
			case "select-multiple":
				for (var j=0, optLen=field.options.length; i<optLen; j++) {
					var option = field.options[j];
					if (option.selected) {
						var optValue = "";
						if (option.hasAttribute) {
							optValue = (option.hasAttribute("value") ? 
								   option.value : option.text);
						} else {
							optValue = (option.attributes["value"].specified ?
							option.value : option.text);
						}
						parts.push(encodeURIComponent(field.name) + "=" + 
							   encodeURIComponent(optValue));
					}
				}
				break;

			case undefined:		//字段集
			case "file": 		//文件输入
			case "submit": 		//提交按钮
			case "reset": 		//重置按钮
			case "button": 		//自定义按钮
				break;

			case "radio": 		//单选按钮
			case "checkbox": 	//复选框
				if (!field.checked) {
					break;
				}
				/* 执行默认操作 */

			default:
				parts.push(encodeURIComponent(field.name) + "=" + 
					   encodeURIComponent(field.value));
		}
	}
	return parts.join("&");
}

try-catch语句

try{
	// 可能会导致错误的代码
} catch(error) {
	// 在错误发生时怎么处理
}

例如:

try {
	window.someNonexistentFunction(); //调用不存在的函数
} catch (error) {
	alert(error.message);
}

finally子句

例如:

function testFinally() {
	try {
		return 2;
	} catch (error) {
		return 1;
	} finally {
		return 0;
	}
} // 返回0

抛出错误

function process(values) {
	
	if (!(values instanceof Array)) {
		throw new Error("process() : Argument must be an array.");
	}

	values.sort();

	for (var i=0, len=values.length; i<len; i++) {
		if (values[i]>100) {
			return values[i];
		}
	}
	
	return -1;
}

捕获错误的目的在于避免浏览器以默认的方式处理它们;而抛出错误的目的在于提供错误发生具体原因的消息。

错误事件

window.onerror = function(message, url, line) {
	alert(message);
}

window.onerror = function(message, url, line) {
	return false;
}

只要能够适当的使用try-catch语句,就不会有错误交给浏览器,也就不会触发error事件。

图像也支持error事件

var image = new Image();
EventUtil.addHandler(image, "load", function(event) {
	alert("Image loaded!");
});

EventUtil.addHandler(image, "error", function(event) {
	alert("Image not loaded!");
});
image.src = "smilex.gif"; // 指定不存在的文件

大体上来说,基本类型的值应该使用typeof来检测,而对象的值应该使用instanceof来检测

function getQueryString(url) {
	if (typeof url == "string") {
		var pos = url.indexOf("?");
		if (pos>-1) {
			return url.substring(pos + 1);
		}
	}
	return "";
}

function reverseSort(values) {
	if (values instanceof Array) {
		values.sort();
		values.reverse();
	}	
}

通信错误

function addQueryStringArg(url, name, value) {
	if (url.indexOf("?") == -1) {
		url += "?";	
	} else {
		url += "&";
	}

	url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
	return url;
}

例如:

var url = "http://www.somedomain.com";
var newUrl = addQueryStringArg(url, "redir", 
				"http://www.someotherdomain.com?a=b & c=d");

alert(newUrl);

区分致命错误和非致命错误

for (var i=0, len=mods.length; i<len; i++) {
	try {
		mods[i].init();
	} catch (ex) {
		// 在这里处理错误
	}
}

把错误记录到服务器

function logError(sev, msg) {
	var img = new Image();
	img.src = "log.php?nev=" + endoceURIComponent(sev) + "&msg=" + 
		   encodeURIComponent(msg);
}

例如:

for (var i=0, len=mods.length; i < len; i++) {
	try {
		mods[i].init();
	} catch (ex) {
		logError("nonfatal", Module init failed: " + ex.message);
	}
}

调试技术

将消息记录到控制台

function sum(num1, num2) {
	console.log("Entering sum(), arguments are " + num + "," + num2);

	console.log("Before calculation");
	var result = num1 + num2;
	console.log("After calculation");
	console.log("Exiting sum()");
	return result;
}

function sum(num1, num2) {
	java.lang.System.out.println("Entering sum(), arguments are " + num1 + "," + num2);

	java.lang.System.out.println("Before calculation");
	var result = num1 + num2;
	java.lang.System.out.println("After calculation");
	
	java.lang.System.out.println("Exiting sum()");
	return result;
}

function log(message) {
	if (typeof console == "object") {
		console.log(message);
	} else if (typeof opera == "object") {
		opera.postError(message);
	} else if (typeof java == "object" && typeof java.lang == "object") {
		java.lang.System.out.println(message);
	}	
}

将消息记录到当前页面

function log(message) {
	var console = document.getElementById("debuginfo");
	if (console === null) {
		console = document.createElement("div");
		console.id = "debuginfo";
		console.style.background = "#dedede";
		console.style.border = "1px solid silver";
		console.style.padding = "5px";
		console.style.width = "400px";
		console.style.position = "absolute";
		console.style.right = "0px";
		console.style.top = "0px";
		document.body.appendChild(console);
	}
	console.innerHTML += " <p> " + message + " </p> ";
}

抛出错误

function divide(num1, num2) {
	if (typeof num1 != "number" || typeof num2 != "number") {
		throw new Error("divide(): Both arguments must be numbers.");
	}
	return num1 / num2;
}

function assert(condition, message) { //condition是求值结果应该为true的条件,message是条件为false时要抛出的错误
	if (!condition) {
		throw new Error(message);
	}
}

例如:

function divide(num1, num2) {
	assert(typeof num1 == "number" && typeof num2 == "number", 
				"divide(): Both arguments must be numbers.");
	return num1 / num2;
}

xml

将xml解析为DOM文档

DOMParser类型

var parser = new DOMParser();
var xmldom = parser.parseIromString("<root><child/></root>", "text/xml");

alert(xmldom.documentElement.tagName); //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"

var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);

var children = xmldom.getElementsByTagName("child");
alert(children.length); //2
 
var parser = new DOMParser();
var xmldom = parser.parseFromString("<root>", "text/xml");

var errors = xmldom.getElementsByTagName("parsererror");
if (errors.length > 0) {
	alert("Parsing error!");
}

将DOM文档序列化为xml字符串

XMLSerializer类型

var serializer = new XMLSerializer();
var xml = serializer.serializeToString(xmldom);
alert(xml);

IE加载XML文件

var xmldom = createDocument();
xmldom.async = false;
xmldom.load("example.xml");

if (xmldom.parseError != 0) {
	alert("An error occurred:\nError Code: "
	+ xmldom.parseError.errorCode + "\n"
	+ "Line: " + xmlcom.parseError.line + "\n"
	+ "Line Pos: " + xmldom.parseError.linepos + "\n"
	+ "Reason: " + xmldom.parseError.reason);
} else {
	
	alert(xmldom.documentElement.tagName); //"root"
	alert(xmldom.documentElement.firstChild.tagName); //"child"
	
	var anotherChild = xmldom.createElement("child");
	xmldom.documentElement.appendChild(antherChild);

	var children = xmldom.getElementsByTagName("child");
	alert(chilren.length); //2
	
	alert(xmldom.xml);
} 

var xmldom = createDocument();
xmldom.async = true;

xmldom.onreadystatechange = function() {
	if (xmldom.readyState == 4) {
		if (xmldom.parseError != 0) {
			alert("An error occurred:\nError Code: "
			+ xmldom.parseError.errorCode + "\n"
			+ "Line: " + xmlcom.parseError.line + "\n"
			+ "Line Pos: " + xmldom.parseError.linepos + "\n"
			+ "Reason: " + xmldom.parseError.reason);

		} else {
			
			alert(xmldom.documentElement.tagName); //"root"
			alert(xmldom.documentElement.firstChild.tagName); //"child"
	
			var anotherChild = xmldom.createElement("child");
			xmldom.documentElement.appendChild(antherChild);

			var children = xmldom.getElementsByTagName("child");
			alert(chilren.length); //2
	
			alert(xmldom.xml);
		}
	}
};

xmldom.load("example.xml");

跨浏览器处理XML

function parseXml(xml) {
	var xmldom = null;

	if (typeof DOMParser != "undefined") {
		xmldom = (new DOMParser()).parseFromString(xml, "text/xml");
		var errors = xmldom.getElementsByTagName("parsererror");
		if (errors.length) {
			throw new Error("XML parsing error:" + errors[0].textContent);
		}
	} else if (document.implementation.hasFeature("Ls", "3.0)) {
		var implementation = document.implementation;
		var parser = implementation.createLSParser(implementation.MODE_SYNCHRONUS, null);
		var input = implementation.createLSInput();
		input.stringData = xml;
		xmldom = parser.parse(input);	
	} else if (typeof ActiveXobject != "undefined") {
		xmldom = createDocument();
		xmldom.loadXML(xml);
		if (xmldom.parseError != 0) {
			throw new Error("XML parsing error: " + xmldom.parseError.reason);

		}
	} else {
		throw new Error("No XML parser available.");
	}
	
	return xmldom;

}

例如:

var xmldom = null;

try {
	xmldom = parseXml("<root><child/></root>");
} catch (ex) {
	alert(ex.message);
}

跨浏览器序列化XML

function serializeXml(xmldom) {
	
	if (typeof XMLSerializer != "undefined") {
		return (new XMLSerializer()).serizlizeToString(xmldom);
	} else if (document.implementation.hasFeature("LS", "3.0")) {
		var implementation = document.implementation;\
		var serializer = implementation.createLSSerializer();
		return serializer.writeToString(xmldom);
	} else if (typeof xmldom.xml != "undefined") {
		return xmldom.xml;
	} else {
		throw new Error("Could not serialize XML DOM.");
	}
}

例如:

var xml = serializeXml(xmldom);

DOM3级XPath

var supportsXPath = document.implementation.hasFeature("XPath", "3.0");

Ajax

XMLHttpRequest对象(简称XHR)

创建XHR对象

//适用于IE7之前的版本

function createXHR() {
	if (typeof arguments.callee.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 {
				var xhr = new ActiveXObject(versions[i]);
				arguments.callee.activeXString = versions[i];
				return xhr;
			} catch (ex) {
				//跳过
			}
		}
	}

	return new ActiveXObject(arguments.callee.activeXString);
}

IE7、Firefox、Opera、Chrome和Safari都支持原生的XHR对象

var xhr = new XMLHttpRequest();

跨浏览器创建XHR对象

function createXHR() {

	if (typeof XMLHttpRequest != "undefined") {
		return new XMLHttpRequest();
	} else if (typeof ActiveXObject != "undefined") {
		if (typeof arguments.callee.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 {
					var xhr = new ActiveXObject(versions[i]);
					arguments.callee.activeXString = versions[i];
					return xhr;
				} catch (ex) {
					//跳过
				}
			}
		}

		return new ActiveXObject(arguments.callee.activeXString);
	} else {
		throw new Error("No XHR objectt available.");
	}
}

XHR的用法

要调用的第一个方法open()

xhr.open("get", "example.php", false); //启动一个针对example.php的请求

发送同步请求

xhr.open("get", "example.txt", false);
xhr.send(null);

if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
	alert(xhr.statusText);
} else {
	alert("Request was unsuccessful: " + xhr.status);
}

readyState属性

0:未初始化。尚未调用open()方法。
1:启动。已经调用open()方法。
2:发送。已经调用send()方法,但尚未接收到响应。
3:接收。已经接收到部分响应数据。
4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。

var xhr = createXHR();
xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
		if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
			alert(xhr.responseText);
		} else {
			alert("Request was unsuccessful: " + xhr.status);
		}
	}
};
xhr.open("get", "example.txt", true);
xhr.send(null);

HTTP头部信息

setRequestHeader()方法可以设置自定义的请求头部信息

var xhr = createXHR();
xhr.onreadystatechange = function(event) {
	if (xhr.readyState == 4) {
		if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
			alert(xhr.responseText);
		} else {
			alert("Request was unsuccessful: " + xhr.status);
		}
	}
};
xhr.open("get", "example.txt", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);

调用XHR对象的getResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息

而调用getAllResponseHeaders()方法可以取得一个包含所有头部信息的长字符串

var myHeader = xhr.getResponseHeader("MyHeader");
var allHeaders = xhr.getAllResponseHeaders();

GET请求

对于XHR而言,位于传入open()方法的URL末尾的查询字符串必须经过正确的编码才行

查询字符串中每个参数的名称和值都必须使用endoceURIComponent()进行编码,然后才能放到URL的末尾;而且所有名-值对都必须由和好(&)分隔

例如:

xhr.open("get", "example.php?namel=value& name2=value2", true);

辅助向现有URL的末尾添加查询字符串参数

function addURLParam(url, name, value) {
	url += (ul.indeOf("?") == -1 ? "?" : "&");
	url += endodeURIComponent(name) + "=" + encodeURIComponent(value);
	return url;
}

例如:

var url = "example.php";

//添加参数
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");

//初始化请求
xhr.open("get", url, false);

POST请求

通常用于向服务器发送应该被保存的数据

POST请求应该把数据作为请求的主体提交,而GET请求传统上不是这样

xhr.open("post", "example.php", true);

使用XHR模仿表单提交:首先将Content-Type头部信息设置为appliaction/x-www-form-urlencoded,也就是表单提交时的内容类型,其次是以适当格式创建一个字符串

function submitData() {
	var xhr = createXHR();
	xhr.onreadystatechange = function(event) {
		if (xhr.readyState == 4) {
			if ((xhr.status >=200 && xhr.status < 300) || xhr.status == 304) {
				alert(xhr.responseText);	
			} else {
				alert("Request was unsuccessful: " + xhr.status);
			}
		}
	};

	xhr.open("pose", "postexample.php", true);
	xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	var form = document.getElementById("user-info");
	xhr.send(serialize(form));
}

<?php
	header("Content-Type: text/plain");
	echo <<<EOF
Name: {$_POST['user-name']}
Email: {$_POST['user-email']}
EOF;
?>

浏览器差异

IE8为XHR对象添加了一个timeout属性,表示请求在等待响应多少毫秒之后就终止

var xhr = createXHR();
xhr.onreadystatechange = function(event) {
	try {
		if (xhr.readyState == 4) {
			if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
				alert(xhr.responseText);
			} else {
				alert("Request was unsuccessful: " + xhr.status);
			}
		}
	} catch (ex) {
		//假设由ontimeout事件处理程序处理
	}
};

xhr.open("get", "timeout.php", true);
xhr.timeout = 1000;
xhr.ontimeout = function() {
	alert("Request did not return in a second.");
};
xhr.send(null);

Firefox、Opera、Chrome和Safari都实现了load事件替代readystatechange事件

var xhr = createXHR();
xhr.onload = funciton(event) {
	if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
		alert(xhr.responseText);	
	} else {
		alert(Request was unsuccessful: " + xhr.status);
	}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);

为用户创建进度指示器

var xhr = createXHR();
xhr.onload = function(event) {
	if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
		alert(xhr.responseText);	
	} else {
		alert("Request was unsuccessful: " + xhr.status);
	}
};
xhr.onprogress = function(event) {
	var divStatus = document.getElementById("status");
	divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes";
};

xhr.open("get", "altevents.php", true);
xhr.send(null);

跨域请求

XDomainRequest对象

var xdr = new XDomainRequest();
xdr.onload = function() {
	alert(xdr.responseText);
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);

要检测错误,可以像下面这样指定一个onerror事件处理程序

var xdr = new XDdmainRequest();
xdr.onload = funciton() {
	alert(xdr.responseText);
};
xdr.onerror = function() {
	alert("An error occurred.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);

与XHR一样,XDR对象也支持timeout属性以及ontimeout事件处理程序

var xdr = new XDomainRequest();
xdr.onload = function() {
	alert(xdr.responseText);
};
xdr.onerror = function() {
	alert("An error occurred.");
};
xdr.timeout = 1000;
xdr.ontimeout = function() {
	alert("Request took too long.");
}; 
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);

为支持POST请求,XDR对象提供了contentType属性,用来表示发送数据的格式

var xdr = new XDomainRequest();
xdr.onload = function() {
	alert(xdr.responseText);
};
xdr.onerror = function() {
	alert("An error occurred.");
};
xdr.open("post", "http://www.somewhere-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("namel=value1 & name2=value2");

跨域XHR

var xhr = createXHR();
xhr.onreadystatechange = funciton() {
	if (xhr.readyState == 4) {
		if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
			alert(xhr.responseText);
		} else {
			alert("Request was unsuccessful: " + xhr.status);
		}
	}
};
xhr.open("get", "http://www.somewhere-else.com/page/", true);
xhr.send(null);

在Ajax中使用JSON

var jsonText = "{\"name\":\"Nicholas C. Zakas\", \"age\":29, \"author\":true}";
var object = JSON.parse(jsonText, function(key, value) {
	switch(key) {
		case "age": return value + 1;
		case "author": return undefined;
		default: return value;
	}
});

alert(object.age); //30;
alert(object.author); //undefined

假设addressbook.php会以下面的格式返回JSON数据

[
	{
		"name": "Nicholas C. Zakas",
		"email": "nicholas@some-domain-name.com"
	},
	{
		"name": "Jim Smith",
		"email": "Jimsmith@some-domain-name.com"
	},
	{
		"name": "Michael Jones"<
		"email": "mj@some-domain-name.com"
	}
]
可以发送一个Ajax请求取得以上数据,然后在客户端使用下列代码生成相应的<ul/>元素

var xhr = createXHR();
xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
		if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
			var contacts = JSON.parse(xhr.responseText);
			var list = document.getElementById("contacts");
			for (var i=0, len=contacts.length; i < len; i++) {
				var li = document.createElement("li");
				li.innerHTML = "<a href=\"mailto:" + contacts[i].email + "\">" + 
						contacts[i].name + "<\a>";
				list.appendChild(li);
			}
		}
	}
};
xhr.open("get", "addressbook.php", true);
xhr.send(null);

JSON对象的stringify()方法

var contact = {
	name: "Nicholas C.Zakas",
	email: "nicholas@some-domain-name.com"
};

var jsonText = JSON.stringify(contact);
alert(jsonText); //{\"name\":\Nicholas C. Zakas\",\:email\":\"nicholas@some-domain-name.com \"}

var jsonText = JSON.stringify([new Function()], function(key, value) {
	if (value instanceof Function) {
		return "(function)";
	} else {
		return value;
	}
});
alert(jsonText); //"[(function)]"

使用POST请求并将JSON文本传给send()方法,可以将JSON数据发送给服务器

var xhr = createXHR();
var contact = {
	name: "Ted Jones",
	email: "tedjones@some-other-domain.com"
};
xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
		if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
			alert(xhr.responseText);
		}		
	}
};
xhr.open("post", "addcontact.php", true);
xhr.send(JSON.stringify(contact));

作用域安全的构造函数

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, "Sofeware Engineer");
alert(window.name); //" "
alert(person1.name); //"Nicholas"

var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby"

构造函数窃取结合使用原型链或者寄生组合

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); //2

惰性载入函数

function createXHR() {
	if (typeof XMLHttpRequest != "undefined") {
		createXHR = function() {
			return new XMLHttpRequest();
		};
	} else if (typeof ActiveXObject != "undefined") {
		createXHR = function() {
			if (typeof arguments.callee.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 {
						var xhr = new ActiveXObject(versions[i]);
						arguments.callee.activeXString = versions[i];
						return xhr;	
					} catch (ex) {
						//skip
					}
				}
			}

			return new ActiveXObject(arguments.callee.activeXString);
		};
	} else {
		createXHR = function() {
			throw new Error("No XHR object available.");
		};
	}

	return createXHR();
}

函数绑定

var handler = {
	message: "Event handled",

	handleClick: function(event) {
		alert(this.message);
	}
};

var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick);

var handler = {
	message: "Event handled",

	handleClick: function(event) {
		alert(this.message);
	}
};

var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", function(event) {
	handler.handleClick(event);
});

function bind(fn, context) {
	return function() {
		return fn.apply(context, arguments);	
	};
}

bind()的使用

var handler = {
	message: "Event handled",

	handleClick: function(event) {
		alert(this.message);
	}
};

var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));	

例如:

var handler = {
	message: "Event handled",

	handleClick: function(event) {
		alert(this.message + ":" + event.type);
	}
};

var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));

函数柯里化

function add(num1, num2) {
	return num1 + num2;
}

function curriedAdd(num2) {
	return add(5, num2);
}

alert(add(2, 3)); //5
alert(curriedAdd(3)); //8

柯里化函数的通用方式

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

给出所有的函数参数

function add(num1, num2) {
	return num1 + num2;
}

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 + ":" + event.type);
	}
};

var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值