Mootools.js实现js的类的方式


Class

MooTools框架的基类。

Class Method: constructor

语法:

var MyClass = new Class(properties);

参数:

  1. 性能 - 可以是以下类型之一:
    • (object)类属性的集合。还接受一些特殊属性,如Extends, Implements和initialize(见下文) 。
    • (function)initialize(初始化)函数(见下文) 。
Property: Extends
  • (class)这个类将被扩展(基类)。
也基类有同名方法,用this.parent访问基类的同名方法,所以你可以重写方法.
Property: Implements
  • (class)这个类的属性将被复制到目标类。
  • (array)类的数组,所有类的属性将被复制到目标类。
Implements与Extends类似,但Implements可以多继承,Implements应该放在Extends之后,但在其它属性之前.
Property: initialize
  • (function)构造函数,当实例被创建时执行。
Property: toElement
  • (function)返回一个html元素。当在document.id()函数里时,此方法会被自动调用。

返回:

  • (class)创建的类。

示例:

Class示例:
var Cat = new Class({ initialize: function(name){ this.name = name; }});var myCat = new Cat('Micia');alert(myCat.name); // alerts 'Micia' var Cow = new Class({ initialize: function(){ alert('moooo'); }});
Extends示例:
var Animal = new Class({ initialize: function(age){ this.age = age; }});var Cat = new Class({ Extends: Animal, initialize: function(name, age){ this.parent(age); // calls initalize method of Animal class this.name = name; }});var myCat = new Cat('Micia', 20);alert(myCat.name); // alerts 'Micia'.alert(myCat.age); // alerts 20.
Implements示例:
var Animal = new Class({ initialize: function(age){ this.age = age; }});var Cat = new Class({ Implements: Animal, setName: function(name){ this.name = name }});var myAnimal = new Cat(20);myAnimal.setName('Micia');alert(myAnimal.name); // alerts 'Micia'.

Class Method: implement

改变基类的原型,和new Class(Implements:function(){})一样,但方便修改已存在的类.

语法:

MyClass.implement(properties);

参数:

  1. properties - (object)添加到基类的属性。

示例:

var Animal = new Class({ initialize: function(age){ this.age = age; }});Animal.implement({ setName: function(name){ this.name = name; }});var myAnimal = new Animal(20);myAnimal.setName('Micia');alert(myAnimal.name); // alerts 'Micia'

Mootools.js 是如何实现类,以及类的相关属性和作用

实现类的步骤

  • 第一步是使用new Class新建类,初始化的固定函数是initialize,不能使用其它名称
  • 子类也是使用new Class新建,父类在子类中,使用Extends:parentClass来继承,Extends与子类的方法名,同一级别
  • 子类中与父类的同名方法,如果需要在父类的同名方法上拓展,需要在子类的同名方法内,使用this.parent(args)
  • 如果需要在类的外面增加方法,可以使用implement方法
// 使用 Class.create 创建类
    var Person = new Class({
        // 初始函数固定为 initialize,
        initialize:function(name) {
            this.name = name;
            this.friends = ['jack', 'mark'];
        },
        getName: function(){
            console.log("My name is " + this.name);
        },
        setFriends:function(friend){
            this.friends.push(friend);
        },
        getFriends:function(){
            console.log(this.friends)
        }
    });

    // 使用 implement 给类添加方法,子类可以继承该方法
    Person.implement({
        getAge:function(age){
            console.log("My age is " + age);
        }
    })

    // 子类通过 new Class 创建类
    var Chinese = new Class({
        // 子类通过 Extends 来继承父类
        Extends:Person,
        initialize:function(name, addr){
            this.parent(name);
            this.addr = addr;
        },
        getAddr:function(){
            console.log("My address is " + this.addr);
        }
    });

    var Japanese = new Class({
        Extends:Person,
        initialize:function(name){
            this.parent(name);
        }
    })

    // 实例化类
    var men = new Chinese('allen', 'BeiJing');
    men.getName(); // My name is allen
    men.getAge(23); // My age is 23
    men.getAddr(); // My address is BeiJing

    // 以下验证 - 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
    var allen = new Person();
    allen.getFriends(); // ["jack", "mark"]

    var women = new Japanese();
    women.setFriends("lisa");
    women.getFriends(); // ["jack", "mark", "lisa"]

    var men = new Chinese();
    men.setFriends('peter');
    men.getFriends(); //["jack", "mark", "peter"]

    var wallen = new Person();
    wallen.getFriends(); //["jack", "mark"]

JS是如何实现类的方法,有几个重要的问题需要搞清楚

  • JS是如何创建类的
  • 子类是如何实现继承父类属性和方法的
  • 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
  • 子类和父类的同名函数,使用this.parent(args)在函数中使用,是如何做到在子类中的同名函数共存的
  • 如何实现,不在类中,而是使用implement往类中添加方法的

下面来通过Mootools.jsclass来具体分析


(function(){

    // 新建一个 Class 的类,new Type 也是一个函数
var Class = this.Class = new Type('Class', function(params){
    // 如果传入的 参数是方法,就把该函数当作初始化的方法
    if (instanceOf(params, Function)) params = {initialize: params};

    var newClass = function(){
        // 解除属性里对其他对象的引用
        reset(this);
        // 如果当前类正在构建,就返回当前类,不做任何操作
        if (newClass.$prototyping) return this;
        // $caller 和 $family 是什么啊
        this.$caller = null;
        this.$family = null;
        // 有初始化函数的话,就传入参数到该初始化函数,没有就返回自身
        var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
        // 这句又是什么意思,一个 $caller ,一个 caller
        this.$caller = this.caller = null;
        return value;
        // extend(this) 把类的方法,都添加到当前新建的类中
        // implement(params) 把 params 的所有方法都添加到当前类中
    }.extend(this).implement(params);

    //指定 constructor ,以便使用 instanceOf 来验证
    newClass.$constructor = Class;
    newClass.prototype.$constructor = newClass;
    // 指定当前类的父类是哪一个
    newClass.prototype.parent = parent;

    return newClass;
});

/*
    在子类拥有和父类同名方法时,使用 this.parent(args) 方法来调用父类的该方法
 */
var parent = function(){
    // :: 如果当前方法没有被调用,那么就说,parent 方法没有被调用
    if (!this.$caller) throw new Error('The method "parent" cannot be called.');
    // 当前函数被调用的名字 function person(age) { this.age = age },则 age 被调用的就是 person 函数,就是得到 person 这个名字
    var name = this.$caller.$name,
        // $owner 当前类对象, 得到当前类对象的父类对象
        parent = this.$caller.$owner.parent,
        // 得到父类相同名字的方法
        previous = (parent) ? parent.prototype[name] : null;
    if (!previous) throw new Error('The method "' + name + '" has no parent.');
    // 父类的该同名函数,添加到当前子类中
    return previous.apply(this, arguments);
};

// 解除属性里对其他对象的引用
// 这个解除的例子,可以看 http://hmking.blog.51cto.com/3135992/675856
var reset = function(object){
    for (var key in object){
        var value = object[key];
        switch (typeOf(value)){
            case 'object':
                var F = function(){};
                F.prototype = value;
                object[key] = reset(new F);
                break;
            case 'array': object[key] = value.clone(); break;
        }
    }
    return object;
};

var wrap = function(self, key, method){
    if (method.$origin) method = method.$origin;
    var wrapper = function(){
        // 如果方法是是被保护的,或者这个方法没有 caller ,就不能被调用
        if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
        var caller = this.caller, current = this.$caller;
        this.caller = current; this.$caller = wrapper;
        // 将 method 绑定到当前对象中
        var result = method.apply(this, arguments);
        this.$caller = current; this.caller = caller;
        return result;
        // 通过extend ,把当前函数的属性附加到 self 里去
    }.extend({$owner: self, $origin: method, $name: key});
    return wrapper;
};

var implement = function(key, value, retain){
    //  Mutators 的 key 只有 Extends 和 Implements
    if (Class.Mutators.hasOwnProperty(key)){
        value = Class.Mutators[key].call(this, value);
        if (value == null) return this;
    }

    if (typeOf(value) == 'function'){
        // 隐藏的方法子类就不要再继承使用了
        // $hidden 和 $protected 去看函数那章
        if (value.$hidden) return this;
        this.prototype[key] = (retain) ? value : wrap(this, key, value);
    } else {
        // merge 应该是同名的函数,这样就直接添加进去就好
        Object.merge(this.prototype, key, value);
    }

    return this;
};

// 为了将父类的的属性继承到子类,会使用中间变量,将父类传递给中间变量,再通过中间变量传递给子类
var getInstance = function(klass){
    // 谁知当前当前类正在构建
    klass.$prototyping = true;

    var proto = new klass;
    // 这里就删除 $prototyping ,也就是构建的过程就是上面这一行咯
    delete klass.$prototyping;
    return proto;
};

// 这里有 overloadSetter ,所以,可能是 Class.implement 方法,来给类额外添加函数的
Class.implement('implement', implement.overloadSetter());

Class.Mutators = {

    // 传给 extends 的参数是 parent
    Extends: function(parent){
        // 指向当前类的父类是 parent 参数
        this.parent = parent;
        // 使用 getInstance 得到父类的全部方法
        this.prototype = getInstance(parent);
    },

    Implements: function(items){
        Array.convert(items).each(function(item){
            var instance = new item;
            for (var key in instance) implement.call(this, key, instance[key], true);
        }, this);
    }
};

})();
/*
 Extends 其实是分两部分,使用 Extends 的时候,是把父类的所有属性和方法,通过 getInstance 来附加到当前类中
 然后当前类的方法中,可以使用 this.parent(args) 方法,来把父类的同名方法加载进来

 Implements 方法中没有指代 this.parent = parent ,所以如果当前类写了和父类同名的方法,就会覆盖父类的方法
 Implements 只是给当前类添加更多的方法
 */

JS面向对象系列


以下摘自苦苦的苦瓜


首先需要说明的是,本文将直接讲解创建和使用类的各种技巧,一些基础的东西不再做解释,要理解如何在JavaScript中实现面向对象的设计,请先参考《JavaScript.高级程序设计(第2版)》(前7章)、《javascript.设计模式》(前四章)、《JavaScript.语言精粹》这三部经典之作。   

        在Mootools中使用Class构造函数创建一个类有两种方式,也就是传递两种不同类型的参数给构造函数,第一种也是标准方式传递一个对象字面量,这个对象字面量可以包括你为类添加的所有属性、方法。例如:

[javascript]  view plain  copy
  1. var Person = new Class({  
  2.     //Methods  
  3.     initialize: function(name,age){  
  4.         this.name = name;  
  5.         this.age = age;  
  6.     },  
  7.     log: function(){  
  8.         console.log(this.name+','+this.age);  
  9.     }  
  10. });  
  11. var mark = new Person('mark',24);  
  12. mark.log();   
  13. //returns'mark,24'  

        第二种是传递一个普通函数,mootools会自动把这个函数包装成只含一个initialize键值的对象字面量,然后你可以使用implement方法对类进行扩展,例如:

[javascript]  view plain  copy
  1. var Person = new Class(function(name,age){  
  2.     this.name = name;  
  3.     this.age = age;  
  4. });  
  5. Person.implement('log',function(){  
  6.     console.log(this.name+','+this.age);  
  7. });  
  8. var mark = new Person('mark',24);  
  9. mark.log(); //returns'mark,24'  

        当然推荐使用的还是第一种方式,直观明了嘛。你如果使用标准方式建立了一个类,也是可以使用implement方法对类进行扩展的,如果你真的认为有必要把一个类的设计拆成几个部分的话(例如在使用掺元对象实现多亲继承时,神马?等等...这个MooTools里实现多亲继承继承不是使用Implements Mutator吗,嗯呐,这个在接下来类的继承中将详细讲解......),呵呵,至于MooTools内部如果对构造函数进行解析,有兴趣的可看看MooTools 1.4 源码分析 - Class 修正版

        Implement and Extend

        Implement方法用来为类添加新的方法、属性。需要注意的是,如果新添加的方法或属性与类中旧有的方法或属性同名,则会覆盖类中旧有的方法、属性。调用Implement方法有两种方式,第一种方式传递两个参数,第一个参数为String类型,存储要添加的方法或属性的名称,第二个参数为方法所对应的函数或属性所对应的值,这种方式每次只能为类添加一个方法或属性:

[javascript]  view plain  copy
  1. Person.implement('log',function(){  
  2.     console.log(this.name+','+this.age);  
  3. });  
  4. Person.implement('city','深圳');  

        第二种方式传递一个对象字面量参数,把要添加的方法属性包含在这个对象中,一次添加多个方法、属性,避免重复调用implement:

[javascript]  view plain  copy
  1. Person.implement({  
  2.     'city''深圳',  
  3.     'log'function(){  
  4.         console.log(this.name+','+this.age);  
  5.     }  
  6. });  

        MooTools关于Class的官方文档中只暴露了implement一个方法,其实对类本身进行操作的还有一个比较重要的方法extend,这个方法之所以没有出现在Class的文档中。这是因为它不是作为Class的特殊方法,而实际上是Type的方法。它的作用是为类创建静态成员,静态成员关联的是类本身,换句话说,静态成员是在类的层次上操作,而不是在实例的层次上操作,每个静态成员都只有一份。调用extend方法的方式同Implement,也是两种方式。

        简单一点讲,implement为实例创建方法和属性,extend为类本身创建方法和变量,请看下面的例子:

[javascript]  view plain  copy
  1. var Person = new Class(function(name,age){  
  2.     this.name = name;  
  3.     this.age = age;  
  4.   
  5. });  
  6. Person.implement({  
  7.     instanceMethod: function(){  
  8.         console.log('Fromaninstance!');  
  9.     }  
  10. });  
  11. Person.extend({  
  12.     classMethod: function(){  
  13.         console.log('Fromtheclassitself!');  
  14.     }  
  15. });  
  16. var mark = new Person('mark',24);  
  17. console.log(typeOf(mark.instanceMethod)); //returns'function'  
  18. mark.instanceMethod(); //returns'Fromaninstance!'  
  19. console.log(typeOf(mark.classMethod)); //returns'null',说明实例是不能调用静态方法的  
  20. console.log(typeOf(Person.classMethod)); //returns'function'  
  21. Person.classMethod(); //returns'Fromtheclassitself!'  
  22. console.log(typeOf(Person.instanceMethod)); //returns'null',同样类也不能直接调用为实例而创建的方法  
  23. Person.prototype.instanceMethod(); //类只能通过这种方式调用原型上的方法  

        私有成员

        严格来讲,JavaScript中没有私有成员的概念,所有对象的属性都是共有的。不过,倒是有一个私有变量的概念,任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问浙西变量。私有变量包括函数的参数、局部变量和在函数内定义的其他函数。所以我们可以通过使用闭包来为类制造私有成员:

[javascript]  view plain  copy
  1. var Person = (function(){  
  2.     //私有变量  
  3.     var numOfPersons = 0;  
  4.     //私有方法  
  5.     var formatName = function(name){  
  6.         return name.capitalize();  
  7.     };  
  8.     return new Class({  
  9.         initialize: function(name,age){  
  10.             this.name = name;  
  11.             this.age = age;  
  12.             numOfPersons++;  
  13.         },  
  14.         //公有方法  
  15.         log: function(){  
  16.             console.log(formatName(this.name)+','+this.age);  
  17.         },  
  18.         getNumOfPersons: function(){  
  19.             return numOfPersons;  
  20.         }  
  21.     });  
  22. })();  
  23. var mark = new Person('mark',24);  
  24. mark.log(); //returns'mark,24'  
  25. console.log(mark.getNumOfPersons()); //returns1  

        使用这个模式有一个好处就是,私有成员在内存中只会存放一份,是由所有实例共享的,不必为每一个实例生成一个副本。但这也延伸出一个问题,来看下面的代码:

[javascript]  view plain  copy
  1. var Person = (function(){  
  2.     //私有变量  
  3.     var name = '';  
  4.     return new Class({  
  5.         initialize: function(v1,v2){  
  6.             name = v1;  
  7.             this.age = v2;  
  8.         },  
  9.         getName: function(){  
  10.             return name;  
  11.         },  
  12.         setName: function(value){  
  13.             name = value;  
  14.         },  
  15.         getAge: function(){  
  16.             return this.age;  
  17.         },  
  18.         setAge: function(value){  
  19.             this.age = value;  
  20.         }  
  21.     });  
  22. })();  
  23. var mark = new Person('mark',24);  
  24. console.log(mark.getName());//'mark'  
  25.   
  26. mark.setName('grey');  
  27. console.log(mark.getName());//'grey'  
  28. console.log(mark.getAge());//24  
  29.   
  30. var john = new Person('john',18);  
  31. console.log(john.getName());//'john'  
  32. console.log(john.getAge()); //18  
  33. console.log(mark.getName()); //'john'  
  34. console.log(mark.getAge()); //24  

        这个例子中的Person构造函数(这里指initialize)与getName()和setName()方法一样,都有权访问私有变量name,在这种模式下,变量name就变成了一个静态的、有所有实例共享的属性,也就是说,在一个实例上调用setName()会影响所有实例,结果就是所有实例getName()都会返回相同的值,而age是实例变量就不存在这个问题。到底是使用实例变量还是静态私有变量,最终还是要视你的需求而定。

        当然上面这个问题只是针对私有变量的,私有方法就不存在这个问题,相比实例方法会更有效率(从内存占用的意义上来说),应为它只会被创建一份。

        使用闭包还带来一个问题,多查找作用域链中的一个层次,就会在一定程度上影响查找的速度(一般情况下可以忽略不计),鱼与熊掌不可兼得啊......

        常量

        最简单设置常量的方法是为类添加一个静态属性,然而静态属性是公有的,类的使用者可以随时改变它的值,这个样的操作后果是很严重的。这里我们可以使用前面介绍的为类设置静态私有变量的方式来模拟常量,然后在实例方法中只创建取值器方法而不创建赋值器方法。这样类的使用者只能使用暴露出来的取值器方法来得到私有变量的值而不能改变它的值。来看下面的代码:

[javascript]  view plain  copy
  1. var Person = (function(){  
  2. //私有变量  
  3. var AGE_UPPER_BOUND = 32;  
  4.     return new Class({  
  5.         initialize: function(v1,v2){  
  6.             //...  
  7.         },  
  8.         getAGEUPPERBOUND: function(value){  
  9.             return AGE_UPPER_BOUND;  
  10.         }  
  11.     });  
  12. })();  

        如果需要使用多个常量,设置一个私有的对象字面量来存储这些常量,然后设置一个通用的取值器方法来取得这些常量:

[javascript]  view plain  copy
  1. var Person = (function(){  
  2.     //私有变量  
  3.     var constants = {  
  4.         AGE_UPPER_BOUND: 32,  
  5.         AGE_LOWER_BOUND: 18  
  6.     };  
  7.     return new Class({  
  8.         initialize: function(v1,v2){  
  9.             //...  
  10.         },  
  11.         getConstants: function(name){  
  12.             return constants[name];  
  13.         }  
  14.     });  
  15. })();  

        继承

        继承的主要好处表现在代码的重用方面,通过建立类之间的继承关系,有些方法我们只需要定义一次就可以了。同样,如果需要修改这些方法或排查其中的错误,那么由于其定义只出现在一个位置,所以非常有利于节省时间和精力。MooTools实现类的继承有两种模式:原型式继承和多亲继承,原型式继承由内建的Extends Mutator来实现,多亲继承由Implements Mutator或implement方法来实现。

        原型式继承这里就不多讲了,着重讲一下多亲继承。在JavaScript里,因为一个对象只能拥有一个原型对象,所以不允许子类继承多个超类,不过我们可以利用多个掺元类(minix class)或掺元对象对一个类进行扩充,这样类的实例就可以拥有mixin类(对象)中的方法、属性,所以这实际上实现了多继承的效果。通常mixin类(对象)包含一些通用的方法,大家可以看看MooTools里Class.Extras模块中三个mixin类的定义(Chain、Events、Options)。

        这里还需要注意的的一点是,在派生具有私有成员的的类或implement mixin类(对象)时,因为在父类(或mixin)中访问这些私有变量的方法是公有的,所以他们也会被遗传下来。所以子类可以间接访问父类(或mixin)的私有成员,但子类自身的实例方法都不能直接访问这些私有属性,而且你也不能在子类中添加能够直接访问他们的方法(作用域变量,你想访问也访问不了啦,呵呵)。

        首先我们先建立一个基类:

[javascript]  view plain  copy
  1. var Animal = new Class({  
  2.     initialize: function(age){  
  3.         this.age = age;  
  4.     }  
  5. });  

        使用Extends Mutator派生一个子类:

[javascript]  view plain  copy
  1. var Cat = new Class({  
  2.     Extends: Animal,  
  3.     initialize: function(name,age){  
  4.         this.parent(age);  
  5.         //callsinitalizemethodofAnimalclass  
  6.         this.name = name;  
  7.     }  
  8. });  
  9. var cat = new Cat('Micia',20);  
  10. console.log(cat.name); //'Micia'  
  11. console.log(cat.age);//20  

        利用Implements Mutator扩充一个类,首先建立一个mixin类:

[javascript]  view plain  copy
  1. var Mixin = new Class({  
  2.     getName: function(){  
  3.         return this.name;  
  4.     },  
  5.     setName: function(value){  
  6.         this.name = value  
  7.     }  
  8. });  
  9. var Cat = new Class({  
  10.     Extends: Animal,  
  11.     Implements: Mixin,  
  12.     initialize: function(name,age){  
  13.         this.parent(age);  
  14.         //callsinitalizemethodofAnimalclass  
  15.         this.name = name;  
  16.     }  
  17. });  
  18. var cat = new Cat('Micia',20);  
  19. console.log(cat.name); //'Micia'  
  20. console.log(cat.age); //20  
  21.   
  22. cat.setName('Dog');  
  23. console.log(cat.getName()); //'Dog'  

        使用implement方法扩充一个类,首先家里一个mixin对象:

[javascript]  view plain  copy
  1. //mixin对象存储一些通用方法,可以被不同的类implement  
  2. var objMixin = (function(){  
  3.     var counter = 0;  
  4.     return {  
  5.         init: function(){  
  6.             counter + = 1;  
  7.         },  
  8.         getCounter: function(){  
  9.             return counter;  
  10.         },  
  11.         getAge: function(){  
  12.             return this.age;  
  13.         },  
  14.         setAge: function(value){  
  15.             this.age = value;  
  16.         }  
  17.     };  
  18. })();  
  19. var Cat = new Class({  
  20.     Extends: Animal,  
  21.     Implements: Mixin,  
  22.     initialize: function(name,age){  
  23.         this.parent(age);  
  24.         //callsinitalizemethodofAnimalclass  
  25.         this.name = name;  
  26.     }  
  27. });  
  28. Cat.implement(objMixin);  
  29. var Dog = new Class({  
  30.     Extends: Animal,  
  31.     Implements: Mixin,  
  32.     initialize: function(name,age){  
  33.         this.parent(age);  
  34.         //callsinitalizemethodofAnimalclass  
  35.         this.name = name;  
  36.     }  
  37. });  
  38. Dog.implement(objMixin);  
  39. var cat = new Cat('Micia',20);  
  40. console.log(cat.name); //'Micia'  
  41. console.log(cat.age); //20  
  42.   
  43. cat.setName('汤姆');  
  44. console.log(cat.getName()); //'汤姆'  
  45.   
  46. cat.setAge(12);  
  47. console.log(cat.getAge()); //12  
  48.   
  49. //对mixin对象的私有属性进行操作  
  50. cat.init();  
  51. console.log(cat.getCounter());//1  
  52.   
  53. var dog = new Dog('小狗',6);  
  54. console.log(dog.name); //'小狗'  
  55. console.log(dog.age); //6  
  56. dog.setName('布鲁托');  
  57. console.log(dog.getName());//'布鲁托'  
  58.   
  59. dog.setAge(8);  
  60. console.log(cat.getAge());//8  
  61.   
  62. //对mixin对象的私有属性进行操作  
  63. dog.init();  
  64. console.log(dog.getCounter()); //2  
  65. console.log(cat.getCounter());//2  

        大家都看明白了吧,呵呵,不过通过上面的代码我们引申出另外一个问题,注意上面的Cat类的设计,我们首先设计了Extends,然后是Implements,再就是Cat类本身的方法属性,MooTools内部对Class构造函数解析时是按照我们设计时的顺序解析的吗?答案是按照我们设计时的顺序解释的。简单来讲MooTools通过for-in对对象进行枚举来遍历每个成员进行解释的,等等......那个ECMAScript最新版对for-in语句的遍历机制又做了调整,属性遍历的顺序是没有被规定的,也就是说随机的,那么MooTools是怎样保证按顺序解释的呢?先看下面这段代码:

[javascript]  view plain  copy
  1. var obj = {  
  2.     Waa: "Waa",  
  3.     aa: 'aa',  
  4.     68: '68',  
  5.     15: '15',  
  6.     tt: 'tt',  
  7.     '-7''-7',  
  8.     _: "___",  
  9.     online: true  
  10. };  
  11. for(var k in obj){  
  12.     console.log(k);  
  13. }  

        把它放在各个浏览器都执行一遍,你会发现IE、火狐、Safari浏览器的JavaScript解析引擎遵循的是较老的ECMA-262第三版规范,属性遍历顺序由属性构建的顺序决定,而Chrome、Opera中使用 for-in 语句遍历对象属性时会遵循一个规律,它们会先提取所有 key的 parseFloat 值为非负整数的属性, 然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。其它浏览器则完全按照对象定义的顺序遍历属性。

        这下明白了吧,只要你为类设计的方法、属性还有Mutator的名称不为数字就可以了(当然如果你非要有这样的嗜好,我也只能@#%$......)。请看下面的代码:

[javascript]  view plain  copy
  1. var Super = new Class({  
  2.     log: function(){  
  3.         console.log('Super');  
  4.     }  
  5. });  
  6. var Mixin = new Class({  
  7.     log: function(){  
  8.         console.log('Mixin');  
  9.     }  
  10. });  
  11. var Sub = new Class({  
  12.     Extends: Super,  
  13.     Implements: Mixin  
  14. });  
  15. var obj = new Sub();  
  16. obj.log();//?  

        在这里obj.log()会返回什么呢?对了是'Maxin',这里Sub类首先继承了Super类,Sub的原型实际就是Super类的一个实例,Super的log方法也就是成了Sub的原型上的一个方法,然后执行Implements Mutator 为Sub类的原型扩展了一个Mixin类的实例上的方法,这时Mixin类实例上的log方法就覆盖了Sub类原型上原来的log方法(继承自Super类)。

        如果把Extends、Implements的顺序颠倒一下:

[javascript]  view plain  copy
  1. var Sub = new Class({  
  2.     Implements: Mixin,  
  3.     Extends: Super  
  4. });  
  5. var obj = new Sub();  
  6. obj.log();  
  7. //?  

        这时obj.log()会返回什么呢?还是'Maxin'吗?其实这里返回的是'Super',Why?前面我们介绍了MooTools对Class构造函数解析时是按照我们设计的顺序解析的,所以在这里首先执行的是Implements Mutator,它首先为Sub类的原型扩展了一个Mixin类的实例上的log方法,然后才是对超类Super的继承,因为在JavaScrpt里每个对象只有一个原型,原型式继承的原理就是超类的一个实例赋予子类的原型,子类原来的原型这时会被超类的实例替换掉,所以这是Sub类原型的引用已经指向了超类的实例,而他自己的原型对象这时被消除了,所以之前从Mixin类得来的那个log方法,对不起跟着一起魂飞湮灭了,所以这里返回的是'Super'。

        当然如果你嫌不过瘾,那就在为Sub类添加一个log方法:

[javascript]  view plain  copy
  1. var Sub = new Class({  
  2.     Implements: Mixin,  
  3.     Extends: Super,  
  4.     log: function(){  
  5.         console.log('sub');  
  6.     }  
  7. });  
  8. var obj = new Sub();  
  9. obj.log(); //?  

        你可以把Sub类的Implements、Extends、log来回颠倒一下看看效果,呵呵,再用implement方法在扩展一个试试:

[javascript]  view plain  copy
  1. var objMixin = {  
  2.     log: function(){  
  3.         console.log('objMixin');  
  4.     }  
  5. };  
  6. var Sub = new Class({  
  7.     Implements: Mixin,  
  8.     Extends: Super,  
  9.     log: function(){  
  10.         console.log('sub');  
  11.     }  
  12. });  
  13. Sub.implement(objMixin);  
  14. var obj = new Sub();  
  15. obj.log(); //?  

        呵呵,别晕掉,一切都是为了把问题搞的跟明白不是......

        最后不要忘记两个重要的方法:parent()和protect(),这里就不多说了,在前面的Class源码分析里有详细介绍。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值