DEV Community

loading...
Cover image for The class Boogeyman in JavaScript

The class Boogeyman in JavaScript

Adam Nathaniel Davis
React acolyte, jack-of-all-(programming)trades, full-stack developer
Originally published at bytebodger.github.io Updated on ・12 min read

JavaScript, and its evil nemesis Object-Oriented Programming, have experienced some remarkable role reversals in the last 10+ years. I'm old enough to remember when JavaScript was little more than a toy. It's only real use was to dump some useless bit of real-time functionality in the middle of a web page. (Oh, look! I've added a timer to the bottom of my page! Or... Oh, look! I built a calculator on my page - yeah, just like the one you already have on your desktop... but with fewer features and more bugs.)

I'm also old enough to remember when the programming world was dominated by developers who would openly scoff at you if your weren't writing your code in a "real" language. And when they said "real" language, they always meant - a compiled, object-oriented, strongly-typed language. To be even more specific, most people preaching about "real" languages in the 90s and the early 00s were really just talking about one language - Java. Starting from the mid-00s, C# soon became included in the hallowed pantheon of "real" languages.

Server-side scripting languages definitely did not qualify as "real" languages. But even the guys writing in server-side scripting languages looked down their noses at the dweebs playing around with JavaScript.

A Tectonic Shift

Obviously, the "heavyweight" OO languages, like Java and C#, are still massively used and their influence only continues to expand. But it's pretty interesting to see how JavaScript has almost flipped the tables on the snobs who used to dismiss it as a worthless toy. Nowadays, some of the juiciest job postings are for devs who can write most - or all - of an application entirely in JavaScript. With tools like Node and Express and MongoDB, there's not even any need to confine your JS skills purely to the frontend.

With the "rise" of JavaScript, it was perhaps inevitable that all those young, new coders - the ones who didn't have to spend years writing Java/C# when those were the only jobs paying any real money - would start to reassess (and even scorn) the principles that originally made Java/C# great. Specifically, I'm seeing a huge trend whereby JS devs aren't shy about openly badmouthing any-and-all of the core tenets of object-oriented programming.

If I sound biased in favor of OO, trust me - I'm not. In many use cases, I believe that OO is a bloated mess. But I also believe that almost any programming tool is just that - a tool. You wouldn't decide to build your house - or all houses - with nothing but a hammer. Because there are times when a hammer is the flat-out wrong tool for the job. Similarly, I'm convinced that, in many cases, OO is also the wrong tool for the job. But just because the hammer is not always the best tool for the job, that doesn't mean that you chuck your hammer in the garbage. Sometimes you need that tool. When you have a "hammering" job, it's just silly to attack it with a screwdriver.

But just as the Java snobs from the 90s and the C# snobs from the 00s tended to dismiss any valid use for JavaScript, I find now that many JS devs tend to dismiss any valid use for OO. Many of my JS colleagues talk about OO like it did something wrong to them in their childhood. I've found myself wanting to show them a doll and say, "Now show me where the bad OO Demon touched you."

The Ambiguity of class

For the most part, these functional-vs-OO language debates are purely academic. When you take a new job, or you're placed into a new (to you) project, most of the tech-stack debates have already been long-since settled. For example, if you begin working as a "Senior Frontend Engineer", where you'll be expected to work in any particular flavor of JS, the odds are that you never need to have much of an argument - with anybody - about whether you should use a functional or OO approach. If the entire legacy environment is written in JS, then it's almost certain that any new coding you do in that environment will be done in JS, and there won't really be any room to debate whether you should throw some Java or some C# into the mix.

But then, JavaScript introduced the class keyword...

Now, JS devs could craft code that ostensibly looked like OO - even if it was never truly OO. If you've already been ingrained in the OO world, this might have felt quite natural to you. OO devs think in terms of classes. It's the core building block of their mental architectural process. If you weren't already ensconced in that mindset, it wasn't much of a problem either. Because JS never forces you to use class. It's just a new keyword that you can reach for - if you see fit.

By my reckoning, this didn't seem to cause too much of a stir when the class keyword was first introduced. Some JS devs embraced it. Others just shrugged. Because all of the "old" JS approaches to objects were still perfectly valid.

But in the last several years, there seems to be a growing backlash amongst the "luminaries" of JS development against any use of class in JS code. There are long, sometimes vitriolic posts made by the JS snobs (and they are sounding more and more like snobs) about why the class keyword is just plain evil.

Although the anti-class brigade can be found in all corners of JS fandom, it seems to be particularly recalcitrant in the React community. As soon as React started gaining massive adoption, there was an entire wave of senior JS devs that were wringing their hands over how to solve the class "problem" that became more firmly entrenched with React - because the original implementation of React almost assumed that most/all of your application would be encased in classes. (To be clear, even with the first publication of React, you were never required to use classes - but most of their documentation assumed that you would be using them, and the practice became an increasing point of contention for the anti-class crowd.)

Lame Arguments

The reasons why some senior JS devs hate classes are too numerous to list here. But here are some of the major points that I've gathered:

  • JavaScript is not an OO language, so we shouldn't be using language constructs that mirror OO functionality.
    I find this to be borderline-laughable. If your devs don't understand the difference between a "true" OO language and a language that uses prototypical inheritance - they shouldn't be your devs. You shouldn't hire them. There are plenty of keywords that mean one thing in a given language, but mean something slightly - or entirely - different in another language. That's just part of the game if you've chosen to learn multiple programming languages.

  • I worked on this really horrible project once that made a mess of OO and it made my life hell. So we should never use the class keyword because it reminds me of that horrible experience.
    For example, many of these anti-class rants center on the mess that can be created by working in an OO project that has made over/poor-use of inheritance. But guess what?? Even many OO devs acknowledge that inheritance is a fickle beast that should only be used with caution. That doesn't mean that we should run screaming anytime someone proposes the use of a class keyword in our codebases. Any seasoned JS dev can give you horror stories about a legacy JS codebase that he had to muck through. That doesn't mean that JS's language constructs are "bad". It just means that you can write crappy code - in any language - regardless of the chosen set of keywords.

  • As a functional language, JS programming should be focused on the creation of pure functions, and classes go against that model.
    OK, sure. I agree with that. But here's the deal: Show me any large, enterprise-level JS application where every single bit of business logic is encapsulated in a pure, stateless function. If you can show me that, I will show you Santa Clause and the Easter Bunny. Because they simply do not exist. Sure, you can write a component - or a simple utility - that qualifies as a pure function - or a collection of pure functions. But once your app grows to a "certain size", it's inevitable that you're gonna start writing some functions that most definitely are not "pure".

  • Classes slow down some of the tools that we use for code optimization.
    OK, so let me get this straight. You have additional tools that you've packed on top of your chosen library, and those tools can't properly handle the language constructs that are (for quite some time) native to the language itself. So your "solution" is to rail against the use of the language constructs themselves??? C'mon, man... Did it ever occur to you that maybe - just maybe - you need to improve the bolt-on tools that you're using, rather than trying to code-shame people who are using the language's core constructs??

  • Classes make something messy (prototypical inheritance) look neat, but doesn't actually fix any of the messiness.
    This was a direct comment on one of my other posts. I respect the poster. But I find the premise to be mind-boggling. For every language that has achieved a sizable install-base, and has thereafter evolved through additional updates to the core language, subsequent updates almost always focus heavily on making the "messy" things seem... cleaner. JavaScript didn't use to ship with Array.prototype functions - so you had to write them yourself. Writing them yourself... worked. But it was kinda... messy. The Array.prototype functions are nothing but syntactic sugar for things that you could already do manually in JavaScript. So... should we remove them? Or demonize their use? Simply because they allow us to do something that we could already do - but now we can do it in a cleaner, simpler manner??

And here's the one that really gets me: It's on the ReactJS site, where they explain the use case for hooks. It says, verbatim:

"Classes confuse both people and machines"

Umm... what???

If you're a dev who's confused by the mere concept of classes, well... you might want to consider a new line of work. If you go into a job interview - even a job interview based purely on JS, and you say, "Well... I can't really answer that question, because classes just confuse the hell outta me," you can pretty much bet that the interviewers will be cutting the session short, thanking you for your time, and throwing your resume in the garbage.

Syntactic Sugar

Here's where, IMHO, all of the anti-class crusaders just miss the whole point. In JavaScript, the class keyword... wait for it... doesn't actually do anything. The introduction of class to the ECMA spec didn't add a single piece of new functionality. All class "does" is it provides you with a shorthand way of doing the exact same thing that you could always do in JavaScript from the beginning. Consider these two examples:

// Example A
const getObject = () => {
   return {
      someProperty : 'foo',
      anotherProperty : 'bar',
      log : value => console.log(value),
   };
};
const myObject = getObject();
console.log(myObject.someProperty);  // 'foo'
console.log(myObject.anotherProperty);  // 'bar'
myObject.log('foo bar');  // 'foo bar'
Enter fullscreen mode Exit fullscreen mode

Or:

// Example B
class myClass {
   someProperty = 'foo';
   anotherProperty = 'bar';
   log = value => console.log(value);
}
const myObject = new myClass();
console.log(myObject.someProperty);  // 'foo'
console.log(myObject.anotherProperty);  // 'bar'
myObject.log('foo bar');  // 'foo bar'    
Enter fullscreen mode Exit fullscreen mode

Functionally speaking, what's the difference between A and B?? Absolutely nothing. So here we have two different syntactic approaches to achieve the exact same result. But some would have you believe that A is somehow... "good". And B is somehow... evil. Hmm...

Now I can hear some of the readers screaming,

"But... BUT! What about the evils of inheritance (extends)?!"

Yeah, umm... you could do all of that in "old school" JavaScript as well. Again, the extends keyword, like the class keyword, doesn't provide a single bit of new functionality in JavaScript. It only gives you a shorthand method to achieve the same effect. In fact, using plain old "legacy" JavaScript, you could even create private class members - by using closures. So you see, these new keywords (which aren't so new anymore), just give you a way to use the same functionality that you could always use - in a slightly different syntax.

Now, if you want to tell me that examples A and B are both somehow "bad", well... I wouldn't agree with you, but at least your argument would be logically consistent. But if you want to tell me that Example A is somehow "good", and Example B is somehow "bad"... then you're just being dogmatic about your personal, chosen syntax.

The Sacred Order of JavaScript

Whenever a new language is launched, its first challenge - if it's to gain a valid foothold in the marketplace - is to foster a growing community of people who are trying to solve real-world problems with that language. These can be professionals and/or hobbyists and/or academics. Once people really dive in and start using the language, they're gonna run into edge cases where they can't figure out how to solve a particular problem in this particular language.

So they start googling... And those searches, eventually, lead to forums dedicated to others trying to answer the same problems. They start finding answers on Stack Overflow. They start sharing "tips and tricks". And, if the language is to grow, so too does the community that's supporting that language. In this respect, JavaScript is no different than any other language that's gained widespread adoption. The community is wide and deep - and that's a very good thing.

The next step in a language's maturation is the rise of its "Sacred Order". This is a step beyond mere "community". If the language keeps growing, eventually there will be certain luminaries who drive the perception of everyone else who considers themselves to be part of the community. About a decade ago, JavaScript started to attain its own Sacred Order. This isn't a knock against JavaScript. There's a Sacred Order of Java, a Sacred Order of C#, etc., etc...

