高级面向对象写法.doc_第1页
高级面向对象写法.doc_第2页
高级面向对象写法.doc_第3页
高级面向对象写法.doc_第4页
高级面向对象写法.doc_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

寻找最好的JavaScript面向对象模式和封装结构作者:admin 发布时间:2010-08-20 15:31:13好久不见,这次发的不是笔记啦,是我在公司内部的前端wiki上更新的文档这个抛弃所有wiki语法要求用户直接手写语义化html用json配置导航的wiki排版相当漂亮,让我这样的懒人也有了码字的欲望,发起人小麦实在系功德无量这篇文章去年就准备写,想用循序渐进的形式推演出一个Module Pattern的 最佳实践,不过想法越多,归纳总结表达出来的成本就越高,所以一直拖延这次发的文档是一个简化版,去掉了各式各样乱七八糟的写法,只包含几个常用的, 说明文字也不多主要看代码-_-b初衷是作为给土豆前端team里新来的同事看的提纲(对了由于某人叛逃到产品设计部门,现在又空出一个名额,有兴 趣的同学抓紧时间投简历,这次是魔都总部的职位,不是成都的),所以要解释一下,文档中提到的TUI是一个js库(名字是很俗,不过我上次发现某年纪一大把的人也跟我一样俗), 土豆一直采用双库并行(不要看成双工并行)的形式,在紧跟开源社区发展的同时自己掌控所有环节和基础架构,没有使用jQuery UI和那套基于DOM的插件结构,而jQuery自己几乎不提供OOP工具(这是好事),实际上自己创建这类工具非常简单快捷,相关的代码我提取了一下直 接帖在末尾了,仅供参考。另外,为了符合Nicholas Zakas在最近的国际会议上传达的精神,我修改了若干变量名跟他ppt里的例子保持一致这件事教育我们,平时多上对保持先进性是多么重要。Tudous JavaScript Guideline OOP and Module介绍土豆在面向对象和模块化设计方面的工具和实践“Dont Repeat Yourself.” (DRY)“Rather than construction, programming is more like gardening.”Quote from: Andy Hunt and Dave Thomas, The Pragmatic Programmer索引1. 创建类,继承,混入,实例化2. 模块化 Module Pattern3. 沙盒,模块间的解耦,与外部通信4. 按需加载模块 On-demand Lazy Load5. 总结创建类,继承,混入,实例化我们依赖的核心工具是TUI.clone简洁的,支持私有属性,不需要prototype的写法:JS是基于原型而不是基于类的面向对象语言,JS是“无类型”的,类是仿造出来的概念,实质只有对象。new只是用来复制对象,构造函数只是用来返回对象, 两者对JS的OOP来说并不是必须的。1. vardog=function(options)2. varprivateAttr=1;/私有属性3. varprivate_method=function();/私有方法4. return5. option:options,/实例属性6. method1:function()7. ;8. ;9. 10. varxiaobai=dog(); 对私有属性/方法的支持比较好 最适合单例模式(Singleton) 延迟单例的初始化,提高页面初始化的速度 缺点:对继承的支持不佳 缺点:在需要频繁创建大量对象,而方法非常多的场合,浪费资源(因为每个实例的方法指向的都是不同的函数对象,每次实例化都要重新生成所有函数)在第2个缺点的场合,传统的prototype写法效率更高,支持继承,但是代码分散,不易读,TUI.clone提供了更好的写法传统的、支持继承的、仿Ruby风格的Class写法:1. /创建新类2. varDog=TUI.newClass(3. initialize:function(options)/构造函数4. this.option=options;5. 6. );7. 8. /实例化9. varxiaobai=newDog();10. 11. /继承12. varCat=TUI.newClass(dog,/只允许单继承13. mixin:TUI.event,/混入其他方法14. initialize:function(options)15. this.superClass.call(this,arguments);/调用父类构造函数16. 17. ); 风格类似mootools的new Class TUI.newClass只是TUI.clone的封装 mixin相当于在构造函数里$.extend(totype, TUI.event) 实际上应该少用继承,多用mixin和组合模式,后者更符合JS的特点 缺点:封装效果不好,不支持私有属性和方法TUI.clone既可以复制构造函数的prototype,也可以直接复制对象(跟jQuery.extend不同,依靠原型继承),所以利用它直接生成实例。这样可以在不引入prototype和语法糖的情况下把第一种方法改造的同样高效对象克隆工厂的写法:1. vardog=(function(jQuery,TUI)/传入需要访问的全局命名空间2. vara=xxx;/不作为状态的私有属性3. varprivate_method=function()/私有方法4. alert(this.option);5. ;6. 7. varPublicObj=/相当于prototype对象8. method1:function()9. private_method.call(this);/访问私有方法, 共享状态10. 11. ;12. 13. returnfunction(options)/对象工厂14. varobj=TUI.clone(PublicObj);/克隆15. obj.aption=options;/构造函数中的常规任务16. $.extend(obj,cat);/mixin其他对象的方法17. returnobj;18. ;19. )(jQuery,TUI);20. 21. varxiaobai=dog(); 效率高,封装好,耦合少易于修改和扩展 缺点:私有属性不能作为实例状态把以上第一种方法和第三种方法结合互补,为module模式模块化 Module Pattern核心工具是TUI.module1. TUI.widget.dog=(function($,TUI)/除了库的命名空间,尽量不访问全局变量2. /所有模块代码都封闭在这个区域内3. 4. vara=xxx;/不作为状态的私有属性5. varprivate_method=function()/私有方法6. alert(this.option);7. ;8. varprivateObj=;/可作为状态的私有属性9. varprivateAttr=function(name,value)/读写私有属性的方法,只能在内部使用10. varp=privatethis.objectId;11. if(!p)12. p=privatethis.objectId=;13. if(value)14. pname=value;15. returnpname;16. ;17. 18. TUI.dog=TUI.newClass(parentClass,19. mixin:TUI.event,/混入其他方法20. initialize:function(options)21. this.superClass.call(this,arguments);/访问父类构造函数22. ,23. public_method:function()24. varb=a+privateAttr.call(this,c);/访问私有属性25. private_method.call(this);/访问私有方法26. 27. ); 28. 29. 30. /工厂方法,给别人使用的接口31. returnfunction(options)32. varobj=newTUI.dog(options)33. /实例加上唯一的ID,类似Ruby,注意只用+new Date()不能避免重复34. obj.objectId=tui_object+(+newDate()*10000+Math.random(1)*10000);35. returnobj;36. ;37. )(jQuery,TUI);为了便于理解,以上代码是集中在一起的简单实现,进一步封装之后,有些步骤可以省略: objectId的初始化已经由TUI.clone实现 privateAttr方法和私有属性map由TUI.newModule实现 TUI.newModule是TUI.namespace和TUI.module.create的封装基于 TUI.clone 和 TUI.newModule 的写法1. TUI.newModule(TUI.widget.dog,function(sandbox, $,TUI)2. TUI.dog=TUI.newClass(parentClass,3. mixin:TUI.event,4. sandbox:sandbox,/有sandbox属性传入时,attr属性会被转化为外部无法访问的私有属性5. attr:/初始化私有属性的默认值6. a:1,7. b:28. ,9. initialize:function(options)10. this.superClass.call(this,arguments);/访问父类构造函数11. ,12. public_method:function()13. varb=this.attr(sandbox,a)+this.attr(sandbox,c);/访问私有属性,通过sandbox参数来验证身份14. 15. ); 16. 17. returnfunction(options)18. varobj=newTUI.dog(options)19. returnobj;20. ;21. ,jQuery,TUI);22. 23. varxiaobai=TUI.widget.dog();24. console.log(xiaobai.sandbox);/undefined25. console.log(xiaobai.attr); /undefined这里的sandbox其实还可以做很多事沙盒,模块间的解耦,与外部通信TUI.module.create方法其实来自TUI.moduleClass的实例,其他独立应用同样可以继承TUI.moduleClass,构造自己的沙盒对象1. varDouwan=TUI.newClass(TUI.moduleClass,2. notify:TUI.clone(TUI.event),/事件在这里可以理解为通信器3. initialize:function(options)4. varme=this;5. 6. this._sandbox.notify=this.notify;/把通信器指向自己,避免跟全局的事件命名冲突7. 8. /给沙盒增加ajax方法,让module内的代码通过沙盒来通信,屏蔽url路径和响应格式之类的细节9. varurl=/path/service.action10. this._sandbox.getJSON=function(param,fn)/模块只需要传url参数11. me.getJSON(url,function(text)12. varjsondata=me.toJSON(text);13. fn(jsondata);/无论返回格式是什么,都传json给模块14. );15. ;16. ,17. toJSON:function(),18. getJSON:function()19. );让应用的不同模块之间解耦,避免在模块内部直接使用外部的命名(除了库的api)1. vardouwanObj=newDouwan();2. 3. varmodule1=douwanObj.module.create(function(sandbox, $,TUI)4. sandbox.getJSON(iid:10000,function(json)5. /通过通信器调用module2.update6. sondbox.notify.fire(module2-update,json.date);7. );8. ,JQuery,TUI);9. 10. varmodule2=douwanObj.module.create(function(sandbox, $,TUI)11. varobj=12. update:function()13. ;14. /注册一个update消息的接收器15. sondbox.notify.bind(module2-update,function(date)16. obj.update(date);17. );18. 19. returnobj;20. ,JQuery,TUI);页面初始化时并不一定需要渲染或操作所有模块,因此有些模块的代码可以放在外部文件里,需要的时候再注入到页面里,类似Python的import按需加载模块 On-demand Lazy Load自动管理各个模块之间的依赖关系,根据代码内容加载不同的文件,这样做的成本太高,适合大型企业应用,我们的设计原则是“恰到好处”,所以通过文件名来管理模块注册模块1. TUI.module.join(/js/fn/saleloader_10.js,domain:,version:0); 配置从url里获取,第二个配置参数是可选的,优先级更高,用于调试(指向开发环境) 域名其实可以省略,js文件的域名通过TUI.domain和autodomain.js脚本来自动配置 版本号很重要,支持a_10.js/a.v10.js/a_v10.js等写法使用模块1. TUI.module.use(/fn/saleloader,function()2. adExtension.load();/这个是saleloader.js内的方法,必须等js加载完后执行3. 4. /这个区域是执行saleloader.js内代码的安全空间5. ); 类似YUI3的Y().use,省去了版本冲突之类无用的特性 第一次use时会向页面里加载对应的script 函数区域内的代码是异步执行的,会等到saleloader.js加载完后依次执行,如果已经加载过了,直接执行,类似jQuery.ready总结 以上方法虽然有递进关系,但并不是要表示最后面的才是最好的方法 最好的方法不是唯一的,要根据场合选择最适合的方法,以上方法都有适用场合 本文档涉及到的API:TUI.clone,TUI.newClass,TUI.moduleClass,TUI.module.create,TUI.newModule,TUI.module.join,TUI.module.use,TUI.event=开始帖源码的分割线=TUI.clone/* * public 继承一个类或复制一个对象 * note * param object|function oldone是需要继承的构造函数或需要复制的对象 * param object|function ex为函数时,是子类的构造函数,或者用来加工新对象 * ex为对象时,为子类的方法, 其中initialize方法为构造函数, mixin为混入的超类 * return object|function */clone: function(oldone, ex)var newobj,isClass = !oldone | $.isFunction(oldone), /继承操作constructorFn = ex & !$.isFunction(ex) & ex.initialize | ex; /子类构造函数if (!isClass) newobj = function()if(constructorFn)constructorFn.apply(this, arguments);totype = oldone;return new newobj(); else /为module内部定义的类提供相关方法var c = _sandbox: ex.sandbox, _default: ex.attr ;newobj = function() /构造函数if (this.constructor = newobj) / 如果this指向子类实例,已经执行过以下的初始化代码this.objectId = TUI-object- + +obj_uuid; /实例的唯一IDvar p = c;if (p._sandbox & p._default)this.attr(p._sandbox, p._default); /初始化私有属性的默认值if(constructorFn) /执行构造函数的自定义部分constructorFn.apply(this, arguments);/ 原型继承, 子类构造函数里需要显示调用父类构造函数var newproto = oldone ? this.clone(totype) : ;/ 混入其他超类方法if (ex.mixin)this.mix.apply(this, (newproto).concat(ex.mixin);/ 加入子类方法, 覆盖混入和继承this.mix(newproto, ex, constructor: newobj, / 恢复superClass: oldone | Object /在子类的构造函数中可以用this.superClass访问父类);delete newproto.initialize;if (c._sandbox) delete newproto.sandbox; /沙盒一定要删除,不能暴露出去newproto.attr = function(sandbox, attrname, value) /通过sandbox参数杜绝来自外部的访问return sandbox.attr.call(this, a

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论