I was once scolded for using ES6 class syntax needlessly on singletons under the claim "there is no need for prototyping a singleton as there will never be a need to 'instantiate' one more than once, implying that explicit definition is adequate." However, with recognition to the fact that explicit definition is adequate, I do not see how wrapping singletons into class definitions is poor practice.
In this question Converting Singleton JS objects to use ES6 classes, it is stated by @Jason_Sebring that prototyping singletons can be a drain on productivity - as though the task of adding a few extra lines of code were of some unacceptable detriment to productivity.
There are also examples of persons promoting the use of ES6 class syntax to produce singletons like so http://amanvirk.me/singleton-classes-in-es6/
Ultimately, It appears to me that it comes down to a matter of preference. I prefer class/prototype syntax because I subjectively prefer the code organizaiton. But I am prepared to switch should anyone point out a technical reason that singleton classes should be avoided.
So I ask the following question, are there any technical reasons to avoid singleton classes?
Edit in response to @loganfsmyth:
Last week at work I needed to capture media from a clients local device in a browser. I found that there was no module on npm that really did what I wanted so I custom rolled one. Here is an example of a singleton from that module that I converted over from a class.
function hasGetUserMedia() {
return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia);
}
module.exports = {
createStream : (width,height)=>{
return new Promise((resolve, reject)=>{
if(hasGetUserMedia()){
//setup video
navigator.getUserMedia({audio: true,video:true}, (localMediaStream) =>{
var video = document.createElement('video');
video.src = window.URL.createObjectURL(localMediaStream);
video.width = width;
video.height = height;
var recorder = new MediaRecorder(localMediaStream);
resolve({
video : video,
recorder : recorder,
stream : stream
});
},
(err) =>{
reject(err);
});
}
else{
reject(new Error('no_valid_user_media'));
}
});
}
takePhoto: (scale,video,mimetype,quality)=>{
return new Promise((resolve,reject)=>{
if(!video){
reject(new Error('no_stream_exist'));
return;
}
var canvas = document.createElement("canvas");
canvas.width = video.videoWidth * scale;
canvas.height = video.videoHeight * scale;
canvas.getContext('2d')
.drawImage(video, 0, 0, canvas.width, canvas.height);
canvas.toBlob(canvas,(blob)=>{
resolve(blob);
},mimetype,quality);
});
}
takeVideo : (recorder,video,sampleRate,duration,mimetype)=>{
return new Promise((resolve,reject)=>{
if(!video){
reject(new Error('no_stream_exist'));
return;
}
var chunks = [];
recorder.ondataavailable = (e)=>{
chunks.push(e.data);
}
recorder.start(sampleRate);
setTimeout(()=>{
recorder.stop();
resolve(new Blob(chunks,{type:mimetype}));
},duration);
});
}
uploadMedia : (blob,streamName,socket,progressCallback)=>{
return new Promise((resolve, reject)=>{
var stream = ss.createStream();
ss(socket).emit(streamName,stream,{});
ss.createBlobReadStream(blob).pipe(stream);
stream.on('finish',()=>{
resolve();
});
stream.on('error',(err)=>{
reject(err);
});
if(progressCallback){
stream.on('data',(chunk)=>{
progressCallback(chunk);
});
}
});
}
}