In general, Sacred Orders, like the embryonic "communities", are a net-good for the language. They foster the "thought leaders" who drive the language forward. But Sacred Orders also have a downside...

Once the Sacred Order is established, their edicts tend to be taken as gospel. This can be a very good thing. But it can also have negative side effects when the Sacred Order's preferences start being adopted as unquestioned dogma.

The Sacred Order of JavaScript seems to be flogging this idea that "classes are bad, mmmkay??" Because they're the Sacred Order, most of the "lesser" devs just seem to fall inline. After all, what junior-to-midlevel dev wants to be railing against the edicts of an established luminary like, say, Dan Abramov? I mean, if you don't have that kinda clout, who are you to question the latest trend that's being foisted upon us by the acknowledged thought leaders?

But even "thought leaders" have biases. Faults, even. There are some things that they rail against purely because they don't personally like them. And when this happens, everyone else tends to fall in lockstep behind them. The class keyword definitely falls into this category. The Sacred Order says classes are bad. Therefore, it must be so, right??

I've already covered how class is just a bit of JS syntactic sugar. So let's look at something else that's also syntactic sugar - but the Sacred Order loves it. I'm talking about arrow functions.

What's the difference between these two code snippets?

// Example A
const myFunction = function() { console.log('it ran') }
myFunction();  // 'it ran'
Enter fullscreen mode Exit fullscreen mode

