在 ExtJS 中调用超类方法的更好方法

Posted

技术标签:

【中文标题】在 ExtJS 中调用超类方法的更好方法【英文标题】:Better way to call superclass method in ExtJS 【发布时间】:2010-12-15 20:51:16 【问题描述】:

我读过的所有 ExtJS 文档和示例都建议像这样调用超类方法:

MyApp.MyPanel = Ext.extend(Ext.Panel, 
  initComponent: function() 
    // do something MyPanel specific here...
    MyApp.MyPanel.superclass.initComponent.call(this);
  
);

我使用这种模式已经有一段时间了,主要问题是,当你重命名你的类时,你还必须更改所有对超类方法的调用。这很不方便,我经常会忘记,然后我必须追踪奇怪的错误。

但是阅读Ext.extend() 的源代码后我发现,我可以使用Ext.extend() 添加到原型中的superclass()super() 方法:

MyApp.MyPanel = Ext.extend(Ext.Panel, 
  initComponent: function() 
    // do something MyPanel specific here...
    this.superclass().initComponent.call(this);
  
);

在这段代码中,将 MyPanel 重命名为其他名称很简单 - 我只需更改一行即可。

但我有疑问...

我在任何地方都没有看到这方面的记录,而且古老的智慧说,我不应该依赖未记录的行为。

我没有在 ExtJS 源代码中发现单独使用这些 superclass()supr() 方法。当您不打算使用它们时,为什么要创建它们?

也许这些方法曾在某些旧版本的 ExtJS 中使用,但现在已弃用?但这似乎是一个非常有用的功能,为什么要弃用它?

那么,我应该使用这些方法吗?

【问题讨论】:

其实可以试试this.constructor.superclass.initComponent.call(this);吗? 谢谢,但这与 Jabe 描述的问题相同 - 它仅适用于一个层次结构。 【参考方案1】:

这是我使用的一种模式,并且一直想在博客上介绍它。

Ext.ns('MyApp.MyPanel');

MyApp.MyPanel = (function()
  var $this = Ext.extend(Ext.Panel, 
    constructor: function() 
        // Using a 'public static' value from $this
        // (a reference to the constructor)
        // and calling a 'private static' method
        this.thing = $this.STATIC_PROP + privateStatic();
        // Call super using $super that is defined after 
        // the call to Ext.extend
        $super.constructor.apply(this, arguments);
    ,
    initComponent: function() 
        $super.initComponent.call(this);
        this.addEvents([Events.SOMETHING]); // missing docs here
    
  );
  var $super = $this.superclass;

  // This method can only be accessed from the class 
  // and has no access to 'this'
  function privateStatic() 
    return "Whatever";
  


  /** 
    * This is a private non-static member
    * It must be called like getThing.call(this);
    */
  function getThing() 
     return this.thing;
  

  // You can create public static properties like this
  // refer to Events directly from the inside
  // but from the outside somebody could also use it as
  //  MyApp.MyPanel.Events.SOMETHING
  var Events = $this.Events = 
      SOMETHING: 'something'
  

  return $this;
)();

MyApp.MyPanel.STATIC_STRING = 10;

//Later somewhere
var panel = new MyApp.Mypanel();
panel.on(MyApp.Mypanel.Events.SOMETHING, callback);

使用此模式可以获得很多功能,但您不必使用所有功能

【讨论】:

很好,但是这种方法引入了相当多的样板代码,如果你调用很多父类方法可能会很好,但我通常只需要调用 initComponent() 的父类,什么都没有否则,在这种情况下,额外的代码恕我直言不值得。 是的,这种模式适用于所有使用继承的 OO 代码,您可以在其中调用父类的方法。在Ext中,我经常从父母那里调用onRender,afterRender,beforeDestroy,因为我覆盖了它。 这太好了,谢谢!我特别喜欢这个,因为如果处理得当,它可以通过 JSLint / JSHint。我遇到了一些问题linting this format from an old Sencha tutorial. @ReneSaarsoo,您(或 Juan Mendes)如何在这种设计中保留非静态私有方法? @b.long 最简单的解决方案是让你的私有非静态函数只是像privateStatic 这样的函数,但你必须使用privateNonStatic.call(this, arg1, arg2) 调用它们。不是很优雅...【参考方案2】:

