JS设计模式
来源:互联网 发布:python iso8601 编辑:程序博客网 时间:2024/05/16 06:07
在学习使用Javascript之前,我的程序猿生涯里面仅有接触的编程语言是C#跟Java——忽略当年在大学补考了N次的C与VB。
从静态编程语言,转到动态语言JS,刚开始的时候,遇到不少困难与错误。可能因为先入为主,在JS编程之中,往往不由自主地以C#的逻辑、编程思路、设计模式进行JS开发。
时间长了,渐渐发现JS设计模式对于前端开发的重要性,但在分享JS设计模式之前,希望首先分享几点个人觉得很重要的JS设计模式基础。有些地方,可能研究得也不是很深入,有什么错误的话,望指正。
动态类型语言
静态类型语言,在编译阶段,就需要确定变量的类型。但动态类型语言,只有程序运行时,根据当时语言执行环境,变量才会赋予其类型。
所以C#,不能抛开runtime与编译器单独运行,Java也离不开JVM。同时也有一大堆问题需要考虑的,跨平台、类型转换、反射、Ioc、接口编程、抽象化、动态代理等等一大堆概念技术的产生,还不是因为静态语言的强类型特性,需要更多的手段让其得到灵活多态的特性。
<code class="hljs" style="margin: 0px; display: block; overflow-x: auto; color: rgb(0, 0, 0); padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-class" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Main</span> </span>{ <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Function</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//在编译的时候,.net runtime必须要知道dog是一只狗</span> Dog dog = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> Dog(); Console.WriteLine(dog.Name); } }</code>
但,JS只需要个文本编辑器,没有编译通过不通过的概念,加载了浏览器就能跑。
<br style="margin: 0px; padding: 0px;" /> var a = 1;<br style="margin: 0px; padding: 0px;" /> console.log(typeof a);//number<br style="margin: 0px; padding: 0px;" /> var a = "a";<br style="margin: 0px; padding: 0px;" /> console.log(typeof a);//string<br style="margin: 0px; padding: 0px;" />
原型模式
ECMAScript 5之前JS是没有类的,W3C的大牛是有所发现的,所以ES6会新的类语法,但是各大浏览器(IE)猴年马月才跟进就不得而知了——绝对不是针对IE。
在静态类型语言开发中,类是任意实体的结构,当需要这个实体的时候,系统依照这个结构,“画”出这个==对象==。
譬如,要设计车,首先是要设计稿,设计稿就是类,然后照着设计稿生产的每一台车就是具体的对象。
所以,静态类型语言当中,得到一个对象的最关键就是先有这个对象的设计稿(类),就是知道它的类型。
但是,JS不一样。
JS是以原型模式作为设计基础。得到一个对象之前,我们不关心对象属于什么类型,它的设计稿是怎么样的,JS是通过拷贝一个其他对象而获得对象的。
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//定义超人是超人,超人可以飞</span><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> superman = { name: <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superman"</span>, fly: <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.name + <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">" is flying"</span>); }};superman.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//superman is flying</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//万一,想要个妹子呢,搞个女超人如何?女超人也会飞?要不就复制一个超人的对象吧。通过Object.create复制一个超人,但定义它是女超人。</span><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> superwomen = <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">Object</span>.create(superman);superwomen.name = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superwomen"</span>;superwomen.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//superwomen is flying</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//DC的不喜欢,来个漫威吧,再搞个钢铁侠?钢铁侠也会飞</span><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> ironman = <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">Object</span>.create(superman);ironman.name = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"ironman"</span>;ironman.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//ironman is flying</span></code>
千万别误解了superman就是类,而superwomen、ironman就是superman实例化得到的对象,superman、superwomen、ironman都是对象,superwomen、ironman都是superman对象拷贝的结果。
==1、所有的数据都是对象==
上面的例子,通过var obj={}
创建对象,其等价于var obj=new Object()
。Object对象可以创建对象,但不仅是Object才是对象,所有的数据都是对象,包括Number、Boolean、String、Function、Object。这是原型模式很重要的一条原则。
正是这一点,我们也可以利用function定义对象:function obj(){};
。
不过与var obj=new Object()
不同,function定义的对象,是一个带构造器的函数,通过new关键字调用构造器可以获取其原型对象。参考以下例子。
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//定义一个对象</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> superman = { name: <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superman"</span>, fly: <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.name + <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">" is flying"</span>); } }; superman.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//superman is flying</span> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//定义一个函数,同时也是一个带构造器的函数</span> <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">superman2</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.name = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superman"</span>; <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.fly = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.name + <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">" is flying"</span>); }; }; <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//错误:superman2是函数,fly()不是函数</span> superman2.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//error:undefined is not a function</span> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//错误:superman2()是调用函数,但函数没有返回带fly()函数的对象</span> superman2().fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//error:Cannot read property 'fly' of undefined</span> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//正确:new不是C#实例化关键字,new是调用superman2的构造器并返回其对象</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> sm = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> superman2(); sm.fly();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//superman is flying</span></code>
==2、对象的属性请求,会从对象传递到对象原型==
当需要获得对象属性时,会首先请求对象本身,如果,对象本身没有属性可以响应,则响应其对象原型prototype;如果prototype也没有属性可以响应,则进一步请求原型的原型,从而形成一条原型链。当然,原型链的层级不能太长,具体长度没有研究过,我觉得3级也差不多了。
<br style="margin: 0px; padding: 0px;" /> //定义人<br style="margin: 0px; padding: 0px;" /> function human() {<br style="margin: 0px; padding: 0px;" /> this.sexy = "male";<br style="margin: 0px; padding: 0px;" /> };<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//定义所有英雄的原型</span> <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">heroBase</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.trait = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"strong cool"</span>; }; <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//英雄的原型是人</span> heroBase.prototype = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> human(); <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//定义超人</span> <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">superman</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.fly = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superman"</span>); }; }; <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//超人的原型就是英雄</span> superman.prototype = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> heroBase(); <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> superman().sexy);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//male</span> <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> superman().trait);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//strong cool</span></code>
这个步骤就是:
- 超人的性别是神马?
- 光看超人不知道,要看超人原型:英雄
- 看英雄也不知道,要看英雄原型:人
人的sexy属性告诉我们,他是男人
好吧,也许你发现了,上面例子是有bug的,超人是外星人;但你肯定发现了,这不就是继承吗?继承都有了,多态还能远吗?
==3、JS就是通过原型模式实现继承的,且是单一继承==
我个人的理解是,JS的继承跟C#的继承虽然实现形式不同,但有一点原则是相同的,单一继承非多继承。如上例子换成以下代码:superman.prototype = new heroBase();
superman.prototype = new human();
。最终结果是,超人的原型首先定为英雄,但又被人所覆盖了。符合单一继承的原则。多态
JS的多态是通过两点实现的:1、原型模式实现继承;2、动态类型实现泛型
回想一下,我们刚学习C#的那个经典例子
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs cs" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Hero</span>{ <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">virtual</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Fly</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ Console.WriteLine(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"hero fly"</span>); }}<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">SuperMan</span> : <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Hero</span>{ <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">override</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Fly</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">base</span>.Fly(); Console.WriteLine(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"SuperMan fly"</span>); }}<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">IronMan</span> : <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Hero</span>{ <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">override</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Fly</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">base</span>.Fly(); Console.WriteLine(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"IronMan fly"</span>); }}<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">Main</span>{ <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">MainFunction</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ LetsFly(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> SuperMan()); LetsFly(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> IronMan()); } <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">LetsFly</span><span class="hljs-params" style="margin: 0px; padding: 0px;">(Hero hero)</span> </span>{ hero.Fly(); }}</code>
再来看看JS的实现
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">superman</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.fly = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"superman fly"</span>); };}<span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">bird</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">this</span>.fly = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(<span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"bird fly"</span>); };};<span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">letsFly</span><span class="hljs-params" style="margin: 0px; padding: 0px;">(hero)</span> </span>{ hero.fly();}<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> sm = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> superman();<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> b = <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">new</span> bird();letsFly(sm);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//superman fly</span>letsFly(b);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//bird fly;</span></code>
LetsFly()方法,C#跟JS接受的同样都是Hero这个对象
对于C#而言,LetsFly方法要求不管是神马Hero,只要实现了基类Hero的Fly,它就能执行LetsFly方法,以后即使有再多的英雄,继承Hero即可。
而JS,LetsFly方法要求不管是神马对象,只要有Fly方法,它就能执行LetsFly方法,以后即使有再多的英雄,英雄会Fly即可。
对比之下,优缺点是显而易见的,前者受到束缚,尤其在复杂类型之间继承与引用提现的更加凸显,但类型安全性高;
后者无拘无束,但类型安全性低,如上例子,LetsFly传入的可能不是Hero,很可能是Bird。
闭包与高阶函数
关于闭包“closure”,刚开始接触的时候,百度了N遍相关教程还是看得我一头雾水,
听说还会有内存泄露的问题,好吧,在开发中干脆就不用吧。直到有一天,偶尔看到高阶函数,对于闭包以及整个JS语言的理解可以说产生了翻天覆地的变化。
在这之前,先给大家回顾一下,闭包之所以成为闭包之前,有一个很重要的前提概念:变量的生命周期。
在函数体内,var定义的变量,变量属于函数内,函数外无法访问;反之如果没有定义var的变量,像x=12
,x属于全局变量。
全局变量的生命周期是永久的,由页面加载到关闭;而内部变量,从函数执行到结束,GC检测到内部变量没有再被使用则会销毁它。
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> fn = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> a = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"a"</span>; b = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"b"</span>; <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> fni = <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ c = <span class="hljs-string" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">"c"</span>; }();}();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//console.log(a);//a is not defined</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(b);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//b</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(c);<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//c</span></code>
关键就在这,如果内部变量在函数执行结束后,仍然有被使用呢?
解决方案就是——高阶函数。
高阶函数名字看起来高端,实际很简单,满足以下条件其一就是高阶函数:
1、函数的参数是函数;
2、函数的返回值是函数;
function myFunction() { console.log("我是个函数"); }; //高阶函数1——函数的参数是函数 function fnHO1(fn) { fn(); } //高阶函数2——函数的返回值是函数 function fnHO2() { return myFunction; }; //调用高阶函数1 fnHO1(myFunction);//我是个函数 //调用高阶函数2 fnHO2();//神马都没有发生 //调用高阶函数2,获得的是它的内部函数体 var f2 = fnHO2(); //再执行,才是调用它的内部函数 f2();//我是个函数
在高阶函数的基础,我们来看看,史上最经典的闭包实例。
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">add_outer</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> i = <span class="hljs-number" style="margin: 0px; padding: 0px;">1</span>; <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//返回值是内部函数,inner调用了其外部的变量</span> <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//所以inner执行结束时,i没有被销毁</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">return</span> <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">inner</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ i++; <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">return</span> i; };};<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//执行add_outer获取的是它的内部函数体inner,但没有执行</span><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> inner = add_outer();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//真正执行的时候,变量i没有被销毁,形成递增</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//2</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//3</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//4</span></code>
我对于闭包的理解很简单:==函数返回值是个内部函数,内部调用了函数的内部变量==。
两个条件,缺一不可。如果将以上例子改一下,内部有函数,但不是返回值,就不是闭包了。
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">add_outer</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> i = <span class="hljs-number" style="margin: 0px; padding: 0px;">1</span>; <span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//返回值不是函数,形成不了闭包</span> <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">inner</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ i++; <span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(i); }; inner();};<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//执行多次,值也不会变</span>add_outer();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//2</span>add_outer();<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//2</span></code>
也许大家会疑问,既然内部变量i在函数结束后仍然使用,导致GC无法回收其内存,那不就是内存泄露吗?
是的。其实我们反过来想,如果我们不使用闭包的方式实现以上累加的例子,改为使用全局变量存放变量i
,全局变量i
是否同样也是不能被回收呢?!
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 0px; white-space: pre-wrap; word-wrap: break-word;"><code class="hljs javascript" style="margin: 0px; padding: 5px !important; line-height: 1.5 !important; font-family: 'Courier New', sans-serif !important; font-size: 12px !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background: rgb(255, 255, 255);"><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//function add_outer() {</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">var</span> i = <span class="hljs-number" style="margin: 0px; padding: 0px;">1</span>; <span class="hljs-function" style="margin: 0px; padding: 0px;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">function</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(163, 21, 21);">inner</span><span class="hljs-params" style="margin: 0px; padding: 0px;">()</span> </span>{ i++; <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">return</span> i; };<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//};</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">////执行add_outer获取的是它的内部函数体inner,但没有执行</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//var inner = add_outer();</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//真正执行的时候,变量i没有被销毁,形成递增</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//2</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//3</span><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(0, 0, 255);">console</span>.log(inner());<span class="hljs-comment" style="margin: 0px; padding: 0px; color: green;">//4</span></code>
因此,闭包与高阶函数,只是函数式编程的编写方式,即使并不是造成内存泄露的原因。关于闭包与内存泄露的问题,请移步 http://www.cnblogs.com/yakun/p/3932026.html
原型模式、闭包与高阶函数应该可以说是JS设计模式的基础要领吧。在下一章,再分享一下JS的几种常用设计模式。
还是像前面所说的,有什么地方
- JS设计模式
- js设计模式
- js 设计模式 参考
- js设计模式
- JS设计模式
- js设计模式
- JS设计模式 零
- JS设计模式 零
- JS设计模式 零
- JS设计模式0
- JS设计模式
- JS设计模式
- js设计模式
- js设计模式
- JS设计模式
- JS设计模式学习
- js --- 设计模式
- js中的设计模式
- 字典树 hdu 1251
- python 安装pip
- Linux下高性能网络编程中的几个TCP/IP选项_SO_REUSEADDR、SO_RECVBUF、SO_SNDBUF、SO_KEEPALIVE、SO_LINGER、TCP_CORK、TCP_NODE
- Java中hashCode的作用
- iOS开发UI篇—UIScrollView控件介绍
- JS设计模式
- 2012年5月SAT香港真题解析
- 同步机制摘要
- 【Caffe】训练MNIST数据集模型
- Cocos2d-x中获取系统时间
- Linux常用命令总结
- 【C++ 11新特性】 function
- hdu 5126 stars ( CDQ分治 + 树状数组)
- string 与 CString的区别