Or:

// Example B
const myFunction = () => console.log('it ran');
myFunction();  // 'it ran'
Enter fullscreen mode Exit fullscreen mode

Obviously, there is no functional difference. They do the exact same thing. You see, arrow functions are nothing more than syntactic sugar. They allow you to do the exact same thing that you were always doing in JavaScript, in a slightly different syntax. Yes... an arrow function is generally seen to be "cleaner". But there's nothing wrong about declaring your functions in the old-school format.

So does the Sacred Order of JavaScript rail against arrow functions the same way that they rail against the class keyword?? Of course not. They love arrow functions. You can't get through any modern JS tutorial without having a proper understanding of arrow functions. But there are plenty of modern JS tutorials that go to great lengths to purposely avoid the class keyword. Why?? Umm... because the Sacred Order says that the class keyword is bad. (Mmmkay...?)

My point here is not to tell you that arrow functions are, in any way, "bad". Arrow functions are awesome! But it's silly to reject the class keyword - that does the exact same thing as "old-school" JavaScript, but to wholeheartedly embrace arrow functions - which also do the exact same thing as "old-school" JavaScript.

Stop Focusing on Keywords

I'm not trying to argue that JS doesn't have its share of good practices and questionable practices. Every language has numerous examples where inexperienced devs created horrific code that leveraged a given keyword. That doesn't mean that the keyword was somehow "evil". It just means that the code sucked. And no amount of hand-wringing over the class keyword will ever remedy that.

Just consider the while keyword. Nearly every language has the ability for you to create a while loop. In general, while loops are always at least a little bit tricky, because the loop doesn't have any built-in assurance that it will stop after a given number of iterations. If you've never created code that launched an infinite while loop, then you haven't done much coding.

Does that mean we should remove the while keyword from the language? Of course not. Does that mean we should code-shame anyone who feels comfortable using a while loop? Of course not. The vast majority of your loops should not be while loops. But there are places where a while loop is absolutely the right choice. The "answer" to this problem isn't to banish the while loop. Nor is it to rant against its use. The simple fact is that the while loop, although it may have disadvantages, is still a valuable tool to have in your tool belt. The answer isn't to throw the tool in the garbage. The answer is to hire/train devs who understand the pros-and-cons of every tool so that they may make wise choices going forward.

And with JavaScript's class keyword, the approach should be exactly the same. Don't throw out the tool because of some dogma that you learned when you were coding in 2007. Instead, endeavor to understand the tools at your disposal and make the best possible choices going forward.

Discussion (7)

Collapse
cesarkohl profile image
Cesar Kohl • Edited

What a relief!

From 2016 to 2000, I focused entirely on PHP losing the direct contact that I once had with JS since 2009. Now that I'm back it's impossible not to be shocked, even lost, on the plethora of strong opinions about a mere... programming language. Even in the 2000s, when Java/C# coworkers named JS "a software's butterfly", the comments wouldn't go further than that.

It's astonishing the power that an opinion by a googler/facebooker/youtuber/etc has nowadays. I remember that some years ago in the "pre-Stack Overflow era", the source of knowledge were books written by "dinosaurs". I even had one signed! Now in this "opinionated" JS era, everyone is savoring syntactic sugar content and that once strong-harsh-bold-trustable source, whatever who may be, has been lost. It's interesting to realize that this scenario may be one of many around the same issue: the cultural loss of a central source of truth (like mainstream media [!] and universities) in favor of decentralization towards banal (fake news and unreviewed cheap online courses).

Sincerely, OOP "vs" functional? Typed "vs" untyped? Arrow functions "vs" ... functions? What?

