javascript面向对象包装类Class的类库解析

来源:互联网 发布:常用数据结构和算法 编辑:程序博客网 时间:2024/06/07 05:38

javascript是个入门门槛很低的语言,甚至一个从来没有接触过javascript的技术人员,几小时内就可以写出一个简单有用的程序代码。

但是如果因此你就下结论:javascript是门简单的语言。那你就大错特错了。想写出高性能的代码,同样需要具备一个高级程序员的基本素养。

一个java或者c++程序员,不一定能写出高性能的javascript代码,但更容易写出高性能的javascript代码。

javascript的简单是基于它“胸襟广阔”的包容性。它声明时,不需要指定类型,甚至可以任意的转换类型。它面向对象,却没有类(Class)的限制。它是一门崇尚自由又非常严谨的语言,如果你是一个自由主义者,那么,拥抱javascript吧!

面向对象编程 (OOP) 是一种流行的编程方法。但javascript的OOP,较之JAVA、c++有很大的同,主要体现它的继承方式不同。javascript是基于原型PROTOTYPE继承的。所有对象都是基于原型链,最终追述到Object对象。

这里不想讨论过多的关于javascript的继承方式和其它语言的继承方式的不同之处。主要讨论如何封装javascript的Class,以便更好的管理和维护基础代码,减少重复代码,以及更好的模块化编程。

下面是几个github上找到的比较好的Class封装类库:

  一、MY-CLASS 

项目地址:https://github.com/jiem/my-class

先看基本用法:

a、新建一个类

