DEV Community

Discussion on: 4 Dangerous Problems in JavaScript Easily Solved by The Builder Design Pattern

deathshadow60 profile image
deathshadow60 • Edited on

As someone who started out in assembler over four decades ago, this approach really bothers me just due to the overhead of all the blasted "functions for nothing"

I mean in ASM a HLL function call works out to a push to the stack of the current execution point, push of all the argument values, changing execution point, extending the stack for locals, then on exit de-allocating locals, de-allocating the parameters, and then popping the return address.

I don't even want to THINK about the nightmare an interpreted loosely typed language like JavaScript makes of that... but there's a reason you're better off in production code using a for loop instead of Array.each, as the old way runs circles around it, more so if you get the derpy needlessly and painfully cryptic arrow functions involved.

All these new JS functions with their callbacks, and techniques like this of applying even more methods to the class, just seem to create more code and more execution overhead for little if any real-world benefit. More so when you get into that "return this" so you can daisy-chain them TRASH. It was derpy when the monument to developer ignorance and incompetence that is jQuery did it, and I see little here to change my opinion on that topic. You're just producing more copies of the pointer slowing the code down dramatically and wasting more RAM.

Your first example is card stacked too with bad choices... I mean to check if it's a value or undefined, check for undefined. If there's that many parameters just omit them and use arguments[].

Whilst there could be advantages in clarity on the calling side of things, as well as more versatility in being able to omit values, I'm just not sure the performance drop and added complexity is worth it.

Hence why I like passing JSON-type objects and then using Object.assign to apply it to "this", or using a for-loop if specific validations are needed for some values. Simply add a object as a property of the class that contains your validation methods, then if it exists while looping run it.

As this:

const sally = new FrogBuilder({
    "name" : "sally",
    "gender" : "female",
    "eyes" : [ { "volume" : 1.1 }, { "volume" : 1.12 } ],
    "scent" : "blueberry",
    "heart" : { "rate" : 12 },
    "weight" : 5,
    "height" : 3.1,
    "legs" : [
        { "size" : "small" },
        { "size" : "small" },
        { "size" : "small" },
        { "size" : "small" }
    "tongue" : { "width" : 12, "color" : "navy blue", "type" : "round" },
    "habitat" : "water",
    "skin" : "oily"

Would -- to me at least -- be a far more useful format to deal with, particularly given how often JSON is used for information exchange or storage.

Just Object.assign in the constructor, or for loop to apply validation or applications as needed off a object property containing the handler methods, and be done with it.

wkrueger profile image
Willian Krueger


And if you wish stricter organization on a bigger project, just use damn typescript (it will also change your views of OOP, type systems and make you notice how stupid the builder pattern is).

tailcall profile image
Anton Istomin

As someone with a C background I can relate to this reasoning.

However in the end of the day, seeing that most resource heavy operations are DOM manipulations and network I/O, the costs for creating an extra builder class seem neglible compares to them.

Besides, big objects like that are created relatively rarely, so the overhead likely won't affect anything too much. And it's always possible to optimise if measurements show that this particular object takes too long to build.

Given the benefits of the builder pattern I can live with all of that. However I agree it's wise to use plain objects where possible, JS is tooled perfectly around them and in many cases its tools are just enough.

thompcd profile image
Corey Thompson

I wouldn't say the Builder pattern exists solely for legibility purposes. Being able to do things async / transactionally instead of a serial burst is one of the first use cases that comes to mind. Building something with a multi-stage setup is something I do a lot in C# for automating serial port hardware, such as detecting a USB HID is on the tree before attempting to open a port is much easier with a method like this.

It's much easier to do incremental construction rather than a try/catch around a giant constructor and requiring catches for 10 different failure modes in the client. In my experience, a non-builder approach makes the coupling between the caller and constructor very tight in order to accommodate the error handling in these situations.

I first learned ASM also, but I've come to terms that ASM exists in a world apart from higher level languages. I like to use patterns like this to ensure that, from a dev perspective, I can building something with a solid foundation and rely on the compiler to optimize for the machine code. And since this post is on JS and I mentioned compiling, I'll just mention that's why I now use Svelte 3. 😊

otherdev profile image

I totally felt the same reading this. What a waste of resources for literally no benefit apart from legibility, which is hardly a reason unless you don't care about performance.