It seems that if previously we knew nothing now we just think we do. I'm hoping someday we'll figure out the difference between comprehension and opinion.

Collapse
bytebodger profile image
Adam Nathaniel Davis Author

Thank you for the feedback! And it sounds like we've had some similar career experiences.

If you read any more of my stuff, you'll probably see that one of my recurring themes is the rejection of dogma and "fanboy-ing". After you've done this stuff for long enough, it's almost impossible not to develop some very strong opinions about certain things. I'm certainly no different. There are some tools/languages/frameworks/etc that I truly dislike. And of course, there are some that I love.

But what seems to be missing from the professional discourse is anyone else's acknowledgment that most of their strong feelings are just that - feelings. Opinions. Anecdotal observations. Instead, someone decides that "classes are bad" or "tabs are wrong" or... whatever. And then they try to yell down anyone who dares to disagree with them.

And seeing all of this in the JS community is especially rich for anyone with a little historical perspective. JS devs used to be grateful to just get an interview. Now, many of them act more conceited than the worst Java/C++ devs from back-in-the-day.

Collapse
peerreynders profile image
peerreynders • Edited

Your article doesn't acknowledge that the introduction of the class keyword undermined the growing "Object Factory" movement in JS user land.

If your devs don't understand the difference between a "true" OO language and a language that uses prototypical inheritance - they shouldn't be your devs.

Quotes or not, you are betraying your own bias here - that "Class-based Object Orientation" is the only "true" OO. JavaScript has always been "Object-Oriented" it just was never "class-based". In JS there never was a "class construct" - there are only object instances that could be instantiated in a variety of ways - constructor functions only being one of them.

While an object may have been created by a specific constructor function subsequent "augmentation" can morph an object into another shape to pass entirely different duck typing constraints. So JS objects can at runtime change their shape and therefore membership from one object category to another.

In "class-based" OO an object implements all its interfaces from birth - it can't decide to change class allegiance during its lifetime.

In JS any code can build an object. In class-based OO typically only constructors can prepare object instances.

So in JS class is just some mental model of how an object ought to behave (according to class-based conventions) but doesn't really enforce how any object instance will behave during its lifetime.

As a functional language, JS programming should be focused on the creation of pure functions, and classes go against that model.

JavaScript isn't a functional language. Functions as first-class citizens aren't enough to push it into that category.

Functional languages tend to be:

  • immutable by default. That predicates support of persistent data structures right out of the box. JS is mutable by default and persistent data structures are not part of the language.
  • recursive - largely because that is how you get around the lack of mutability. JS uses iteration and mutability is needed for iteration to work.

Obviously, there is no functional difference.

For that particular implementation, yes. But that's also an oversimplification.

The difference is that in arrow functions the function context (this) refers to the context that created it. In a long form function expression the function context refers to its execution context (usually the object the function is being invoked on as a method; also an added function name can make debugging easier).

An arrow function can only act as a method for the object that originally created it - it can't be "reused" on another object.

So does the Sacred Order of JavaScript rail against arrow functions the same way that they rail against the class keyword??

You are overgeneralizing here. You are talking about the "Sacred Order of React". The React community is known to be Gung Ho on arrow functions. In the wider JS community there are pockets that are pushing back against the overuse of arrow functions especially if it is primarily motivated by (developer) "aesthetics".

I Don't Hate Arrow Functions
ESLint Plugin: proper-arrows

Back to Object Factories:

In 2008 Douglas Crockford published "JavaScript the Good Parts". It might as well have been subtitled "How to get your head out of the class-based OO clouds and learn to use JavaScript (objects) without losing your sanity."

In "Chapter 5: Inheritance - Functional" he introduces the "functional constructor" template which eventually became the "Object Factory" pattern. The following section "Parts" goes into using composition instead of inheritance (following the GoF advice to "favor object composition over class inheritance" p.20).

An object factory doesn't return the actual object but a "holder of functions". Those functions aren't even methods as the don't use this but they instead maintain state on the values within the closure that created them and anything inside the closure is essentially private.

"Closures are a poor man's object."

