Skip to main content
 首页 » 编程设计

js高级应用技巧

2022年07月16日136freeliver54
  1 /*------------ 高级函数 -------------*/ 
  2 //作用域安全的构造函数 
  3 function Person(name, age, job) { 
  4     this.name = name; 
  5     this.age = age; 
  6     this.job = job; 
  7 } 
  8 var person = new Person("Nicholas", 29, "Software Engineer"); 
  9 console.log(person.name);   //Nicholas 
 10 console.log(person.age);    //29 
 11 /*当没有使用new操作符来调用构造函数时,由于该this对象是在运行是绑定的,所以直接调用Person(),this会映射到全局对象window上,导致错误对象属性的意外增加。由于window内置了name属性,因此很有可能出错*/ 
 12 var person1 = Person("Nicholas", 29, "Software Engineer");  //作为普通函数调用,将属性添加到window对象中 
 13 console.log(window.name);   //Nicholas 
 14 console.log(window.age);    //29 
 15 console.log(window.job);    //Software Engineer 
 16  
 17 //解决:首先确认this对象是正确类型的实例,如果不是,会创建新的实例并返回 
 18 function Person(name, age, job) { 
 19     if (this instanceof Person) { 
 20         this.name = name; 
 21         this.age = age; 
 22         this.job = job; 
 23     } else { 
 24         return new Person(name, age, job); 
 25     } 
 26 } 
 27 var person1 = Person("Nicholas", 29, "Software Engineer"); 
 28 console.log(window.name);   //"" 
 29 console.log(person1.name);  //Nicholas 
 30 var person2 = new Person("Shelly", 34, "Teacher"); 
 31 console.log(person2.name);  //Shelly 
 32  
 33 //关于作用与安全的构造函数注意: 
 34 //如果你是用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏 
 35 function Polygon(sides) { 
 36     if (this instanceof Polygon) { 
 37         this.sides = sides; 
 38         this.getArea = function () { 
 39             return 0; 
 40         }; 
 41     } else { 
 42         return new Polygon(sides); 
 43     } 
 44 } 
 45 function Rectangle(width, height) { 
 46     Polygon.call(this, 2); 
 47     this.width = width; 
 48     this.height = height; 
 49     this.getarea = function () { 
 50         return this.width * this.height; 
 51     }; 
 52 } 
 53 var rect = new Rectangle(5, 10); 
 54 console.log(rect.sides);    //undefined 
 55  
 56 //使用原型链或寄生组合解决这个问题 
 57 function Polygon(sides) { 
 58     if (this instanceof Polygon) { 
 59         this.sides = sides; 
 60         this.getArea = function () { 
 61             return 0; 
 62         }; 
 63     } else { 
 64         return new Polygon(sides); 
 65     } 
 66 } 
 67 function Rectangle(width, height) { 
 68     Polygon.call(this, 2); 
 69     this.width = width; 
 70     this.height = height; 
 71     this.getArea = function () { 
 72         return this.width * this.height; 
 73     }; 
 74 } 
 75 //继承Polygon,一个Rectangle实例,同时也是一个Polygon实例 
 76 Rectangle.prototype = new Polygon(); 
 77 var rect = new Rectangle(5, 10); 
 78 console.log(rect.sides);    //2 
 79  
 80  
 81 //惰性载入函数 
 82 function createXHR() { 
 83     if (typeof XMLHttpRequest !== "undefined") { 
 84         return new XMLHttpRequest(); 
 85     } else if (typeof ActiveXObject !== "undefined") { 
 86         if (typeof arguments.callee.activeXString !== "string") { 
 87             var version = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"]; 
 88             for (var i = 0, len = versions.length; i < len; i++) { 
 89                 try { 
 90                     var xhr = new ActiveXObject(versions[i]); 
 91                     arguments.callee.activeXString = versions[i]; 
 92                     return xhr; 
 93                 } catch (ex) { 
 94                     //跳过 
 95                 } 
 96             } 
 97         } 
 98         return new ActiveXObject(arguments.callee.activeXString); 
 99     } else { 
100         throw new Error("No XHR object available"); 
101     } 
102 } 
103 /*每次调用createXHR()的时候,它都要对浏览器所支持的能力仔细检查,即使每次调用时分支的结果都不变。*/ 
104 //解决方法:惰性载入技巧 
105 //表示函数执行的分支仅会发生一次,在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数 
106 function createXHR() { 
107     if (typeof XMLHttpRequest !== "undefined") { 
108         createXHR = function () { 
109             return new XMLHttpRequest(); 
110         }; 
111     } else if (typeof ActiveXObject !== "undefined") { 
112         createXHR = function () { 
113             if (typeof arguments.callee.activeXString !== "string") { 
114                 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"]; 
115                 for (var i = 0, len = versions.length; i < len; i++) { 
116                     try { 
117                         var xhr = new ActiveXObject(versions[i]); 
118                         arguments.callee.activeXString = versions[i]; 
119                         return xhr; 
120                     } catch (ex) { 
121  
122                     } 
123                 } 
124             } 
125             return new ActiveXObject(arguments.callee.activeXString); 
126         }; 
127     } else { 
128         createXHR = function () { 
129             throw new Error("No XHR object available"); 
130         }; 
131     } 
132     return createXHR();     //调用新赋的函数 
133 } 
134  
135 //函数绑定 
136 //函数绑定要创建一个函数,可以在特定环境中以指定参数调用另一个函数 
137 //该技巧长航和回调函数与事件处理程序一起使用,以便将函数作为变量传递的同时保留代码执行环境 
138 var handler = { 
139     message:"Event handled", 
140     handlerClick:function (event) { 
141         alert(this.message); 
142     } 
143 }; 
144 var btn = document.getElementById("my-btn'"); 
145 EventUtil.addHandler(btn, "click", handler.handlerClick);     //undefined, this指向了btn 
146 //解决:使用闭包保存函数的环境 
147 var handler = { 
148     message:"Event handled", 
149     handlerClick:function (event) { 
150         alert(this.message); 
151     } 
152 }; 
153 var btn = document.getElementById("my-btn'"); 
154 EventUtil.addHandler(btn, "click", function (event) { 
155     handler.handlerClick(event);            //Event handled, 内部闭包函数切断了this对外部的指向,保留了执行环境 
156 }); 
157  
158 /** 
159  * 将函数绑定到指定环境的函数 
160  * @param fn 函数 
161  * @param context 环境 
162  * @return {Function} 
163  */ 
164 function bind(fn, context) { 
165     return function () { 
166         return fn.apply(context, arguments); 
167     }; 
168 } 
169 /* 
170  在bind()中创建了一个闭包,闭包使用apply()调用传入函数,并给apply()传递context对象和参数。注意这里使用的arguments对象是内部函数的,而非bind()的。当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。bind()函数按如下方式使用: 
171  */ 
172 var handler = { 
173     message:"Event handled", 
174     handlerClick:function (event) { 
175         alert(this.message); 
176     } 
177 }; 
178 var btn = document.getElementById("my-btn'"); 
179 EventUtil.addHandler(btn, "click", bind(handler.handlerClick, handler)); 
180 /* 
181  旦要将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就凸显出来了。它们主要用于事情处理程序以及setTimeout()和setInterval()。然而,被绑定函数与普通函数相比有更多的开销——它们需要更多的内存,同时也因为多重函数调用稍微慢一点——所以最好只在必要时使用。 
182  */ 
183  
184  
185 //函数柯里花 
186 //用于创建已经设置好了一个或多个参数的函数 
187 /* 
188  函数柯里花的基本方法和函数绑定是一样的,使用一个闭包返回一个函数。 
189  两者区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数 
190  */ 
191 function add(num1, num2) { 
192     return num1 + num2; 
193 } 
194 function curriedAdd(num2) { 
195     return add(5, num2); 
196 } 
197 console.log(add(2, 3));  //5 
198 console.log(curriedAdd(3)); //8 
199 //尽管并非柯里化的函数,但它很好的展示其概念 
200  
201 //创建柯里花函数的通用方式: 
202 function curry(fn) { 
203     /* 
204      将外部函数参数转换成数组,并获取第一个参数之后的所有参数(不包括第一个,因为第一个是函数) 
205      */ 
206     var args = Array.prototype.slice.call(arguments, 1); 
207     return function () { 
208         //获取所有内部函数参数 
209         var innerArgs = Array.prototype.slice.call(arguments); 
210         //外部参数和内部参数组合 
211         var finalArgs = args.concat(innerArgs); 
212         //返回fn函数,并将最后的参数传入 
213         return fn.apply(null, finalArgs); 
214     }; 
215 } 
216  
217 //example: 
218 function add(num1, num2) { 
219     return num1 + num2; 
220 } 
221 var curriedAdd = curry(add, 5); 
222 console.log(curriedAdd(3)); //8 
223 //或者 
224 function add(num1, num2) { 
225     return num1 + num2; 
226 } 
227 var curriedAdd = curry(add, 5, 12); 
228 console.log(curriedAdd());      //17 
229  
230 //更为复杂的bind()函数 
231 function bind(fn, context) { 
232     //获取外部函数第二个之后的所有参数的数组 
233     var args = Array.prototype.slice.call(arguments, 2); 
234     return function () { 
235         //获取内部fn函数的所有参数 
236         var innerArgs = Array.prototype.slice.call(arguments); 
237         //外部参数+内部参数 
238         var finalArgs = args.concat(innerArgs); 
239         //返回fn函数,并传入最后的所有参数,执行环境指向context 
240         return fn.apply(context, finalArgs); 
241     }; 
242 } 
243  
244 var handler = { 
245     message:"Event handled", 
246     handleClick:function (name, event) { 
247         alert(this.message + ":" + name + ":" + event.type); 
248     } 
249 }; 
250 var btn = document.getElementById("my-btn"); 
251 EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));   //Event handled:my-btn:click 
252 //柯里化函数和绑定函数提供了强大的动态函数创建功能,但不该滥用,会带来额外开销 
253  
254  
255 /****************  自定义事件*******************/ 
256 /** 
257  * 绑定函数和柯里化函数 
258  * @param fn 执行的函数 
259  * @param context 绑定的环境 
260  * @return {Function} 返回带参数的函数 
261  */ 
262 function bind(fn, context) { 
263     //获取外部函数第二个之后的所有参数的数组 
264     var args = Array.prototype.slice.call(arguments, 2); 
265     return function () { 
266         //获取内部fn函数的所有参数 
267         var innerArgs = Array.prototype.slice.call(arguments); 
268         //外部参数+内部参数 
269         var finalArgs = args.concat(innerArgs); 
270         //返回fn函数,并传入最后的所有参数,执行环境指向context 
271         return fn.apply(context, finalArgs); 
272     }; 
273 } 
274  
275 //对非DOM元素实现自定义事件 
276 function EventTarget() { 
277     //存储事件处理程序的属性对象 
278     this.handlers = {}; 
279 } 
280 EventTarget.prototype = { 
281     //重新将constructor指向EventTarget构造函数 
282     constructor:EventTarget, 
283     /** 
284      * 注册给定类型时间的事件处理程序 
285      * @param type 自定义的事件类型 
286      * @param handler 处理该事件类型的函数 
287      */ 
288     addHandler:function (type, handler) { 
289         //如果handlers属性中没有存在一个针对该事件类型的数组 
290         //则创建一个新的。(一个事件类型可以对应多个事件处理函数,因此要用数组保存) 
291         //然后使用push()将该处理程序添加到数组的末尾 
292         if (typeof this.handlers[type] === "undefined") { 
293             this.handlers[type] = []; 
294         } 
295         this.handlers[type].push(handler); 
296     }, 
297     /** 
298      * 触发事件 
299      * @param event 一个至少包含type属性的对象 
300      */ 
301     fire:function (event) { 
302         //给event对象设置一个target属性 
303         if (!event.target) { 
304             event.target = this; 
305         } 
306         //如果该事件类型的执行函数存在, 
307         //调用各个函数,并给出event对象 
308         if (this.handlers[event.type] instanceof Array) { 
309             var handlers = this.handlers[event.type]; 
310             for (var i = 0, len = handlers.length; i < len; i++) { 
311                 handlers[i](event); 
312             } 
313         } 
314     }, 
315     /** 
316      * 注销事件类型的事件处理程序 
317      * @param type 事件类型 
318      * @param handler 执行的函数 
319      */ 
320     removeHandler:function (type, handler) { 
321         if (this.handlers[type] instanceof Array) { 
322             var handlers = this.handlers[type]; 
323             //搜索事件处理程序的数组找到要删除的处理程序的位置 
324             //找到了就退出循环,然后将该项目丛数组中删除 
325             for (var i = 0, len = handlers.length; i < len; i++) { 
326                 if (handlers[i] === handler) { 
327                     break; 
328                 } 
329             } 
330             handlers.splice(i, 1); 
331         } 
332     } 
333 }; 
334 //创建一个新对象 
335 var target = new EventTarget(); 
336 //添加一个事件处理程序 
337 target.addHandler("message", handlerMessage); 
338 //触发事件 
339 target.fire({ 
340     type:"message", 
341     message:"Hello world" 
342 }); //Message received:Hello world 
343  
344 //删除事件处理程序 
345 target.removeHandler("message", handlerMessage); 
346 //再次,应没有处理程序 
347 target.fire({ 
348     type:"message", 
349     message:"Hello world" 
350 }); 
351  
352 //其他对象可以继承EvntTarget并获取这个行为 
353 function Person(name, age) { 
354     //继承属性 
355     EventTarget.call(this); 
356     this.name = name; 
357     this.age = age; 
358 } 
359 //浅复制 
360 function object(o) { 
361     function F() { 
362     } 
363  
364     F.prototype = o; 
365     return new F(); 
366 } 
367 function inheritPrototype(subType, superType) { 
368     var prototype = object(superType.prototype);      //创建对象 
369     prototype.constructor = subType;      //增强对象 
370     subType.prototype = prototype;        //指定对象 
371 } 
372 //继承原型中的方法 
373 inheritPrototype(Person, EventTarget); 
374 Person.prototype.say = function (message) { 
375     this.fire({ 
376         type:"message", 
377         message:message 
378     }); 
379 } 
380  
381 function handleMessage(event) { 
382     alert(event.target.name + " says:" + event.message); 
383 } 
384 //创建新person 
385 var personN = new Person("NIcholas", 29); 
386 //添加一个事件处理程序 
387 personN.addHandler("message", handleMessage); 
388 //在该对象上调用1个方法,触发消息事件 
389 personN.say("Hi there"); 
390  
391 //拖放 
392 var DragDrop = function () { 
393     var dragging = null; 
394  
395     function handleEvent(event) { 
396         //获取事件和目标 
397         event = EventUtil.getTarget(event); 
398         var target = EventUtil.getTarget(event); 
399  
400         //确定事件类型 
401         switch (event.type) { 
402             case "mousedown": 
403                 if (target.className.indexOf("draggable") > -1) { 
404                     dragging = target; 
405                 } 
406                 break; 
407             case "mousemove": 
408                 if (dragging !== null) { 
409                     //get event 
410                     event = EventUtil.getEvent(event); 
411                     //assign location 
412                     dragging.style.left = event.clientX + "px"; 
413                     dragging.style.top = event.clientY + "px"; 
414                 } 
415                 break; 
416             case "mouseup": 
417                 dragging = null; 
418                 break; 
419         } 
420     } 
421  
422     //公公接口 
423     return { 
424         enable:function () { 
425             EventUtil.addHandler(document, "mousedown", handleEvent); 
426             EventUtil.addHandler(document, "mousemove", handleEvent); 
427             EventUtil.addHandler(document, "mouseup", handlerEvent); 
428         }, 
429         disable:function () { 
430             EventUtil.removeHandler(document, "mousedown", handleEvent); 
431             EventUtil.removeHandler(document, "mousemove", handleEvent); 
432             EventUtil.removeHandler(document, "mouseup", handlerEvent); 
433         } 
434     }; 
435 }();

本文参考链接:https://www.cnblogs.com/webFrontDev/archive/2012/11/24/2786029.html