0

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);
                });
            }
        });
    }
}
Eric Widmann
  • 106
  • 6
  • 4
    For me it is a code smell rather than a technical issue. The motivation for grouping statics into a class is essentially for namespacing, and that is important for some languages which is why the pattern exists. In JS a file is generally a module, and modules already act as their own namespace, so it is superfluous. Writing in one language using the style from another isn't the end of the world, but to me at least it is an indication that the developer may not be writing code in a way that matches the expectations of the community for that language, which makes me less likely to use that code. – loganfsmyth Dec 22 '17 at 20:38
  • It's just a personal preference - there is no right or wrong here. Some (such as me) find that defining ALL your objects using the ES6 class syntax (whether there will be only one instance or many instances) is the cleanest way to code. For a more specific opinion, you'd have to show the actual code you're talking about. But, since all of this is nothing but opinion, your question is probably "off-topic" for stackoverflow. – jfriend00 Dec 22 '17 at 22:10
  • Not exactly sure what you mean by a "singleton class" (as the code you've shown is just an object literal, and that is fine), but if you refer to `new class {…}` then have a look [here](https://stackoverflow.com/q/38739499/1048572) and if you refer to classes that you construct normally but which return the same instance every time have a look at the generic [What is so bad about singletons?](https://stackoverflow.com/q/137975/1048572) – Bergi Dec 23 '17 at 19:50
  • @bergi [What is so bad about singletons?](https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) Mentions how using singleton pattern can negatively effect program "coupling" or how modular a program is, adding murkiness to the operation of program state transitions. I don't understand why the above code is the de facto method of bundling functionality in the JS sphere. – Eric Widmann Dec 23 '17 at 20:32
  • @EricWidmann The basic problem of singletons is if they encapsulate global state. Your module does not have any state, that's why it doesn't need instances. It's just one module object. – Bergi Dec 23 '17 at 20:37
  • @Bergi Thank you for linking the post, here is a good counter to my previous query, [blog](https://jorudolph.wordpress.com/2009/11/22/singleton-considerations/). You also make the point that singletons should not encapsulate "global states" as they would then in essence hide the global state from the global context. Thank you for the help everyone. – Eric Widmann Dec 23 '17 at 20:46

0 Answers0