This approach created "simplified OO" with no need for new or this, provided encapsulation and favoured composition ("make the wrong thing hard and right thing easy"). The known tradeoff was that each "object" had its own set of functions acting as methods - i.e. there was no sharing of function implementations across object instances or via the prototype chain.

For more detail see Eric Elliot's JavaScript Factory Functions vs Constructor Functions vs Classes (2016).
(Edit: Better Ryan Carniato: OOP with Functions in JavaScript (2019) - Factory Oriented Programming)

The ES2015 proposal for class declarations and expressions essentially undermined "simplified OO", bringing new and this back to the forefront and worse - making extends easy again (Allen Holub: Why extends is Evil (2003)).

To add insult to injury the change was made to accomodate those people:

JavaScript is most despised because it isn’t some other language. If you are good in some other language and you have to program in an environment that only supports JavaScript, then you are forced to use JavaScript, and that is annoying. Most people in that situation don’t even bother to learn JavaScript first, and then they are surprised when JavaScript turns out to have significant differences from the some other language they would rather be using, and that those differences matter (JtGP, p.2).

... and it retroactively justified their attitude to dismiss JavaScript until it had classes even though nothing had really changed - other than implementation inheritance now being really easy.

That created some resentment.


React (2013): React.createClass().

  • Why don't you just go out in public with a "why doesn't this stupid language have classes" poster? BTW that function creates a component, not a class.

Don't touch this.props, this.state and use this.setState for state updates.


In an alternate universe React would have

  • been very clear that React is in charge of managing the component instance (state) - the developer's code only gets to contribute at very specific points in the component instance lifecycle.
  • accepted a holder of functions (think Strategy but organized as a collection of functions rather than a self contained object) with createComponent where each function would have been invoked during the various lifecycle phases of an instance to customize its rendering behavior (render - transforming the inputs to reactElements - being the only mandatory one).
  • bypassed this entirely. Instead each function would have been passed an instance argument restricted to whatever interface to the component instance React was willing to expose during that part of the lifecycle (e.g. props(), state(), setState() etc.).

That approach wouldn't need to adapt in the face of class - and there would be no need to move to franken-function components with hooks.

And sorry, "Functional Component" is an oxymoron - "Components are objects".
However some components may only need a pure render function.

Collapse
bytebodger profile image
Adam Nathaniel Davis Author

Your article doesn't acknowledge that the introduction of the class keyword undermined the growing "Object Factory" movement in JS user land.

I wasn't aware of the "Object Factory movement in JS user land." But I can't really offer an apology here. The factory pattern can be extremely useful. But it can also be a complete PITA when it's shoehorned somewhere that it doesn't "fit". The factory pattern can be an extremely useful tool - or it can be a pointless abstraction that only complicates the solution.

Quotes or not, you are betraying your own bias here - that "Class-based Object Orientation" is the only "true" OO.

I fully understand that JS is - and has always been - deeply dependent upon objects. But when almost-anyone talks about "OOP", they are referring to concepts that don't actually exist in JS. Specifically, they're referring to class-based objects.

I fully agree/understand that JS has objects. That it's based on objects. But when most programmers talk about OOP, they're not talking about the idea that a language uses objects or is based on objects. They're talking about class-based object instantiation.

In JS there never was a "class construct"

Right. That's called prototypical inheritance.

that could be instantiated in a variety of ways - constructor functions only being one of them.

Umm... Not really. The whole basis of "prototypical inheritance" is that that there are no guidelines by which you create new objects. You can only create an object by copying another object. That's pretty much the definition of "prototypical inheritance".

So in JS class is just some mental model of how an object ought to behave (according to class-based conventions) but doesn't really enforce how any object instance will behave during its lifetime.

TRUE In JS, class is just an "example" of which object to use as the prototype.

JavaScript isn't a functional language. Functions as first-class citizens aren't enough to push it into that category.

I think you missed the point of that passage. I 200% agree that JS is NOT an FP language. That passage was supposed to be a quote of the arguments against the class keyword. If you think I'm making things up as I go along, take a look at my article about the BS of people who want to claim that JS is a FP language: dev.to/bytebodger/javascript-s-fun...

