解讀sencha touch移動框架的核心架構(一)


sencha的前身就是Extjs了,sencha 框架是世界上第一個基於HTML5的Mobile App框架

那么何謂框架,傳統軟件工程對於庫和框架的區分主要着眼於對應用運行流程的控制權,框架提供架構,控制運行流程,讓開發者在合適的地方書寫針對具體問題的代碼

sencha提倡的就是組件化編程,是面向對象的技術的一種近一步的發展延伸,類的概念仍然是組件技術中一個基礎的概念,但是組件技術更核心的概念是接口。

sencha是重量級的架構了,一般來說很少會有公司用到內部的這種架構,因為太像java的Swing了

不過我在項目中用sencha組織的代碼結構拿來主義本來不是我的風格,自己也改動了很多代碼, 但是整體的設計還是參考的sencha了

有項目圖為證:3萬行以上的純js代碼架構哦~

AMD + sencha核心

AMD模塊

sencha架構模塊

AMD/CMD的實現只用了100行不到的代碼,只是為簡單的管理模塊

在強類型語言中,繼承機制是得到語言本身的支持的,就算代碼開發者比較一般,也不會出太大的問題,但是js就不同了,要實現這一套機制就有一套很復雜的模擬

當然這里性能與維護擴展本來就是一個單選題 - -.

當然像backbone這種也是類似的提供純架構方案

為什么我選擇sencha作為參考架構呢?

高端大氣上檔次

關於Ext/sencha的好與壞這里就不多加討論了,它的精華是需要你去慢慢體會的

 


按照最新sencha 2.3.1源碼的區分

從Ext4.0開始,風格大變,提供一個完整的沙箱模式

就只針對core文件夾的內容,具體就是這么幾個文件了,

Ext.js,Base.js,Class.js,ClassManager.js,Loader.js

這里是架構的核心文件,架構真心有點復雜,先看看歷史的演變,老版本的架構

 


EXTJS組件化編程

組件化的簡單定義:

組件化的基礎是面向對象,在面向對象編程的基礎上將一個或多個小部件封裝到一起,成為一個模塊,讓其成為一個新的組件(適合我們需求的組件),每一個模塊都應當具備單獨運行的能力

image

通過繼承來實現了對象的封裝,ExtJS最底層的那個基類是Observable,它提供了最基礎的事件機制,通過層層的封裝與擴展,它的派生類中是越來越龐大,所提供的功能也越來越多,就比如Window這個組件,它繼承自Panel並添加了默認的resizabel,closable,draggable等屬性(Panel也可以加入類似的函數,但是需要編碼實現)。而Panel又繼承自Container,並加入了渲染Title和tbar等功能

 


先看老版本的實現繼承的機制

Ext.extend( superclass, overrides )  在4.0后就作廢了,統一換成了Ext.define的架構方式

先看3.4版本的,之后在過渡4.0,發現是一個多么大的跨越

Ext.apply(Ext, {

        /**
         * This method deprecated. Use {@link Ext#define Ext.define} instead.
         * @method
         * @param {Function} superclass
         * @param {Object} overrides
         * @return {Function} The subclass constructor from the `overrides` parameter, or a generated one if not provided.
         * @deprecated 2.0.0 Please use {@link Ext#define Ext.define} instead
         */
        extend: function() {
            // inline overrides
            var objectConstructor = objectPrototype.constructor,
                inlineOverrides = function(o) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        continue;
                    }
                    this[m] = o[m];
                }
            };

            return function(subclass, superclass, overrides) {
                // First we check if the user passed in just the superClass with overrides
                if (Ext.isObject(superclass)) {
                    overrides = superclass;
                    superclass = subclass;
                    subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                        superclass.apply(this, arguments);
                    };
                }

                //<debug>
                if (!superclass) {
                    Ext.Error.raise({
                        sourceClass: 'Ext',
                        sourceMethod: 'extend',
                        msg: 'Attempting to extend from a class which has not been loaded on the page.'
                    });
                }
                //</debug>

                // We create a new temporary class
                var F = function() {},
                    subclassProto, superclassProto = superclass.prototype;

                F.prototype = superclassProto;
                subclassProto = subclass.prototype = new F();
                subclassProto.constructor = subclass;
                subclass.superclass = superclassProto;

                if (superclassProto.constructor === objectConstructor) {
                    superclassProto.constructor = superclass;
                }

                subclass.override = function(overrides) {
                    Ext.override(subclass, overrides);
                };

                subclassProto.override = inlineOverrides;
                subclassProto.proto = subclassProto;

                subclass.override(overrides);
                subclass.extend = function(o) {
                    return Ext.extend(subclass, o);
                };

                return subclass;
            };
        }(),

        /**
         * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
         *
         * @param {Object} cls The class to override
         * @param {Object} overrides The properties to add to `origClass`. This should be specified as an object literal
         * containing one or more properties.
         * @method override
         * @deprecated 2.0.0 Please use {@link Ext#define Ext.define} instead.
         */
        override: function(cls, overrides) {
            if (cls.$isClass) {
                return cls.override(overrides);
            }
            else {
                Ext.apply(cls.prototype, overrides);
            }
        }
    });

在4.0以前的架構還是相對比較簡單的,只是用了一個關鍵的繼承,不像4.0以后那就是一套完整的沙箱架構

針對JS的繼承機制,那本紅本書高3已經把細節講的很透徹了

當然也有漏掉的,比如創建一個空函數的作用

var F = function() {},
     F.prototype = superclassProto;
     subclassProto = subclass.prototype = new F();

backbone的繼承機制基本大同小異

 


實現詳解

一般繼承的手段無非就是兩種,原型繼承與對象冒充

⒈原型繼承就是利用了原型的查找合函數的作用域,好處就是內存處只有一份,壞處就是原型是共享的,如果有引用類型所有的實例都可以被修改

⒉對象冒充就是拷貝屬性了,把所有的實例屬性與方法都拷貝一次,壞處自然就是每一份拷貝都是占獨立的一份空間了

原型繼承自然就是引用原型鏈了,這個prototype沒什么好說的,但是不能繼承靜態屬性,因為這個不在原型鏈上

對象冒充,就是通過call,apply處理的,因為JS有運行作用域的,要注意了prototype上的的屬性是不會被處理的

現在看看Ext的完美解決方案

看下面例子,實現一個最基本的繼承

var a = function(id){
    this.name = id;
}

a.prototype = {
    sayA : function(){
        alert(this.name)
    }
};

var b = Ext.extend(a, {
    constructor:function(name){
        this.name = name;
    },
    sayB : function(){
        this.constructor.superclass.constructor(111)
    }
});

c =  new b('Aaron')

ext需要解決的問題

1 構造器問題:類b沒有構造器,直接是通過Ext.extend生成的,所以內部可定需要類似工廠方法,生成一個類的包裝器

2 父類調用:原型只能繼承原型鏈上的屬性與方法,如果要調用父類構造器,如何處理?所以ext需要留一個接口出來可以調用父類

3 靜態方法處理

4 實現繼承后,構造器引用被修改

 

可以先思考下以上問題,具體的實現請等下一章~

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM