dom Framework oop模块v2

正在重构整个框架,让命名空间对象dom也出于同一个继承体系下,就像mootools1.3的Type对象那样。

v2的目标大致如下:

  1. 减少入侵性,只保留ECMA262v5及极个别最有用的扩展,lang模块将与核心模块合而为一
  2. 模块即纯对象,去掉没有多大用处的protect方法,实现extend与include都能调用父方法
  3. 去掉构造器中的智能调用父构造器的功能,以后调用父方法统一为this._super()
  4. 增加不使用new关键字就能进行实例化(需要在配置对象中配置)
//by 司徒正美 http://www.cnblogs.com/rubylouvre/ 2010.10.14
var 
PROTO = "prototype",
CTOR = "constructor",
A_proto = Array[PROTO],
A_slice = A_proto.slice,
to_s = Object[PROTO].toString,
is = function (obj,type) {
    return   (type === "Object" && obj === Object(obj)) ||
    (type === "Array" && Array.isArray && Array.isArray(obj)) || // ECMA-5 15.4.3.2
    (type === "Null" && obj === null) ||
    (type === "Undefined" && obj === void 0 ) ||
    obj && to_s.call(obj).slice(8,-1) === type;
}
function extend(target,source){
    for(var name in source)
        if(source.hasOwnProperty(name) && !target[name]){
            target[name] = source[name];
        }
    return target;
}
//Object扩展
//fix ie for..in bug
var _dontEnum = [  'propertyIsEnumerable', 'isPrototypeOf','hasOwnProperty','toLocaleString', 'toString', 'valueOf', 'constructor'];
for (var i in {
    toString: 1
}) _dontEnum = false;
//第二个参数仅在浏览器支持Object.defineProperties时可用
extend(Object,{
    create:function( proto, props ) {//ecma262v5 15.2.3.5
        var ctor = function( ps ) {
            if ( ps &&  Object.defineProperties )
                Object.defineProperties( this, ps );
        };
        ctor[PROTO] = proto;
        return new ctor( props );
    },
    keys: function(obj){//ecma262v5 15.2.3.14
        var result = [],dontEnum = _dontEnum,length = dontEnum.length;
        for(var key in obj ) if(obj.hasOwnProperty(key)){
            result.push(key)
        }
        if(dontEnum){
            while(length){
                key = dontEnum[--length];
                if(obj.hasOwnProperty(key)){
                    result.push(key);
                }
            }
        }
        return result;
    }
});
//用于创建javascript1.6 Array的迭代器
function iterator(vars, body, ret) {
    return eval('[function(fn,scope){'+
        'for(var '+vars+'i=0,l=this.length;i<l;i++){'+
        body.replace('_', 'fn.call(scope,this[i],i,this)') +
        '}' +
        ret +
        '}]')[0];
};
//注释照搬FF官网
extend(Array[PROTO],{
    //定位类 返回指定项首次出现的索引。
    indexOf: function (el, index) {
        var n = this.length, i = ~~index;
        if (i < 0) i += n;
        for (; i < n; i++)
            if ( this[i] === el) return i;
        return -1;
    },
    //定位类 返回指定项最后一次出现的索引。
    lastIndexOf: function (el, index) {
        var n = this.length,
        i = index == null ? n - 1 : index;
        if (i < 0) i = Math.max(0, n + i);
        for (; i >= 0; i--)
            if (this[i] === el) return i;
        return -1;
    },
    //迭代类 在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。
    forEach : iterator('', '_', ''),
    //迭代类 在数组中的每个项上运行一个函数,并将函数返回真值的项作为数组返回。
    filter : iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'),
    //迭代类  在数组中的每个项上运行一个函数,并将全部结果作为数组返回。
    map :  iterator('r=[],', 'r[i]=_', 'return r'),
     //迭代类  在数组中的每个项上运行一个函数,若存在任意的结果返回真,则返回真值。
    some : iterator('', 'if(_)return true', 'return false'),
     //迭代类  在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。
    every : iterator('', 'if(!_)return false', 'return true'),
    //归化类 javascript1.8  对该数组的每项和前一次调用的结果运行一个函数,收集最后的结果。
    reduce: function (fn, lastResult, scope) {
        if (this.length == 0) return lastResult;
        var i = lastResult !== undefined ? 0 : 1;
        var result = lastResult !== undefined ? lastResult : this[0];
        for (var n = this.length; i < n; i++)
            result = fn.call(scope, result, this[i], i, this);
        return result;
    },
    //归化类 javascript1.8 同上,但从右向左执行。
    reduceRight: function (fn, lastResult, scope) {
        var array = this.concat().reverse();
        return array.reduce(fn, lastResult, scope);
    }
});