You are overgeneralizing here. You are talking about the "Sacred Order of React". The React community is known to be Gung Ho on arrow functions. In the wider JS community there are pockets that are pushing back against the overuse of arrow functions especially if it is primarily motivated by (developer) "aesthetics".

I hope we can "agree to disagree" on this one. I'll admit that arrow functions are particularly-loved within React. I will not admit that this love is somehow limited to React. When I look at Svelte, or Vue, or even (modern) Angular, I see no indication that love of arrow functions is, in any way, confined to React.

For more detail see Eric Elliot's JavaScript Factory Functions vs Constructor Functions vs Classes (2016).

I have massive respect for Eric Elliot's intellect and overall knowledge. That does not mean that I agree with all of his positions. In some cases, I think he's borderline-delusional. He's probably forgotten more about JS than I've ever learned. But that doesn't mean that all of his JS contentions are correct.

Why don't you just go out in public with a "why doesn't this stupid language have classes" poster? BTW that function creates a component, not a class.

I'm getting confused at this point. React has a construct called a "component". But that only exists in React terminology. React compiles down to JS. And JS has nothing called a "component".

And sorry, "Functional Component" is an oxymoron - "Components are objects".
However some components may only need a pure render function.

You can call it an oxymoron if you want, but the simple fact is that, in React, you can create a "component" (which does not actually exist in JS...) with a function or a class. So you can call it an oxymoron if you like, but the simple fact is that I can create a React component without ever using class and without ever explicitly creating an object.

Collapse
peerreynders profile image
peerreynders • Edited

it can be a pointless abstraction that only complicates the solution.

Strangely enough some people still find this confusing (I don't) and feel that it's necessary to enforce encapsulation of non-public details and not simply rely on developer discipline. The object factory accomplished that with ES5 features. If those are features you value then it isn't pointless. In some ways an object factory is just an extremely simple builder.

they're referring to class-based objects.

.. people who have been introduced to OOP via languages (Java, C#, etc.) that implement class-based object-orientation - it's a variation of Baby Duck Syndrome.

Teaching OO: Putting the Object back into OOD (2003)

Almost everyone who teaches object orientation uses the class as a fundamental building block. Such an approach misses the central point of object orientation: the objects themselves, and what they portend for flexibility and effective design. This weblog is a case study in teaching object orientation.

James Coplien doesn't make this claim on behalf of JavaScript but as the foundation to Data, Context and Interaction. His position is that that you can't practice "true" object-oriented programming in Java or C# because they deal with classes, not objects.
fulloo.info

The point is that the belief that object-orientation has to be class-based is narrow-minded. People unable or unwilling to transcend that aren't accepting JavaScript for what it is but are still wishing that it is something else they are already familiar with.

That's called prototypical inheritance.

Prototypical inheritance is about "implementation inheritance", not class. Classes don't exist to enable implementation inheritance. For example VB5 had classes and interface inheritance - but not implementation inheritance. In class-based languages an object instance is a member of a class (or in the case of C++ possibly multiple classes) - the object instance holds the data while the class holds the functions that can act as methods on that instance data.

When using initialization by literal notation the archetypical object instance holds its own references to the functions that may act as methods on it - there is no class construct holding the methods that may act on all the object instances the belong to the class. The methods from Object (which is another object) are acquired via implementation inheritance.

The language feature that comes closest to identifying a class is the constructor function.

Given that it is quite common to initialize objects with the literal notation and subsequently augment those objects with the helpers from Object the constructor can only tell you what the object was at the time of creation - right now it could be something entirely different.

You can only create an object by copying another object.

Using initialization by literal notation the object's constructor is simply Object - not very useful for "classification".

In JS, class is just an "example" of which object to use as the prototype.

It's a creational blueprint - after creation all bets are off.

I think you missed the point of that passage.

While I was aware of your position your followup was

OK, sure. I agree with that.

That in no way disagrees with the "As a functional language" bit.

So I was

  • Making your point for you.
  • Making clear that I don't see JS as a functional language (which doesn't limit the usefulness of some functional practices, like higher order functions).

I hope we can "agree to disagree" on this one.

Framework enthusiasts still don't equate to the entirety of the JS community.
In my experience some vocal framework enthusiasts belittle others favouring the traditional function style. Kyle Simpson just won't be kowtowed into shutting up.

You yourself seem to favour arrow functions. But as it has already been pointed to you, using arrow functions everywhere is a case of pessimizing prematurely.

It can also squander an opportunity to communicate intent to the reader:

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
  }

  // Intent to use as an event listener/callback to this
  // particular instance: 
  // Use arrow function bound to the object instance
  //
  increment = _event => this.setState({ count: this.state.count + 1});

  // Intent to be only used as a public method;
  // i.e. there is no need to bind a separate function
  // to the instance, a method shared via prototype is good enough:
  // Use shorthand method syntax
  //
  render (){
    return (
    <div>
      <button onClick={this.increment}>add 1</button>
      <p>{this.state.count}</p>
    </div>
  );
}

