최근 에 Node.js에서 싱글 톤을 작성하는 방법에 대한 이 기사 를 보았습니다. 다음과 같은 require
상태 문서를 알고 있습니다 .
모듈은 처음로드 된 후 캐시됩니다. 를 여러 번 호출
require('foo')
하면 모듈 코드가 여러 번 실행되지 않을 수 있습니다.
따라서 필요한 모든 모듈은 단일 상용구 코드없이 단일 항목으로 쉽게 사용할 수있는 것 같습니다.
질문:
위의 기사가 싱글 톤 생성에 대한 해결책을 제공합니까?
최근 에 Node.js에서 싱글 톤을 작성하는 방법에 대한 이 기사 를 보았습니다. 다음과 같은 require
상태 문서를 알고 있습니다 .
모듈은 처음로드 된 후 캐시됩니다. 를 여러 번 호출
require('foo')
하면 모듈 코드가 여러 번 실행되지 않을 수 있습니다.
따라서 필요한 모든 모듈은 단일 상용구 코드없이 단일 항목으로 쉽게 사용할 수있는 것 같습니다.
질문:
위의 기사가 싱글 톤 생성에 대한 해결책을 제공합니까?
답변:
https://nodejs.org/api/modules.html#modules_caching
(v 6.3.1)
캐싱
모듈은 처음로드 된 후 캐시됩니다. 이것은 (무엇보다도) require ( 'foo')에 대한 모든 호출이 동일한 파일로 해석된다면 정확히 동일한 객체를 반환한다는 것을 의미합니다.
require ( 'foo')를 여러 번 호출하면 모듈 코드가 여러 번 실행되지 않을 수 있습니다. 이것은 중요한 기능입니다. 이를 통해 "부분적으로 수행 된"개체가 반환 될 수 있으므로주기를 유발하는 경우에도 전 이적 종속성을로드 할 수 있습니다.
모듈이 코드를 여러 번 실행하도록하려면 함수를 내보내고 해당 함수를 호출하십시오.
모듈 캐싱주의 사항
모듈은 확인 된 파일 이름을 기반으로 캐시됩니다. 모듈은 호출 모듈의 위치 (node_modules 폴더에서로드)에 따라 다른 파일 이름으로 해석 될 수 있으므로, require ( 'foo')가 다른 파일로 해석되는 경우 항상 똑같은 객체를 반환한다는 보장은 없습니다. .
또한 대소 문자를 구분하지 않는 파일 시스템 또는 운영 체제에서 확인 된 다른 파일 이름이 동일한 파일을 가리킬 수 있지만 캐시는 여전히 파일을 다른 모듈로 취급하고 파일을 여러 번 다시로드합니다. 예를 들어 require ( './ foo') 및 require ( './ FOO')는 ./foo 및 ./FOO가 동일한 파일인지 여부에 관계없이 두 개의 다른 객체를 반환합니다.
그래서 간단히 말해서.
싱글 톤을 원한다면; 개체를 내 보냅니다 .
싱글 톤을 원하지 않는다면; 함수를 내보내십시오 (그리고 그 함수에서 물건을 / 물건을 돌려 보내십시오 / 무엇이든).
명확하게 말하면 제대로 작동하면 https://stackoverflow.com/a/33746703/1137669 (Allen Luce의 답변)를 참조하십시오. 다르게 확인 된 파일 이름으로 인해 캐싱이 실패 할 때 발생하는 상황을 코드에서 설명합니다. 그러나 항상 동일한 파일 이름으로 확인하면 작동합니다.
es6 기호를 사용하여 node.js에서 진정한 싱글 톤 생성 또 다른 솔루션 : 이 링크에서
이 답변은 CommonJS (Node.js 자체 모듈 가져 오기 / 내보내기 방법)를 나타냅니다 . Node.js는 대부분 ECMAScript 모듈 로 전환 될 것입니다 : https://nodejs.org/api/esm.html (알지 못한 경우 ECMAScript는 JavaScript의 실제 이름입니다)
ECMAScript로 마이그레이션 할 때 지금은 https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards 를 읽으십시오.
위의 모든 것은 너무 복잡합니다. 디자인 패턴이 실제 언어의 결함을 보여주고 있다는 생각의 학교가 있습니다.
프로토 타입 기반 OOP (클래스리스)를 사용하는 언어에는 싱글 톤 패턴이 전혀 필요하지 않습니다. 즉석에서 단일 (톤) 개체를 만든 다음 사용하면됩니다.
노드의 모듈은 기본적으로 캐시되지만, 예를 들어 모듈 변경 사항의 핫 로딩을 원하는 경우 조정할 수 있습니다.
그러나 예, 공유 객체를 전체적으로 사용하려면 모듈 내보내기에 넣는 것이 좋습니다. "단일 패턴"으로 복잡하게 만들지 마십시오. JavaScript에서는 필요하지 않습니다.
There is a school of thought which says design patterns are showing deficiencies of actual language.
아니요. Node의 모듈 캐싱이 실패하면 해당 싱글 톤 패턴이 실패합니다. OSX에서 의미있게 실행되도록 예제를 수정했습니다.
var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
이것은 작성자가 예상 한 결과를 제공합니다.
{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }
그러나 약간의 수정은 캐싱을 무효화합니다. OSX에서 다음을 수행하십시오.
var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
또는 Linux에서 :
% ln singleton.js singleton2.js
그런 다음 sg2
require 라인을 다음과 같이 변경하십시오 .
var sg2 = require("./singleton2.js");
그리고 bam , 싱글 톤이 패배합니다.
{ '1': 'test' } { '2': 'test2' }
이 문제를 해결할 수있는 방법을 모르겠습니다. 싱글 톤과 같은 것을 만들 필요가 있고 글로벌 네임 스페이스 (및 그로 인해 발생할 수있는 많은 문제)를 오염 시켜도 괜찮다면 작성자 getInstance()
와 exports
줄을 다음 과 같이 변경할 수 있습니다 .
singleton.getInstance = function(){
if(global.singleton_instance === undefined)
global.singleton_instance = new singleton();
return global.singleton_instance;
}
module.exports = singleton.getInstance();
즉, 저는 이와 같은 작업을 수행해야하는 프로덕션 시스템에서 한 번도 발생하지 않았습니다. 또한 Javascript에서 싱글 톤 패턴을 사용할 필요성을 느끼지 못했습니다.
모듈 문서 의 모듈 캐싱주의 사항 을 조금 더 살펴 보겠습니다.
모듈은 확인 된 파일 이름을 기반으로 캐시됩니다. 모듈은 호출 모듈의 위치 (node_modules 폴더에서로드)에 따라 다른 파일 이름으로 해석 될 수 있으므로 require ( 'foo')가 다른 파일로 해석되는 경우 항상 똑같은 객체를 반환한다는 보장은 없습니다. .
따라서 모듈이 필요할 때 어디에 있는지에 따라 모듈의 다른 인스턴스를 얻을 수 있습니다.
모듈과 같은 소리는 싱글 톤 생성에 대한 간단한 해결책 이 아닙니다 .
편집 : 아니면 그들 이다 . @mkoryak과 마찬가지로 단일 파일이 (심볼릭 링크를 사용하지 않고) 다른 파일 이름으로 해석 될 수있는 경우를 생각 해낼 수 없습니다. 그러나 (@JohnnyHK 주석처럼) 서로 다른 node_modules
디렉토리 에있는 파일의 여러 복사본 이 각각 개별적으로로드되고 저장됩니다.
node_modules
있고 각각이 같은 모듈에 의존 하는 경우를 언급한다고 생각 node_modules
합니다.
require('./db')
두 개의 개별 파일에 db
모듈 의 코드가 두 번 실행됩니다
require('../lib/myModule.js');
한 파일과 require('../lib/mymodule.js');
다른 파일을 호출 했지만 동일한 객체를 전달하지 않았습니다.
node.js (또는 브라우저 JS)의 싱글 톤은 완전히 불필요합니다.
모듈은 캐시되고 상태 저장되므로 제공 한 링크에 제공된 예제를 훨씬 더 간단하게 쉽게 다시 작성할 수 있습니다.
var socketList = {};
exports.add = function (userId, socket) {
if (!socketList[userId]) {
socketList[userId] = socket;
}
};
exports.remove = function (userId) {
delete socketList[userId];
};
exports.getSocketList = function () {
return socketList;
};
// or
// exports.socketList = socketList
may not
적용 할 때 npm link
개발하는 동안 다른 모듈. 따라서 eventBus와 같은 단일 인스턴스에 의존하는 모듈을 사용할 때는주의하십시오.
ES6 클래스를 사용하는 유일한 답변
// SummaryModule.js
class Summary {
init(summary) {
this.summary = summary
}
anotherMethod() {
// do something
}
}
module.exports = new Summary()
다음과 함께이 싱글 톤이 필요합니다.
const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()
여기서 유일한 문제는 매개 변수를 클래스 생성자에 전달할 수 없지만 init
메서드 를 수동으로 호출하여 피할 수 있다는 것입니다.
summary
다시 초기화하지 않고 다른 클래스에서 동일한 인스턴스 를 어떻게 사용할 수 있습니까?
js에서 싱글 톤을 수행하기 위해 특별한 것이 필요하지 않습니다.이 기사의 코드는 다음과 같을 수도 있습니다.
var socketList = {};
module.exports = {
add: function() {
},
...
};
node.js 외부 (예 : 브라우저 js)에서는 래퍼 함수를 수동으로 추가해야합니다 (node.js에서 자동으로 수행됨).
var singleton = function() {
var socketList = {};
return {
add: function() {},
...
};
}();
싱글 톤은 JS에서 괜찮습니다. 그렇게 장황 할 필요는 없습니다.
예를 들어 서버 계층의 다양한 파일에서 동일한 ORM / DB 인스턴스를 사용하기 위해 싱글 톤이 필요한 경우 node에서 참조를 전역 변수에 채울 수 있습니다.
존재하지 않는 경우 전역 변수를 생성하는 모듈을 작성하고 그에 대한 참조를 반환합니다.
@ allen-luce는 여기에 복사 된 각주 코드 예제를 사용하여 제대로 작동했습니다.
singleton.getInstance = function(){
if(global.singleton_instance === undefined)
global.singleton_instance = new singleton();
return global.singleton_instance;
};
module.exports = singleton.getInstance();
그러나 new
키워드를 사용할 필요 가 없다는 점에 유의하는 것이 중요 합니다. 오래된 개체, 기능, iife 등이 작동합니다. 여기에서는 OOP 부두가 발생하지 않습니다.
참조를 반환하는 함수 내에서 일부 obj를 닫고 해당 함수를 전역으로 만들면 보너스 포인트가 있습니다. 전역 변수를 다시 할당해도 이미 생성 된 인스턴스가 방해를받지는 않습니다.
module.exports = new Foo()
module.exports 다시 실행하지 않기 때문에 당신이 정말 멍청한 짓하지 않는 한,
간단하게.
foo.js
function foo() {
bar: {
doSomething: function(arg, callback) {
return callback('Echo ' + arg);
};
}
return bar;
};
module.exports = foo();
그럼 그냥
var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });