二、KLASS
項(xiàng)目地址:https://github.com/ded/klass
先看使用方法:
a、新建一個(gè)類
// 聲明一個(gè)類 var Person = klass(function (name) { this.name = name }) .statics({//靜態(tài)方法 head: ':)', feet: '_|_' }) .methods({//實(shí)例方法 walk: function () {} })
b、繼承一個(gè)類
// SuperHuman 繼承 Person var SuperHuman = Person.extend(function (name) { // 自動(dòng)調(diào)用父類的構(gòu)造方法 }) .methods({ walk: function() { // 顯式聲明調(diào)用父類的walk方法 this.supr() this.fly() }, fly: function() {} }) new SuperHuman('Zelda').walk()
c、字面量方式聲明一個(gè)類
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、實(shí)現(xiàn)一個(gè)類的方法
因?yàn)橛袝r(shí)候你可能希望覆寫或者混合一個(gè)實(shí)例方法,可以這樣:
// 可以傳遞一個(gè)字面量去繼承 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的基本設(shè)計(jì)思路很明確,極力的模仿其它語言的繼承方式。比如:子類構(gòu)造方法調(diào)用父類的構(gòu)造方法,還可以顯式的聲明調(diào)用父類的方法。
這種判斷都是基于正則匹配:fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/;關(guān)鍵字"super"
如果顯示的聲明了要調(diào)用父類的方法,那么聲明方法的時(shí)候,就包裝成一個(gè)內(nèi)部調(diào)用父類方法且返回相同值的函數(shù),給當(dāng)前類的方法。
另一方面,構(gòu)造方法,也是比較靈活的。如果顯示的聲明了initialize,那么這就是構(gòu)造方法。否則如果參數(shù)是個(gè)function那么它就做為構(gòu)造方法,否則就用父類的構(gòu)造方法。
通過statics方式添加靜態(tài)方法,通過實(shí)例的implements和靜態(tài)方法methods添加實(shí)例方法。
通過父類的extend實(shí)現(xiàn)繼承。
同時(shí),類庫為commonJS和瀏覽環(huán)境都提供了支持!
/** * 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用來驗(yàn)證是否可能通過正則找出調(diào)用super父類方法的方法 var fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/, noop = function (){}, proto = 'prototype', isFn = function (o) { return typeof o === f; }; // 基礎(chǔ)類 function klass(o) { return extend.call(typeof o == f ? o : noop, o, 1); } // 包裝成一個(gè)借用super同名方法的函數(shù) 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]; // 借用執(zhí)行并保存返回值 var ret = fn.apply(this, arguments); // 恢復(fù)原this.super this.supr = tmp; // 返回返回值,保證wrap后的返回值跟原來一致 return ret; }; } // 如果o和super有同名方法,且o顯式聲明借用super的同名方法,就wrap成一個(gè)待執(zhí)行函數(shù)供使用 // 如果沒有顯式的聲明借用super的同名方法,或者是o獨(dú)有的方法,或者不是方法就直接用 function process(what, o, supr) { for (var k in o) { // 如果是非繼承方法,按方法注釋規(guī)則執(zhí)行,最終都放進(jìn)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]; } } } // 繼承方法的實(shí)現(xiàn),fromSub是用來控制是否繼承而來,上面的klass里面fromSub是1,表明非繼承而來,構(gòu)造函數(shù)不借用super執(zhí)行 function extend(o, fromSub) { // noop做為媒介類實(shí)現(xiàn)原型繼承的解除引用 noop[proto] = this[proto]; var supr = this, prototype = new noop(), // 創(chuàng)建實(shí)例對(duì)象供原型繼承使用,解除引用 isFunction = typeof o == f, _constructor = isFunction ? o : this,// 如果o是一個(gè)構(gòu)造方法就用,否則由this來決定構(gòu)造函數(shù) _methods = isFunction ? {} : o, // 如果o是一個(gè){...}應(yīng)該用methods放到fn原型里,如果里面有initialize就是構(gòu)造函數(shù),如果o是函數(shù)就由上面_constructor決定o是構(gòu)造函數(shù) fn = function () { // 因?yàn)閗class借助了kclass,所以最終實(shí)際上返回的就是fn,fn其實(shí)就新類的構(gòu)造函數(shù) //1 如果o是{...}就會(huì)被methods直接過濾并添加到fn的原型里,如果o里面有initialize,那么fn的原型里就有initialize,那么它就是構(gòu)造方法 //2 如果o是function,methods什么也添加不到fn的原型里,但是_constructor會(huì)接受o當(dāng)構(gòu)造函數(shù) //3 如果o是{....},同時(shí)里面也沒有initialize,那么就是this當(dāng)構(gòu)造函數(shù),如果在klass里由call決定,顯然構(gòu)造函數(shù)是noop,如果在非基礎(chǔ)類里,構(gòu)造函數(shù)就是父類的構(gòu)造函數(shù) // 由于o不是函數(shù)不會(huì)自動(dòng)調(diào)用父類的構(gòu)造函數(shù),只是把父類的構(gòu)造函數(shù)當(dāng)做當(dāng)前類的構(gòu)造函數(shù)----這都是由于this的指向決定的 console.log(this); if (this.initialize) { this.initialize.apply(this, arguments); } else { // 調(diào)用父類構(gòu)造方法 // 如上面3,o不是函數(shù),不會(huì)調(diào)用父類的構(gòu)造方法 // 基礎(chǔ)類無父類,不會(huì)調(diào)用父類構(gòu)造方法 fromSub || isFn(o) && supr.apply(this, arguments); // 調(diào)用本類構(gòu)造方法 // 參考上面2,3要么是noop要么是o console.log(_constructor==noop); _constructor.apply(this, arguments); } }; // 構(gòu)造原型方法的接口 fn.methods = function (o) { process(prototype, o, supr); fn[proto] = prototype; return this; }; // 執(zhí)行實(shí)現(xiàn)新類原型,保證新類的constructor fn.methods.call(fn, _methods).prototype.constructor = fn; // 保證新類可以被繼承 fn.extend = arguments.callee; // 添加實(shí)例方法或者靜態(tài)方法,statics:靜態(tài)方法,implement實(shí)例方法 fn[proto].implement = fn.statics = function (o, optFn) { // 保證o是一個(gè)object對(duì)象,如果o是一個(gè)字符串,那么就是添一個(gè)方法的情況,如果o是一個(gè)object對(duì)象說明是批量添加的 // 因?yàn)橐獜膐里面拷貝 o = typeof o == 'string' ? (function () { var obj = {}; obj[o] = optFn; return obj; }()) : o; // 添加實(shí)例方法或者靜態(tài)方法,statics:靜態(tài)方法,implement實(shí)例方法 process(this, o, supr); return this; }; return fn; } // 后臺(tái)用,nodejs if (typeof module !== 'undefined' && module.exports) { module.exports = klass; } else { var old = context.klass; // 防沖突 klass.noConflict = function () { context.klass = old; return this; }; // 前臺(tái)瀏覽器用 //window.kclass = kclass; context.klass = klass; } }(this, 'function');
本文導(dǎo)航