- 注册时间
- 2009-11-15
- 最后登录
- 2010-10-20
- 在线时间
- 54 小时
- 阅读权限
- 100
- 积分
- 1900
- 帖子
- 99
- 精华
- 1
- UID
- 11
  
|
本帖最后由 Sanakan 于 2009-12-29 05:00 编辑
原文地址:http://keetology.com/blog/2009/1 ... es-a-class-for-this
翻译中理解有误的话要多多指出
Mutants Arise!
Mutator是一个可以改变你的类的结构的一个很特殊的函数。它们是产生特别功能和优雅化继承和混入的的有力工具。
建立一个Mutatorr有二个部分:mutator的关键字 和mutator的实际函数 。关键字既是mutator的名字,也是在构建类时候的keyword。当类的构造函数遇到mutator关键字的时候就会调用mutator函数。
Mootools把mutators 储存在Class.Mutators对象中,当你传一个对象给Class构造函数的时候,Mootools检查这个对象的的每一个键在Class.Mutators对象的是不是有mutator函数的对应的名字在里面。如果找到了,它就调用这个函数并且把键的值传给它做处理。
MooTools有两个内建的mutators: Extends 和 Implements。Extends mutator取得传给它的类的名字,然后直接继承它,而Implements取得名字后把那些类的方法添加到新类中(或者混入他们- mixin),继承和混入在下一个部分会解释到。
Extend and Extends Implement and Implements
不要把Extends 和Implements mutators与extend 和implement方法混淆了。因为他们是不同的东西。我已经在上一篇文章中谈论过了,如果搞混了,可以回头看一下
除了MooTools-Core中包含的这些mutators,还有一些用户为不同目的自己写的。但是不要被看起来比较复杂的mutators迷惑了,实际上写一个你自己的非常简单。
那么让我们先来写一个简单的!在上一篇文章中,我提到extend方法用来建立类方法,像这样:- var Barn = new Class();
- Barn.extend({
- fowlCount: 0,
- countFowls: function(){
- return this.fowlCount;
- },
- addFowl: function(){
- this.fowlCount += 1;
- }
- });
- Barn.fowlCount; // returns 0;
- Barn.addFowl();
- Barn.fowlCount; // returns 1;
复制代码 但是,如果我想要把它写到类声明里面去要怎么办呢?在PHP中我们有static关键字用来建立类方法。因此让我们来做一个新的mutator实现同样的静态方法:- // Our 'Static' mutator..
- Class.Mutators.Static = function(items){
- this.extend(items);
- };
- // Using out 'Static' mutator..
- var Barn = new Class({
- Static: {
- fowlCount: 0,
- countFowls: function(){
- return this.fowlCount;
- },
- addFowl: function(){
- this.fowlCount += 1;
- }
- },
- initialize: function(name){
- this.name = name;
- }
- });
- Barn.fowlCount; // returns 0;
- Barn.addFowl();
- Barn.fowlCount; // returns 1;
复制代码 当Class遇到这个对象的时候,它发现键名字:Satatic.因为我们有一个有相同名字的新的mutator,Class就调用这个mutator函数并且把键值传给它(在这个例子中是一个有属性和方法的对象).我们的mutator非常简单,使用this.extends把传过来的的对象的属性和方法变成class的属性和方法。(Mutator函数总是绑定到class本身上的,因此this指向你的类)
让我们建一个复杂点的!在上一篇文章中,我提到一个新的函数叫做protect()用来产生类的保护函数:- var Secretive = new Class({
- secretFunction: (function(){
- return 'The cake is a lie.';
- }).protect(),
- tellSecret: function(){
- return this.secretFunction();
- }
- });
- var secrets = new Secretive();
- secrets.tellSecret(); // returns 'The cake is a lie.'
- typeof(secrets.secretFunction); // returns 'function'
- secrets.secretFunction(); // Error: 'The method secretFunction cannot be called.'
复制代码 不用在我们想要保护的每一个方法上都使用protect(),我们可以建立一个Protected mutator来帮我们做:- // Our 'Protected' mutator...
- Class.Mutators.Protected = function(items){
- for (var fn in items) {
- if (items[fn] instanceof Function && !items[fn]._protected) items[fn] = items[fn].protect();
- }
- this.implement(items);
- };
- // Using our 'Protected' mutator..
- var Secretive = new Class({
- Protected: {
- secretFunction: function(){
- return 'The cake is a lie.';
- },
- anotherSecret: function(){
- }
- },
- tellSecret: function(){
- return this.secretFunction();
- }
- });
- var secrets = new Secretive();
- secrets.tellSecret(); // returns 'The cake is a lie.'
- typeof(secrets.secretFunction); // returns 'function'
- secrets.secretFunction(); // Error: 'The method secretFunction cannot be called.'
复制代码 Coming in MooTools 2.0: Pattern-Based Mutators
在MooTools当前版本中Mutators是基于key-to-key对应的方式。为了能够使用一个mutator,你需要在传送给类的构造函数的对象上和mutator上有相同的名字。(比如,用Extends mutators,必须在你的类的声明对象上 写 Extends:ClassName)
MooTools 2.0引入了一个新的类特征:pattern-based mutators. 使用这些mutators,不需要key-to-key和mutator的名字对应:你的键直接对应一个确定的模式。例如protected mutator, 它确定每一个键是否声明为protected (eg. 'protected myFunction': fn(){...},).
It’s Not All Genes
javascript的继承不一样的地方在于它的继承不是直接支持的。我们通过原型和构造函数替代。不是从superclass继承而来,javascript允许直接从其他对象继承:- // Superclass..
- var Super = function(){};
- Super.prototype = {
- whoIs: function(){
- return 'Super';
- }
- };
- var sup = new Super();
- sup.whoIs(); // returns 'Super'
- // Subclass..
- var Sub = function(){};
- Sub.prototype = new Super();
- var sub = new Sub(); // returns 'Super'
- sub.whoIs();
- // Subclass changes..
- Sub.prototype.whoIs = function(){
- return 'Sub';
- };
- sub.whoIs(); // returns 'Sub'
复制代码 MooTools支持一种模拟传统的基于类的语言的结构,也恰当的加入了一种优雅的继承过程。使用Extends Mutator可以有效的做和上面代码一样的事,但是采取了一种不需要写prototypes的清爽的方式:- // Superclass..
- var Super = new Class({
- whoIs: function(){
- return 'Super';
- }
- });
- var sup = new Super();
- sup.whoIs(); // returns 'Super'
- // Subclass..
- var Sub = new Class({
- Extends: Super
- });
- var sub = new Sub(); // returns 'Super'
- sub.whoIs();
- // Subclass changes..
- Sub.implement({
- whoIs: function(){
- returns 'Sub';
- }
- });
- sub.whoIs(); // returns 'Sub'
复制代码 在写Javascript的时候,你可能会遇到的有一个问题是怎么样取得重载后的父类的方法。如果你使用prototypes,可能会是这样子:- // Superclass..
- var Super = function(){};
- Super.prototype = {
- whoIs: function(){
- return 'Super';
- }
- };
- var sup = new Super();
- sup.whoIs(); // returns 'Super'
- // Subclass..
- var Sub = function(){};
- Sub.prototype = new Super();
- // Subclass changes..
- Sub.prototype.whoIs = function(){
- var parent = Super.prototype.whoIs.apply(this);
- return 'Sub, from ' + parent;
- };
- sub.whoIs(); // returns 'Sub, from Super'
复制代码 因为你是直接从objecus继承下来的,如果不使用父类的原型就不能够得到已经被覆盖掉的父类方法
幸好MooTools通过给每一个类添加一个小巧的方法让它变得简单:- // Superclass..
- var Super = new Class({
- whoIs: function(){
- return 'Super';
- }
- });
- var sup = new Super();
- sup.whoIs(); // returns 'Super'
- // Subclass..
- var Sub = new Class({
- Extends: Super,
- });
- var sub = new Sub(); // returns 'Super'
- sub.whoIs();
- // Subclass changes..
- Sub.implement({
- whoIs: function(){
- returns 'Sub, from ' + this.parent();
- }
- });
- sub.whoIs(); // returns 'Sub'
复制代码 parent是通过Extends添加到所有classes 的实际方法,在任何调用this.parent()的类的方法种,它取得你调用方法的名字然后查看父类中是不是有同名字的方法,如果是,它就调用绑定在父类上的那个方法,如果没找到,它就抛出一个错误 。
因为它是一个实际的方法不是构造的,它在你的所有的方法都可以能够用-甚至在构造函数种,这样保证你的代码不重复(DIY):- var Tea = new Class({
- initialize: function(){
- this.ingredients = ['water', 'tea leaves'];
- },
- prepare: function(){
- var last = this.ingredients.pop();
- var msg = ['Your tea is made up of', this.ingredients.join(', '), 'and', last].join(' ');
- this.ingredients.push(last);
- return msg;
- }
- });
- new Tea().prepare(); // returns 'Your tea is made up of water and tea leaves.'
- var MilkTea = new Class({
- Extends: Tea,
- initialize: function(){
- this.parent();
- this.ingredients.push('milk');
- },
- prepare: function(){
- var msg = this.parent();
- }
- });
- new MilkTea().prepare(); // returns 'Your tea is made up of water, tea leaves and milk. Enjoy!'
复制代码 Respect Your Parents!
因为parent是所有类的一个使用Extends得到的方法,而不是一个组成部分来的。在子类种添加一个叫parenrt的方法可能会导致异想不到的错误。基本的原则是不添加名字叫parent 的方法,除非你不使用任何继承特征。
还记得我们通过protect关于保护方法的讨论吗?通过使用Extends继承你可以取得父类的保护方法:- var Secret = new Class({
- secretMethod: (function(){
- return 'Shh!';
- }).protect(),
- tellSecret: function(){
- return this.secretMethod();
- }
- });
- var secret = new Secret();
- secret.secretMethod(); // throws error..
- secret.tellSecret(); // returns 'Shh!'
- var SubSecret = new Class({
- Extends: Secret,
- divulge: function(){
- return 'Keep it a secret, ' + this.secretMethod();
- }
- });
- var another = new SubSecret();
- another.secretMethod(); // throws error..
- another.divulge(); // returns 'Keep it a secret, Shh!'
复制代码 I'd Mix it
Mixins是MooTools提供的更有意思的功能之一,一个Mixin是用来添加功能给其他类功能的特殊的一种类,但是不是单独使用它自己(就能够做到)。Mixins鼓励代码重用,并且可以用来给你的类添加很多的功能。
举个例子来说,看一下这段代码:- var Phone = new Class({
- sound: 'phone.ogg',
- initialize: function(number){
- this.number = number;
- },
- call: function(from){
- this.ring();
- new Notification('Call from ' + this.from);
- },
- ring: function(sound){
- sound = sound || this.sound;
- new Sound(sound).play();
- }
- });
- var AlarmClock = new Class({
- sound: 'alarm.ogg',
- initialize: function(alarmTime){
- this.time = alarmTime;
- },
- alarm: function(time){
- if (time == this.time) {
- this.ring();
- new Notification('Wake up sleepy head!');
- }
- },
- ring: function(sound){
- sound = sound || this.sound;
- new Sound(sound).play();
- }
- });
复制代码 如你所见,这两个类拥有相同的2个成员:sound属性和ring方法。现在他们不是DRY的,因为你把这些代码写了两次。那么我们要怎么做呢?也许我们可以建一个超级类,然后让这两个来继承它。RingableObjects?恩,听起来不好。。。
在这种情况下就可以使用Mixins了:- // A Mixin..
- var Ringer = new Class({
- sound: 'ring.ogg',
- ring: function(sound){
- sound = sound || this.sound;
- new Sound(sound).play();
- }
- });
- var Phone = new Class({
- Implements: Ringer,
- initialize: function(number){
- this.number = number;
- this.sound = 'phone.ogg';
- },
- call: function(from){
- this.ring();
- new Notification('Call from ' + this.from);
- }
- });
- var AlarmClock = new Class({
- Implements: Ringer,
- initialize: function(alarmTime){
- this.time = alarmTime;
- this.sound = 'alarm.ogg';
- },
- alarm: function(time){
- if (time == this.time) {
- this.ring();
- new Notification('Wake up sleepy head!');
- }
- }
- });
复制代码 加入一个mixin给你的类,你可以使用已经有的Implements mutator.这个mutator取一个对象或者一组对象:- // A single mixin..
- new Class({
- Implements: MyMixin
- });
- // Multiple mixins..
- new Class({
- Implements: [MyMixin, Events, Options]
- });
复制代码 当class的构造函数看到你的构造对象(class constructor object)里有Implements这个关键字的时候它检查值看是不是类,如果是,就创建一个新的实例,然后把所有的方法和属性都拷贝到你类中去。
使用Mixins,你可以给一个新的类添加小的,相关的方法和属性,然后把他们加到你自己的类中去。因为对你可以添加进去的mixins没有数量限制,你可以从其他类中集合更多的功能。这就是多重继承的一个变形。
因为一个mixin只是一个class,理论上讲,你可以使用别的类作为mixin.但是要注意的是新版本的MooTools有一些怪异的地方.最明显的是从1.2.2版之后包括(MooTools 2).mixin的initialize方法不会被调用。因此做下面这样的事不会实现:-
- var Ringer = new Class({
- initialize: function(){
- this.sound = 'ring';
- },
- ring: function(){
- return this.sound;
- }
- });
- var Mobile = new Class({
- Implements: Ringer,
- call: function(){
- return this.ring();
- }
- });
- new Mobile().call();
- // returns 'ring' in 1.2.1 below.
- // returns undefined in 1.2.2 up.
复制代码 解决办法是把mixni需要的任何成员在class自己中声明,并且去掉initialize方法:- var Ringer = new Class({
- sound: 'ring',
- ring: function(){
- return this.sound;
- }
- });
复制代码 But, but! My Mixin needs initialize!
现在如果你正在想:“等等,我不能这样做!在我的mixin用之前,我需要做一大堆的初始化工作。”那么你可能错误的使用了mixins.Mixins意味着小,有用--并且根本不需要初始化。mixins 的主要用途在于添加相关的小的功能给你的类,而不是可以自运行的成熟的类。要是你发现你的mixin太大了或者说它自己可以单独拿来用,就不要把它作为mixin,你可以继承它或者把它的实例作为一个类的一个成员。
另外一个怪异的地方在于 mixins是怎样添加进去的。像我刚才说的,对于每一个添加到类中的 mixin,Class的构造函数实例化他们,然后把所有的成员和属性复制到你的类中。因为它们是自己拷贝进去的,在原来的mixin中出现的变化不会反映到你的类中。-
- var Meme = new Class({
- hitMe: function(){
- return 'HOW IS BABBY FORMED?';
- }
- });
- var MyMeme = new Class({
- Implements: Meme
- });
- Meme.implement({
- hitMe: function(){
- return 'Is this gonna be forever????';
- }
- });
- new MyMeme().hitMe(); // returns 'HOW IS BABBY FORMED?'..
- new Meme().hitMe(); // returns 'Is this gonna be forever????'
复制代码 为了使你的mixin的新变化可以被传播,你必须在你的类上面也要implement这个mixin.
再没有其他的方式了。幸运的是,我们可以使用之前讨论的Native.implement方法简化这个过程:- var Meme = new Class({
- hitMe: function(){
- return 'HOW IS BABBY FORMED?';
- }
- });
- var MyMeme = new Class({
- Implements: Meme
- });
- new MyMeme().hitMe(); // returns 'HOW IS BABBY FORMED?'..
- Native.implement([Meme, MyMeme],{
- hitMe: function(){
- return 'Is this gonna be forever????';
- }
- });
- new MyMeme().hitMe(); // returns 'Is this gonna be forever????'..
- new Meme().hitMe(); // returns 'Is this gonna be forever????'..
复制代码 最后,你不能够使用this.parent来从一个mixin调用原来的方法.因为mixins是直接添加进你的类中的,你不是真正继承它而是复制的他们的方法。因此this.parent失败了:- var Meme = new Class({
- hitMe: function(){
- return 'HOW IS BABBY FORMED?';
- }
- });
- var MyMeme = new Class({
- initialize: function(){}
- });
- MyMeme.Lolz = new Class({
- Extends: MyMeme,
- Implements: Meme,
- hitMe: function(){
- // do other suff;
- return this.parent();
- }
- });
- new MyMeme.Lolz().hitMe(); // throws an error..
复制代码 那么怎样调用已经覆盖掉的方法呢?简短的答案就必须通过apply来调用你的mixin的原型- var Meme = new Class({
- hitMe: function(){
- return 'HOW IS BABBY FORMED?';
- }
- });
- var MyMeme = new Class({
- initialize: function(){}
- });
- MyMeme.Lolz = new Class({
- Extends: MyMeme,
- Implements: Meme,
- hitMe: function(){
- // do other suff;
- return Meme.prototype.hitMe.apply(this);
- }
- });
- new MyMeme.Lolz().hitMe(); // returns 'HOW IS BABBY FORMED?'.
复制代码 有点丑?在我们谈论Native的那部分中,我提到了generics。可不可通过mixin的generics,Meme.hitMe 来更好的调用呢?
不幸的是,MooTools 没有一个Class.genericize方法。因此这里就提供一个MooTools1.2.2及以上的:- /*
- *Class.genericize
- *Creates generics for class methods.
- */
- Class.genericize = function(klass){
- if (klass instanceof Function) {
- var proto = Class.instantiate(klass);
- for (var i in proto) Native.genericize(klass, i);
- }
- };
- var Meme = new Class({
- hitMe: function(){
- return 'HOW IS BABBY FORMED?';
- }
- });
- Class.genericize(Meme);
- var MyMeme = new Class({
- initialize: function(){}
- });
- MyMeme.Lolz = new Class({
- Extends: MyMeme,
- Implements: Meme,
- hitMe: function(){
- // do other suff;
- return Meme.hitMe(this);
- }
- });
- new MyMeme.Lolz().hitMe(); // returns 'HOW IS BABBY FORMED?'..
复制代码 Seriously Mark? Class.genericize?
我意识到上面提出的这个Class.genericize方法看起来有点奇怪,但是绝大多数情况下它工作得很好。虽然它没有覆盖到所有的状态,比如它不能够为新扩展的方法产生generics。
加入这段代码的目的是为了指出MooTools是如此的灵活和它能够处理各种看起来很复杂的事情,来让你的代码更好。在MooTools文档中没有列出的地方有多漂亮的小细节,仅仅把我们拥有的这些放在一起就可以得到许多新功能。
当然在所有的问题之后,有一个好消息:还记得函数的protect方法吗?好消息是 在mixins当中,它们依然成立:- var Secret = new Class({
- tellSecret: (function(){
- return 'ssssh!'
- }).protect()
- });
- var Teller = new Class({
- Implements: Secret,
- divulge: function(){
- return this.tellSecret();
- }
- });
- var teller = new Teller();
- teller.tellSecret(); // throws an error..
- teller.divulge(); // returns 'ssssh!'..
复制代码 Events, Options and Chain
有三个内置的mixin:Events, Options and Chain。
-----------------
小测试一:
X的值是什么?- var A = new Class({
- tell: function(){
- return 'A!';
- }
- });
- var B = new Class({
- tell: function(){
- return 'B!';
- }
- });
- var C = new Class({
- Extends: A,
- Implements: B
- });
- var x = new C().tell();
复制代码 小测试二:- var A = new Class({
- tell: function(){
- return 'A!';
- }
- });
- var B = new Class({
- tell: function(){
- return this.parent();
- }
- });
- var C = new Class({
- Extends: A,
- Implements: B
- });
- var x = new C().tell();
复制代码 Keeping it Stylish
...
---------------------分割线------------------------
地址:http://github.com/mootools/mooto ... urce/Class/Class.js
理解有不对的地方要指出哈
第一:implement加到类的原型- Class.implement({
-
- implement:function(key, value){}
- -->
- var proto = this.prototype;
- case 'function':
- if (value._hidden) return this;
- proto[key] = Class.wrap(this, key, value);//包装到原型
- break;
- -->
- wrap: function(self, key, method){
- if (method._origin) method = method._origin;
- return function(){
- ...
- var result = method.apply(this, arguments);//挂为原型上的方法
- ...
- }.extend({_owner: self, _origin: method, _name: key});
复制代码 第二 extend添加方法到类的构造函数- Function.implement({
-
- extend: function(properties){
- for (var property in properties) this[property] = properties[property];
- return this;
- },
- ...
-
- });
复制代码 第三 Extends mutator和Implements mutator- /*Mootools检查这个对象的的每一个键在Class.Mutators对象的是不是有mutator函数的对应的名字在里面。如果找到了,它就调用这个函数并且把键的值传给它做处理
- var mutator = Class.Mutators[key];
-
- if (mutator){//mutators are applied to the class when you invoke new Class, so they can only be used to alter classes themselves, not instances of them.
- //http://www.clientcide.com/your-questions/mootools-class-mutators/
- value = mutator.call(this, value);
- if (value == null) return this;
- }
- Extends: function(parent){//Extends mutator取得传送给它的class的名字后,直接继承这个class
-
- this.parent = parent;
- this.prototype = Class.instantiate(parent);//Class.instantiate经典的原型继承
- /*instantiate: function(F){
- *F._prototyping = true;
- *var proto = new F;
- *delete F._prototyping;
- *return proto;
- *}
- */
- this.implement('parent', function(){//this.parent可以访问父类的被覆盖的方法
- var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
- if (!previous) throw new Error('The method "' + name + '" has no parent.');
- return previous.apply(this, arguments);
- }.protect());
- },
- Implements: function(items){//Implements mutator取得传送给它的class的名字后,把它们的方法和属性添加到新类。
- $splat(items).each(function(item){
- ...
- this.implement(item);//-->
- }, this);
-
- }
-
- };
- -->
- Class.implement({
-
- implement: function(key, value){
- ...
- if ($type(key) == 'object'){
- for (var p in key) this.implement(p, key[p]);//-->
- return this;
- }
- -->
- Class.implement({
- implement: function(key, value){
- ...
- switch ($type(value)){
- case 'function'://包装函数到原型
- if (value._hidden) return this;
- proto[key] = Class.wrap(this, key, value);
- break;
- ...
-
- case 'array'://复制数组到原型
- proto[key] = $unlink(value);
- break;
-
- default: proto[key] = value;//复制属性值到原型
-
- }
复制代码 ------------------------------分割线---------------------------------
JavaScript The Definitive Guide, 5th Edition
9.8. Example: A defineClass( ) Utility Method
动物园里选犀牛http://docstore.mik.ua/orelly/ |
-
1
查看全部评分
-
|