(function() {  // 新建类  var Person = my.Class({    // 添加静态方法    STATIC: {      AGE_OF_MAJORITY: 18    },    // 构造函数    constructor: function(name, age) {      this.name = name;      this.age = age;    },    // 实例方法    sayHello: function() {      console.log('Hello from ' + this.name + '!');    },    // 实例方法    drinkAlcohol: function() {      this.age < Person.AGE_OF_MAJORITY ?        console.log('Too young! Drink milk instead!') :        console.log('Whiskey or beer?');    }  });  // 暴露给命名空间  myLib.Person = Person;})();var john = new myLib.Person('John', 16);john.sayHello(); //log "Hello from John!"john.drinkAlcohol(); //log "Too young! Drink milk instead!"

b、继承一个类

(function() {  //Dreamer 继承 Person  var Dreamer = my.Class(Person, {    // 构造方法    constructor: function(name, age, dream) {      Dreamer.Super.call(this, name, age);      this.dream = dream;    },    // 实例方法    sayHello: function() {      superSayHello.call(this);      console.log('I dream of ' + this.dream + '!');    },    // 实例方法    wakeUp: function() {      console.log('Wake up!');    }  });  // Super访问父类  var superSayHello = Dreamer.Super.prototype.sayHello;  // 暴露给全局命名空间  myLib.Dreamer = Dreamer;})();var sylvester = new myLib.Dreamer('Sylvester', 30, 'eating Tweety');sylvester.sayHello(); //log "Hello from Sylvester! I dream of eating Tweety!"sylvester.wakeUp(); //log "Wake up!"

c、给类添加新方法

// 给myLib.Dreamer添加新方法my.extendClass(myLib.Dreamer, {  // 添加静态方法  STATIC : {    s_dongSomeThing : function(){        console.log("do some thing!");        }  },  // 添加实例方法  touchTheSky: function() {    console.log('Touching the sky');  },  // 添加实例方法  reachTheStars: function() {    console.log('She is so pretty!');  }});

d、实现一个类的方法

// 声明一个新类myLib.ImaginaryTraveler = my.Class({  travel: function() { console.log('Traveling on a carpet!'); },  crossOceans: function() { console.log('Saying hi to Moby Dick!'); }});(function() {  //Dreamer 继承 Person 实现 ImaginaryTraveler的方法  var Dreamer = my.Class(Person, ImaginaryTraveler, {    // 构造方法    constructor: function(name, age, dream) {      Dreamer.Super.call(this, name, age);      this.dream = dream;    }   // ...  });  // 暴露给全局命名空间  myLib.Dreamer = Dreamer;})();var aladdin = new Dreamer('Aladdin');aladdin instanceof Person; //truealaddin instanceof ImaginaryTraveler; //falsealaddin.travel();aladdin.wakeUp();aladdin.sayHello();

如果怕忘记new操作符

var Person = my.Class({  //you can now call the constructor with or without new  constructor: function(name, city) {    if (!(this instanceof Person))      return new Person(name, city);    this.name = name;    this.city = citye;  }});

下面看一下my.class的源代码解析:

my.Class实现思路基本是这样的,如果只有一个参数,那么声明的是一个基础类,这个参数是用来声明新类的方法和属以及构造函数。它不是继承而来,但它可以被继承。

继承的思路,就是如果有两个参数,第一个参数做为父类被继承,第二参数用来声明新类的方法和属性以及构造函数,它同样可以被继承。

如果有三个以上参数那么,除出第一个参数做为继承的父类,最后一个参数用声明新类的方法和属性以及构造函数。中间的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。

同时,类库为commonJS和浏览环境都提供了支持!

/*globals define:true, window:true, module:true*/(function () {  // Namespace object  var my = {};  // 保证AMD分模块可用  if (typeof define !== 'undefined')    define([], function () {      return my;    });  else if (typeof window !== 'undefined')    // 保证客户端可用    window.my = my;  else    // 保证后台可用    module.exports = my;  //============================================================================  // @method my.Class  // @params body:Object  // @params SuperClass:function, ImplementClasses:function..., body:Object  // @return function  my.Class = function () {    var len = arguments.length;    var body = arguments[len - 1];    // 最后一个参数是指定本身的方法    var SuperClass = len > 1 ? arguments[0] : null;     // 第一个参数是指继承的方法,实例和静态部分均继承    var hasImplementClasses = len > 2;    // 如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象    var Class, SuperClassEmpty;        // 保证构造方法    if (body.constructor === Object) {      Class = function() {};    } else {      Class = body.constructor;      // 保证后面不覆盖constructor      delete body.constructor;    }    // 处理superClass部分    if (SuperClass) {      // 中间件实现实例属性的继承      SuperClassEmpty = function() {};      SuperClassEmpty.prototype = SuperClass.prototype;      Class.prototype = new SuperClassEmpty();    // 原型继承,解除引用      Class.prototype.constructor = Class;    // 保证constructor      Class.Super = SuperClass;    // 父对象访问接口            // 静态方法继承,重载superClass方法      extend(Class, SuperClass, false);    }        // 处理ImplementClass部分,其实只继承实例属性部分,除SuperClass #arguments[0]# 和 body #arguments[length-1]#    if (hasImplementClasses)      for (var i = 1; i < len - 1; i++)        // implement是继承的实例属性部分, 重载父对象implementClass方法        extend(Class.prototype, arguments[i].prototype, false);            // 处理本身声明body部分,静态要STATIC指定,实例部分要删除STATIC部分    extendClass(Class, body);        return Class;  };  //============================================================================  // @method my.extendClass  // @params Class:function, extension:Object, ?override:boolean=true  var extendClass = my.extendClass = function (Class, extension, override) {    // 静态部分继承静态部分    if (extension.STATIC) {      extend(Class, extension.STATIC, override);      // 保证实例部分不继承静态方法      delete extension.STATIC;    }    // 实例属性继继承实例部    extend(Class.prototype, extension, override);  };  //============================================================================  var extend = function (obj, extension, override) {    var prop;    // 其实这里的flase是表明,覆盖父对象的方法    if (override === false) {      for (prop in extension)        if (!(prop in obj))          obj[prop] = extension[prop];    } else {       // 这里其实不覆盖父对象的方法,包括toString      for (prop in extension)        obj[prop] = extension[prop];      if (extension.toString !== Object.prototype.toString)        obj.toString = extension.toString;    }  };})();

二、KLASS

 项目地址:https://github.com/ded/klass

先看使用方法:

a、新建一个类

// 声明一个类var Person = klass(function (name) {  this.name = name})  .statics({//静态方法    head: ':)',    feet: '_|_'  })  .methods({//实例方法    walk: function () {}  })

b、继承一个类

// SuperHuman 继承 Personvar SuperHuman = Person.extend(function (name) {  // 自动调用父类的构造方法})  .methods({    walk: function() {      // 显式声明调用父类的walk方法      this.supr()      this.fly()    },    fly: function() {}  })new SuperHuman('Zelda').walk()

c、字面量方式声明一个类

var Foo = klass({  foo: 0,  initialize: function() {    this.foo = 1  },  getFoo: function () {    return this.foo  },  setFoo: function (x) {    this.foo = x    return this.getFoo()  }})

d、实现一个类的方法

因为有时候你可能希望覆写或者混合一个实例方法,可以这样:

// 可以传递一个字面量去继承var Alien = SuperHuman.extend({  beam: function() {    this.supr()    // beam into space  }})var Spazoid = new Alien('Zoopo')if (beamIsDown) {  // 覆写beam方法  Spazoid.implement({    beam: function() {      this.supr()      // fallback to jets      this.jets()    }  })}

下面看一下klass源代码解析:

klass的基本设计思路很明确,极力的模仿其它语言的继承方式。比如:子类构造方法调用父类的构造方法,还可以显式的声明调用父类的方法。

这种判断都是基于正则匹配:fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/;关键字"super"

如果显示的声明了要调用父类的方法,那么声明方法的时候,就包装成一个内部调用父类方法且返回相同值的函数,给当前类的方法。

另一方面,构造方法,也是比较灵活的。如果显示的声明了initialize,那么这就是构造方法。否则如果参数是个function那么它就做为构造方法,否则就用父类的构造方法。

通过statics方式添加静态方法,通过实例的implements和静态方法methods添加实例方法。

通过父类的extend实现继承。

同时,类库为commonJS和浏览环境都提供了支持!

/**  * Klass.js - copyright @dedfat  * version 1.0  * https://github.com/ded/klass  * Follow our software http://twitter.com/dedfat :)  * MIT License  */!function (context, f) {  // fnTest用来验证是否可能通过正则找出调用super父类方法的方法  var fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/,      noop = function (){},      proto = 'prototype',      isFn = function (o) {        return typeof o === f;      };  // 基础类  function klass(o) {    return extend.call(typeof o == f ? o : noop, o, 1);  }  // 包装成一个借用super同名方法的函数  function wrap(k, fn, supr) {    return function () {      // 缓存原this.super      var tmp = this.supr;      // 暂把this.super改造成借用super的同名方法above      // 供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法使用      this.supr = supr[proto][k];      // 借用执行并保存返回值      var ret = fn.apply(this, arguments);      // 恢复原this.super      this.supr = tmp;      // 返回返回值,保证wrap后的返回值跟原来一致      return ret;    };  }   // 如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待执行函数供使用   // 如果没有显式的声明借用super的同名方法,或者是o独有的方法,或者不是方法就直接用  function process(what, o, supr) {    for (var k in o) {      // 如果是非继承方法,按方法注释规则执行,最终都放进what      if (o.hasOwnProperty(k)) {        what[k] = typeof o[k] == f          && typeof supr[proto][k] == f          && fnTest.test(o[k])          ? wrap(k, o[k], supr) : o[k];      }    }  }  // 继承方法的实现,fromSub是用来控制是否继承而来,上面的klass里面fromSub是1,表明非继承而来,构造函数不借用super执行  function extend(o, fromSub) {    // noop做为媒介类实现原型继承的解除引用    noop[proto] = this[proto];        var supr = this,        prototype = new noop(), // 创建实例对象供原型继承使用,解除引用        isFunction = typeof o == f,        _constructor = isFunction ? o : this,// 如果o是一个构造方法就用,否则由this来决定构造函数        _methods = isFunction ? {} : o,    // 如果o是一个{...}应该用methods放到fn原型里,如果里面有initialize就是构造函数,如果o是函数就由上面_constructor决定o是构造函数        fn = function () { // 因为kclass借助了kclass,所以最终实际上返回的就是fn,fn其实就新类的构造函数                    //1 如果o是{...}就会被methods直接过滤并添加到fn的原型里,如果o里面有initialize,那么fn的原型里就有initialize,那么它就是构造方法          //2 如果o是function,methods什么也添加不到fn的原型里,但是_constructor会接受o当构造函数          //3 如果o是{....},同时里面也没有initialize,那么就是this当构造函数,如果在klass里由call决定,显然构造函数是noop,如果在非基础类里,构造函数就是父类的构造函数          //  由于o不是函数不会自动调用父类的构造函数,只是把父类的构造函数当做当前类的构造函数----这都是由于this的指向决定的          console.log(this);          if (this.initialize) {            this.initialize.apply(this, arguments);          } else {            // 调用父类构造方法            // 如上面3,o不是函数,不会调用父类的构造方法            // 基础类无父类,不会调用父类构造方法            fromSub || isFn(o) && supr.apply(this, arguments);            // 调用本类构造方法            // 参考上面2,3要么是noop要么是o            console.log(_constructor==noop);            _constructor.apply(this, arguments);          }        };    // 构造原型方法的接口    fn.methods = function (o) {      process(prototype, o, supr);      fn[proto] = prototype;      return this;    };    // 执行实现新类原型,保证新类的constructor    fn.methods.call(fn, _methods).prototype.constructor = fn;    // 保证新类可以被继承    fn.extend = arguments.callee;    // 添加实例方法或者静态方法,statics:静态方法,implement实例方法    fn[proto].implement = fn.statics = function (o, optFn) {      // 保证o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量添加的      // 因为要从o里面拷贝      o = typeof o == 'string' ? (function () {        var obj = {};        obj[o] = optFn;        return obj;      }()) : o;      // 添加实例方法或者静态方法,statics:静态方法,implement实例方法      process(this, o, supr);      return this;    };    return fn;  }  // 后台用,nodejs  if (typeof module !== 'undefined' && module.exports) {    module.exports = klass;  } else {        var old = context.klass;    // 防冲突    klass.noConflict = function () {      context.klass = old;      return this;    };    // 前台浏览器用    //window.kclass = kclass;    context.klass = klass;  }}(this, 'function');

三、还有一种简单实现

实现思路很简单,就是利用ECMAScript 5 原型式继承Object.create方法,封装成一个方法,如果不支持ECMAScript5的环境,就平移退化到

function F() {};  F.prototype = superCtor.prototype;  ctor.prototype = new F();  ctor.prototype.constructor = ctor;

同样的,除最后一个参数是当前类的方法声明,其它参数均做为继承父类,需要循环继承,但当这里处理的相对比较简单,没涉及到覆盖。你可以自己动手添加。

var Class = (function() {            /**       * Inherits function.(node.js)       *        * @param ctor subclass's constructor.       * @param superctor superclass's constructor.       */      var inherits = function(ctor, superCtor) {         // 显式的指定父类        ctor.super_ = superCtor;                    // ECMAScript 5  原型式继承并解除引用        if (Object.create) {              ctor.prototype = Object.create(superCtor.prototype, {                  constructor: {                      value: ctor,                      enumerable: false,                      writable: true,                      configurable: true                  }              });          } else {              // 无Object.create方法的平稳退化            function F() {};              F.prototype = superCtor.prototype;              ctor.prototype = new F();              ctor.prototype.constructor = ctor;          }      };            /**       * Class function.       */      return function() {          // 最后一个参数是新类方法、属性和构造函数声明        var subClazz = arguments[arguments.length - 1] || function() {};          // initialize是构造函数,否构造函数就是一个空函数        var fn = subClazz.initialize == null ? function() {} : subClazz.initialize;          // 继承除最一个参数以的类,多继承,也可以用作扩展方法         for (var index = 0; index < arguments.length - 1; index++) {              inherits(fn, arguments[index]);          }          // 实现新类的方法        for (var prop in subClazz) {                            if (prop == "initialize") {                  continue;              }                            fn.prototype[prop] = subClazz[prop];          }                  return fn;      }        })(); 

看下面实例:

/**   * The definition of Cat Class.   */  var Cat = Class({            /**       * Constructor.       *        * @param name Cat's name       */      initialize: function(name) {        this.name = name;      },            /**       * Eat function.       */      eat: function() {          alert(this.name + " is eating fish.");      }  });    /**   * The definition of Black Cat Class.   */  var BlackCat = Class(Cat, {            /**       * Constructor.       *        * @param name Cat's name.       * @param age Cat's age.       */      initialize: function(name, age) {          // call the constructor of super class.          BlackCat.super_.call(this, name);          this.age = age;                },            /**       * Eat function.       */      eat: function() {          alert(this.name + "(" + this.age + ") is eating dog.");      }  });    /**   * The definition of Black Fat Cat Class.   */  var BlackFatCat = Class(BlackCat, {            /**       * Constructor.       *        * @param name Cat's name.       * @param age Cat's age.       * @param weight Cat's weight.       */      initialize: function(name, age, weight) {          // call the constructor of super class.          BlackFatCat.super_.call(this, name, age);          this.weight = weight;      },            /**       * Eat function.       */      eat: function() {          alert(this.name + "(" + this.age + ") is eating dog. My weight: " + this.weight);      }  });      /**   * The definition of Dog Class.   */  var Dog = Class({});    var cat = new BlackFatCat("John", 24, "100kg");  cat.eat();    // true  alert(cat instanceof Cat);    // true  alert(cat instanceof BlackCat);    // true  alert(cat instanceof BlackFatCat);    // true  alert(cat.constructor === BlackFatCat);    // false  alert(cat instanceof Dog);  

四、mootools类库的Class

源码解析可以看这里:http://www.cnblogs.com/hmking/archive/2011/09/30/2196504.html

看具体用法:

a、新建一个类

var Cat = new Class({    initialize: function(name){        this.name = name;    }});var myCat = new Cat('Micia');alert(myCat.name); // alerts 'Micia' var Cow = new Class({    initialize: function(){        alert('moooo');    }});

b、继承的实现

var Animal = new Class({    initialize: function(age){        this.age = age;    }});var Cat = new Class({    Extends: Animal,    initialize: function(name, age){        this.parent(age); // calls initalize method of Animal class        this.name = name;    }});var myCat = new Cat('Micia', 20);alert(myCat.name); // alerts 'Micia'.alert(myCat.age); // alerts 20.

c、扩充类的实现

var Animal = new Class({    initialize: function(age){        this.age = age;    }});var Cat = new Class({    Implements: Animal,    setName: function(name){        this.name = name    }});var myAnimal = new Cat(20);myAnimal.setName('Micia');alert(myAnimal.name); // alerts 'Micia'.

由于写的比较笼统,可能有很多地方没有解析到,也可能有不准确的地方,还望指正。

看完上面几种解析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么实现看个人喜好了。但基本的思都是一样的基于原型的继承方式和循环拷贝新方法。

http://www.cr173.com/html/19070_all.html

0 0
原创粉丝点击