//修正IE67下unshift不返回数组长度的问题
//http://www.cnblogs.com/rubylouvre/archive/2010/01/14/1647751.html
if([].unshift(1) !== 1){
    A_proto.unshift = function(){
        var args = [0,0];
        for(var i=0,n=arguments.length;i<n;i++){
            args[args.length] = arguments[i]
        }
        A_proto.splice.apply(this, args);
        return this.length; //返回新数组的长度
    }
}
//String扩展
var metaObject = {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '"' : '\\"',
    '\\': '\\\\'
},rquote = /[\x00-\x1f\\]/g;
extend(String[PROTO],{
    //javascript1.5 firefox已实现
    quote:function () {
        var str = this.replace(rquote,function(chr){
            var meta = metaObject[chr];
            return meta ? meta :  '\\u' + ('0000'+chr.charCodeAt(0).toString(16)).slice(-4);

        });
        return '"'+ str +'"';
    },
    //ecma262v5 15.5.4.20
    //http://www.cnblogs.com/rubylouvre/archive/2009/09/18/1568794.html
    trim: function(){
        var str = this.replace(/^(\s|\u00A0)+/, ''),
        ws = /\s/,
        i = str.length;
        while (ws.test(str.charAt(--i)));
        return str.slice(0, i + 1);
    }
});

//Math扩展
//http://www.cnblogs.com/rubylouvre/archive/2010/10/09/1846941.html
var native_random = Math.random;
Math.random = function(min, max, exact) {
    if (arguments.length === 0) {
        return native_random();
    } else if (arguments.length === 1) {
        max = min;
        min = 0;
    }
    var range = min + (native_random()*(max - min));
    return exact === void(0) ? Math.round(range) : range.toFixed(exact);
}

extend(Function[PROTO],{
    //ecma262v5 15.3.4.5
    bind:function(scope) {
        if (arguments.length < 2 && scope===void 0) return this;
        var fn = this, argv = arguments;
        return function() {
            var args = [], i;
            for(i = 1; i < argv.length; i++)
                args.push(argv[i]);
            for(i = 0; i < arguments.length; i++)
                args.push(arguments[i]);
            return fn.apply(scope, args);
        };
    }
});
function _numarr(s) { // 补零用的辅助函数
    var r=[],k=-1,i=0,j,a=s.split(""),z=a.length;
    for(;i < z;++i){
        for(j=0;j < z;++j){
            r[++k]=a[i]+a[j];
        }
    }
    return r;
}
var numarr = _numarr("0123456789");
function toISOString() {
    var   ms = this.getUTCMilliseconds(),
    pad0 = (ms < 10) ? "00" : (ms < 100) ? "0" : "";
    return this.getUTCFullYear() + '-' +
    numarr[this.getUTCMonth() + 1] + '-' +
    numarr[this.getUTCDate()]      + 'T' +
    numarr[this.getUTCHours()]     + ':' +
    numarr[this.getUTCMinutes()]   + ':' +
    numarr[this.getUTCSeconds()]   + '.' +
    pad0 + this.getUTCMilliseconds() + 'Z';
}
extend(Date[PROTO],{
//ecma262v515.9.5.43
    toISOString:toISOString,
//ecma262v5 15.9.5.44
    toJSON:toISOString
});
extend(Date,{
//ecma262v5 15.9.4.4
    now : function(){
        return new Date().valueOf();
    }
});

全新的类工厂。

var oneObject = function(array,val){
    var result = {},value = val !== void 0 ? val :1;
    for(var i=0,n=array.length;i < n;i++)
        result[array[i]] = value;
    return result;
},
extendObject = oneObject(['_super',PROTO,  'extend', 'include','inherit','ancestors','parent']),
includeObject = oneObject(['_super',CTOR]),
classMethods =  {
    inherit: function(parent) {
        if (parent && parent[PROTO]) {
            this[PROTO] = Object.create(parent[PROTO]);//高效设置原型链
            this.parent = parent;
        }
        this.ancestors =  [];
        while (parent) {//收集所有父类,用于构建方法链时查找同名方法
            this.ancestors.push(parent);
            parent = parent.parent;
        }
        return this[PROTO][CTOR] = this;
    },
    extend: function(){//扩展类成员
        var parent = this.parent,names, name,n,method;
        arguments.length && A_slice.call(arguments).filter(function(module){
            return is(module,"Object");
        }).forEach(function(module){
            names = Object.keys(module);
            n = names.length;
            while(n){
                name = names[--n];
                //避开FF为Object私自添加的原型属性toSource watch unwatch
                if(extendObject[name]===1)
                    continue
                method = module[name];
                this[name] = method;
                if(is(method,"Function") && parent){
                    method.parent = parent[name];
                    method._name = name;
                }
            }
        },this)
        return this;
    },
    include:function(){//扩展原型成员
        var parent = this.parent && this.parent[PROTO],names, name,n,method;
        arguments.length && A_slice.call(arguments).filter(function(module){
            return is(module,"Object");
        }).forEach(function(module){
            names = Object.keys(module)
            n = names.length;
            while(n){
                name = names[--n];
                if(includeObject[name]===1)
                    continue
                method = module[name];
                this[PROTO][name] = method;
                if(is(method,"Function") && parent){
                    method.parent = parent[name];
                    method._name = name;
                }
            }
        },this)
        return this;
    }
}
function _super() {//构建方法链
    var self= arguments.callee.caller,parent = self.parent;
    if(parent){
        return parent.apply(this,arguments.length? arguments : self.arguments);
    }else{
        throw 'this method "'+ self._name +'" no super method.';
    }
}
function classPropsInject(klass,props){
    ['extend','include'].forEach(function(name){
        var modules = props[name];
        if(is(modules,"Object") || is(modules,"Array")){
            klass[name].apply(klass,[].concat(modules));
            delete props[name];
        }
    })
}
function oop(obj){
    obj = obj || {};
    var superclass = obj.inherit; //父类
    delete obj.inherit;
    var nonew = !!obj.nonew;//不用new关键字进行实例化
    delete obj.nonew;
    var klass = function() {
        var that = this;
        if(that.singleton && klass.instance){
            return klass.instance;
        }
        if(nonew && !(that instanceof klass)){
            that = new klass;
        }
        that.init && that.init.apply(that,arguments);
        if(that.singleton){
            klass.instance = that;
        }
        return that;//为nonew准备的出口
    };
    extend(klass,classMethods).inherit(superclass).extend(superclass);
    classPropsInject(klass,obj);
    klass._super = klass[PROTO]._super = _super;
    return klass.include(obj);
}

