8 ms·
Object-oriented Programming in vanilla JavaScript
- BenoitEssiambre 9y agoOn this theme of the joys of plain old unencumbered javascript, I did one titled: Callback Heaven: Continuation Passing Style Patterns for JavaScript: https://medium.com/@b.essiambre/continuation-passing-style-patterns-for-javascript-5528449d3070 https://medium.com/@b.essiambre/continuation-passing-style-p...
- codeulike 9y agoIt all looks very tidy when you see it set out like that.
- BigJono 9y agoI love prototypical inheritence. I find it much tidier and easier to understand than the class based inheritence in other C family languages. I really wish that class syntax did not become a thing in Javascript. IMO that was the biggest mistake made so far in it's development. Just call a spade a spade instead of piling another level of abstraction on top.
- kowdermeister 9y agoIt added a bunch of other useful things, like getters/setters, constructor function, simpler method descriptions, etc. I don't have a problem with the class title, after all it describes a class of things. When you extend something you really extend the scope of your class, so I still find it adequate. There are differences in OO behavior between Java and C# but I haven't heard people complaining about naming classes classes.
- mileycyrusXOXO 9y agoThat is because Java and C# use classical OO. Javascript does not, it uses prototypal OO. Classes are static contracts that define the state and behavior of object instances. Javascript objects are not contractually obligated to have the state and behavior of a 'class'.
- kowdermeister 9y agoI know that and probably that's why people feel a bit annoyed by the Class keyword. However it's really close to what classes should represent. https://en.wikipedia.org/wiki/Class_(computer_programming) https://en.wikipedia.org/wiki/Class_(computer_programming) first two paragraph matches the JS use case well. > Javascript objects are not contractually obligated to have the state and behavior of a 'class'. I don't know what you mean by "contractually obligated", but if I instantiate a class in JS then it will have a state and behavior I defined in that class. If your problem is that if I change that class later, then the instances will change as well, then yes, it works like that. Calling something a class in my mind is not strictly limited to classical OO, it's just a loose concept different languages implement differently.
- sAbakumoff 9y agoShameless plug - here is my description of how inheritance works in JS. https://github.com/sAbakumoff/Pritle/blob/master/2.Inhertiance.md https://github.com/sAbakumoff/Pritle/blob/master/2.Inhertian...
- oblosys 9y agoThe inheritance example is incomplete, as it should do `Editor.prototype.constructor = Editor;` after `Editor.prototype = ..`. Without this, `david.constructor` will be `User`, and a check like `david.constructor === Editor` will yield false. It's not a huge deal, since the class is usually checked with `instanceof`, but it shows that classes in ES5 are quite subtle.
- amelius 9y agoMozilla has a nice writeup: https://developer.mozilla.org/nl/docs/Web/JavaScript/Inheritance_and_the_prototype_chain https://developer.mozilla.org/nl/docs/Web/JavaScript/Inherit... But note: > The lookup time for properties that are high up on the prototype chain can have a negative impact on performance, and this may be significant in code where performance is critical. Additionally, trying to access nonexistent properties will always traverse the full prototype chain.
- chrisseaton 9y ago> The lookup time for properties that are high up on the prototype chain can have a negative impact on performance, and this may be significant in code where performance is critical. Additionally, trying to access nonexistent properties will always traverse the full prototype chain. This makes no sense to me - surely every non-trivial JavaScript implementation has inline caching, and are not doing any kind of lookup at all in performance critical code?
- hannofcart 9y agoThere are two things I have always wondered: 1) if there are people who come back to using inheritance AFTER doing programming with typed systems that support typeclasses (like Haskell). If so, would you able to articulate why you think inheritance is still a superior pattern? 2) if there were any use cases that could be solved using inheritance that could not be solved more elegantly without it?
- nerdponx 9y agoWhat are typeclasses if not inheritance? As someone without a formal CS background, a type with multiple typeclasses sure looks to me like a class inheriting from multiple interfaces.
- naasking 9y ago> What are typeclasses if not inheritance? Overloading not inheritance. It is closer to the notion of interfaces in traditional OO languages, but it's a mistake to confuse interface implementation as inheritance. This is a common problem with traditional OO languages: they conflate inheritance with subtyping, ie. "Y inherits the behaviour of X" is not always the same as "Y can be used anywhere an X can be used" (subtyping). C++ has "private inheritance" to separate these notions now, but it's a kludge from this initial design mistake. So type classes use subtyping, but not inheritance. Interface implementation is also closer to subtyping than inheritance.
- elnygren 9y agoOOP classes (well, their instances) combine data and functionality into a single "thing" (well, object). Inheritance is nasty because you are not just composing structures of data or interfaces of functionality but both at the same time. When you separate data from functionality into separate "things", composing things out of them becomes nicer. I'm not a Haskell programmer, but AFAIK type classes contain functionality, NOT data (state). Example of what I'm talking about: Clojure's Protocols.
- marcosdumay 9y ago> if there were any use cases that could be solved using inheritance that could not be solved more elegantly without it? There are behavior mixins, that you can look at Django views as an example. There are interface implementations when you want to have a few almost complete standard ones. Both are cumbersome to implement on Haskell. If you make sure to not use any widely used class, you can hack them out of some sets of hierarchical type classes with possible some overlapping. But leaving half-done stuff at your API to be filled by client code is never pretty there.
- mici 9y agoGreat article! A few things have caught my eye: > MyApp.users = MyApp.user || {}; Is this a typo, or intentional? If it is intentional, can someone explain the thought behind it? Also, in some examples Medium (or the author's text editor) seems to have changed apostrophes and quotation marks, this makes the examples harder to try.
- test1235 9y ago> MyApp.users = MyApp.user it's a typo
- elnygren 9y agofoo = foo || {} "mark foo as being foo if it is already defined or as an empty new object {} if it is not" It's just to not accidentally set a already defined foo as an empty object incase foo is already defined. Yeah... JavaScript... Another common idiom: const PORT = process.env.PORT || 8080 The || (logical OR) can be used for setting a default incase the value before it is falsey (null, undefined, false...).
- wruza 9y agoI think he meant users vs user.
- mici 9y agoYes, sorry if that was unclear, I meant plural vs singular.
- rco8786 9y agoWhy the “Yeah...JavaScript...”? The inline || is an incredibly common and useful feature in many languages. Also Op was just talking about the plural/singular difference.
- chriswarbo 9y agoYou're right that we might use "short-circuiting" (AKA lazy) `||` in many languages to use a default value, but it doesn't usually work for variables. For example, in Python: >>> foo or "default" Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'foo' is not defined In Javascript, variables are either bound (e.g. with `var`, as a function argument, etc.) or else they're treated as properties of the `window` object (behaving effectively as globals). This is very unusual, and can be thought of as mixing "data" with "code": the `window` object acts like a reified version of (global) binding environment, which in most other languages is implicit. This has implications for things like the usual distinction between bound and free variables (since we can imperatively add fields to `window` at any point), and hence for things like alpha-equivalence.
- ChrisSD 9y agoThe post seems to be missing composition. One way of doing it is like this: const foo = { someVal: true, methodA() {}, methodB() {} }; const bar = Object.assign({}, foo, { methodC() {} }); // bar now has all the properties of foo, plus methodC
- test1235 9y agoUnfortunately, Object.assign() isn't supported by any IE except Edge. https://docs.microsoft.com/en-us/scripting/javascript/reference/object-assign-function-object-javascript https://docs.microsoft.com/en-us/scripting/javascript/refere...
- nailer 9y agoAnd? Is IE really that common to support, even in enterprises, in 2017? In 2018? In 2036? IE already requires twice the budget - no grid, no flex, no ES8, and maintaining polyfills for all these things (if they're possible) requires maintenance and slows down build time. Even slow moving corporates have AD managed Chrome or Edge since half the products they purchase don't work with IE. The only reason people keep supporting it is people keep making it a big deal if something doesn't work on an unmaintained browser. We have to draw the line somewhere.
- nicoburns 9y agoShort answer is yes. Typically the requirement is IE 10 or 11 now, rather than 6 or 8 as it used to be. So progress is being made.
- nailer 9y agoDo you have someone who'll double your frontend budget, or a situation where IE users that have no other browsers available are so lucrative it's worth doubling your budget, to support them? Frontend is grid, flex, and ES8. Supporting the older equivalents is expensive and it's web developers, caught in a culture of "it's good to support outdated browsers" who are paying the cost themselves, not clients.
- raju 9y agoI did a write up on my blog [1] that attempts to explain how prototypal inheritance works. That said, whenever I teach OOP in JS I have found that a lot of developers do not understand the prototypal nature of JS. Another source of confusion is the `this` keyword, how it works, and consequently how apply/call (which this article uses) affect `this' inside the function (or method). [1] - http://looselytyped.com/blog/2012/08/22/on-prototypal-inheritance-part-ii/ http://looselytyped.com/blog/2012/08/22/on-prototypal-inheri...
- kalekold 9y agoThere are much nicer patterns available to program in an Object Oriented (or rather JavaScript) style. In the following gist I demonstrate (using a simple container library) private, protected and public members (including methods and properties) along with setting each object up for correct prototypal inheritance. https://gist.github.com/nomad-software/78be4f1dd429325c5ebede48237f5120 https://gist.github.com/nomad-software/78be4f1dd429325c5ebed... I would always start with this pattern for readability and maintainability sake and if performance becomes an issue switch to the `Obj.prototype.method = f()` form to avoid duplicating method instances.
- tiuPapa 9y agoWhy symbol.iterator?
- spmurrayzzz 9y agoThis lets you specify the default iterator for an object. Lets you use `for of` on the object just like you can with arrays, maps, strings, sets, and typedarrays.
- nailer 9y agoJust use `class`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... If, for some reason, you need to support old browsers, you can transpile to ES5, ES3, stone tablets and similar.
- amatera 9y agoClasses are just syntactic sugar. They use prototype under the hood. So maybe it's good to know what happen there.
- mikegerwitz 9y agoI wrote a paper on Classical OOP with ECMAScript 3/5 back in 2012 during my work on (the then-not-GNU) ease.js, which goes into quite a bit of detail on how prototypes work: https://mikegerwitz.com/papers/coope/coope.pdf https://mikegerwitz.com/papers/coope/coope.pdf I also provide a lot of information on the implementation of GNU ease.js here: https://www.gnu.org/software/easejs/manual/Implementation-Details.html#Implementation-Details https://www.gnu.org/software/easejs/manual/Implementation-De...
- sametmax 9y agoIt's a good paper, but it really needs a more accessible, much less dense, layout, and some syntax highlighting.
- EdSharkey 9y agoHere's a fun tool for visualizing prototype chains in random code. http://www.objectplayground.com http://www.objectplayground.com
- z3t4 9y agovar MyApp = MyApp || {}; This is an anti-pattern, only use it for polyfilling. Use local variable scope!
- mileycyrusXOXO 9y agoI agree this is an anti-pattern; you get better namespace encapsulation by wrapping your code in an immediately invoked function.
- vilmosi 9y agoI used this pattern when you had multiple files extending the same global state. For example when lazy loading based on the page you're on. It can definitely be overused, which is probably why it's an anti pattern, but it had it's uses before webpack.
- illlogic2 9y agoWhy not https://eloquentjavascript.net/ https://eloquentjavascript.net/? The 2nd edition is still good from my understanding. The author is also trying to update it and is close to their funding goal: https://eloquentjavascript.net/3rd_edition/ https://eloquentjavascript.net/3rd_edition/
- hood_syntax 9y agoThanks for bringing this up, I didn't know they were looking for funding for a third edition.
- Veen 9y agoSome parts of this article are word-for–word copied from the MDN article on Inheritance and the prototype chain [0] For example: > While this is often considered to be one of JavaScript's weaknesses, the prototypal inheritance model is in fact more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model. Although the MDN content is CC-licensed it behoves the author to be clearer about what is quotation and what is original content. [0]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inhe...
- snek 9y agoI feel like this article is 30 steps backward instead of a few steps forward.
- leyth 9y agoPlagiarized, isn’t it?