Skip to main content
 首页 » 编程设计

javascript策略模式(Strategy)

2022年07月16日47zhenyulu
  1 <!DOCTYPE html> 
  2 <html> 
  3 <head> 
  4     <title></title> 
  5     <meta charset="UTF-8"/> 
  6 </head> 
  7 <body> 
  8 <img src="1.jpg" alt=""/> 
  9  
 10 <script> 
 11 /* 
 12  设计原则 
 13  
 14  1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。 
 15  
 16  把会变化的部分取出并“封装”起来,好让其他部分不会受到影响。代码变化引起的不经意后果变少,系统变得更有弹性。 
 17  
 18  几乎是每个设计模式的精神所在。所有的模式都提供了一套方法让“系统中的某部分改变不会影响其他部分”。 
 19  
 20  
 21  2.针对接口编程,而不是针对实现编程。 
 22  
 23  针对接口编程真正的意思是“针对超类型(supertype)编程”。 
 24  变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型 
 25  
 26  
 27  3.多用组合,少用继承。 
 28  29  */ 
 30  
 31 /** 
 32  * 策略模式 
 33  * 
 34  * 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 
 35  * 
 36  * 本质: 
 37  * 分离算法,选择实现。 
 38  * 
 39  * 策略模式的重心不是如何实现算法,而是如何组织,调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。 
 40  * 
 41  * 这里鸭子的行为即是算法族。 
 42  * 
 43  * 为了实现让算法能独立于使用它的客户,策略模式引入了一个上下文对象,这个对象负责持有算法,但是不负责绝对具体选用哪个算法,把选择算法的功能交给了客户,由客户选择好具体的算法后,设置到上下文对象中,让上下文对象持有客户选择的算法。当客户通知上下文对象执行功能的时候,上下文对象则转调具体的算法。这样一来,具体的算法和直接使用算法的客户是分离的。 
 44  * 具体的算法和使用它的客户分离以后,使得算法可独立于使用它的客户而变化,并且能够动态地切换需要使用的算法,只要客户端动态地选择使用不同的算法,然后设置到上下文对象中,在实际调用的时候,就可以调用到不同的算法。 
 45  * 
 46  * 1.功能 
 47  * 策略模式的功能是把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换。 
 48  * 策略模式的中心不是如何来实现算法,而是如何组织,调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。 
 49  * 
 50  * 2.策略模式和if-else语句 
 51  * 多个if-else语句表达的就是一个平等的功能结构。而策略模式就是把各个平等的具体实现封装到单独的策略实现类了,然后通过上下文来与具体的策略类进行交互。 
 52  * 因此多个if-else语句可以考虑使用策略模式。 
 53  * 
 54  * 3.算法的平等性 
 55  * 策略模式的很大的特点就是各个策略算法的平等性。所有策略算法在实现上也是相互独立的,相互之间没有依赖的。 
 56  * 所以策略算法是相同行为的不同实现。 
 57  * 
 58  * 4.谁来选择具体的策略算法 
 59  * 一个是在客户端,当使用上下文的时候,由客户端来选择具体的策略算法,然后把这个策略算法设置给上下文。 
 60  * 另一个是由上下文来选择具体的策略算法。 
 61  * 
 62  * 5.运行时策略的唯一性 
 63  * 运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但使用时只能使用一个。 
 64  * 
 65  * 6.增加新的策略 
 66  * 策略模式可以让你很灵活地扩展新的算法。 
 67  * 
 68  * 
 69  * 策略模式的调用顺序 
 70  * 1.先是客户端来选择并创建具体的策略对象。 
 71  * 2.然后客户端创建上下文。 
 72  * 3.接下来客户端就可以调用上下文的方法来执行功能了,在调用的时候,从客户端传入算法需要的参数。 
 73  * 4.上下文接到客户的调用请求,会把这个转发给它持有的Strategy。 
 74  * 
 75  * 
 76  * Context和Strategy的关系 
 77  * 通常是上下文使用具体的策略实现对象,反过来,策略实现对象也可以从山下问获取所需要的数据。 
 78  * 
 79  * 
 80  * 策略模式结合模板方法模式 
 81  * 对于一系列算法的实现上存在公共功能的情况,策略模式可以有以下三种实现方式: 
 82  * 1.在上下文当中实现公共功能,让所有具体的策略算法回调这些方法。 
 83  * 2.将策略的借口改成抽象类,然后在其中实现具体算法的公共功能。 
 84  * 3.为所有的策略算法定义一个抽象的父类,让这个父类去实现策略的接口,然后在这个父类中去实现公共的功能。 
 85  * 如果这个时候发现一系列算法的实现步骤都是一样的,只是在某些局部步骤上有所不用的情况,那就可以在这个抽象类里面定义算法实现的骨架,然后让具体的策略算法去实现变化的部分。这样的一个结构自然就变成策略模式结合模板方法模式了。那个抽象类就成了模板方法模式的模板类。 
 86  */ 
 87  
 88 (function () { 
 89     // 示例代码 
 90  
 91     // 具体算法 
 92     function ConcreteStrategyA() {} 
 93  
 94     ConcreteStrategyA.prototype.algorithmInterface = function () {/*具体算法的实现*/}; 
 95  
 96     function ConcreteStrategyB() {} 
 97  
 98     ConcreteStrategyB.prototype.algorithmInterface = function () {}; 
 99  
100     function ConcreteStrategyC() {} 
101  
102     ConcreteStrategyC.prototype.algorithmInterface = function () {}; 
103  
104     // 上下文对象,通常会持有一个具体的策略对象 
105     function Context(strategy) { 
106         this.strategy = strategy; 
107     } 
108  
109     //上下文对客户端提供的操作接口,可以有参数和返回值 
110     Context.prototype.contextInterface = function () { 
111         // 转调具体的策略对象进行算法运算 
112         this.strategy.algorithmInterface(); 
113     }; 
114  
115 }()); 
116  
117 (function () { 
118     // 容错恢复机制 
119     function DbLog() { 
120         this.log = function (msg) { 
121             if (msg && msg.trim().length > 5) { 
122                 fds;  // make mistake 
123             } 
124             console.log('现在把' + msg + '记录到数据库中'); 
125         }; 
126     } 
127  
128     function FileLog() { 
129         this.log = function (msg) { 
130             console.log('现在把' + msg + '记录到文件中'); 
131         }; 
132     } 
133  
134     function LogContext() { 
135         this.log = function (msg) { 
136             var strategy = new DbLog(); 
137             try { 
138                 strategy.log(msg); 
139             } catch (e) { 
140                 strategy = new FileLog(); 
141                 strategy.log(msg); 
142             } 
143         }; 
144     } 
145  
146     var log = new LogContext(); 
147     log.log('"记录日志"'); 
148     log.log('"再次记录日志"'); 
149  
150 }()); 
151  
152  
153 /* 
154  适用性 
155  
156  1.许多相关的类仅仅是行为有异。“策略”提供了一种同多个行为中的一个行为来配置一个类的方法。 
157  2. 
158  需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。 
159  3.算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。 
160  4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。 
161  */ 
162  
163 /* 
164  优点: 
165  
166  1.定义了一系列算法。 
167  2.避免多重条件语句。 
168  3.更好的扩展性。 
169  
170  缺点: 
171  
172  1.客户必须了解每种策略的不同。 
173  2.增加了对象数目。 
174  3.只适合扁平的算法结构。 
175  对于出现需要嵌套使用多个算法的情况,可以考虑使用装饰者模式,或是变形的职责链模式。 
176  
177  
178  相关模式 
179  策略模式和状态模式 
180  从模式结构上看是一样的,但是实现的功能确实不一样的。 
181  状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。 
182  另外策略模式可以让客户端来选择需要使用的策略算法;而状态模式一般是上下文,或者是在状态实现类里面来维护具体的状态数据们通常不由客户端来制定状态。 
183  
184  策略模式和模板方法模式 
185  可组合使用。模板方法重在封装算法骨架,而策略模式重在分离并封装算法实现。 
186  
187  策略模式和享元模式 
188  可组合使用。 
189  
190  */ 
191  
192  
193 (function () { 
194     // 抽象上下文类 
195     var Duck = function (quackBehavior) { 
196         this.quackBehavior = quackBehavior; 
197     }; 
198     Duck.prototype = { 
199         display: function () { 
200             throw new Error('This is abstract method'); 
201         }, 
202         performQuack: function () { 
203             this.quackBehavior.quack(); 
204         }, 
205         swim: function () { 
206             console.log('All ducks float, even decoys'); 
207         } 
208     }; 
209  
210     // 具体上下文类 
211     var MallardDuck = function () { 
212         Duck.apply(this, arguments); 
213     }; 
214     MallardDuck.prototype.__proto__ = Duck.prototype; 
215     MallardDuck.prototype.display = function () { 
216         console.log('I\'m a real Mallard duck.'); 
217     }; 
218  
219     // 抽象算法类 
220     var QuackBehavior = function () { 
221     }; 
222     QuackBehavior.prototype.quack = function () { 
223         throw new Error('this is an interface'); 
224     }; 
225  
226     // 具体算法类 
227     var MuteQuack = function () { 
228     }; 
229     MuteQuack.prototype.__proto__ = QuackBehavior.prototype; 
230     MuteQuack.prototype.quack = function () { 
231         console.log('slience'); 
232     }; 
233  
234     // 客户端 
235     var MiniDucksSimulator = function () { 
236         this.main(); 
237     }; 
238     MiniDucksSimulator.prototype = { 
239         main: function () { 
240             var mallard = new MallardDuck(new MuteQuack()); 
241             mallard.performQuack(); 
242         } 
243     }; 
244     new MiniDucksSimulator(); 
245  
246     // 动态设定行为 
247     Duck.prototype.setQuackBehavior = function (qb) { 
248         this.quackBehavior = qb; 
249     }; 
250  
251     var ModelDuck = function () { 
252         Duck.apply(this, arguments); 
253     }; 
254     ModelDuck.prototype.__proto__ = Duck.prototype; 
255     ModelDuck.prototype.display = function () { 
256         console.log('T\'m a model duck.'); 
257     }; 
258  
259     var QuackSpeakerPowered = function () { 
260     }; 
261     QuackSpeakerPowered.prototype.quack = function () { 
262         console.log('I\'m quacking with a speaker'); 
263     }; 
264  
265     MiniDucksSimulator.prototype.main = function () { 
266         var model = new ModelDuck(new MuteQuack()); 
267         model.performQuack(); 
268         model.setQuackBehavior(new QuackSpeakerPowered()); 
269         model.performQuack(); 
270     }; 
271     new MiniDucksSimulator(); 
272  
273  
274     // 策略,定义计算报价算法的接口 
275     var Strategy = function () { 
276     }; 
277     Strategy.prototype.calcPrice = function (goodsPrice) { 
278         throw new Error('This is an abstract interface'); 
279     }; 
280  
281     var NormalCustomerStrategy = function () { 
282     }; 
283     NormalCustomerStrategy.prototype.__proto__ = Strategy.prototype; 
284     NormalCustomerStrategy.prototype.calcPrice = function (goodsPrice) { 
285         console.log('对于新客户或者普通客户,没有折扣'); 
286         return goodsPrice; 
287     }; 
288  
289     // 具体算法实现 
290     var OldCustomerStrategy = function () { 
291     }; 
292     OldCustomerStrategy.prototype.__proto__ = Strategy.prototype; 
293     OldCustomerStrategy.prototype.calcPrice = function (goodsPrice) { 
294         console.log('对于老客户,统一折扣5%'); 
295         return goodsPrice * (1 - 0.05); 
296     }; 
297  
298     // 价格管理,主要完成计算向客户所报价格的功能 
299     var Price = function (strategy) { 
300         this.strategy = strategy; 
301     }; 
302     Price.prototype.quote = function (goodsPrice) { 
303         return this.strategy.calcPrice(goodsPrice); 
304     }; 
305  
306     // 选择并创建需要使用的策略对象 
307     var strategy = new OldCustomerStrategy(); 
308     // 创建上下文 
309     var ctx = new Price(strategy); 
310     // 计算报价 
311     var quote = ctx.quote(1000); 
312     console.log('向客户报价:' + quote); 
313 }()); 
314  
315  
316 (function () { 
317     // 表单验证 
318     var r_space = /\s+/; 
319  
320     // HTML转义 
321     var ENCODECHAR = { 
322         '<': '&lt;', 
323         '>': '&gt;', 
324         '&': '&amp;', 
325         '"': '&quot;' 
326     }; 
327  
328     // 验证策略 
329     var VALIDTYPES = { 
330         'nonEmpty': { 
331             validate: function (value) { 
332                 return value !== ''; 
333             }, 
334             msg: '此项不能为空' 
335         }, 
336         'email': { 
337             validate: function (value) { 
338                 return (/^[\w\-]+@[\w\-]+(?:\.[\w\-]+)+$/.test(value)); 
339             }, 
340             msg: function (value) { 
341                 return (value ? '请输入正确格式的邮箱' : '请输入你的邮箱'); 
342             } 
343         }, 
344         'phone': { 
345             validate: function (value) { 
346                 return (/^1[3458]\d{9}$/.test(value)); 
347             }, 
348             msg: function (value) { 
349                 return (value ? '请输入正确格式的手机号码' : '请输入你的手机号码'); 
350             } 
351         } 
352     }; 
353  
354     var formHooks = { 
355         'radio': 'checked', 
356         'checkbox': 'checked' 
357     }; 
358  
359     var formEventsHooks = { 
360         'text': formEventsGetter('blur'), 
361         'textarea': formEventsGetter('blur'), 
362         'checkbox': formEventsGetter('click'), 
363         'select': formEventsGetter('change'), 
364         'radio': formEventsGetter('click') 
365     }; 
366  
367     function formEventsGetter(type) { 
368         return function (el, context, item) { 
369             $(el).on(type, function () { 
370                 context.errHandler = []; 
371                 parseEachEleCfg(item); 
372  
373                 validating(item, context.errHandler); 
374  
375                 context.handleError(); 
376             }); 
377         }; 
378     } 
379  
380     /** 
381      * 验证器构造器 
382      * @param {Object} formInstance 用户自定义规则 
383      * @constructor 
384      */ 
385     function Validator(formInstance) { 
386         var form = formInstance.form; 
387         if (!form) return; 
388  
389         this.form = form; 
390  
391         /** 
392          [{ 
393             elem:elem, 
394             value: '', 
395             type: '' 
396             [optional] ,checker: {checker: func, description: ''} 
397          }, ..] 
398          */ 
399         this.config = []; 
400  
401         this.callbackLists = { 
402             success: [], 
403             failure: [] 
404         }; 
405  
406         /* 
407          this.errHandler; 
408          */ 
409  
410         if (formInstance.types) $.extend(VALIDTYPES, formInstance.types); 
411  
412         this.parsed = false; 
413         this.isDefaultPrevented = false; 
414         this.ajax = typeof formInstance.ajax === 'boolean' ? 
415             formInstance.ajax : true; 
416  
417         if (formInstance.success) this.on('success', formInstance.success); 
418         if (formInstance.failure) this.on('failure', formInstance.failure); 
419         if (formInstance.beforeSend) this.beforeSend = formInstance.beforeSend; 
420  
421         if (formInstance.formElementsEvented) { 
422             this.parseConfig(); 
423             this.parsed = true; 
424             this.addFormEvents(this.config); 
425         } 
426  
427         var removeClassFn = function (e) { 
428             $(e.target).removeClass('processing'); 
429         }; 
430         this.on('success', removeClassFn); 
431         this.on('failure', removeClassFn); 
432  
433         this.submit(); 
434     } 
435  
436     // 防止XSS 
437     Validator.encodeValue = function (value) { 
438         for (var i in ENCODECHAR) { 
439             if (ENCODECHAR.hasOwnProperty(i)) 
440                 value = value.replace(new RegExp(i, 'g'), ENCODECHAR[i]); 
441         } 
442  
443         return value; 
444     }; 
445  
446     Validator.prototype = { 
447         // 为每个表单元素添加事件侦听 
448         addFormEvents: function (cfg) { 
449             var me = this; 
450             var elem, formType, item; 
451             for (var i = 0, len = cfg.length; i < len; i++) { 
452                 item = cfg[i]; 
453                 elem = item.elem; 
454                 formType = elem.type; 
455  
456                 formEventsHooks[formType](elem, me, item); 
457             } 
458         }, 
459         hasErrors: function () { 
460             return !!this.errHandler.length; 
461         }, 
462         on: function (type, cb) { 
463             if (!this.callbackLists[type]) { 
464                 throw new Error('no matched event type'); 
465             } 
466  
467             this.callbackLists[type] = this.callbackLists[type].concat( 
468                     Object.prototype.toString.call(cb) === '[object Array]' ? 
469                     cb : [cb] 
470             ); 
471         }, 
472         off: function (type) { 
473             if (!this.callbackLists[type]) return; 
474  
475             delete this.callbackLists[type]; 
476         }, 
477         emit: function (type, args) { 
478             if (!this.callbackLists[type]) { 
479                 throw new Error('no matched event type'); 
480             } 
481  
482             var list = this.callbackLists[type]; 
483  
484             if (type === 'failure' && args && args[0] && args[0].preventDefault) { 
485                 args[0].preventDefault(); 
486             } 
487  
488             for (var i = 0, len = list.length; i < len; i++) { 
489                 if (typeof list[i] === 'function' && list[i].apply(this.form, args) === false) 
490                     break; 
491             } 
492         }, 
493         submit: function () { 
494             var me = this; 
495  
496             if (!this.form) return; 
497  
498             $(this.form).on('submit', function (e) { 
499                 var $this = $(this); 
500  
501                 if ($this.hasClass('processing')) return; 
502  
503                 $this.addClass('processing'); 
504  
505                 me.isDefaultPrevented = false; 
506                 e._preventDefault = e.preventDefault; 
507                 e.preventDefault = function () { 
508                     e._preventDefault(); 
509                     me.isDefaultPrevented = true; 
510                 }; 
511  
512                 // 解析配置,parsed为false时,可再次解析 
513                 if (!me.parsed) { 
514                     me.parseConfig(); 
515                     me.parsed = true; 
516                 } 
517  
518                 // 验证 
519                 me.validate(); 
520  
521                 // 验证有错误 
522                 if (me.hasErrors()) { 
523                     me.handleError(); 
524  
525                     me.emit('failure', [e]); 
526                     return; 
527                 } 
528  
529                 // ajax提交默认阻止表单提交 
530                 if (me.ajax) { 
531                     e._preventDefault(); 
532                 } 
533  
534                 var def; 
535                 var form = this; 
536  
537                 /* 
538                  执行me.beforeSend方法,在成功,提交之前执行, 
539                  如果返回false就触发失败回调 
540                  可以返回deferred对象,进行异步操作 
541                  */ 
542                 if (me.beforeSend && (def = me.beforeSend()) === false) { 
543                     K.handyWarn({ 
544                         msg: me.beforeSend.errorMsg 
545                     }); 
546  
547                     me.emit('failure', [e]); 
548                     return; 
549                 } 
550  
551                 // 如果是deferred对象,序列执行回调 
552                 if (def && (typeof def.pipe === 'function' || typeof def.then === 'function')) { 
553                     def = def.pipe || def.then; 
554                     // 因为是异步操作,必须阻止默认表单提交,与异步提交表单不同 
555                     if (!e.isDefaultPrevented()) e._preventDefault(); 
556  
557                     return def(function () { 
558                         me.isDefaultPrevented = false; 
559                         me.emit('success', [e]); 
560                         // 提交表单 
561                         if (!me.isDefaultPrevented && !me.ajax) form.submit(); 
562                     }, function () { 
563                         me.emit('failure', [e]); 
564                     }); 
565                 } else { 
566                     me.emit('success', [e]); 
567                 } 
568             }); 
569         }, 
570         validate: function () { 
571             /** 
572              [{ 
573                 elem: elem, 
574                 msg: '' 
575              }, ...] 
576              */ 
577             this.errHandler = []; 
578  
579             var item; 
580  
581             // 遍历配置项 
582             for (var i = 0, len = this.config.length; i < len; i++) { 
583                 item = this.config[i]; 
584  
585                 if (parseEachEleCfg(item) === false) continue; 
586  
587                 validating(item, this.errHandler); 
588             } 
589         }, 
590         // 解析HTML标签中的“data-valid”属性,将有的保存 
591         parseConfig: function () { 
592             var elems = $('*[data-valid]:not([disabled]):not([readonly])', this.form); 
593             var elem, ruler; 
594  
595             for (var i = 0, len = elems.length; i < len; i++) { 
596                 elem = elems[i]; 
597                 ruler = elem.getAttribute('data-valid'); 
598  
599                 if (ruler) 
600                     this.config.push({ 
601                         elem: elem, 
602                         type: ruler 
603                     }); 
604             } 
605         }, 
606         // 处理错误 
607         handleError: function () { 
608             var errs = this.errHandler; 
609  
610             if (errs.length) { 
611                 var head = errs.shift(); 
612                 var elem = head.elem; 
613  
614                 K.handyWarn({ 
615                     msg: head.msg, 
616                     rel: elem, 
617                     relPos: 'right' 
618                 }); 
619  
620                 if (elem.value) { 
621                     elem.select(); 
622                 } else { 
623                     elem.focus(); 
624                 } 
625             } 
626         } 
627     }; 
628  
629     // 验证值,如果不符则保存到错误队列中 
630     function validating(item, errHandler) { 
631         var checkers = item.checker; 
632         var description, checker, value, args, elem; 
633  
634         for (var i = 0, len = checkers.length; i < len; i++) { 
635             checker = checkers[i].checker; 
636             description = checkers[i].description; 
637             elem = item.elem; 
638  
639             value = elem[formHooks[elem.type.toLowerCase()] || 'value']; 
640  
641             // fix IE用value兼容HTML5的placeholder 
642             if (elem.getAttribute('placeholder') === value) { 
643                 value = ''; 
644             } 
645  
646             if (value && typeof value === 'string') { 
647                 value = Validator.encodeValue(value); 
648             } 
649  
650             args = [value].concat(description); 
651  
652             if (!checker.validate.apply(elem, args)) { 
653                 errHandler.push({ 
654                     elem: elem, 
655                     msg: typeof checker.msg === 'function' ? checker.msg.apply(elem, args) : checker.msg 
656                 }); 
657             } 
658         } 
659  
660         elem = null; 
661     } 
662  
663     var r_brackets = /^([\w-]+)(?:\(([^)]+)\)|)$/; 
664  
665     function parseEachEleCfg(item) { 
666         if (!(item.checker && item.checker.length)) { 
667             var type, description, checker; 
668             var types = item.type && item.type.split(r_space) || []; 
669  
670             if (!types.length) return false; 
671  
672             // 单个元素可以有多个checker,以空格分隔,且单个checker可有相应的描述语 
673             // “charLen(24)”, 括号里面是描述语, 
674             // 描述语用在错误信息中 
675             item.checker = []; 
676             for (var i = 0, len = types.length; i < len; i++) { 
677                 type = types[i].match(r_brackets); 
678                 if (!type) continue; 
679                 checker = VALIDTYPES[type[1]]; 
680                 description = type[2] && type[2].split(',') || []; 
681  
682                 if (!checker) { 
683                     __console.error('没有相应的验证规则:' + type); 
684                     continue; 
685                 } 
686  
687                 item.checker.push({ 
688                     checker: checker, 
689                     description: description 
690                 }); 
691             } 
692         } 
693  
694         return true; 
695     } 
696 }()); 
697 </script> 
698 </body> 
699 </html>

本文参考链接:https://www.cnblogs.com/webFrontDev/archive/2013/05/26/3100220.html