二、KLASS
项目地址:https://github.com/ded/klass
先看使用方法:
a、新建一个类
// 声明一个类 var Person = klass(function (name) { this.name = name }) .statics({//静态方法 head: ':)', feet: '_|_' }) .methods({//实例方法 walk: function () {} })
b、继承一个类
// SuperHuman 继承 Person var 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');