關於JavaScript繼承,方式很是多,包含compile-to-javascript的語言TypeScript, CoffeeScript以及站點MDN, GitHub, Modernizr各類polyfill都給出了穩妥的實現方案。
從ES5的角度看,這當中一些方案在功能上OK,但在語義上卻不盡如人意。
本人從這些方案中採取一些比較潮的思路,整理出一份方案,可實現與原生DOM類繼承的風格一致,達到功能和語義兼得的效果(固然,就別再老想着99後ES3了)。
假設你的WebApp是基於ES5執行環境的,可以評估或fork此方案。
javascript
//exports Function.prototype.extends //exports global.getPrototypeNames //基於ES5的繼承實現函數 Function.prototype.extends=(function(){ function getOwnPropertyDescriptors(object){ return Object.getOwnPropertyNames(object).reduce(function(pds,pn){ return pds[pn]=Object.getOwnPropertyDescriptor(object,pn),pds; },{}); } /** * 繼承一個類。 * 如有興許類,則共享興許類截至到當前的快照屬性(constructor除外), * 這些屬性中的getters,setters和methods須考慮到要是通用的(如Array的那些methods) **/ function inherits(s){ var c,p; c=this; if(typeof s==="function"){ p=Object.create(s.prototype,getOwnPropertyDescriptors(c.prototype)); }else{ p=c.prototype; } if(arguments.length>1){ Array.prototype.slice.call(arguments,1).forEach(function(s){ var pds=getOwnPropertyDescriptors(s.prototype); delete pds.constructor; Object.defineProperties(p,pds); }); } c.prototype=p; } return inherits; }()); //測試準備 //~~~~~~~~~~~~~~~~~~~~~~~~ // BaseList extends Array //~~~~~~~~~~~~~~~~~~~~~~~~ function BaseList(){ this.length=this.length; } BaseList.prototype.add=function(e){ return this.push(e); }; BaseList.extends(Array); //~~~~~~~~~~~~~~~~~~~~~~~~ // ItemList extends BaseList //~~~~~~~~~~~~~~~~~~~~~~~~ function ItemList(){ BaseList.call(this); } ItemList.extends(BaseList,EventTarget); ItemList.prototype.item=function item(index){ index>>>=0; return index<this.length?this[index]:null; }; //~~~~~~~~~~~~~~~~~~~~~~~~ // ElementList extends ItemList //~~~~~~~~~~~~~~~~~~~~~~~~ function ElementList(){ ItemList.call(this); } ElementList.extends(ItemList); ElementList.prototype.namedItem=function namedItem(name){ var index=this.findIndex(function(elem){return elem.name===name;}); return index===-1?null:this[index]; }; //測試工具函數之獲取原型鏈名單 var getPrototypeNames=(function(){ function typeOf(value){ return Object.prototype.toString.call(value).slice(8,-1); } function isObject(value){ return typeof value==="object"&&value!==null||typeof value==="function" } function constructorNameOf(proto){ return typeof proto.constructor==="function"?proto.constructor.name:typeOf(proto) } function getPrototypeNames(object){ var names,proto; names=[]; proto=Object.getPrototypeOf(object); while(isObject(proto)){ names.push(constructorNameOf(proto)); proto=Object.getPrototypeOf(proto) } return names; } return getPrototypeNames; }()); //運行測試 var list=new ElementList(); console.dir(list); console.log("list: "+getPrototypeNames(list).join(" > ")); list.push(document.documentElement); list.push(document.head); console.assert(list.item(1)===document.head,"The second item of list is document.head");