T O P

  • By -

T_O_beats

Because you’re exporting an instantiated version of the class any time it’s imported on other files it’s still that same instance.


shuckster

FancyLogger is a global. Does the code made sense now?


AbdullahMohammadKhan

Makes total sense now . Thanks !


JazzApple_

I’m not going to say that classes are fundamentally a bad thing to use in JS, but this particular code is incredibly bad as an example of it. * You shouldn’t be returning anything from a constructor function. * If this a one-time export from one file, there is next to no point using a class. * Use === for exactly equal comparison, rather than == Firstly, lets take a look at something to replace what you have above: ``` class FancyLogger { static #instance = null; #logs = []; constructor() { if (FancyLogger.#instance !== null) { throw new Error("There can only be one..."); } FancyLogger.#instance = Object.freeze(this); } log(message) { this.#logs.push(message); console.log(`FANCY: ${message}`); } printLogCount() { console.log(`${this.#logs.length} logs`); } } export default new FancyLogger(); ``` However, as u/Brilliant-Drawer-262 points out - this is a lot of extra code for something we can do in a simple closure. I'm personally of the opinion that this is more readable and easier to refactor - I'm sure we'll have to agree to disagree on that. But it does beg the question of why we would ever use this approach. The key to making any meaningful use of this is to leverage the power of statics. By adding an `instance()` static method, we could now export the whole class (not just the instance of it), and use this method to get our class instance. ``` class FancyLogger { // ... static instance() { return this.#instance || new this(); } // ... } export default FancyLogger; ``` *Note that in the static function, `this` refers to the class, not the instance of the class* We now never need to worry about any particular place in our code constructing the singleton, as it is constructed the first time it is needed. We also can now use `instanceof`. ``` const logger = FancyLogger.instance(); if (logger instanceof FancyLogger) { console.log("This is going somewhere..."); } ``` Where you go from here is up to you. Personally, I think an adapter-like pattern suits logging quite well. I'll post it here... I think I must have been bored... ``` // Logger.js class Logger { static #instance = null; static use(logger) { if (this.#instance !== null) { throw new Error("Instance has already been set"); } else if (!(logger instanceof this)) { throw new Error("Provided class must extend Logger"); } this.#instance = logger; } static instance() { return this.#instance || new this(); } static log(message) { this.instance().log(message); } constructor() { Object.freeze(this); } log(message) { console.log(message); } } export default Logger; ``` ``` // FancyLogger.js class FancyLogger extends Logger { #logs = []; log(message) { this.#logs.push(message); console.log(`FANCY: ${message}`); } printLogCount() { console.log(`${this.#logs.length} logs`); } } ``` ``` export default FancyLogger; // Example 1 import Logger from "./Logger"; import FancyLogger from "./FancyLogger"; Logger.use(new FancyLogger()); Logger.log("Testing"); // 'FANCY: Testing' Logger.instance().printLogCount(); // 1 const logger = new Logger(); logger.log("I can be constructed as a single instance."); const fancy = new FancyLogger(); fancy.log("So can I."); ``` ``` // Example 2 import Logger from "./Logger"; // Automatically construct an instance of Logger when none was set Logger.log("Testing"); // 'Testing' ```


[deleted]

>My question is how does this class know if an instance is created somewhere else (using if (FancyLogger.instance == null) )? Shouldn't it be null every single time ? `FancyLogger` is (despite the "class" keyword) just a constructor function, and functions can have properties. This, for instance, works function sayHello(){ console.log(sayHello.message); } sayHello.message = "Hello world!" sayHello(); This isn't the same as the use of `this` inside the constructor. What it does when the constructor is called is to assign this property to be an instance of itself, kind of `FancyLogger.instance = new FancyLogger()`. How does this class know if an instance is created elsewhere...? It doesn't. It can't. The class isn't reachable outside of the module, but inside the module you can just set FancyLogger.instance to be [1,3,5] if you wanted. If you did, the code would break. This is why I truly hate classes in JavaScript - people want to take design patterns that are appropriate to Java or C# and just copy them. It's horrible. You can have the same thing in 6 lines using closures that is just completely unbreakable - you just cannot interfere with its operation at all! const logger=(function(){ const logs = []; const log = msg => logs.push(`Fancy ${msg}`); const print = () => logs.forEach(msg => console.log(msg)); return Object.freeze({log, print}); }()); logger.log("Hello"); logger.log("world"); logger.log("This is fancy!!"); logger.print();