1. 安全的类型检测
// 安全类型检测/* 检测是不是原生的函数 */function isFunction(value) { ???return Object.prototype.toString.call(value) == "[object Function]";}
2.作用域安全的构造函数
// 作用域安全的构造函数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("sundily", 22, ‘Software Engineer‘);
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"]; ???????????for (var index = 0; index < versions.length; index++) { ???????????????try { ???????????????????new ActiveXObject(versions[index]); ???????????????????arguments.callee.activeXString = versions[index]; ???????????????????break; ???????????????} catch (error) { ???????????????} ???????????} ???????} ???????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"]; ???????????????for (var index = 0; index < versions.length; index++) { ???????????????????try { ???????????????????????new ActiveXObject(versions[index]); ???????????????????????arguments.callee.activeXString = versions[index]; ???????????????????????break; ???????????????????} catch (error) { ???????????????????????// skip ???????????????????} ???????????????} ???????????} ???????????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", "MSXML2.XMLHttp.6.0"]; ???????????????for (var index = 0; index < versions.length; index++) { ???????????????????try { ???????????????????????new ActiveXObject(versions[index]); ???????????????????????arguments.callee.activeXString = versions[index]; ???????????????????????break; ???????????????????} catch (error) { ???????????????????????// skip ???????????????????} ???????????????} ???????????} ???????????return new ActiveXObject(arguments.callee.activeXString); ???????}; ???} else { ???????return function () { ???????????throw new Error("no xhr object available"); ???????}; ???}})();
4 函数绑定——一个将函数绑定到指定环境的函数 bind()
/* 函数绑定 */var handler = {message: "event handler",handlerClick: function () {alert(this.message);}};var btn = document.getElementById("mybtn");btn.addEventListener("click", handler.handlerClick.bind(handler), false);
只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,就应该用函数绑定
主要应用与事件处理程序以及setInterval() ?setTimeout() ????比普通函数需要更多的内存,所以最好必要时用
5 .函数柯里化(function currying)——用于创建已经设置好了一个或者多个参数的问题
函数柯里化基本方法和函数绑定时一样的,使用一个闭包返回一个函数
动态创建柯里化:调用另一个函数并为他传入要柯里化的函数和必要的参数
/* 函数柯里化:调用另一个函数并为他传入要柯里化的函数和必要的参数主要工作 就是将返回函数的参数进行排序concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。*/function curry(fn) {var args = Array.prototype.slice.call(arguments, 1);//arguments中第一个参数时fn 所以要从1开始slice()return function () {var innerArgs = Array.prototype.slice.call(arguments);var finalArgs = args.concat(innerArgs);return fn.apply(null, finalArgs);//调用函数fn自己本身,apply的第一个参数就是null,finalArgs就是传入的参数}}es5中bind()方法也实现了柯里化// bind()的柯里化var handler={message:"event handler",handlerClick:function(name,event){alert(this.message+":"+name+":"+event.type);};}var btn=document.getElementById("mybtn");btn.addEventListener(‘click‘,handler.handlerClick.bind(handler,"mybtn"));
6. 防篡改对象
? 1. 不可扩展的对象
var person={name:"sundjly"};Object.preventExtensions(person);//不能再给person添加属性和方法 但仍然可以修改 删除已有的成员// 使用Object.isExtensible()方法确定对象是否可以扩展Object.isExtensible(person);//false
1. 1密封的对象(sealed object):密封对象不可扩展,而且已有属性方法不能删除,
Object.seal(person);Object.isSealed(person);//判断对象是否被密封
1.2冻结对象 Object.freeze(person);//无法对对象做出操作
7. 高级定时器:
js是单线程的,由代码队列先后顺序执行。js中没有任何代码是立即执行的,但一旦进程空闲则尽快执行。
setInterval:这种重复定时器的规则有两个问题:
? 1. 某些间隔会被跳过
? 2. 多个定时器的代码执行之间的间隔可能比预期小
/* 避免setInterval()重复定时器的两个缺点,可以使用链式setTimeout()调用这个模式链式调用setTimeout()函数,每次函数执行的时候都会创建一个新的定时器,第二个setTimeout调用arguments.callee获取当前函数的引用优点:在前一个定时器代码执行之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔,而且它可以保证在下次定时器代码执行之前,至少等待间隔时间,避免了连续运行。*/setTimeout(function() {// 处理函数setTimeout(arguments.callee, interval);}, interval);
具体的例子:
setTimeout(function () { ???var div = document.querySelector("#div"); ???left = parseInt(div.style.left) + 5; ???div.style.left = left + "px"; ???if (left < 200) { ???????setTimeout(arguments.callee, 50); ???}}, 50);
8.Yielding Processes
? 1. 脚本长时间运行的的原因:
????? a. 过长的,过深嵌套的函数调用
????? b. 进行大量处理的循环
? 2. 如果处理不必同步完成,数据不必按顺序完成,那么可以使用定时器分割循环——数组分块(array chunking)
????? a. 基本思路:为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目进行处理,接着在设置另一个定时器
/* 数组分块技术:shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。array变量的本质就是一个“待办事宜”列表,包含要处理的列表*/setTimeout(function () { ???// 取出第一个条目并处理 ???var item = array.shift(); ???process(item); ???// 若还有条目,再设置定时器 ???if (array.length > 0) { ???????setTimeout(arguments.callee, 100); ???}}, 100);
3.
function chunk(array, process, context) { ???setTimeout(function () { ???????var item = array.shift(); ???????process.call(context, item); ???????if (array.length > 0) { ???????????setTimeout(arguments.callee, 100); ???????} ???}, 100);}
一旦某个函数需要花费50ms以上的时间完成,那么最好考虑能否将任务分割为一系列可以使用定时器的小任务
9 函数节流
DOM操作比起非dom操作交互需要更多的内存和CPU时间,连续尝试过多的DOM相关操作可能会导致浏览器挂起,崩溃。尤其是IE中onresize事件,为了绕开这个问题,可以使用定时器对该函数进行节流。
基本思路:代码可以在间断的情况下连续重复的执行,目的只有在执行函数的请求停止一段时间之后才执行:
/* 函数节流: */var processor = { ???timeoutID: null, ???// 实际进行处理的方法 ???performProcessing: function () { ???????// 实际执行的代码 ???}, ???// 初始处理调用的方法 ???process: function () { ???????clearTimeout(this.timeoutID); ???????var that = this; ???????this.tomeoutID = setTimeout(function () { ???????????that.performProcessing(); ???????}, 100); ???}};// 尝试开始执行processor.process();// 简化function throttle(method, context) { ???clearTimeout(method.tId); ???method.tId = setTimeout(function () { ???????method.call(context); ???}, 100);}// 列子function resizeDiv() { ???var div = document.querySelector("#div"); ???div.style.height = div.offsetWidth + "px";}window.onresize = function () { ???throttle(resizeDiv);}
只要代码是周期性执行的,都要用函数节流,控制处理的频率,确保浏览器不会再短时间内进行过多的计算
10 自定义事件:
事件——一种叫观察者的设计模式(创建松散耦合代码的技术)
观察者模式是由两类对象组成:主体和观察者。主体负责发布事件,观察者通过订阅这些事件来观察主体,在DOM中,DOM元素就是主体,你的事件处理程序就是观察者
事件是与DOM交互最常见的方式。
自定义事件的适用场景:当代码中存在多个部分在特定时刻互相交互的情况下
/* 自定义事件 */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 index = 0; index < handlers.length; index++) { ???????????????handlers[index](event); ???????????} ???????} ???}, ???removeHandler: function (type, handler) { ???????if (this.handlers[type] instanceof Array) { ???????????var handlers = this.handlers[type]; ???????????for (var index = 0; index < handlers.length; index++) { ???????????????if (handlers[index] == handler) { ???????????????????break; ???????????????} ???????????} ???????????handlers.slice(index, 1); ???????} ???}};function handleMessage(event) { ???alert("message received" + event.message);}var target = new EventTarget();target.addHandler("message", handleMessage); //添加事件处理程序target.fire({ ???type: "message", ???message: "hello world"}); //触发事件target.removeHandler("message", handleMessage); //删除事件处理程序
12 拖放
/* 拖放<div class="draggable" style="position:absolute; background:red;"></div>*/var DragDrop = function () {// DragDrop对象封装了拖放的所有基本功能,这是一个单例,并使用了模块模式来隐藏某些实现细节 但是该方法没有提供任何方法表示拖动开始,正在拖动,或者已经结束var dragging = null, //存放被拖动的元素diffX = 0,diffY = 0;function handleEvent(event) {// 获取事件目标var target = event.target;// 确定事件类型switch (event.type) {case "mousedown":if (target.className.indexof("draggable") > -1) {dragging = target;diffX = event.clientX - target.offsetLeft;diffY = event.clientY - target.offsetWidth;}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 () {document.addEventListener("mousedown", handleEvent, false);document.addEventListener("mousemove", handleEvent, false);document.addEventListener("mouseup", handleEvent, false);},disable: function () {document.removeEventListener("mousedown", handleEvent, false);document.removeEventListener("mousemove", handleEvent, false);document.removeEventListener("mouseup", handleEvent, false);}}}();
JS高级程序设计之高级技巧
原文地址:http://www.cnblogs.com/sundjly/p/7898991.html