가정하기 때문에 cycling 을 발생한다.
그렇기 때문에 메서드 안에서 runtime 시 디펜던시가 호출되게 하는 방법이 있다.
(require 는 한번 호출시 캐슁하기때문에 성능 문제는 없다)
1.메서드 안에서 require 사용
//a.js function a(){ } a.prototype.onlyAFunction = function(){ return 'a'; } a.prototype.withBFunction = function(){ return require('./b').onlyBFunction(); } module.exports = new a(); //b.js function b(){ } b.prototype.onlyBFunction = function(){ return 'b'; } b.prototype.withAFunction = function(){ return require('./a').onlyAFunction(); } module.exports = new b(); //main.js var a = require('./a'); var b = require('./b'); console.log(a.onlyAFunction()); console.log(a.withBFunction()); console.log(b.onlyBFunction()); console.log(b.withAFunction());결과
a
b
b
a
2.한쪽에만 의존성이 있을경우 dependency injecttion 을 사용할수 있으나
코드간 의존성이 생기기때문에 개인적으로 비추다..
//a.js var b = require('./b'); function a(){ } a.prototype.onlyAFunction = function(){ return 'a'; } a.prototype.withBFunction = function(){ return new b(this).onlyBFunction(); } module.exports = new a(); //b.js function b(a){ this.a = a; } b.prototype.onlyBFunction = function(){ return 'b'; } module.exports = b; //main.js var a = require('./a'); var b = require('./b')(a); console.log(a.onlyAFunction()); console.log(a.withBFunction()); console.log(b.onlyBFunction()); console.log(b.withAFunction());3.객체를 생성하지 않고 exports의 property로 사용하는 경우
아래의 경우 객체 생성시 사용되는 의존성이 아니기때문에
(userService, planService 등과 같이 서비스로 사용할시)
가장 깔금하나 모듈 생성시 function(생성자) 를 사용해야 할 경우 문제가 발생함으로 공통코딩 규칙으로 잡기 머하다.
//a.js console.log('a starting'); exports.done = false; var b = require('./b4'); exports.onlyAFunction = function(){ return 'a'; } exports.withBFunction = function(){ return b.onlyBFunction(); } console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done'); //b.js console.log('b starting'); exports.done = false; var a = require('./a4'); exports.onlyBFunction = function(){ return 'b'; } exports.withAFunction = function(){ return a.onlyAFunction(); } console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done'); //main.js console.log('main starting'); var a = require('./a4'); var b = require('./b4'); console.log(a.onlyAFunction()); console.log(a.withBFunction()); console.log(b.onlyBFunction()); console.log(b.withAFunction()); console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
결과
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
a
b
b
a
in main, a.done=true, b.done=true
5.위에서 언급한 생성자 사용시 발생하는 문제다.
아래 문제는 노드에서 require 싸이클이 발생할 경우
중간에 완성이 들된 object 를 리턴하기 때문에 발생한다.
참조 http://nodejs.org/api/modules.html#modules_cycles
//a.js console.log('a starting'); exports.done = false; var b = require('./b5'); function a(){ } a.prototype.onlyAFunction = function(){ return 'a'; } a.prototype.withBFunction = function(){ return b.onlyBFunction(); } module.exports = new a(); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done'); //b.js console.log('b starting'); exports.done = false; var a = require('./a5'); function b(){ } b.prototype.onlyBFunction = function(){ return 'b'; } b.prototype.withAFunction = function(){ return a.onlyAFunction(); } module.exports = new b(); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done'); //main.js console.log('main starting'); var a = require('./a5'); var b = require('./b5'); console.log(1); console.log(a.onlyAFunction()); console.log(2); console.log(a.withBFunction()); console.log(3); console.log(b.onlyBFunction()); console.log(4); console.log(b.withAFunction()); console.log('end');2
결과
a starting
b starting
in b, a.done = false
b done
in a, b.done = undefined
a done
1
a
2
b
3
b
4
/Users/yeonghuni/workspace/wishbeen/test/test5/b5.js:19
return a.onlyAFunction();
^
TypeError: Object #<Object> has no method 'onlyAFunction'
at b.withAFunction (/Users/yeonghuni/workspace/wishbeen/test/test5/b5.js:19:12)
4번후 b.withAFunction()을 호출할때 에러가 발생한다.
왜냐하면 최초 a 생성시 b가 필요해서 b를 생성한다.
이때 b에서 다시 a 를 호출하는대 싸이클링이 생성되기 때문에
덜만들어진 a의 객체를 리턴한다. (이때 a.onlyAFunction)이 존재하지 않는다.
그러므로 실재로 코드를 호출하게 되면 런타임시 에러가 발생한다.
이때 정의된 a 가 왜? 런타임에서 에러가 발생하는가?
자바스크립트에서 idetifier resoultion 은 scope chain 을 대상으로 이루어진다.
각각의 함수는 실행시 자신의 execution context 에서 실행되게 되는대 이때 execution context는 activation/variable object + 부모[[scope(chain)]]이다.
(scope chain 은 function 이 정의 될때 자신이 호출되는 function 의 scope 체인(리스트) 속성 맨앞에 activavion/variable object 를 추가하여 만들어진다.)
위의 경우 new b() 를 하게 되면 b의 activavion/variable object 의 a는 생성되다만 object 이다.
b.withAFucntion 을 할경우 b를 호출 한다 이때 b에는 함수가 없음으로 prototype을 뒤지는대 이때 b.prototyp.withAFunction 이 호출된다.
이때 b.prototyp.withAFunction 은 b가 require 될때 생성되었기 때문에 scope 의 a 객체는
정의 되다 만 객체이다.
정의되다만 객체에는 a의 메서드가 없기 때문에 에러가 발생한다.
https://github.com/joyent/node/blob/master/src/node.js#L738
물런 a가 레퍼런스로 넘어갔으니 b가 완성되고 a 가 완성된 후
코드가 호출 되니 a에 메서드가 호출되야 하지 않느냐 라고 물어볼수 있다.
activation/variable object 는 해당 object 의 현재 모습을 copy 해서 저장하기 때문이다.
즉 main에서 b.withAFunction 은 함수 생성 순간에 복사된 a 오브젝트를 지속적으로 참조한다.