web-dev-qa-db-fra.com

Meilleure façon d'appeler la méthode de superclasse dans ExtJS

Toute la documentation ExtJS et les exemples que j'ai lus suggèrent d'appeler des méthodes de superclasse comme celle-ci:

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

J'utilise ce modèle depuis un certain temps et le principal problème est que lorsque vous renommez votre classe, vous devez également changer tous les appels aux méthodes de superclasse. C'est assez gênant, souvent j'oublierai et ensuite je dois retrouver d'étranges erreurs.

Mais en lisant la source de Ext.extend() j'ai découvert qu'à la place je pourrais utiliser les méthodes superclass() ou super() que Ext.extend() ajoute au prototype:

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

Dans ce code, renommer MyPanel en autre chose est simple - je dois juste changer la ligne.

Mais j'ai des doutes ...

  • Je n'ai vu cela documenté nulle part et la vieille sagesse dit que je ne devrais pas me fier à un comportement non documenté.

  • Je n'ai pas trouvé une seule utilisation de ces méthodes superclass() et supr() dans le code source ExtJS. Pourquoi les créer alors que vous n'allez pas les utiliser?

  • Peut-être que ces méthodes ont été utilisées dans une ancienne version d'ExtJS mais sont maintenant obsolètes? Mais cela semble une fonctionnalité si utile, pourquoi la désapprouveriez-vous?

Alors, dois-je utiliser ces méthodes ou non?

29
Rene Saarsoo

Oui, en effet, supr() n'est pas documenté. J'avais hâte de l'utiliser dans ExtJS 3.0.0 (un membre du personnel d'Ext a répondu dans les forums, ils l'avaient ajouté dans cette version), mais cela semble horriblement cassé.

Il ne traverse actuellement pas la hiérarchie d'héritage, mais monte plutôt d'un niveau, puis se coince à ce niveau, boucle à l'infini et fait exploser la pile (IIRC). Ainsi, si vous avez deux ou plusieurs supr() d'affilée, votre application se cassera. Je n'ai trouvé aucune information utile sur supr() ni dans la documentation ni dans les forums.

Je ne connais pas les versions de maintenance 3.0.x, car je n'ai pas obtenu de licence de support ...

14
Jabe

Je pense que cela est résolu dans ExtJS 4 avec 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]);
    }
});
29
tykovec

Voici un modèle que j'utilise et que j'ai l'intention de bloguer à ce sujet depuis un moment.

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);

Il y a beaucoup de fonctionnalités que vous obtenez en utilisant ce modèle, mais vous n'avez pas à les utiliser toutes

5
Juan Mendes

Vous pouvez utiliser cette fonctionnalité Javascript peu connue ( 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);
    }
});

voir documentation MDC

Edit: En fait, cela ne fonctionnera pas avec initComponent car ce n'est pas le constructeur. Je remplace toujours le constructeur, personnellement (malgré ce que suggèrent les exemples Ext JS). Je continuerai à y penser un peu.

3
neonski

Je changerais simplement votre code en:

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

De cette façon, vous ne gardez qu'une seule référence du nom de votre classe, maintenant $ cls. N'utilisez que $ cls dans vos méthodes de classe et tout ira bien.

2
karlipoppins

J'ai trouvé cette solution il y a quelques heures heheh ...

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;
}

Ensuite, vous pouvez faire:

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);
    }
});

Testé uniquement avec (Chromium 8.0.552.237 (70801) Ubuntu 10.10) et (Firefox 3.6.13).

J'espère que cela aide quelqu'un, je passais presque à GWT.

0
rseidi