Наследование JavaScript с помощью Require.js и шаблона раскрывающегося модуля

Вопрос:

Казалось бы, простой вопрос, который я исследовал в течение последних 2 недель (пожалуйста, полегче, поскольку я новичок во всем этом!):

Как аккуратно реализовать наследование в JavaScript при использовании Require.js и шаблона раскрывающегося модуля?


Пример:

Вот пример модуля, который является базовым классом некоторого типа «Component»:

define('Component', [], function () {
   "use strict";

   var _privateVar = 10;
   var _doPrivateThings = function () {  /* do stuff */ };    
   var init = function () { /* do stuff */ };
   var update = function () {  /* do stuff */ };

   return {
      init : init,
      update : update
   };

});

Далее я хочу реализовать CakeComponent, который должен наследовать все от Component и позволить мне редактировать/добавлять методы и свойства:

define('CakeComponent', ['Component'], function (Component) {
   "use strict";
   
   // Setup inheritance
   var CakeComponent = function() {}
   CakeComponent.prototype = new Component();

   // Add/edit methods/properties
   CakeComponent.prototype.newMethod = function () { /* do stuff */ };

   return {
      init : CakeComponent.init,
      update : CakeComponent.update,
      newMethod : CakeComponent.newMethod
   };

});

Во-первых, я не уверен, что это имеет смысл, но, во-вторых, мой CakeComponent кажется немного грубым, потому что теперь у меня повсюду эта избыточность CakeComponent, и мне пришлось «повторно раскрывать» методы init и update.

Я бы предпочел что-то вроде этого (я понимаю, что это не имеет смысла, это просто псевдокод):

define('CakeComponent', ['Component'], function (Component) {
   "use strict";

   this.extends(Component);
   var newMethod = function () { /* do stuff */ };

   return {
      newMethod : newMethod
   };

});

Любые советы или предложения будут действительно оценены. Спасибо.


Более подробная информация

  • Может быть, мне всегда следует создавать объект класса в оболочке define? Я видел, как люди делают это, но это казалось ненужным, пока я не столкнулся с этой проблемой.
  • Будет ли вообще полезен в этом контексте метод .call() объекта функции? например используя Component.call()
  • @Bergi, пожалуйста, смотрите ниже:

define([], function () {
    "use strict";

    var Component = function () {

        var _privateVar = 10;
        var _doPrivateThings = function () {  /* do stuff */ };
        this.init = function () { /* do stuff */ };
        this.update = function () {  /* do stuff */ };

    };

    return Component;

});

person Community    schedule 24.04.2015    source источник
comment
Вы уже ознакомились с Как реализовать наследование в шаблоне прототипа JS Revealing??   -  person Bergi    schedule 24.04.2015
comment
new Component();, кажется, вообще не работает, ваш Component - это объект модуля, а не конструктор класса?   -  person Bergi    schedule 24.04.2015
comment
@Bergi Да, я читал это раньше, спасибо, но моя проблема действительно связана с requirejs, а не только с наследованием RVP или защищенных переменных. Хм, я посмотрю, почему это не работает, спасибо   -  person    schedule 24.04.2015
comment
Вы действительно хотите иметь классы (несколько экземпляров) или объекты-модули (одиночки)? В первом случае вам нужно return конструктор из ваших definer функций, а не литерал объекта, как вы делаете сейчас. Если вы хотите иметь модули, что вы подразумеваете под наследованием?   -  person Bergi    schedule 25.04.2015
comment
Кажется, я понимаю, что ты говоришь. Я добавил точку в вопросе «Дополнительные подробности» моего исходного сообщения, но добавленный мной пример по-прежнему возвращает литерал объекта. Как бы я использовал RMP, не возвращая литерал объекта? Должен ли я использовать prototype и отказываться от частных свойств и методов, если я хочу наследования?   -  person    schedule 25.04.2015
comment
А, ваш новый пример возвращает конструктор Component, это лучше. Хотя вам не нужно использовать .prototype, вам лучше это сделать. Вы бы не возвращали литералы из конструктора, а просто использовали бы this. Нет, вы по-прежнему можете использовать закрытые переменные и методы в своем конструкторе.   -  person Bergi    schedule 25.04.2015
comment
Итак, чтобы определить частные переменные и методы, я просто опускаю this? (см. обновленный код в разделе Дополнительные сведения)   -  person    schedule 25.04.2015
comment
Да, именно так. Теперь вы можете расширить этот шаблон, используя прототип для методов, которым не нужны привилегии, и вы можете наследовать от класса, как всегда   -  person Bergi    schedule 25.04.2015
comment
Отлично, большое спасибо за помощь, @Bergi!   -  person    schedule 28.04.2015


Ответы (1)


Я уже видел эту модель, которая использовалась раньше и называлась универсальным определением модуля:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['Component'], factory);
    } else {
        root.CakeComponent = factory(root.Component);
    }
}(this, function (Component) {
    return {
        newMethod: function(){ /* do stuff */ }
    };
}));

Вы можете попробовать это, что не является "настоящим" наследованием - в случае, если оно не работает - в зависимости от среды вам также может понадобиться передать базовые функции, что очень жаль:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['Component'], factory);
    } else {
        root.CakeComponent = factory(root.Component);
    }
}(this, function (Component) {
    return {
        init: Component.init,
        update: Component.update,
        newMethod: function(){ /* do stuff */ }
    };
}));

Вы можете прочитать больше об этой теме в этой фантастической статье о заголовке Определение универсального модуля

person hexerei software    schedule 24.04.2015
comment
Ух ты, это выглядит подло — что вы имеете в виду под зависимостью от окружения? Как в браузере против node.js? Как вы думаете, было бы лучше определить объект класса (например, var Component = function() { ... }) в каждом модуле? - person ; 24.04.2015
comment
@JoeRocc верно, на самом деле реализация для node.js будет выглядеть немного иначе. Переопределение компонента в каждом модуле не кажется хорошей идеей. RequireJS должен иметь возможность подключиться к этому. Ознакомьтесь с этой статьей об определении универсального модуля: dontkry.com/posts/code/. - person hexerei software; 24.04.2015
comment
@hexeri_software Насколько я понял, в этой статье предлагается не использовать UMD, а выбрать одно определение, а затем использовать сборку (обзор), чтобы выбрать окончательное определение модуля в постобработке? Я не уверен, как это помогает? - person ; 24.04.2015
comment
@JoeRocc Нижняя часть статьи - дело вкуса, я имел в виду верхнюю часть. НО попробуйте следующее: вот различные глобальные решения для браузеров, узлов и распространенных JS, все из которых требуют JS-совместимости: github.com/umdjs/umd - person hexerei software; 24.04.2015
comment
Спасибо за ваше, хотя я не уверен, как это поможет мне с наследованием? @Bergi прокомментировал выше, что мне может понадобиться вернуть конструктор из фабрики define и (я думаю) использовать прототипы, если я хочу получить правильное наследование. Что ты думаешь по этому поводу? Я действительно не понимал, что парадигма «модуля» более ориентирована на синглтон. - person ; 25.04.2015
comment
У @JoeRocc не было времени протестировать ваш особый случай с UMD, это была просто идея для широко используемого подхода, и, насколько мне известно, вы можете экспортировать что угодно (объекты, функции и т. д.) без ограничений к синглтонам. читайте больше здесь и здесь - person hexerei software; 01.05.2015