Ember原始碼學習

weixin_33816946發表於2012-06-02

ember.js是當今最強大的javascript MVC框架。當一把鬍子拉碴的大牛人跑進JS界搞這東西時,昭示了JS全盛時期的到來。

可能你聽說過javascriptmvc.js這個時期在jQuery支援下赫赫有名的框架,或者現在更為流行的backbone,但相對於ember.js,它們就黯然失色了。不過正由於看上去非常高階,也嚇跑了不少人。在富應用日益流行的今天,越來越多工作轉到前端了,JS的程式碼變得非常龐大,如此組織它們是一個難題。如果公司是使用EXT這樣的強大UI框架,這可免談了。但許多公司只能擺弄一下jQuery,jQuery可是堆程式碼的利器。堆得快,倒得快,這也是其特色。jQuery易入門,因此其開發者龍蛇混雜,當然這是相對於國外來說,國內考慮到成本與中國特色,前端人員的素質是極差的,他們寫的jQuery程式碼是非常難維護的。因此要引入規範與絕束。MVC無疑是當中最可靠的選項。如果大家的業務程式碼與UI的編寫也按MVC的規定來寫,所有都有章可循,這維護成本就大大降低。

有人說框架會讓程式設計師變成填空題的碼農,MVC這種強約束,讓程式設計師成為了流水線的工人了。農民工只能搞些土產品,但工人可以組裝精密儀器。歡欣雀躍吧,前端的碼農們!你們現在升級了無產階級的中流砥柱——流水線工人了!你會明白後端的程式設計師為什麼這麼高吧,因為他們早在十年前,在JAVA,C#,C++,JAVA的偉大框架的統治下,實現了農轉非!相對於後端的工業國家,前端的國度可憐得像非洲部落酋長國,即使jQuery的現世,只不過讓部落民轉變成法老的子民。



if ('undefined' === typeof Ember) {

    Ember = {};
    //暴露到全域性作用域下
    if ('undefined' !== typeof window) {
        window.Em = window.Ember = Em = Ember;
    }

}
Ember.isNamespace = true;

Ember.toString = function() { 
    return "Ember";
};

Ember.VERSION = '0.9.8.1';

Ember.ENV = 'undefined' === typeof ENV ? {} : ENV;

//決定是否快取計算值,可以使用volatile禁止它
Ember.CP_DEFAULT_CACHEABLE = (Ember.ENV.CP_DEFAULT_CACHEABLE !== false);

Ember.VIEW_PRESERVES_CONTEXT = (Ember.ENV.VIEW_PRESERVES_CONTEXT !== false);

Ember.K = function() { 
    return this;
};
//除錯相關

if ('undefined' === typeof Ember.assert) { 
    Ember.assert = Ember.K;
}
if ('undefined' === typeof Ember.warn) { 
    Ember.warn = Ember.K;
}
if ('undefined' === typeof Ember.deprecate) { 
    Ember.deprecate = Ember.K;
}
if ('undefined' === typeof Ember.deprecateFunc) {
    Ember.deprecateFunc = function(_, func) {
        return func;
    };
}
//向前相容
if ('undefined' === typeof ember_assert) { 
    window.ember_assert = Ember.K;
}
if ('undefined' === typeof ember_warn) { 
    window.ember_warn = Ember.K;
}
if ('undefined' === typeof ember_deprecate) { 
    window.ember_deprecate = Ember.K;
}
if ('undefined' === typeof ember_deprecateFunc) {

    window.ember_deprecateFunc = function(_, func) {
        return func;
    };
}
//除錯相關
Ember.Logger = window.console || { 
    log: Ember.K,
    warn: Ember.K,
    error: Ember.K
};
//用於儲存一些靜態方法與特徵偶測的結果
var platform = Ember.platform = {} ;

//類工廠,只是Object.create的別名
platform.create = Object.create;