我认为这在 ExtJS 4 中通过 callParent 解决了。

Ext.define('My.own.A', 
    constructor: function(test) 
        alert(test);
    
);

Ext.define('My.own.B', 
    extend: 'My.own.A',

    constructor: function(test) 
        alert(test);

        this.callParent([test + 1]);
    
);

【讨论】:

这个问题与 Ext JS 3.x 有关(供以后可能阅读此内容的用户)【参考方案3】:

几个小时前我想出了这个解决方案嘿嘿......

function extend (parentObj, childObj) 
    parentObj = parentObj || function () ;

    var newObj = function () 
        if (typeof this.initialize == 'function') 
            this.initialize.apply(this, arguments);
        
    

    newObj.prototype.__proto__ = parentObj.prototype;

    for (var property in childObj) 
        newObj.prototype[property] = childObj[property];
    

    newObj.prototype.superclass = function (method)  
        var callerMethod = arguments.callee.caller,
            currentProto = this.constructor.prototype.__proto__;

        while (callerMethod == currentProto[method]) 
            currentProto = currentProto.__proto__;
         

        return currentProto[method]; 
    ;

    return newObj;

那么你可以这样做:

var A = function ()  
    this.name = "A Function!";
;

A.prototype.initialize = function () 
    alert(this.name);


var B = extend(A, 
    initialize: function () 
        this.name = "B Function!";
        this.superclass('initialize').apply(this);
    
);

var C = extend(B, 
    initialize: function () 
        this.superclass('initialize').apply(this);
    
);

仅使用 (Chromium 8.0.552.237 (70801) Ubuntu 10.10) 测试 和 (Firefox 3.6.13)。

希望这对某人有所帮助,我几乎要切换到 GWT。

【讨论】:

Function.callerObject.__proto__ 都是非标准的。此外,后者已被弃用。不过,这个想法看起来很有趣。【参考方案4】:

我只需将您的代码更改为:

var $cls = MyApp.MyPanel = Ext.extend(Ext.Panel, 
  initComponent: function() 
    // do something MyPanel specific here...
    $cls.superclass.initComponent.call(this);
  
);

这样你只保留一个类名的引用,现在是 $cls。只在你的类方法中使用 $cls 就可以了。

【讨论】:

如果我将每个类都包裹在一个闭包中,这个解决方案就可以工作。目前我没有,所以这个解决方案不起作用。也许有一天我会,所以这变得可行。【参考方案5】:

确实,supr() 没有记录在案。我一直期待在 ExtJS 3.0.0 中使用它(Ext 工作人员在论坛中回复,他们已将其添加到该版本中),但它似乎非常糟糕。

它目前不遍历继承层次结构,而是上一层,然后卡在这一层上,无休止地循环并炸毁堆栈(IIRC)。因此,如果您连续有两个或更多supr(),您的应用程序将会崩溃。我在文档和论坛中都没有找到关于 supr() 的任何有用信息。

我不知道维护版本 3.0.x,因为我没有获得支持许可证...

【讨论】:

谢谢,它确实不适用于多级继承层次结构。仅供参考:3.0.3 在这方面没有任何变化。 Sencha Touch 1.1 依然如此【参考方案6】:

您可以使用这个鲜为人知的 javascript 功能(arguments.callee):

MyApp.MyPanel = Ext.extend(Ext.Panel, 
    constructor: function() 
        // Do your thing
        this.thing = 1;

        // Call super
        arguments.callee.superclass.constructor.apply(this, arguments);
    
);

见MDC documentation

编辑:实际上,这不适用于 initComponent,因为它不是构造函数。我总是重写构造函数,个人(尽管 Ext JS 示例建议)。会继续考虑这个。

【讨论】:

JavaScript 1.4:弃用被调用者作为 Function.arguments 的属性,将其保留为函数的局部参数变量的属性。不是一回事! 啊,对不起,你是对的。我把它与完全弃用的“来电者”混淆了。删除被调用者会很麻烦。

以上是关于在 ExtJS 中调用超类方法的更好方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 的超类中调用超类方法

在Objective C中调用超类方法的时间是不是重要?

在 Matlab 中调用 .NET 超类方法

从超类调用子类的方法

如何在多重继承中调用超类方法?

我可以模拟超类方法调用吗?