12 ms·
When I use classes, when I don’t, what I do instead and why
- cdevs 9y agoWhat about the cognitive load on someone reading person1 person2 person3. Now I have to realize person 3 in your code is person2 and person2 was person1, person1 and person2 are now dangling and will never be touched again hopefully. I love the idea of pure functions but I have always thought there was a missing piece in functional programming.
- Anderkent 9y agoNo sane language makes you write person1 person2 person3. Immutability doesn't mean you can't reuse names; only that the reassignment is in lexical scope, and doesn't impact any other references to what you saw as person1.
- cdevs 9y agoRust calls it shadowing and throws this example: fn main() { let x = 5; let x = x + 1; let x = x * 2; println!("The value of x is: {}", x); } Sure this is a syntax example and you shouldn't be creating Immutable variable x and then adding +1 to it for no reason. If you did have a reason you should add it to the variable name so it's more likely developers say let xPlusOne = x + 1; Or a kitchen example: Let cream = getRefridgeratorItem(cream); Let whippedCream = blend(cream); So you reply no sane language makes you do this but why didn't the author show that? His goal was to make things more readable but lets be honest functional programmers would judge you all day for making a promise and then copying over that promise with something else. You may do it at work but the purist trolls hate reading it.
- sshine 9y agoTL;DR: Read JavaScript: The Good Parts.
- deleted 9y ago[deleted]
- Veen 9y agoThat book was written in 2008. JavaScript classes were introduced with ECMASCRIPT 2015.
- ealexhudson 9y agoThat doesn't really matter, this complaint about "this" predates the use of classes.
- thinkxl 9y agoJavaScript classes are syntactic sugar, the class type doesn't exist in ES2015; everything still works with prototypical inheritance. So, I'd say that this book is still relevant Today. If you don't understand how inheritance work behind the scenes, you don't understand JavaScript.
- deleted 9y ago[deleted]
- acdha 9y agoIt seems odd to read something written in 2017 about the confusion from using `this` which only mentions arrow functions briefly in passing. A fairly high percentage of discussion on that issue could be replaced with “use an arrow function, it works the way you expect”, and browser support should be basically the same as ES6 classes – doesn't work in IE11 but multiple tools are available for backwards compatibility.
- supergreg 9y agoDo arrow functions get added to the prototype or the instance of the object? Can they be created directly in the class body or have to be added inside the class constructor?
- jwdunne 9y agoSince I last checked it was still experimental, albeit available in Babel, but there is work on syntax for property initialisers in class bodies e.g: class X { id = (x) => x; } See here: https://github.com/jeffmo/es-class-fields-and-static-properties https://github.com/jeffmo/es-class-fields-and-static-propert...
- martin-adams 9y agoMy understanding is that arrow functions aren’t used as part of a class definition, but for things like anonymous functions in callbacks, preserving the ‘this’ context when something else is invoking it. E.g. el.onClick((e) => {this.doSomething();}); In that case something else will invoke the onClick callback but we want the ‘this’ context to the same as when we set up the onClick.
- tylerhou 9y agoDisadvantage is that creating a new function on every render ruins performance optimizations like `React.PureComponent`. Although that's a pretty minor detail, (you shouldn't prematurely optimize, and sometimes PureComponent is actually more expensive than rerendering since computing whether props have changed can be more expensive than just rerendering.)
- spion 9y ago`this` is hard to learn. No it isn't. Its a function argument that is implicitly passed "left of the dot". Whatever object that function was attached to when you call it? Thats the object thats gonna become the `this` argument. Normal arguments, you pass them like so: `someFunction(normalArgument1, normalArgument2)` The argument `this` you pass it like so: `thisArgument.someFunction(normalArgument1, normalArgument2)` From this it immediately becomes clear why you can't pass that function away by itself. When whoever got it calls it, its going to have nothing "left of the dot" to call it with. Done, `this` explained.
- katastic 9y agoYeah, if you can't grasp 'this', good luck figuring out delegates. (Note: Some/many languages call these "Closures") 'this' is just a context pointer. classes don't actually store their functions (except virtual ones). class A{ int data; void method1(int input){data = input;} } A my_instance; is actually struct A {int data;} method1_for_struct_A(A *context, int input){ context->data = input; } //normal local function A my_instance; and this: my_instance.method1(3); becomes this: method1_for_struct_A(my_instance, 3); //send a pointer to the class we're operating on And once you understand that, delegates are easy, because they're just... sending the context pointer around. So in D: //note: global function, NOT apart of any class. delegate void my_function(int input) { data = input; //where is data coming from? // the surrounding context. } void my_test_function() { int data = 2; my_function(3); // is a delegate and gets implicitly sent the context //pointer for my_test_function, and now, it can access data. // Note this is a RUN-TIME adjustable function. Delegates can be passed // around. } and internally my_function is actually (note the pattern): void my_function(context_pointer, int input) { context_pointer.data = input; } and the compiler implicitly sends the context with it: my_function(3) becomes my_function(my_test_function_context, 3); So basically, delegates are just an extension of what classes already use under-the-hood. Delegates are "fat" function pointers. Function pointers point to a function, and the "fat" pointer includes an additional "context" pointer to the surrounding data frame so you can dereference it to access the surrounding data. You can do all of this in plain C if you don't mind writing all the boilerplate code yourself. Virtual functions are similar to delegates. The class actually contains the function pointer / delegate instead of re-writing it at compile-time. Which allows them to be overridden at run-time. Whereas "this" usually means you can figure it out statically, at compile-time, so the function call is a pure function call and doesn't incur the penalty of looking up a pointer for the virtual call. I bet I'm probably missing something or slightly off here or there, so someone feel free to chime in and correct me. Consider the code pseudo-code. I might have flipped something from memory.
- azangru 9y agoThe title would benefit from the word javascript :-)
- thinkxl 9y agoI don't know why, but just by reading the title my mind implied JavaScript, and I noticed it doesn't have that word only after reading your comment. Weird.
- stochastic_monk 9y agoI thought of C++, but I have never written any JavaScript in my life. It's worth considering that even in C++, there are times when inventing a class to simply perform a function is excessive.
- graycat 9y agoI don't have a sushi knife because I don't need one because I don't eat sushi. I was in a project in rule based programming, but I left that project and f'got about such programming because I don't have any rules I want to program. I was in a high end project for object-oriented programming and another for object-oriented data definitions (OSI/ISO CMIP/S) but f'got about those because I don't have any objects to program or use for data. In my current project, I have defined a few classes, but it's not really object oriented programming because I do next to nothing with methods, never use inheritance, and usually allocate only one instance of each class. So I use classes as really just versions of structures as in C and, much better than C, as in PL/I where the structures are terrific and in execution time much more efficient than objects. For fast program to program communications over TCP/IP sockets, I do like class instance de/serialization. Basically, net, to me, object oriented programming is a solution for a niche problem I don't have, have never had, and likely never will have. Also the OP illustrates more of why I don't pay attention to object-oriented programming: The syntax of that example is tricky gibberish I want nothing to do with. Object oriented programming seems to have helped a lot with Microsoft's .NET. Okay, I use .NET. But I'm not writing .NET classes! Sorry guys, I don't eat sushi and in my code don't see any real need for object-oriented programming. More generally, I've been programming for a long time, and it all looks much the same to me: (A) Define places for the data need to store, (B) manipulate the data with assignment statements, if-then-else, do-while, call-return, and occasionally allocate storage(I let Microsoft's garbage collection handle freeing the storage), and (C) use some .NET classes. I could use a sushi knife if I ate sushi, but I don't. I could use all that stuff about objects if I had objects in my programming, but mostly I don't. To borrow from the old movie The Treasure of the Sierra Madre, "Objects? What objects? We don't need no stinkin' objects!"
- supergreg 9y agoThe case would be solved if they added some syntax to bind a function to the class. Something like class Foo { bind function bar() { console.log(this); } } So behind the scene it would do `this.bar = this.bar.bind(this);`
- spraak 9y agoThat'd be awesome. Submit a proposal! https://github.com/tc39/proposals https://github.com/tc39/proposals
- supergreg 9y agoLooked around and there's a proposal for an easier way to bind `this` and some discussion about extending that to class methods: https://github.com/tc39/proposal-bind-operator/issues/39 https://github.com/tc39/proposal-bind-operator/issues/39 Using arrow functions at the class level would also solve this without adding new syntax. Edit, this is a thing already: https://babeljs.io/docs/plugins/transform-class-properties/ https://babeljs.io/docs/plugins/transform-class-properties/
- martin-adams 9y agoInteresting, that would mean you would have confusion if someone used your class like: foo.bar.bind(somethingElse); How would the callee know that this would potentially conflict?
- abiox 9y ago> that would mean you would have confusion does this confusion not exist now?
- martin-adams 9y agoDon’t know, my concern was what the intended behaviour was if a declaration stated the bind context as well as the caller.
- 9y ago
- nulagrithom 9y agoUnderstanding 'this' in JavaScript is simply table stakes at this point. You and anyone else who touches your JS code should have a decent grasp on 'this'. Citing "complexity of this" while using React is just absurd in my opinion... You Don't Know JS: this & Object Prototypes cleared it up nicely for me.[0] Love the series in general too. Also -- and I'm not sure how it's typical done when taking a functional approach -- where do these functions "live"? What if we want to use a Person in another module/component/whatever? Do we basically just have a collection of "static" functions on a helper class that we're going to be using? I'm not sure that's a real gain. Would rather see the functions available on the Person itself (though maybe that's just my OOP ways talking). And then there's the "pure function" part. Without explicit documentation and no other knowledge of this individual's functional predilections, I would be surprised when the setName function actually creates an entirely new Person and then sets it name. I don't know. I really do enjoy functional approaches, but I don't think avoiding 'this' is a good reason, and I feel like the example given isn't the best scenario to illustrate. [0] https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes https://github.com/getify/You-Dont-Know-JS/tree/master/this%...
- k__ 9y agoAlso, if you only use arrow functions you don't even have that problem.
- moron4hire 9y agoThen you had different problems. You don't always want to capture `this`.
- always_good 9y agoExample?
- moron4hire 9y agoI almost always want event listeners to have `this` refer to the element on which they were added, not the class from which they came. It's the default case for all but arrow functions, so it takes no extra effort in most cases, and if I really need other state, I find it is clearer if it comes through a closure. Plus, making it a habit makes it easier to keep it straight.
- baron816 9y agoIf anyone is interest, you can write stateful React components without 'this' or 'class.' I explain how here: https://medium.com/@baronmaximilianwilleford/react-without-this-39a76b8f2160 https://medium.com/@baronmaximilianwilleford/react-without-t... I've been doing it for a while and haven't had any problems and have really enjoyed doing it.
- samfisher83 9y agoWhy Can't JavaScript just have c++/java/c# type classes? At least it makes it easy to identify what you class variables are and what your class functions are.
- saas_co_de 9y agoYeah I was just thinking, why can't javascript be easy and simple like C++?
- dlbucci 9y agoWhat bugs me about the class syntax more than anything is that it encourages prototype classes, which in my humble opinion, are just worse than the Crockford module pattern. C Modules solve this/binding issues, and give you privacy. Maybe they use more memory (in theory), but I never thought that was a great argument unless you actually measure usage and it's noticeably worse. You're basically creating those functions anyway when you call bind, so I wouldn't be surprised if there's not a big effect for most applications.
- southpolesteve 9y agoI also find `this` is terrible for linting. Most tools won't be able to figure you what you attached to `this` at runtime. It's a grab bag object ripe for bugs.
- seasonalgrit 9y agoIf you're going to go with a functional approach (which he introduces in the section "How to avoid 'this'"), instead of creating a brand-new function for each particular piece of data (e.g., setName, setGreeting) -- which will result in a ton of extra code -- just use a simple, generic assoc function. "Inventing a class with its own interface to hold a piece of information is like inventing a new language to write every short story." -- Rich Hickey
- deckar01 9y agoI actually have to maintain a js codebase that uses a generic “addProperty” function at its core to generate getters and setters. It is terrible to debug. Properly encapsulated class methods are much easier to read and debug. This problem has been solved for a long time. Some data doesn’t need a class, but when it does, don’t roll your own.
- pshc 9y agoI use classes to bind a data structure to a group of functions. That's mostly it. Inheritance, overrides, super calls? The less I use those, the less I miss them.
- dboreham 9y agoThis is in fact what classes are.
- wellpast 9y agoThis post is painfully confusing the important concepts of complexity & "complecting"/coupling. Javascript's this semantics may be confusing and hard to reason about but that is a different charge than the complecting/coupling sort of complexity which is the _much, much_ bigger problem in the world of building software systems. The much bigger problem with a Person class is not to do with this or that programming language vagaries vis a vis the this keyword. The bigger problem exists in all programming languages: ADTs like Person classes introduce unnecessary coupling of information -- and this is what really matters: this is what hurts productivity. It sucks that I have to learn the finicky vagaries of this in Javascript, but once I do, I'm fairly good going forward. However, no amount of learning on my side is going to help me when I need to add new pieces of information to some people data -- your ADT has decided for and _requires_ me to jam my knowledge and facts into an unnecessary rigid categorization in order to evolve your ADT-ridden data processing system. This post's refactoring of class hasn't changed that game for me. You've just moved this to each function's first parameter and called it "person" -- so while you've saved me momentarily from having to learn about this semantics[1], you haven't gotten rid of the truly offensive bit: the whole notion of the "person" classification itself and the rigid, upfront, unnecessary, and likely-to-be-wrong-in-the-future assumption that only its members can be given these things called names. [1] Which I should have learnt anyway being a Javascript programmer.
- s4vi0r 9y agoWhat do you mean by ADTs introduce unnecessary coupling of information? ADTs allow you to clarify and safely handle otherwise vague data. Maybe I'm misunderstanding, but your comment reads to me like you're in support of stringly typed programming.
- wellpast 9y ago> ADTs allow you to clarify and safely handle otherwise vague data. This is false. You can program with clear, strong, safe semantics without needing to taxonomize/classify your business domain/entities with ADTs. In most business applications, ADTs hurt you: they are used to pre-classify the world in ways (unnecessary ways) that very frequently collide with future system evolution & future use-cases.
- ryanmarsh 9y agoCode should be written first and foremost for humans to understand, and incidentally for compilers to understand. https://mitpress.mit.edu/sicp/front/node3.html https://mitpress.mit.edu/sicp/front/node3.html If we can all agree about that then we have to ask ourselves if encapsulating data and behavior at the granularity of classes makes the most sense for human comprehension. In naive OO examples (Animal -> Cat) it does. In practice however, it's debatable (at best). I highly recommend this video by Brian Will who does a much better job explaining this problem than I could. https://www.youtube.com/watch?v=QM1iUe6IofM https://www.youtube.com/watch?v=QM1iUe6IofM Douglas Crockford also does a good job of explaining some of the trouble with classes. https://www.youtube.com/watch?v=bo36MrBfTk4#t=28m50s https://www.youtube.com/watch?v=bo36MrBfTk4#t=28m50s
- Veedrac 9y ago> In naive OO examples (Animal -> Cat) it does. An inheritance hierarchy consists of three things: a taxonomy, behavioural inheritance and structural inheritance. The first is only useful on occasion, and the last two don't even apply to Animal → Cat; "animal" is not a behavioural constraint, and a cat certainly doesn't have a platonic ideal of an animal hidden somewhere inside its gut.
- Waterluvian 9y agoI avoid classes as a default behaviour, using them when they're clearly a good fit. The ease of testability that functions give me is worth its weight in gold. I love how much I can do with JavaScript without classes. Same with Python! You'd be surprised how often you don't need them.
- connorelsea 9y agoA lot of the typical use cases for classes in a language like Java are replaced by the user's choice of module format in JavaScript (ES6, commonjs, etc.). Though, I don't think "this" was that hard to learn (See: YDKJS) and classes/prototypes definitely are an important tool in the JavaScript toolbelt.
- dboreham 9y agoThis unfortunately is one of those cases where the fact that someone has taken some time to write a nice looking article about a subject does not signal that they have something interesting and valuable to say.
- zamalek 9y agoA few years back I wrote an JS+async to JS compiler. I made a very real attempt to forget everything I knew about statically typed languages. I organically found a very compositional form. The parser created a tree containing objects such as, for an if statement, { if: { ... }, accept: () => ... }. The next stage would decompose that into more compositions, maybe by adding a block and condition property. While it probably had garbage performance, the power of dynamic languages finally clicked. I used to be an aspiring game developer; writing a component entity system in C# (my weapon of choice) required absurd amounts of carefully crafted code. Statically-typed languages have a ton of front-loaded overhead if you need something outside of their formula. You can approach problems any way that you please in dynamic languages (such as the functional approach in the article) and, while they still really aren't for me, I completely understand why people use them.
- weberc2 9y agoIt's not clear to me what you mean by the power of dynamic languages. I'm inclined to think your perspective on static languages is limited to C# or some other OOP static languages. In particular, your example doesn't seem very novel for a statically typed language. Heck, I just built a reasonably elegant parser framework in Go and it doesn't even have generics. This makes me wonder if you're not attributing your OOP frustrations to static typing.
- zamalek 9y agoI really did mean to say OOP - I have been attempting a real project in Go and I'm smitten. Still, even Go has a prescribed way of approaching problems; as unusually broad as it is for a statically typed language. As a very relevant example, there is far more syntactical overhead in Go if you wish to fake FP (e.g. `func Foo(a A) func(b) func(c) func(d) e` to allow currying); where-as in JavaScript you simply bind or apply. I've yet to see an article gushing over how natural functional is in Go (which is fine, Go isn't designed for that). This must be the 20th article I've seen on HN about functional in JavaScript.
- weberc2 9y ago
- jryan49 9y agoToo bad avoiding this just looks like a spaghetti mess with no cohesion. If you're going to be using a language, you might as well learn how it works instead of avoiding it with strange hacks. I'm also confused how the 'pure' version is less complicated than the class version. I see it the opposite?
- ES2048 9y ago" Usage of class is not bad. It definitely has its place. If you have some really “hot” code that’s a bottleneck for your application, then using class can really speed things up. But 99% of the time, that’s not the case. And I don’t see how classes and the added complexity of this is worth it for most cases (let’s not even get started with prototypal inheritance). I have yet to have a situation where I needed classes for performance. So I only use them for React components because that’s what you have to do if you need to use state/lifecycle methods (but maybe not in the future). " Why would classes have any inherent performance benefits (and over what) ?
- rhinoceraptor 9y agoSince classes are just sugar over prototypes, their performance is just about the same for instantiation. I assume the article is comparing them to functions that return an object literal with methods. Here is a simple example of equivalent code in a class, function with a closure, and a prototype: class FooClass { constructor (bar) { this.bar = bar; } getBar () { return this.bar; } setBar (bar) { this.bar = bar; } } function FooClosure (bar) { var _bar = bar; return { getBar () { return _bar; }, setBar (bar) { _bar = bar; } }; } function FooPrototype (bar) { this.bar = bar; } FooPrototype.prototype.getBar = function () { return this.bar; }; FooPrototype.prototype.setBar = function (bar) { this.bar = bar; }; When instantiating a lot of objects, the prototype and class are practically the same, but the function closure pattern is a lot slower. On my machine, instantiating one hundred million of the class or prototype each took about 500ms, but the closure function took about 3s.
- adityapurwa 9y agoI don't see why would I want to assign my bound class methods to external variable, and if you do that, you are responsible to bind the method to an instance. The reason it is a class method is because it operates on that class, taking it out and using it externally is against the purpose of it's existence.