if (!platform.create) {
    //向前相容
    var O_ctor = function() {},
    O_proto = O_ctor.prototype;

    platform.create = function(obj, descs) {
        O_ctor.prototype = obj;
        obj = new O_ctor();
        O_ctor.prototype = O_proto;

        if (descs !== undefined) {
            for(var key in descs) {
                if (!descs.hasOwnProperty(key)) continue;
                //第二個引數也進行補完了
                platform.defineProperty(obj, key, descs[key]);
            }
        }

        return obj;
    };
    //標識是贗品
    platform.create.isSimulated = true;
}

var defineProperty = Object.defineProperty;
var canRedefineProperties, canDefinePropertyOnDOM;
//IE8的 Object.defineProperty就是半成品,只能處理DOM屬性
if (defineProperty) {
    try {
        defineProperty({}, 'a',{
            get:function(){}
            });
    } catch (e) {
        /** @private */
        defineProperty = null;
    }
}
//處理其他奇異的BUG
if (defineProperty) {
    // Detects a bug in Android <3.2 where you cannot redefine a property using
    // Object.defineProperty once accessors have already been set.
    /** @private */
    canRedefineProperties = (function() {
        var obj = {};

        defineProperty(obj, 'a', {
            configurable: true,
            enumerable: true,
            get: function() { },
            set: function() { }
        });

        defineProperty(obj, 'a', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: true
        });

        return obj.a === true;
    })();

    // This is for Safari 5.0, which supports Object.defineProperty, but not
    // on DOM nodes.
    /** @private */
    canDefinePropertyOnDOM = (function(){
        try {
            defineProperty(document.createElement('div'), 'definePropertyOnDOM', {});
            return true;
        } catch(e) { }

        return false;
    })();

    if (!canRedefineProperties) {
        /** @private */
        defineProperty = null;
    } else if (!canDefinePropertyOnDOM) {
        /** @private */
        defineProperty = function(obj, keyName, desc){
            var isNode;

            if (typeof Node === "object") {
                isNode = obj instanceof Node;
            } else {
                isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string";
            }

            if (isNode) {
                // TODO: Should we have a warning here?
                return (obj[keyName] = desc.value);
            } else {
                return Object.defineProperty(obj, keyName, desc);
            }
        };
    }
}

platform.defineProperty = defineProperty;

platform.hasPropertyAccessors = true;

if (!platform.defineProperty) {
  platform.hasPropertyAccessors = false;

  platform.defineProperty = function(obj, keyName, desc) {
    ember_assert("property descriptor cannot have `get` or `set` on this platform", !desc.get && !desc.set);
    obj[keyName] = desc.value;//如果不支援,只能取得這個物件的value進行賦值
  };

  platform.defineProperty.isSimulated = true;
}


// UUID部分
var GUID_KEY = '__ember'+ (+ new Date());
var uuid, numberCache, stringCache;

uuid         = 0;
numberCache  = [];
stringCache  = {};
//讓其不可遍歷
var GUID_DESC = Ember.GUID_DESC = {
  configurable: true,
  writable: true,
  enumerable: false
};

var o_defineProperty = Ember.platform.defineProperty;
var o_create = Ember.platform.create;

Ember.GUID_KEY = GUID_KEY;
//生成一個UUID
Ember.generateGuid = function(obj, prefix) {
  if (!prefix) prefix = 'ember';
  var ret = (prefix + (uuid++));
  if (obj) {
    GUID_DESC.value = ret;
    o_defineProperty(obj, GUID_KEY, GUID_DESC);
    GUID_DESC.value = null;
  }

  return ret ;
};

//取得每個物件的UUID對應的鍵名,UUID是用於物件的,因此對於基本資料型別,它們的返回值是規定好的
Ember.guidFor = function(obj) {

  // special cases where we don't want to add a key to object
  if (obj === undefined) return "(undefined)";
  if (obj === null) return "(null)";

  var cache, ret;
  var type = typeof obj;

  switch(type) {
    case 'number'://處理不可變物件
      ret = numberCache[obj];
      if (!ret) ret = numberCache[obj] = 'nu'+obj;
      return ret;

    case 'string'://處理不可變物件
      ret = stringCache[obj];
      if (!ret) ret = stringCache[obj] = 'st'+(uuid++);
      return ret;

    case 'boolean'://處理不可變物件
      return obj ? '(true)' : '(false)';

    default:
      if (obj[GUID_KEY]) return obj[GUID_KEY];
      if (obj === Object) return '(Object)';//跳過原生物件的構造器
      if (obj === Array)  return '(Array)';//跳過原生物件的構造器
      return Ember.generateGuid(obj, 'ember');
  }
};
//元
var META_DESC = {//其特性描述
  writable:    true,
  configurable: false,
  enumerable:  false,
  value: null
};

