高级面向对象写法.docx

上传人:b****6 文档编号:6317921 上传时间:2023-01-05 格式:DOCX 页数:12 大小:221.26KB
下载 相关 举报
高级面向对象写法.docx_第1页
第1页 / 共12页
高级面向对象写法.docx_第2页
第2页 / 共12页
高级面向对象写法.docx_第3页
第3页 / 共12页
高级面向对象写法.docx_第4页
第4页 / 共12页
高级面向对象写法.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

高级面向对象写法.docx

《高级面向对象写法.docx》由会员分享,可在线阅读,更多相关《高级面向对象写法.docx(12页珍藏版)》请在冰豆网上搜索。

高级面向对象写法.docx

高级面向对象写法

寻找最好的JavaScript面向对象模式和封装结构

作者:

admin发布时间:

2010-08-2015:

31:

13

好久不见,这次发的不是笔记啦,是我在公司内部的前端wiki上更新的文档……这个抛弃所有wiki语法要求用户直接手写语义化html用json配置导航的wiki排版相当漂亮,让我这样的懒人也有了码字的欲望,发起人小麦实在系功德无量……

这篇文章去年就准备写,想用循序渐进的形式推演出一个ModulePattern的最佳实践,不过想法越多,归纳总结表达出来的成本就越高,所以一直拖延……这次发的文档是一个简化版,去掉了各式各样乱七八糟的写法,只包含几个常用的,说明文字也不多主要看代码-__-b……初衷是作为给土豆前端team里新来的同事看的提纲(对了由于某人叛逃到产品设计部门,现在又空出一个名额,有兴趣的同学抓紧时间投简历,这次是魔都总部的职位,不是成都的),所以要解释一下,文档中提到的TUI是一个js库(名字是很俗,不过我上次发现某年纪一大把的人也跟我一样俗),土豆一直采用双库并行(不要看成双工并行…)的形式,在紧跟开源社区发展的同时自己掌控所有环节和基础架构,没有使用jQueryUI和那套基于DOM的插件结构,而jQuery自己几乎不提供OOP工具(这是好事),实际上自己创建这类工具非常简单快捷,相关的代码我提取了一下直接帖在末尾了,仅供参考。

另外,为了符合NicholasZakas在最近的国际会议上传达的精神,我修改了若干变量名跟他ppt里的例子保持一致——这件事教育我们,平时多上对保持先进性是多么重要。

Tudou’sJavaScriptGuideline—OOPandModule

介绍土豆在面向对象和模块化设计方面的工具和实践

“Don’tRepeatYourself.”(DRY)

“Ratherthanconstruction,programmingismorelikegardening.”

Quotefrom:

AndyHuntandDaveThomas,ThePragmaticProgrammer

索引

1.创建类,继承,混入,实例化

2.模块化ModulePattern

3.沙盒,模块间的解耦,与外部通信

4.按需加载模块On-demandLazyLoad

5.总结

创建类,继承,混入,实例化

我们依赖的核心工具是TUI.clone

简洁的,支持私有属性,不需要prototype的写法:

JS是基于原型而不是基于类的面向对象语言,JS是“无类型”的,类是仿造出来的概念,实质只有对象。

new只是用来复制对象,构造函数只是用来返回对象,两者对JS的OOP来说并不是必须的。

1.var dog = function(options){

2.   var privateAttr = 1; //私有属性

3.   var private_method = function(){}; //私有方法

4.   return {

5.     option:

 options, //实例属性

6.     method1:

 function(){}

7.   };

8.};

9. 

10.var xiaobai = dog({});

∙对私有属性/方法的支持比较好

∙最适合单例模式(Singleton)

∙延迟单例的初始化,提高页面初始化的速度

∙缺点:

对继承的支持不佳

∙缺点:

在需要频繁创建大量对象,而方法非常多的场合,浪费资源(因为每个实例的方法指向的都是不同的函数对象,每次实例化都要重新生成所有函数)

在第2个缺点的场合,传统的prototype写法效率更高,支持继承,但是代码分散,不易读,TUI.clone提供了更好的写法——

传统的、支持继承的、仿Ruby风格的Class写法:

1.//创建新类

2.var Dog = TUI.newClass({

3.   initialize:

 function(options){ //构造函数

4.     this.option = options;

5.   }

6.});

7. 

8.//实例化

9.var xiaobai = new Dog();

10. 

11.//继承

12.var Cat = TUI.newClass(dog, { //只允许单继承

13.   mixin:

 [TUI.event], //混入其他方法

14.   initialize:

 function(options){

15.     this.superClass.call(this, arguments); //调用父类构造函数

16.   }

17.});

∙风格类似mootools的newClass

∙TUI.newClass只是TUI.clone的封装

∙mixin相当于在构造函数里$.extend(this.prototype,TUI.event)

∙实际上应该少用继承,多用mixin和组合模式,后者更符合JS的特点

∙缺点:

封装效果不好,不支持私有属性和方法

TUI.clone既可以复制构造函数的prototype,也可以直接复制对象(跟jQuery.extend不同,依靠原型继承),所以利用它直接生成实例。

这样可以在不引入prototype和语法糖的情况下把第一种方法改造的同样高效——

对象克隆工厂的写法:

1.var dog = (function(jQuery, TUI){ //传入需要访问的全局命名空间

2.   var a = "xxx"; //不作为状态的私有属性

3.   var private_method = function(){ //私有方法

4.     alert(this.option);

5.   };

6. 

7.   var PublicObj = { //相当于prototype对象

8.     method1:

 function(){

9.       private_method.call(this); //访问私有方法,共享状态

10.     }

11.   };

12. 

13.   return function(options){ //对象工厂

14.     var obj = TUI.clone(PublicObj); //克隆

15.     obj.aption = options; //构造函数中的常规任务

16.    $.extend(obj, cat); //mixin其他对象的方法

17.     return obj;

18.   };

19.})(jQuery, TUI);

20. 

21.var xiaobai = dog({});

∙效率高,封装好,耦合少易于修改和扩展

∙缺点:

私有属性不能作为实例状态

把以上第一种方法和第三种方法结合互补,为module模式——

模块化ModulePattern

核心工具是TUI.module

1.TUI.widget.dog = (function($, TUI){ //除了库的命名空间,尽量不访问全局变量

2.   //所有模块代码都封闭在这个区域内

3.  

4.   var a = "xxx"; //不作为状态的私有属性

5.   var private_method = function(){ //私有方法

6.     alert(this.option);

7.   };

8.   var privateObj = {}; //可作为状态的私有属性

9.   var privateAttr = function(name, value){ //读写私有属性的方法,只能在内部使用

10.     var p = private[this.objectId];

11.     if (!

p)

12.       p = private[this.objectId] = {};

13.     if (value)

14.       p[name] = value;

15.     return p[name];

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.       var b = a + privateAttr.call(this, "c"); //访问私有属性

25.       private_method.call(this); //访问私有方法

26.     }

27.   });  

28. 

29. 

30.   //工厂方法,给别人使用的接口

31.   return function(options){

32.     var obj = new TUI.dog(options)

33.     //实例加上唯一的ID,类似Ruby,注意只用+newDate()不能避免重复

34.     obj.objectId = "tui_object" + ( +new Date()*10000 + Math.random

(1)*10000 );

35.     return obj;

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:

 2

8.     },

9.     initialize:

 function(options){

10.       this.superClass.call(this, arguments); //访问父类构造函数

11.     },

12.     public_method:

 function(){

13.       var b = this.attr(sandbox, "a") + this.attr(sandbox, "c"); //访问私有属性,通过sandbox参数来验证身份

14.     }

15.   });  

16. 

17.   return function(options){

18.     var obj = new TUI.dog(options)

19.     return obj;

20.   };

21.}, [jQuery, TUI]);

22. 

23.var xiaobai = TUI.widget.dog({});

24.console.log(xiaobai.sandbox); //undefined

25.console.log(xiaobai.attr);   //undefined

这里的sandbox其实还可以做很多事——

沙盒,模块间的解耦,与外部通信

TUI.module.create方法其实来自TUI.moduleClass的实例,其他独立应用同样可以继承TUI.moduleClass,构造自己的沙盒对象

1.var Douwan = TUI.newClass(TUI.moduleClass, {

2.   notify:

 TUI.clone(TUI.event), //事件在这里可以理解为通信器

3.   initialize:

 function(options){

4.     var me = this;

5. 

6.     this._sandbox.notify = this.notify; //把通信器指向自己,避免跟全局的事件命名冲突

7. 

8.     //给沙盒增加ajax方法,让module内的代码通过沙盒来通信,屏蔽url路径和响应格式之类的细节

9.     var url = "

10.     this._sandbox.getJSON = function(param, fn){ //模块只需要传url参数

11.       me.getJSON(url, function(text){

12.         var jsondata = me.toJSON(text);

13.         fn(jsondata); //无论返回格式是什么,都传json给模块

14.       });

15.     };

16.   },

17.   toJSON:

 function(){},

18.   getJSON:

 function(){}

19.});

让应用的不同模块之间解耦,避免在模块内部直接使用外部的命名(除了库的api)

1.var douwanObj = new Douwan({});

2. 

3.var module1 = douwanObj.module.create(function(sandbox,$, TUI){

4.   sandbox.getJSON({ iid:

 10000 }, function(json){

5.     //通过通信器调用module2.update

6.     sondbox.notify.fire("module2-update", [json.date]);

7.   });

8.}, [JQuery, TUI]);

9. 

10.var module2 = douwanObj.module.create(function(sandbox,$, TUI){

11.   var obj = {

12.     update:

 function(){}

13.   };

14.   //注册一个update消息的接收器

15.   sondbox.notify.bind("module2-update", function(date){

16.     obj.update(date);

17.   });

18. 

19.   return obj;

20.}, [JQuery, TUI]);

页面初始化时并不一定需要渲染或操作所有模块,因此有些模块的代码可以放在外部文件里,需要的时候再注入到页面里,类似Python的import

按需加载模块On-demandLazyLoad

自动管理各个模块之间的依赖关系,根据代码内容加载不同的文件,这样做的成本太高,适合大型企业应用,我们的设计原则是“恰到好处”,所以通过文件名来管理模块

注册模块

1.TUI.module.join(" { 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){

varnewobj,

isClass=!

oldone||$.isFunction(oldone),//继承操作

constructorFn=ex&&!

$.isFunction(ex)&&ex.initialize||ex;//子类构造函数

if(!

isClass){

newobj=function(){

if(constructorFn)

constructorFn.apply(this,arguments);

};

newobj.prototype=oldone;

returnnewnewobj();

}else{

//为module内部定义的类提供相关方法

varc={_sandbox:

ex.sandbox,_default:

ex.attr};

newobj=function(){//构造函数

if(this.constructor===newobj){//如果this指向子类实例,已经执行过以下的初始化代码

this.objectId="TUI-object-"+++obj_uuid;//实例的唯一ID

varp=c;

if(p._sandbox&&p._default)

this.attr(p._sandbox,p._default);//初始化私有属性的默认值

}

if(constructorFn)//执行构造函数的自定义部分

constructorFn.apply(this,arguments);

};

//原型继承,子类构造函数里需要显示调用父类构造函数

varnewproto=oldone?

this.clone(oldone.prototype):

{};

//混入其他超类方法

if(ex.mixin)

this.mix.apply(this,([newproto]).concat(ex.mixin));

//加入子类方法,覆盖混入和继承

this.mix(newproto,ex,{

constructor:

newobj,//恢复

superClass:

oldone||Object//在子类的构造函数中可以用this.superClass访问父类

});

deletenewproto.initialize;

if(c._sandbox){

deletenewproto.sandbox;//沙盒一定要删除,不能暴露出去

newproto.attr=function(sandbox,attrname,value){//通过sandbox参数杜绝来自外部的访问

returnsandbox.attr.call(this,attrname,value);

};

}

newobj.prototype=newproto;

returnnewobj;

}

}

TUI.moduleClass

/**

*存放实例的私有状态

*@private

*/

varprivateAttr={};

/**

*module的抽象类

*@note可以继承到其他应用上,构造独有的sandbox

*/

TUI.moduleClass=TUI.newClass({

initialize:

function(){

this.notify=newTUI

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1