Привет, читатель. Я попробовал переписать функцию Ext.extend(), которая используется для реализации наследования в ExtJS, в простом для понимания виде. Также в конце заметки содержится схемка, визуально показывающая возникающие в процессе наследования связи.

Посмотрим исходный код Ext.extend(), который можно найти в adapter/ext/ext-base-debug.js:

  1.  Ext.apply(Ext, {
  2.      extend : function() {
  3.          // inline overrides
  4.          var io = function(o){
  5.              for(var m in o){
  6.                  this[m] = o[m];
  7.              }
  8.          };
  9.          var oc = Object.prototype.constructor;
  10.   
  11.          return function(sb, sp, overrides){
  12.              if(typeof sp == 'object'){
  13.                  overrides = sp;
  14.                  sp = sb;
  15.                  sb = overrides.constructor != oc ? 
  16.                      overrides.constructor : 
  17.                      function(){sp.apply(this, arguments);};
  18.              }
  19.              var F = function(){},
  20.                  sbp,
  21.                  spp = sp.prototype;
  22.   
  23.              F.prototype = spp;
  24.              sbp = sb.prototype = new F();
  25.              sbp.constructor=sb;
  26.              sb.superclass=spp;
  27.              if(spp.constructor == oc){
  28.                  spp.constructor=sp;
  29.              }
  30.              sb.override = function(o){
  31.                  Ext.override(sb, o);
  32.              };
  33.              sbp.superclass = sbp.supr = (function(){
  34.                  return spp;
  35.              });
  36.              sbp.override = io;
  37.              Ext.override(sb, overrides);
  38.              sb.extend = function(o){return Ext.extend(sb, o);};
  39.              return sb;
  40.          };
  41.      }()
  42.  });

Попытка сразу отследить все связи в уме слегка вынесла мозг. Я решил разложить все по полочкам. Используя Ext.apply() можно сгруппировать разбросанные присваивания в удобочитаемый вид. Вот что у меня вышло:

  1.  Ext.apply(Ext, {
  2.      extend : function() {
  3.          // inline overrides
  4.          var io = function(o) {
  5.              for (var m in o) {
  6.                  this[m] = o[m];
  7.              }
  8.          };
  9.          return function(sb, sp, overrides) {
  10.   
  11.              // Если передается только конструктор и объект со свойствами
  12.              // для нового прототипа (далее объект конфигурации)
  13.              if (typeof sp === 'object') {
  14.                  overrides = sp;
  15.                  sp = sb;
  16.                  // Если конструктор указан в объекте конфигурации, то 
  17.                  // используем его
  18.                  if (overrides.constructor !== Object) {
  19.                      sb = overrides.constructor;
  20.                  }
  21.                  // Если не указано конструктора, то генерируем дефолтный
  22.                  else {
  23.                      sb = function() {
  24.                          sp.apply(this, arguments);
  25.                      };
  26.                  }
  27.              }
  28.   
  29.              // <-- Теперь sb это конструктор класса потомка,
  30.              // <-- sp это конструктор класса предка
  31.   
  32.              // Ссылка на прототип класса предка
  33.              var spp = sp.prototype;
  34.   
  35.              // Создаем пустой конструктор с прототипом класса предка
  36.              var F = function() {};
  37.              F.prototype = spp;
  38.   
  39.              Ext.apply(sb, {
  40.                  // Создаем ссылки на Ext.override и Ext.extend для использования
  41.                  // напрямую из объекта конструктора
  42.                  override    : function(o) {
  43.                      Ext.override(sb, o);
  44.                  },
  45.                  extend      : function(o) {
  46.                      return Ext.extend(sb, o);
  47.                  },
  48.                  // Прототип класса потомка - объект, прототип которого 
  49.                  // ссылается на прототип класса предка
  50.                  prototype   : new F(),
  51.                  // Ссылка на прототип класса предка
  52.                  superclass  : spp
  53.              });
  54.   
  55.              // <-- Теперь конструктор sb содержит пустой объект, прототип которого 
  56.              // <-- ссылается на прототип класса предка
  57.   
  58.              // Наполняем прототип класса потомка
  59.              Ext.apply(sb.prototype, {
  60.                  // Ссылка на конструктор класса потомка
  61.                  constructor : sb,
  62.                  // Получение прототипа класса предка
  63.                  superclass  : function() {
  64.                      return spp;
  65.                  },
  66.                  supr        : sb.prototype.superclass,
  67.                  override    : io
  68.              });
  69.   
  70.              // <-- Теперь объект прототипа содержит ссылку на конструктор
  71.   
  72.              if (spp.constructor == Object) {
  73.                  spp.constructor = sp;
  74.              }
  75.   
  76.              // Пишем свойства объекта конфигурации в прототип конструтора
  77.              // класса потомка
  78.              Ext.override(sb, overrides);
  79.   
  80.              // <-- Наследование завершено
  81.   
  82.              return sb;
  83.          };
  84.      }()
  85.  });

Комментарии должны тебе помочь понять все этапы работы Ext.extend(). В компактном виде функцию можно представить так:

  1.  Ext.apply(Ext, {
  2.      extend : function() {
  3.          var io = function(o) {
  4.              for (var m in o) {
  5.                  this[m] = o[m];
  6.              }
  7.          };
  8.          return function(sb, sp, overrides) {
  9.              if (typeof sp === 'object') {
  10.                  overrides = sp;
  11.                  sp = sb;
  12.                  sb = (overrides.constructor !== Object) ? 
  13.                      overrides.constructor :
  14.                      function() { sp.apply(this, arguments); };
  15.              }
  16.              var F = function() {};
  17.              F.prototype = sp.prototype;
  18.              Ext.apply(sb, {
  19.                  override    : function(o) { Ext.override(sb, o); },
  20.                  extend      : function(o) { return Ext.extend(sb, o); },
  21.                  prototype   : Ext.apply(new F(), {
  22.                      constructor : sb,
  23.                      superclass  : function() { return sp.prototype; },
  24.                      supr        : sb.prototype.superclass,
  25.                      override    : io
  26.                  }),
  27.                  superclass  : sp.prototype
  28.              });
  29.              if (sp.prototype.constructor == Object) {
  30.                  sp.prototype.constructor = sp;
  31.              }
  32.              Ext.override(sb, overrides);
  33.              return sb;
  34.          };
  35.      }()
  36.  });

Следующей схемой можно представить связки, которые возникают при наследовании:

ExtJS Ext.extend method

Надеюсь тебе помогла эта заметка лучше разобраться в наследовании, которое применяется в ExtJS.