该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-09-09
=========================================== 前言 =========================================== 最近喜欢上了mootools(相见恨晚啊),在公开表示了对他的偏爱. 很多朋友都问我为什么要移情别恋,其实理由还是蛮多的. 今天在这里打算列举出一部分.让更多的朋友能够了解一下mootools,也希望有更多的朋友喜欢上他. 文章的标题注定了我会更多的讲述 mootools比prototype好的地方, 希望大家不要被我的误导,以为mootools处处都比prototype好. mootools还是有一些不足的. 本次对比针对 mootools 1.11版 和 prototype 1.51版, 只比较了一些核心代码,其他的工具方法,辅助函数不再本文讨论之内. 开始前,再次重申一遍:我曾经很爱prototype,而且我将永远都会用"伟大"来形容它. 好 下面对比正式开始 ( mootools以下简称moo. 本文所引用的代码, 只是起到说明作用,不保证他们都可以被正确的执行. 同时为了使本文简洁一些,引入的 一些 moo和prototype的代码也只是片段或是伪代码. ) =========================================== 一. 类机制 =========================================== js里的类实际上就是function. 如果不使用任何框架和组件,那么想创建一个自己类也不是难事,方法如下: var PersonClass=function(name,gender){ this.name=name; this.gender=gender; alert("My name is "+this.name); } var myGirlFriend=new PersonClass('Vickey','female'); 执行 后, 会创建一个PersonClass类的实例myGirlFriend, 并执行function内的语句. 那些语句可以理解为是类的构造函数. Prototype 现在来看看在prototype的帮助下如何去定义这个类: var PersonClass = Class.create(); PersonClass.prototype.initialize=function(name,gender){ this.name=name; this.gender=gender; alert("My name is "+this.name); }; var myGirlFriend=new PersonClass('Vickey','female'); //如果想给类增加属性和方法时使用 PersonClass.prototype.XXX=...; //或者是使用 prototype提供的 Object.extend(PersonClass.prototype, {...} ); (关于Object.extend稍后在对比继承机制时再细说.) 再来看看prototype是实现类机制的核心代码. var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } 通过看代码不难看出,prototype的Class实际上只是帮助我们抽象出了"类的构造函数". 而当我们在prototype的这种机制下进行类的定义时,实际上带来的好处是非常有限的. prototype的Class只是从结构对我们的类进行了重新规划. 而这样的规划意义并不是很大. 而且prototype带有强制性,即, initialize 是必须要定义的. 实际上这里存在一个缺陷, Class应该提供一个默认的initialize(一个空函数就好), 或者是在create返回的function里进行必要的判断. (prototype1.6的类机制变化比较大,但是还没仔细研究过,所以不敢轻易评论). Mootools 现在来看看在 moo的帮助下如何去定义一个类: var PersonClass = new Class( { initialize: function(name,gender){ this.name=name; this.gender=gender; alert("My name is "+this.name); } }); var myGirlFriend=new PersonClass('Vickey','female'); 其中类的 initialize 不是必须的. 如果你想给 PersonClass 增加属性和方法,你可以在new Class的参数里直接以 json方式定义. 也可以使用 如下方式 PersonClass.implement ({ age:0 , getName : function() {return this.name;} } , {...}, ..... ); implement支持多个{}.关于implement稍后在对比继承机制时再细说. 在来看一下moo类机制的一些核心代码. var Class = function(properties){ var klass = function(){ return (arguments[0] !== null && this.initialize && $type(this.initialize) == 'function') ? this.initialize.apply(this, arguments) : this; }; $extend(klass, this); klass.prototype = properties; klass.constructor = Class; return klass; }; Class.prototype = { extend: function(properties){ var proto = new this(null); for (var property in properties){ var pp = proto[property]; proto[property] = Class.Merge(pp, properties[property]); } return new Class(proto); }, implement: function(){ for (var i = 0, l = arguments.length; i < l; i++) $extend(this.prototype, arguments[i]); } }; 代码的具体原理就不细说了.大家在moo的Class里看到了 extend 和implement,那下面就来具体说一说moo和prototype的 继承机制吧. =========================================== 二. 继承机制 =========================================== Prototype prototype提供的继承很简单. Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; } 他只是把source里的属性赋给destination,同时会覆盖destination里的同名属性. 他可以用于对象,也可以用于类,当要实现类的继承时,destination要使用 MySubClass.prototype. prototype的继承机制可以说是非常薄弱的. Mootools moo提供了三种继承机制: 首先他也提供了简单的继承机制: Objcet.extend (注意,不是上面代码中 Class 里的 extend) 他的代码如下 var $extend = function(){ var args = arguments; if (!args[1]) args = [this, args[0]]; for (var property in args[1]) args[0][property] = args[1][property]; return args[0]; }; Object.extend = $extend; 他的使用方法和 prototype 完全一样. 但是大家可能注意到了 这句 if (!args[1]) args = [this, args[0]]; 这句的纯在使得下面的代码写法成为可能. var myObjcet={....}; myObjcet.extend=$extend; myObjcet.extend(objA); myObjcet.extend(objB); myObjcet.extend(objC); 简单的一句话,让extend的用法增加了更多的灵活性,不得不赞一个了!!! 下面说说重点, moo的类里的extend和 implement 先说 implement,之前已经说了一些了 var MyClassA = new Class(); MyClassA.implement( { methodA : function() {... } } ); 执行后 MyClassA 将拥有 methodA. implement用来向类中添加属性和方法(会覆盖同名属性和方法),相当于 Object.extend (MyClassA.prototype , {... } ) 但是Object.extend 不支持多个source,implement可以,示例如下: MyClassA.implement( objA , objB, objC ... ); 下面来看看moo的Class.extend. moo的Class.extend才是我们期待的真正的"类继承",看一下官方的示例 var Animal = new Class({ initialize: function(age){ this.age = age; } }); var Cat = Animal.extend({ initialize: function(name, age){ this.parent(age); //will call the previous initialize; this.name = name; } }); 看那个parent() !!!! 通过moo的Class.extend实现的继承提供一个关键的方法 parent(). 使用他你可以调用父类中的同名方法,好像java里的super一样. 这个示例已经可以说明一切了. 关于prototype和moo的类机制和继承机制的对比就到这里,孰优孰劣大家心里应该有数了吧. =========================================== 三.抽象对象 =========================================== 再来看一看"抽象对象". 这个虽然对于开发人员来说用处不大,但还是对比一下吧,小细节也能看出作者的用心. Prototype prototype的抽象对象很简单 var Abstract = new Object(); 具体的意义不大. Mootools moo的的抽象对象相对更完善一些. var Abstract = function(obj){ obj = obj || {}; obj.extend = $extend; return obj; }; 支持自定义抽象(以参数形式传入),同时会为抽象对象自动添加extend方法. =========================================== 四. 关于 $() =========================================== Prototype prototype的$大家都比较熟悉了, 工作原理就是 通过id取得一个页面元素(或者直接传入一个页面元素对象),然后给他增加一些prototype提供的方法和属性,来方便开发人员对页面元素的使用. Mootools moo在这方面做的差不多. 不同的主要有两点, 首先moo为页面元素增加的方法和属性与prototype的不同(这个稍后会介绍),另外一个不同是moo的$兼具了对象管理的一个功能. 他引入了一个 Garbage 类, 来对页面元素进行一个统一的管理和回收(主要是回收). 可以更好的减少js(或浏览器)造成的内存泄露等问题. 具体的大家可以看一下代码,在这里就不详细说明了. =========================================== 五.关于 Array Enumerable Hash =========================================== prototype 和 moo 都提供了集合迭代方法 (each) 这个网上已经有一篇不错的对比文章,我就不在这里重复了 http://blog.fackweb.cn/?p=50. moo的 forEach/each方法: function(fn, bind){..} 那个bind 结合代码 和 上面那篇文章, 大家应该可以很好的看出来prototype和moo的不同与优劣. prototype里面有 Enumerable 的概念,moo没有. 但是我个人一直觉得 Enumerable 比较鸡肋. 在实际开发中,很少使用. Enumerable的功能完全可以 用普通json对象 或者是 Hash来实现. moo的作者也许同样这么认为.所以 不再 设置一个 鸡肋的 Enumerable类. 但是请大家放心, Enumerable 能做的事情, 在moo里也能完成. 可以理解为 moo的 Array + Hash +{} 完全可以接替 prototype的 Array + Enumerable + Hash +{} 当然对于一些工具方法两者提供的都不太一样,不好比较,但是那些方法都是附属品. 我们完全可以自己来实现,所以不在这次的比较范畴之内. =========================================== 六. 关于 Element =========================================== 两者的 Element 从作用上看类似.都是一种对页面元素的包装,为页面元素添加了一些诸如 addEvent remove style之类的方法. 但是大家通过看代码可以发现 moo的实现明显更简洁 更OO. 同时还有一个关键的不同,prototype又提取出了一个Form对象,里面包含了很多表单相关的方法. 同时还衍生出了 serializeElements Method 等等很多类和方法,代码瞬间变得异常复杂和难以琢磨. 而moo中没有Form对象,在moo中,Form本身就是一个Element 他没什么特别的,这样的思想类似components模式 普通Element具备的方法 Form 都应该具备, Form具备的方法 Element也都应该包含.form 和 其他页面元素没什么不同. form元素只是一个包含了 input select textarea等子元素,同时拥有action target等属性而已. 一个div 一个span 一个td... 同样可以包含input select textarea子元素,同样可以拥有.action target属性. 浏览器处理他们的方式可能不同,但是在moo面前,大家完全平等. 其实prototype里 form和普通页面元素几乎也是平等的,但是问题就是,既然是平等的,又何必硬生生的造出Form以及那么多的衍生物呢? =========================================== 七.Ajax =========================================== Prototype prototype的ajax实现主要是靠一个 Ajax类 来实现.(但是这个类形同虚设,大家更多的是和 Ajax.Request 类打交道. 先来看一个prototype下一个简单的ajax提交实例: var myAjax = new Ajax.Request( url,{parameters: myData , onComplete: callBackFunction } ); 其中 myData 可以是字符 : "name=Vickey&gender=female"; 也可以是对象 { name : Vickey, gender : female } Mootools moo首先在将ajax机制分层.提取出了一个基类:XHR. 目前XHR有两个子类, 一个是 Ajax ,另一个是Json.Remote. 在moo下一个简单的ajax提交实例: var myAjax =new Ajax(url, {data : myData , onComplete: callBackFunction }).request(); 大家可以看到request成为了Ajax对象的一个方法,这样的设计显然是更合理更自然 也更OO的. 而且关键的一点是,你可以提前创建好你需要的ajax对象.在需要发出请求时再发出请求. var myAjax =new Ajax(...); ..... myAjax.request(); 同时还有一个重要特性, request是支持参数的,这个参数就是你要提交的数据. 也就是说,你可以在new Ajax时不指定数据或者指定一个默认数据. 在提交的时候可以提交另一个data.如. myAjax.request(yourData); 其中data可以是字符串,可以是对象, 也可以是一个页面元素. 要用ajax提交一个form 或者一个 div下的所有表单元素,只是改变一下 myData. var myData= $("formID"); // var myData= $("divID"); 然后就和普通的ajax提交完全一样了. myAjax.request(myData); 当然还有更oo的方式 : myData.send({onComplete: callBackFunction }); 用后一种方式的时候要保证提交的元素有action属性,没有你就赋一个 myData.action=url. prototype里如何实现这一功能呢?? Prototype Form.request($("formID") ,{ onComplete: callBackFunction }); 当然prototype里也可以类似moo的做法 , 只要让myData=$("formID").serialize(true) 就可以了. 但是这一个小小的不同,反映出了设计上的差距. Mootools moo的Json.Remote类,简单,但是很实用: var jSonRemoteRequest = new Json.Remote( url , {onComplete:callBackFunction }).send({name: 'Vickey',gender: 'female' }); 这个类和Ajax类的本质区别是, 他提交的是一个序列化后的 json字符串("{name: 'Vickey',gender: 'female' } "),而不是把 json对象转化成QueryString ("name=Vickey&gender=female"); =========================================== 结束语 =========================================== 写这篇文章不是要批评prototype,以我现在的水平还没那个资格. 只是和mootools对比后, prototype在设计上的不足立刻就显现了出来. 虽然prototype新版本变化很多,很多我上面提到的一些不足都改正了,而且也加入了很多以前不具备的新的特性. 但是prototype现在的发展停留在:"修补不足,增加功能"的阶段,而没有从设计上进行深层次的重构,所以我不认为他在mootools面前有足够的底气. 至于jquery我没有深入研究过,但是它的设计觉得完全是prototype风格的, 注意,我说的是设计风格,而不是代码风格. 代码上他可能写的更精妙,更有趣,但是设计上依然是prototype风格:薄弱的类机制,靠简单的extend支撑起整个系统. JQuery在很多方面很出色,但是归根结底他和prototype走在一条路上,只是在有些方面他走的更快. mootools并非完美无缺,但是至少现在他美的地方比prototype更多,缺的地方比prototype更少. 所以,我选择mootools. 你呢?? 不要听评论,不要看介绍, 只要看看他们的源代码, 同时动手用他们写些东西, 你的答案自然会浮现出来. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-09-09
核心的ui支持你没说,这可能才是关键。
jQuery不是你想的那种设计风格,我个人认为jQuery发挥了javascript语言的特性,而不是模仿java之类。 如果谁读jQuery代码,注意两个方面: 1、状态。不是类似java的保存方法,jQuery没有类,只有函数。javascript有他处理问题的思路,我们不能照搬java那一套。 2、晚期绑定。 可能性能是个问题,但这才是语言发展的方向! |
|
返回顶楼 | |
发表时间:2007-09-09
谢谢楼上的指正
ui支持本身不属于太核心的东西吧 我觉得我这里的核心应该类似于 core base的意思. 而不是最常用 最重要的部分. 另外我觉得jquery并没有模仿java 其实moo prototype都没有模范java 他们都是在做自己. 至于jquery的深层的东西确实研究不多,但是他的底层实现上来看 他模范prototype的痕迹比较重. 同样简单的 jQuery.extend, 而且同样是大量的利用jQuery.extend来构造出整个框架. |
|
返回顶楼 | |
发表时间:2007-09-09
再补充一点,其实prototype和jquery的一些不足不是体现在他们本身的代码上.而是体现在 当你用他们来构造你自己的 js框架或组件时.
例如我总说的 他们薄弱的extend, 其实对于他们自己来说 那个extend已经足够用了. 但是你想想, 当你自己把他们作为基础组件,来在其基础上在进行开发时,你就会发现他们的很多不足了. |
|
返回顶楼 | |
发表时间:2007-09-09
在jQuery中根本不存在“类”,也不是面向对象的。
“extend”也不是继承的意思,在java中是,在prototype.js中,jQuery不完全是。如果你非要把javascript当成java来理解学习的话,也可以这样理解。 prototype.js中的extend充其量只是个minix的作用,只是我们很多人把它当成“继承”了。 “另外我觉得jquery并没有模仿java 其实moo prototype都没有模范java 他们都是在做自己.” 不是它们模仿,而是我们用java的思想来理解它们!!! |
|
返回顶楼 | |
发表时间:2007-09-09
ui在ajax lib选择时很重要。如果yui-ext的ui做了不好,估计根本没有现在的光景。借了yui的光,现在还自立门户。呵!
YUI-EXT的成功也就在ui上。你们认为呢? 另外mootools的命名不是很好,为什么不用package的概念呢? |
|
返回顶楼 | |
发表时间:2007-09-09
我觉得大家并没有刻意的去用JAVA的思想来理解他们, 大家可能都是用OO的思想来理解的.只是恰巧java是比较流行的OO语言.
我想如果这是在一个.net的论坛里 我就会用C#来举例了 呵呵 其实moo prototype jquery他们是否学习了java 以及是否该用java的思想来理解 已经不是问题的关键了 问题的关键就是 哪个设计更优雅 使用更方便 结构更健壮. 从目前的情况来看 我会选择moo. 我对jquery始终都不喜欢.可能这里有一些莫名的偏见在里面吧. 有时候我这人太感性了 呵呵 就好像我莫名其妙的讨厌一个人,但是你问我讨厌他哪里 或者他哪里得罪我了 我还真说不上来 哈哈 |
|
返回顶楼 | |
发表时间:2007-09-09
"另外mootools的命名不是很好,为什么不用package的概念呢?"
因为他不想成为 又一个dojo 又一个yui吧 毕竟简单轻巧才是他最求的吧. 其实我觉得JS里搞命名空间实际意义不是很大 至少现在看来是这样. 因为我们一般很少会写太过复杂的js代码,而且很少会在同一个页面内同时使用两个以上的大型js框架 |
|
返回顶楼 | |
发表时间:2007-09-09
radar 写道 ui在ajax lib选择时很重要。如果yui-ext的ui做了不好,估计根本没有现在的光景。借了yui的光,现在还自立门户。呵!
YUI-EXT的成功也就在ui上。你们认为呢? 另外mootools的命名不是很好,为什么不用package的概念呢? 我不这么认为,说明你还没有深入了解EXT |
|
返回顶楼 | |
发表时间:2007-09-09
fins 写道 "另外mootools的命名不是很好,为什么不用package的概念呢?"
因为他不想成为 又一个dojo 又一个yui吧 毕竟简单轻巧才是他最求的吧. 其实我觉得JS里搞命名空间实际意义不是很大 至少现在看来是这样. 因为我们一般很少会写太过复杂的js代码,而且很少会在同一个页面内同时使用两个以上的大型js框架 package怎么了?dojo的问题是package吗? 我指的是,mootools占用了太多的顶层变量名。 $extend Ajax $ fx ... ... 万一你不小心定义一个 $开头的变量呢? 可能这不是主要问题,如果都放到 var mootools={};下就没这个问题了啊!为什么不呢? |
|
返回顶楼 | |