var META_KEY = Ember.GUID_KEY+'_meta';

Ember.META_KEY = META_KEY;

// Placeholder for non-writable metas.
var EMPTY_META = {//空元
  descs: {},
  watching: {}
};

if (Object.freeze) Object.freeze(EMPTY_META);

var createMeta = Ember.platform.defineProperty.isSimulated ? o_create : (function(meta) { return meta; });


Ember.meta = function meta(obj, writable) {

  var ret = obj[META_KEY];
  if (writable===false) return ret || EMPTY_META;//如何不可寫,直接返回或返回空元

  if (!ret) {//如果此物件剛剛設定
    o_defineProperty(obj, META_KEY, META_DESC);
    ret = obj[META_KEY] = createMeta({
      descs: {},
      watching: {},
      values: {},
      lastSetValues: {},
      cache:  {},
      source: obj
    });

    // make sure we don't accidentally try to create constructor like desc
    ret.descs.constructor = null;

  } else if (ret.source !== obj) {
    ret = o_create(ret);//複製
    ret.descs    = o_create(ret.descs);//複製
    ret.values   = o_create(ret.values);//複製
    ret.watching = o_create(ret.watching);//複製
    ret.lastSetValues = {};
    ret.cache    = {};
    ret.source   = obj;

    o_defineProperty(obj, META_KEY, META_DESC);
    ret = obj[META_KEY] = createMeta(ret);
  }
  return ret;
};
//取得物件的某些元資訊
Ember.getMeta = function getMeta(obj, property) {
  var meta = Ember.meta(obj, false);
  return meta[property];
};

Ember.setMeta = function setMeta(obj, property, value) {
  var meta = Ember.meta(obj, true);
  meta[property] = value;
  return value;
};
Ember.metaPath = function(obj, path, writable) {
  var meta = Ember.meta(obj, writable), keyName, value;

  for (var i=0, l=path.length; i<l; i++) {
    keyName = path[i];
    value = meta[keyName];

    if (!value) {
      if (!writable) { return undefined; }
      value = meta[keyName] = { __ember_source__: obj };
    } else if (value.__ember_source__ !== obj) {
      if (!writable) { return undefined; }
      value = meta[keyName] = o_create(value);
      value.__ember_source__ = obj;
    }

    meta = value;
  }

  return value;
};

Ember.wrap = function(func, superFunc) {

  function K() {}

  var newFunc = function() {
    var ret, sup = this._super;
    this._super = superFunc || K;
    ret = func.apply(this, arguments);
    this._super = sup;
    return ret;
  };

  newFunc.base = func;
  return newFunc;
};
//用於判定是否為類陣列
mber.isArray = function(obj) {
  if (!obj || obj.setInterval) { return false; }
  if (Array.isArray && Array.isArray(obj)) { return true; }
  if (Ember.Array && Ember.Array.detect(obj)) { return true; }
  if ((obj.length !== undefined) && 'object'===typeof obj) { return true; }
  return false;
};
//將一切轉換為陣列
//      Ember.makeArray();          => []
//      Ember.makeArray(null);      => []
//      Ember.makeArray(undefined); => []
//      Ember.makeArray('lindsay'); => ['lindsay'] 
//      Ember.makeArray([1,2,42]);  => [1,2,42]
Ember.makeArray = function(obj) {
  if (obj === null || obj === undefined) return [];
  return Ember.isArray(obj) ? obj : [obj];
};

本篇到此為止,只是知道它費了很大勁模擬Object.defineProperties,與搞了個META機制,暫時不知有什麼用。

相關文章