W moim systemie mam kilka „klas” ładowanych do przeglądarki, z których każda jest oddzielnym plikiem podczas programowania i łączona razem w celu produkcji. Podczas ładowania inicjalizują one właściwość obiektu globalnego G
, tak jak w tym przykładzie:
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");
Zamiast używać mojego własnego globalnego obiektu, rozważam uczynienie każdej klasy własnym modułem AMD , zgodnie z sugestią Jamesa Burke'a :
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");
});
Problem polega na tym, że wcześniej nie było zależności między pracownikiem a firmą w czasie deklarowania: można było umieścić deklarację w dowolnej kolejności, ale teraz, używając RequireJS, wprowadza to zależność, która jest tutaj (celowo) okrężna, więc powyższy kod nie działa. Oczywiście w programie byłoby addEmployee()
dodanie pierwszej liniivar Employee = require("Employee");
że to zadziałałoby , ale uważam to rozwiązanie za gorsze od nieużywania RequireJS / AMD, ponieważ wymaga ode mnie, programisty, bycia świadomym tej nowo utworzonej zależności cyklicznej i zrobienia czegoś z tym.
Czy istnieje lepszy sposób rozwiązania tego problemu w przypadku RequireJS / AMD, czy też używam RequireJS / AMD do czegoś, do czego nie został zaprojektowany?
źródło
function(exports, Company)
ifunction(exports, Employee)
. W każdym razie, dzięki za RequireJS, to wspaniale.Myślę, że jest to dość wada w większych projektach, w których (wielopoziomowe) zależności cykliczne pozostają niewykryte. Jednak za pomocą madge możesz wydrukować listę zależności cyklicznych, aby się do nich zbliżyć.
źródło
Jeśli nie potrzebujesz, aby Twoje zależności były ładowane na początku (np. Podczas rozszerzania klasy), możesz to zrobić: (pobrane z http://requirejs.org/docs/api.html# okrągły )
W pliku
a.js
:define( [ 'B' ], function( B ){ // Just an example return B.extend({ // ... }) });
A w drugim pliku
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(); } }; });
W przykładzie PO wyglądałoby to następująco:
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"); });
źródło
Przejrzałem dokumenty dotyczące zależności cyklicznych: http://requirejs.org/docs/api.html#circular
Jeśli istnieje zależność cykliczna z a i b, w module jest napisane, aby dodać wymaganie jako zależność w module:
define(["require", "a"],function(require, a) { ....
wtedy gdy potrzebujesz „a” po prostu zadzwoń do „a” w ten sposób:
return function(title) { return require("a").doSomething(); }
To zadziałało dla mnie
źródło
Po prostu uniknąłbym zależności cyklicznej. Może coś takiego:
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);
Uważam, że obejście tego problemu i zachowanie zależności cyklicznej nie jest dobrym pomysłem. Po prostu wydaje się ogólną złą praktyką. W tym przypadku może to zadziałać, ponieważ naprawdę potrzebujesz tych modułów do wywołania eksportowanej funkcji. Ale wyobraź sobie przypadek, w którym moduły są wymagane i używane w samej definicji funkcji. Żadne obejście nie sprawi, że to zadziała. Prawdopodobnie dlatego require.js szybko kończy się niepowodzeniem po wykryciu cyklicznej zależności w zależnościach funkcji definicji.
Jeśli naprawdę musisz dodać obejście, prostszy IMO wymaga zależności w samą porę (w tym przypadku w twoich wyeksportowanych funkcjach), wtedy funkcje definicji będą działać poprawnie. Ale nawet czystsza IMO ma na celu całkowite uniknięcie zależności cyklicznych, co wydaje się naprawdę łatwe w twoim przypadku.
źródło
Wszystkie opublikowane odpowiedzi (z wyjątkiem https://stackoverflow.com/a/25170248/14731 ) są błędne. Nawet oficjalna dokumentacja (stan na listopad 2014) jest błędna.
Jedynym rozwiązaniem, które zadziałało, jest zadeklarowanie pliku „strażnika” i zdefiniowanie w nim dowolnej metody, która zależy od zależności cyklicznych. Konkretny przykład można znaleźć pod adresem https://stackoverflow.com/a/26809254/14731 .
Oto dlaczego powyższe rozwiązania nie będą działać.
var a; require(['A'], function( A ){ a = new A(); });
a następnie użyj go
a
później, ponieważ nie ma gwarancji, że ten blok kodu zostanie wykonany przed blokiem kodu, który używaa
. (To rozwiązanie jest mylące, ponieważ działa w 90% przypadków)exports
nie jest narażony na te same warunki wyścigu.rozwiązaniem tego jest:
//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); } });
teraz możemy użyć tych modułów A i B w module 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 });
źródło
W moim przypadku rozwiązałem zależność cykliczną, przenosząc kod „prostszego” obiektu na bardziej złożony. Dla mnie to była kolekcja i klasa wzorcowa. Sądzę, że w twoim przypadku dodałbym do klasy Pracownik części firmy specyficzne dla pracownika.
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"); });
Trochę hacky, ale powinno działać w prostych przypadkach. A jeśli dokonasz refaktoryzacji
addEmployee
przyjmując pracownika jako parametr, zależność powinna być jeszcze bardziej oczywista dla osób z zewnątrz.źródło