Aside: Initializing state inside the constructor is consistent with the idea that state is non-public. Initializing it as a public property doesn't convey that intent.

Private fields (stage 3) won't help either as they are private to the class that defines them and therefore will not be available to subclasses.

This whole preference-based use of arrow function expressions over traditional functions just reinforces the notion that Programmers know the benefit of everything and the tradeoffs of nothing.

i.e. stop using arrow function expressions based on preference and just use them where it makes sense.

I have massive respect for Eric Elliot's intellect

I wasn't using Eric Elliot as an "appeal to authority". It was merely a reference to an illustration of the various object creation approaches. He had some good ideas back in "Programming JavaScript Applications" but has become a bid eccentric since he adopted React.

I'm getting confused at this point.

The point being that in those days React had a disdain for JavaScript not supporting classes right out of the box. Back then they were still obsessed with class-based OO because they liked that compositional model only to later flip to the functional compositional model - ignoring alternate compositional approaches that were already in use with ES5.

But that only exists in React terminology.

React Components, Elements, and Instances (2015)

There are:

  • React Components
  • React Component Instances
  • React Elements

There is no "React Class".

If createClass returns a React Component shouldn't it be called createComponent?
The documentation for React.createClass even stated:

Create a component given a specification.

.

the simple fact is that I can create a React component without ever using class and without ever explicitly creating an object.

The initial idea was that simple components only needed a render method to transform props to React elements. So implementing simple components as a single render (pure) function instead of a whole object made sense. At that point "Functional Component" meant "a function as a render-only component".

The current incarnation of functional components with hooks is an object in a functional straight jacket.

  • The more hooks you use the further your move away from a function "that does one job and does it well".
  • This is accomplished at the cost of additional runtime checks during each render pass - e.g. emulating componentDidMount, componentDidUpdate, and componentWillUnmount with useEffect requires checking of the dependency array - rather than just calling a dedicated function at the appropriate time.
Collapse
gaurang847 profile image
Gaurang • Edited

Amazing article!

Many of my JS colleagues talk about OO like it did something wrong to them in their childhood. I've found myself wanting to show them a doll of the OO Demon and say, "Now show me where the bad man touched you."

I swear I've had friends that behave like that and dis Java because it has so many rules and is complicated. And because they find JavaScript to be much more flexible and forgiving.
And it always ticks me to hear that 'cause while JS is great and all, that's a very bad reason to either dis a language or to like another.

The truth, I often feel, is that they never really put enough effort into properly learning the language. They're just venting out their frustration as the understanding of the language didn't magically seep into them by itself.
Even in Node.js, I've had this colleague who strongly dissed sails.js (an MVC framework for Node.js) because he was used to working with express.js, and sails.js didn't feel flexible to his liking.

I strongly agree with you that it's necessary to view any programming tool as a tool. Learn the pros and cons of each tool.
And not let our personal biases get the better of us. That's very unhealthy and averse to a growth-mindset.

Collapse
bytebodger profile image
Adam Nathaniel Davis Author

Well said! And thank you for the feedback! You're correct that it often comes down to lack of understanding - or an unwillingness to put in the time/effort to achieve that understanding. Ultimately, when we don't want to spend that time/effort to grasp something new/different, we fall back on dogma. Because dogma has an air of authority to it. We can spew dogma and sound smart (to some people). Right now, a common JS dogma is, "There shall be no classes."