且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

旧文新帖(First out:2008-06-07):JavaScript“类”继承的横向比较

更新时间:2022-02-25 22:35:16

本文最早发表于2008-06-07

首入眼帘,代码:

/** * Utility to set up the prototype, constructor and superclass properties to * support an inheritance strategy that can chain constructors and methods. * Static members will not be inherited. * * @method extend * @static * @param {Function} subc the object to modify * @param {Function} superc the object to inherit * @param {Object} overrides additional properties/methods to add to the * subclass prototype. These will override the * matching items obtained from the superclass * if present. */ extend: function(subc, superc, overrides) { if (!superc||!subc) { throw new Error("extend failed, please check that " + "all dependencies are included."); } var F = function() {}; F.prototype=superc.prototype; subc.prototype=new F(); subc.prototype.constructor=subc; subc.superclass=superc.prototype; if (superc.prototype.constructor == Object.prototype.constructor) { superc.prototype.constructor=superc; } if (overrides) { for (var i in overrides) { if (L.hasOwnProperty(overrides, i)) { subc.prototype[i]=overrides[i]; } } L._IEEnumFix(subc.prototype, overrides); } }

然后是 YUI 的儿子,Ext JS:

/** * 继承,并由传递的值决定是否覆盖原对象的属性 * 返回的对象中也增加了override()函数,用于覆盖实例的成员 * @param {Object} subclass 子类,用于继承(该类继承了父类所有属性,并最终返回该对象) * @param {Object} superclass 父类,被继承 * @param {Object} overrides (该参数可选) 一个对象,将它本身携带的属性对子类进行覆盖 * @method extend */ extend : function(){ // inline overrides var io = function(o){ for(var m in o){ this[m] = o[m]; } }; return function(sb, sp, overrides){ if(typeof sp == 'object'){ overrides = sp; sp = sb; sb = function(){sp.apply(this, arguments);}; } var F = function(){}, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); sbp.constructor=sb; sb.superclass=spp; if(spp.constructor == Object.prototype.constructor){ spp.constructor=sp; } sb.override = function(o){ Ext.override(sb, o); }; sbp.override = io; Ext.override(sb, overrides); return sb; }; }()

注:上一个是 v2.0 的;Ext JS v2.1 改进了一点,见:

/** * Extends one class with another class and optionally overrides members with the passed literal. This class * also adds the function "override()" to the class that can be used to override * members on an instance. * * <p> * This function also supports a 2-argument call in which the subclass's constructor is * not passed as an argument. In this form, the parameters are as follows:</p><p> * <div class="mdetail-params"><ul> * <li><code>superclass</code> * <div class="sub-desc">The class being extended</div></li> * <li><code>overrides</code> * <div class="sub-desc">A literal with members which are copied into the subclass's * prototype, and are therefore shared among all instances of the new class.<p> * This may contain a special member named <tt><b>constructor</b></tt>. This is used * to define the constructor of the new class, and is returned. If this property is * <i>not</i> specified, a constructor is generated and returned which just calls the * superclass's constructor passing on its parameters.</p></div></li> * </ul></div></p><p> * For example, to create a subclass of the Ext GridPanel: * <pre><code> MyGridPanel = Ext.extend(Ext.grid.GridPanel, { constructor: function(config) { // Your preprocessing here MyGridPanel.superclass.constructor.apply(this, arguments); // Your postprocessing here }, yourMethod: function() { // etc. } }); </code></pre> * </p> * @param {Function} subclass The class inheriting the functionality * @param {Function} superclass The class being extended * @param {Object} overrides (optional) A literal with members which are copied into the subclass's * prototype, and are therefore shared between all instances of the new class. * @return {Function} The subclass constructor. * @method extend */ extend : function(){ // inline overrides var io = function(o){ for(var m in o){ this[m] = o[m]; } }; var oc = Object.prototype.constructor; return function(sb, sp, overrides){ if(typeof sp == 'object'){ overrides = sp; sp = sb; sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; } var F = function(){}, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); sbp.constructor=sb; sb.superclass=spp; if(spp.constructor == oc){ spp.constructor=sp; } sb.override = function(o){ Ext.override(sb, o); }; sbp.override = io; Ext.override(sb, overrides); sb.extend = function(o){Ext.extend(sb, o);}; return sb; }; }(),

其实啊,Ext JS 好、YUI 也好,它们那一套的继承都源自这个前辈 Kevin Lindsey
要搞清楚这套继承来龙去脉,这 Article 就千万不能错过了:
http://kevlindev.com/tutorials/javascript/inheritance/index.htm
文章问世时间是:Saturday, April 13th, 2002 。

最后,看到一位仁兄的推荐,来自 John Resig 的方案好像“集百家之所长”,见:

// Inspired by base2 and Prototype (function(){ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? //b_super/b/ : /.*/; // The base Class implementation (does nothing) this.Class = function(){}; // Create a new Class that inherits from this class Class.extend = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; }; })();

还有其他基于类的继承方案,如 Lazy Inheritance、Dean Edwards 的 Base2,不是说不好,而是很好,好到太复杂了(Lazy Inheritance 的简介像论文似的—题外话),——个人认为过于复杂有点违背 JS 短小精悍的意思,什么东西过了谱就不行的啦 嘿嘿 所以第一眼就枪毙了。

总结一下..嗯 还没太多的心得,还是***的一位朋友说得好:

無奈
  感覺上又回到原始時代,或者說,回到比原始時代更久遠的上古時代,連建構基本的物件架構就有許多的不便,這樣複雜的結構實在有礙思考。想必在Scripting 領域的 OO 或甚至 Design Patten 又會發展成另一個 Knowledge Domain 吧!過去在其他物件導向語言使用的 Patten,硬是要套到這上面來不見得是一件明智的作法,畢竟 Script 的特性就是如此,與其他語言有一定程度的差別,但也正因為如此,不是 Scripting 是有缺陷的語言,而是在這個領域的設計及規劃方法,全世界都欠缺足夠的經驗,因此就不像使用 Java 或 C# 那般,可以歡歡喜喜的導入前人歸納的各種設計模式。

注:本文的重点是类继承,如果采用 JS 原生的“原型”继承 则简单很多——请君勿鄙之,因为存在就是合理。Just take a look 原型继承 by Douglas Crockford,比标准方案Prototype = new XX(); Make Sense很多,仅是五六行代码。