• Ember源码学习


    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机制,暂时不知有什么用。

  • 相关阅读:
    在博客园里给图片加水印(canvas + drag)
    Chrome开发者工具使用指南
    《古剑奇谭3》千秋戏辅助工具(前端React制作)
    React中useEffect的源码解读
    关于为什么使用React新特性Hook的一些实践与浅见
    使用@babel/preset-typescript取代awesome-typescript-loader和ts-loader
    使用dva改造React旧项目的数据流方案
    在React旧项目中安装并使用TypeScript的实践
    安利一个绘制指引线的JS库leader-line
    微信小程序中悬浮窗功能的实现(主要探讨和解决在原生组件上的拖动)
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2531948.html
Copyright © 2020-2023  润新知