Skip to content

Latest commit

 

History

History
705 lines (623 loc) · 15.4 KB

22 高级函数.md

File metadata and controls

705 lines (623 loc) · 15.4 KB

22 高级技巧

22.1 高级函数

22.1.1 安全的类型检测

typeofinstanceof的局限

function isArray(value) {
	return Object.prototype.toString.call(value) == '[object Array]';
}

function isFunction(value) {
	return Object.prototype.toString.call(value) == '[object Function]';
}

function isRegExp(value) {
	return Object.prototype.toString.call(value) == '[object RegExp]';
}

var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) = '[object JSON]';

22.1.2 作用域安全的构造函数

function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
}
var person = new Person('n', 29, 'Engineer'); //没问题

var person = Person('n', 29, 'Engineer'); //this指向window
alert(window.name); //'n'
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() {
			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 revt = new Rect(5, 10);
console.log(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 revt = new Rect(5, 10);
console.log(rect.sides); //2

22.1.3 惰性载入函数

避免执行不必要的代码

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"],
				i, len;
			for(i = 0, len = versions.length; i < len; i++) {
				try {
					new ActiveXObject(versions[i]);
					arguments.callee.activeXString = versions[i];
					break;
				} catch(e) {
					//TODO handle the exception
				}
			}
		}
		return new ActiveXObject(arguments.callee.activeXString);
	} else {
		throw new Error('no xhr object available')
	}
}
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"],
					i, len;
				for(i = 0, len = versions.length; i < len; i++) {
					try {
						new ActiveXObject(versions[i]);
						arguments.callee.activeXString = versions[i];
						break;
					} catch(e) {
						//TODO handle the exception
					}
				}
			}
			return new ActiveXObject(arguments.callee.activeXString);
		};

	} else {
		createXHR = function() {
			throw new Error('no xhr object available')

		};

	}
	return createXHR();
}
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(e) {
						//TODO handle the exception
					}
				}
			}
			return new ActiveXObject(arguments.callee.activeXString);
		};

	} else {
		return function() {
			throw new Error('no xhr object available')
		};
	}
})();

22.1.4 函数绑定

var handler = {
	msg: 'event handled',
	handleClick: function(event) {
		console.log(this.msg);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', function(event) {
	handler.handleClick(event);//使用闭包保存this
});
function bind(fn, context) {
	return function() {
		return fn.apply(context, arguments);
	};
}

var handler = {
	msg: 'event handled',
	handleClick: function(event) {
		console.log(this.msg);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler));

var handler = {
	msg: 'event handled',
	handleClick: function(event) {
		console.log(this.msg + ':' + event.type);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler));
//es5
var handler = {
	msg: 'event handled',
	handleClick: function(event) {
		console.log(this.msg + ':' + event.type);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', handler.handleClick.bind(handler));

22.1.5 函数柯里化

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

function curriedAdd(num2) {
	return add(5, num2);
}
console.log(add(2, 3)); //5
console.log(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);
	};
} //没考虑执行环境
//demo
function add(num1, num2) {
	return num1 + num2;
}
var curriedAdd = curry(add, 5);
console.log(curriedAdd(3)); //8

var curriedAdd = curry(add, 5, 12);
console.log(curriedAdd()); //17
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(content, finalArgs);
	};
}
var handler = {
	msg: 'event handled',
	handleClick: function(name, event) {
		console.log(this.msg + ':' + name + ':' + event.type);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler, 'my-btn'));

//es5 bind

var handler = {
	msg: 'event handled',
	handleClick: function(name, event) {
		console.log(this.msg + ':' + name + ':' + event.type);
	}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', handler.handleClick.bind(handler, 'my-btn'));

22.2 防篡改对象

22.2.1 不可扩展对象

Object.preventExtensions(object),不能给对象添加新属性和方法了

22.2.2 密封的对象

Object.seal(objcet),不可扩展,且已有成员的[[Configurable]]特性将被设置为false

22.2.3 冻结的对象

Object.freeze(objcet),不可扩展,又是密封的,且对象的[[Writable]]特性将被设置为false

22.3 高级定时器

22.3.1 重复的定时器

setTimeout(function() {
	//处理中
	setTimeout(arguments.callee, interval);
}, interval);

在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔

22.3.2 Yielding Processes

//一般循环
for(var i = 0, len = data.length; i < len; i++) {
	process(data[i]);
}

如果不需要同步完成,且不需按顺序完成,则可以使用定时器分割这个循环,叫做数组分块

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(item);
		//若还有条目,再设置另一个定时器
		if(array.length > 0) {
			setTimeout(arguments.callee, 100);
		}
	}, 100);
}
chunk(data,concat(),prinValue);

22.3.3 函数节流

间隔执行函数,比如用在onresize事件处理程序之类的

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(content);
	}, 100);
}
//demo
window.onresize = function() {
	var div = document.getElementById('myDiv');
	dic.style.height = div.offsetWidth + 'px';
};

//butter
function resizeDiv() {
	var div = document.getElementById('myDiv');
	dic.style.height = div.offsetWidth + 'px';
}
window.onresize = function() {
	throttle(resizeDiv);

22.4 自定义事件

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) {
			if(var i = 0, len = handlers.length; i < len; i++) {
				if(handlers[i] === handler) {
					break;
				}
			}
			handlers.splice(i, 1);
		}
	}
};
//demo
function handleMessage(event) {
	console.log('msg received:' + event.message);
}
//创建一个对象
var target = new EventTarget();
//添加一个事件处理程序
target.addHandler('message', handleMessage);
//触发事件
target.fire({
	type: 'message',
	message: 'hello'
});
//删除事件处理程序
target.removeHandler('message', handleMessage);
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: 'hello'
	});
};
function handleMessage(event) {
	console.log(event.target.name + 'says:' + event.message);
}
//创建新person
var person = new Person('N', 22);
//添加一个事件处理程序
target.addHandler('message', handleMessage);
//再该对象上调用1个方法,它触发消息事件
person.say('hi');

22.5 拖放

//onmousemove事件
EventUtil.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.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;
			default:
				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);
		}
	}
}();

记得绝对定位哦

22.5.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.offsetLTop;

				}
				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;
			default:
				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);
		}
	}
}();

22.5.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.offsetLTop;
					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;
			default:
				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('drags', 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 + 'to(' + event.x + ',' + event.y + ')';
});