В моей системе у меня есть несколько «классов», загруженных в браузер, каждый из которых представляет собой отдельные файлы во время разработки и объединяется вместе для производства. По мере загрузки они инициализируют свойство глобального объекта G
, как в этом примере:
var G = {};
G.Employee = function(name) {
this.name = name;
this.company = new G.Company(name + "'s own company");
};
G.Company = function(name) {
this.name = name;
this.employees = [];
};
G.Company.prototype.addEmployee = function(name) {
var employee = new G.Employee(name);
this.employees.push(employee);
employee.company = this;
};
var john = new G.Employee("John");
var bigCorp = new G.Company("Big Corp");
bigCorp.addEmployee("Mary");
Вместо использования моего собственного глобального объекта я рассматриваю возможность сделать каждый класс отдельным модулем AMD , основываясь на предложении Джеймса Берка :
define("Employee", ["Company"], function(Company) {
return function (name) {
this.name = name;
this.company = new Company(name + "'s own company");
};
});
define("Company", ["Employee"], function(Employee) {
function Company(name) {
this.name = name;
this.employees = [];
};
Company.prototype.addEmployee = function(name) {
var employee = new Employee(name);
this.employees.push(employee);
employee.company = this;
};
return Company;
});
define("main", ["Employee", "Company"], function (Employee, Company) {
var john = new Employee("John");
var bigCorp = new Company("Big Corp");
bigCorp.addEmployee("Mary");
});
Проблема в том, что раньше не было зависимости между сотрудником и компанией во время объявления: вы могли поместить объявление в любом порядке, но теперь, используя RequireJS, это вводит зависимость, которая здесь (намеренно) круговая, поэтому приведенный выше код не работает. Конечно, в addEmployee()
, добавив первую строку var Employee = require("Employee");
будет заставить его работать , но я вижу это решение как хуже не используя RequireJS / AMD , поскольку это требует от меня, разработчик, чтобы быть в курсе этой новой круговой зависимости и сделать что - то об этом.
Есть ли лучший способ решить эту проблему с помощью RequireJS / AMD, или я использую RequireJS / AMD для чего-то, для чего он не предназначен?
источник
function(exports, Company)
иfunction(exports, Employee)
. В любом случае, спасибо за RequireJS, это потрясающе.Я думаю, что это серьезный недостаток для более крупных проектов, где (многоуровневые) циклические зависимости остаются незамеченными. Однако с помощью madge вы можете распечатать список циклических зависимостей, чтобы приблизиться к ним.
источник
Если вам не нужно загружать зависимости в начале (например, при расширении класса), то вы можете сделать следующее: (взято из http://requirejs.org/docs/api.html# круговой )
В файле
a.js
:define( [ 'B' ], function( B ){ // Just an example return B.extend({ // ... }) });
А в другом файле
b.js
:define( [ ], function( ){ // Note that A is not listed var a; require(['A'], function( A ){ a = new A(); }); return function(){ functionThatDependsOnA: function(){ // Note that 'a' is not used until here a.doStuff(); } }; });
В примере с OP это изменится следующим образом:
define("Employee", [], function() { var Company; require(["Company"], function( C ){ // Delayed loading Company = C; }); return function (name) { this.name = name; this.company = new Company(name + "'s own company"); }; }); define("Company", ["Employee"], function(Employee) { function Company(name) { this.name = name; this.employees = []; }; Company.prototype.addEmployee = function(name) { var employee = new Employee(name); this.employees.push(employee); employee.company = this; }; return Company; }); define("main", ["Employee", "Company"], function (Employee, Company) { var john = new Employee("John"); var bigCorp = new Company("Big Corp"); bigCorp.addEmployee("Mary"); });
источник
Я просмотрел документы о круговых зависимостях: http://requirejs.org/docs/api.html#circular
Если существует круговая зависимость с a и b, в вашем модуле говорится, что нужно добавить требование в качестве зависимости в ваш модуль, например:
define(["require", "a"],function(require, a) { ....
тогда, когда вам нужно "a", просто вызовите "a" вот так:
return function(title) { return require("a").doSomething(); }
Это сработало для меня
источник
Я бы просто избегал круговой зависимости. Может быть что-то вроде:
G.Company.prototype.addEmployee = function(employee) { this.employees.push(employee); employee.company = this; }; var mary = new G.Employee("Mary"); var bigCorp = new G.Company("Big Corp"); bigCorp.addEmployee(mary);
Я не думаю, что стоит обойти эту проблему и попытаться сохранить циклическую зависимость. Просто похоже на плохую практику. В этом случае это может сработать, потому что вам действительно нужны эти модули при вызове экспортируемой функции. Но представьте себе случай, когда модули требуются и используются в самих функциях определения. Никакой обходной путь не поможет. Вероятно, поэтому require.js быстро терпит неудачу при обнаружении циклических зависимостей в зависимостях функции определения.
Если вам действительно нужно добавить обходной путь, более чистый IMO должен потребовать зависимость как раз вовремя (в ваших экспортированных функциях в этом случае), тогда функции определения будут работать нормально. Но даже более чистый IMO предназначен для того, чтобы вообще избежать циклических зависимостей, что действительно легко сделать в вашем случае.
источник
Все опубликованные ответы (кроме https://stackoverflow.com/a/25170248/14731 ) неверны. Даже официальная документация (по состоянию на ноябрь 2014 г.) неверна.
Единственное решение, которое сработало для меня, - объявить файл «привратника» и указать ему любой метод, который зависит от циклических зависимостей. См. Https://stackoverflow.com/a/26809254/14731 для конкретного примера.
Вот почему приведенные выше решения не работают.
var a; require(['A'], function( A ){ a = new A(); });
а затем использовать
a
позже, поскольку нет гарантии, что этот блок кода будет выполнен раньше блока кода, который используетa
. (Это решение вводит в заблуждение, потому что оно работает в 90% случаев)exports
он не уязвим для того же состояния гонки.решение этого:
//module A define(['B'], function(b){ function A(b){ console.log(b)} return new A(b); //OK as is }); //module B define(['A'], function(a){ function B(a){} return new B(a); //wait...we can't do this! RequireJS will throw an error if we do this. }); //module B, new and improved define(function(){ function B(a){} return function(a){ //return a function which won't immediately execute return new B(a); } });
теперь мы можем использовать эти модули A и B в модуле C
//module C define(['A','B'], function(a,b){ var c = b(a); //executes synchronously (no race conditions) in other words, a is definitely defined before being passed to b });
источник
В моем случае я решил круговую зависимость, переместив код «более простого» объекта в более сложный. Для меня это была коллекция и модельный класс. Думаю, в вашем случае я бы добавил части компании, относящиеся к сотрудникам, в класс сотрудников.
define("Employee", ["Company"], function(Company) { function Employee (name) { this.name = name; this.company = new Company(name + "'s own company"); }; Company.prototype.addEmployee = function(name) { var employee = new Employee(name); this.employees.push(employee); employee.company = this; }; return Employee; }); define("Company", [], function() { function Company(name) { this.name = name; this.employees = []; }; return Company; }); define("main", ["Employee", "Company"], function (Employee, Company) { var john = new Employee("John"); var bigCorp = new Company("Big Corp"); bigCorp.addEmployee("Mary"); });
Немного хакерский, но он должен работать в простых случаях. И если вы проведете рефакторинг,
addEmployee
чтобы принять Employee в качестве параметра, зависимость должна быть еще более очевидной для посторонних.источник