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