Mootools.js 是如何實現類,以及類的相關屬性和做用

實現類的步驟javascript

  • 第一步是使用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是如何實現類的方法,有幾個重要的問題須要搞清楚java

  • JS是如何建立類的
  • 子類是如何實現繼承父類屬性和方法的
  • 子類繼承父類的屬性,修改了以後,其餘子類再次繼承父類,父類的屬性的值爲什麼不會改變
  • 子類和父類的同名函數,使用this.parent(args)在函數中使用,是如何作到在子類中的同名函數共存的
  • 如何實現,不在類中,而是使用implement往類中添加方法的

下面來經過Mootools.jsclass來具體分析segmentfault

(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面向對象系列app

相關文章
相關標籤/搜索