扩展类成员以及在类方法中调用超类同方法:

var MyMath = oop({});
MyMath.extend({
    PI:3.14,
    getPI:function(){
       return this.PI;
   }
});
var SonMath = oop({inherit:MyMath});
SonMath.extend({
    getPI:function(){
         return this._super()+0.0015926;
    }
});
p(SonMath.getPI());

扩展原型成员,extend、include的属性可以是单个对象,也可以是对象数组。

var movable = {
  run:function(){
    p("能跑")
  },
  fly:function(){
    p("能飞")
  }
}
var recognition  ={
  watch:function(){
    p("看东西")
  },
  smell:function(){
    p("能嗅东西")
  }
}
var Robot = oop({
  init:function(name,type){
    this.name = name;
    this.type = name;
  },
  include:[movable,recognition]
});
var chi = new Robot("小叽","Chobits") ;
p(chi.name);
chi.watch();
chi.fly();

配置单例类

var God = oop({
  init:function(name){
    this.name = name;
    this.alertName = function(){
      p(this.name)
    }
  },
  singleton:true//注意这里,使用singleton属性
});
var god = new God("耶和华");
god.alertName();      //alerts 耶和华
var lucifer = new God("撒旦");
lucifer.alertName();   //alerts 耶和华
p(god === lucifer )//alerts true

配置不使用new关键字使可以实例化。

        var dom2 = oop({
          init:function(selector){
            this.selector = selector
            return this;
          },
          nonew:true,
          getSelectors : function(){
            return (this.selector ||"").split(/\s+/)
          }
        });
      p(dom2('.aaa .bbb .ccc').getSelectors())

继承示例1

      var Animal = oop({
        init:function(name){
          this.name = name;
        },
        getFood:function(){
          return "各种各样的食物"
        },
        extend:{
          getClassName:function(){
            return "Animal";
          }
        }
      });
      var Human = oop({
        inherit:Animal,
        extend:{
          getClassName:function(){
            return this._super()+"-->人";
          }
        }
      });
      var me = new Human("john");
      p(Human.getClassName());
      p(me.getFood());
      var Man = oop({
        inherit:Human,
        init:function(name,sex){
          this._super();
          this.sex = sex;
        },
        getFood: function(){
          return this._super()+",尤其是肉类";
        }
      });
      var Genghis_Khan = new Man("成吉思汗","男");
      p( Genghis_Khan.sex);
      p( Genghis_Khan.name);
      p( Genghis_Khan.getFood());

继承示例2

      var Polygon = oop({
        init:function(sides){
          this.sides = sides
        },
        getArea:function(){
          return 0 //此只是个抽象类,不能用于具体计算
        }
      });
      p("==============Triangle===============")
      var Triangle = oop({
        inherit:Polygon,
        init:function(base,height){
          this._super(3);
          this.base = base;
          this.height = height;
        },
        getArea:function(){
          return 0.5*this.base*this.height;
        }
      });
      var t = new Triangle(2,6);
      p(t.sides);
      p(t.getArea());
      p("==============Rectangle===============")
      var Rectangle = oop({
        inherit:Polygon,
        init:function(length,width){
          this._super(4);
          this.length = length;
          this.width = width;
        },
        getArea:function(){
          return this.length*this.width;
        }
      });
      var r = new Rectangle(7,6);

      p(r.sides);
      p(r.getArea(Rectangle));
      p(r instanceof Polygon)
      p("==============Square===============")
      var Square = oop({
        inherit:Rectangle,
        init:function(side){
          this._super(side,side);
        }
      })
      var s = new Square(6);
      p(s.sides);
      p(s.getArea())
      p(s instanceof Polygon)
      p(s instanceof Square)
原文地址:https://www.cnblogs.com/rubylouvre/p/1851729.html