jsAdvanced-高级技巧

  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 }();
posted @ 2012-11-24 15:52  LukeLin  阅读(378)  评论(1编辑  收藏  举报