klass实现js的类的方式

来源:互联网 发布:印度与中国 知乎 编辑:程序博客网 时间:2024/06/09 23:15

Klass

Klass是一种代码结构,模拟传统OO语言的Class。继承时能像传统OO语言的Class一样,子类构造函数调用父类的构造函数。作为一种代码结构,它有一套命名公约,如initialize,_init等,创建对象时这些方法会被自动调用。

例如:

var klass = function (Parent, props) {    var Child, F, i;    //1.新构造函数    Child = function (Parent, props) {        if(Child.uber && Child.uber.hasOwnProperty("__construct")) {            Child.uber.__construct.apply(this, arguments);        }        if(Child.prototype.hasOwnProperty("__construct")) {            Child.prototype.__construct.apply(this, arguments);        }        };        //2.继承    Parent = Parent || Object;    F = function() {};    F.prototype = Parent.prototype;    Child.prototype = new F();    Child.uber = Parent.prototype;    Child.prototype.constructor = Child;    //3.添加实现方法    for(i in props) {        if(props.hasOwnProperty(i)) {            Child.prototype[i] = props[i];        }    }    return Child;};

看一下上面的Klass代码结构。它有两个参数,分别是父类和子类需要扩展的字面量形式的属性。

第一部分是为子类生成构造函数:如果父类存在构造函数,先调用父类构造函数。如果子类存在构造函数,再调用子类构造函数。(由于PHP的影响,一个潜规则是,类的构造函数最好命名为__construct)。在最后return出生成的构造函数

第二部分是寄生组合继承。

第三部分是为子类添加需要扩展的属性。

现在我们的代码中就可以不再纠结于用哪种模式来实现继承了,直接用Klass。

例如创建一个不继承自任何类的新类:

var Person = klass(null, {    __construct: function (n) {        this.name = n;    },    sayHi: function() {        console.log("hi " + this.name);    }});var p1 = new Person("Jack");p1.sayHi();    //hi Jack

上面代码用klass创建了一个Person的新类,没有继承自任何类,意味着继承Object类(源码中的Parent = Parent || Object;语句)。构造函数里创建name属性,并提供了一个sayHi的方法

现在扩充一个Man类:

var Man = klass(Person, {    __construct: function (n) {        console.log("I am a man.");    },    sayHi: function() {        Man.uber.sayHi.call(this);    }});var m1 = new Man("JackZhang");    //I am a man.m1.sayHi();                       //hi JackZhangconsole.log(m1 instanceof Person);    //trueconsole.log(m1 instanceof Man);       //true



转载自:JavaScript继承详解(Klass) 

klass.js源代码解析:

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');
复制代码





原创粉丝点击