5 ms·
Anyone know why all new Ecmascript APIs are implemented to be used as "newable classes" rather than ordinary functions? The community seems to be moving toward
by adam_arthur 5y ago
Anyone know why all new Ecmascript APIs are implemented to be used as "newable classes" rather than ordinary functions?
The community seems to be moving towards functional programming/away from classes.. and pretty much every mainstream JS lib is function rather than class oriented (yes, I know classes are simply special functions)
Also adds unnecessary verbosity/more syntax noise.
Is there some internal performance reason for why ordinary functions couldn't be optimized to perform the same as "new"? (Seems like it would be easy to wire the prototype internally to the function, resulting in same semantics).
At this point better to be consistent, but I'd still use a date library just to avoid added verbosity of this native solution. Luxon API reads a lot better than this... Though I'm sure there are reasons a standard implementation needs added complexity (has to cover every edge case)
- oaiey 5y agoBecause most original API (DOM, browser, date, etc) are OO oriented and you stick your standard library in one pattern. And speaking of everyone moving to functional programming: i work with several hundred developers in four continents in a very dynamic envirnment.... And I have not seen a FP advocate. Not that FP is not good (I prefer it where applicable) but mindset/winning/moving towards are words I would not choose when it comes to describing the state of FP and OO in the world. There is a strong influence but not to a degree where an established language changes its core paradigm: see Java, C#, JS, ...
- lnxg33k1 5y agoI would be more prone to say that the original API are something that came up by using buzzwords from growing languages or something else, for sure they're a thing, but like it's not that `new` makes an OOP approach if then you miss inheritance, abstraction, encapsulation and polymorphism, I'm not sure if I am being picky but like if there is just a keyword and nothing else more important to the paradigm then I think it might be confusing for some reader to see something like that statement
- jacksnipe 5y agoReally? I have seen — across many jobs and company sizes and frameworks, over about a decade — a slow but inexorable trend of moving toward FP. Be it in React, where hooks are pushing everything to be functional; to the fact that Monads have started showing up in unexpected places in codebases I’ve worked on; to the fundamentally functional nature of frameworks I have to use every day working at a FAANG. Along the way, until the FAANG, I never met any functional advocates. What I did meet are people who reach for the appropriate functional tools when building solutions; others see the much better solutions they arrive at, and copy.
- LAC-Tech 5y agoReact hooks are procedural programming constructs in a thin veneer of FP. They're about as functional as java was object oriented.
- jitl 5y ago> Is there some internal performance reason for why ordinary functions couldn't be optimized to perform the same as "new"? (Seems like it would be easy to wire the prototype internally to the function, resulting in same semantics). Not particularly -- JIT vms should inline `(() => new Thingy)()` as `new Thingy`. The temporal API provides a lot of nice factory functions that don't need any `new` keyword. Here are the first several examples of the Temporal API all concatenated together with comments removed: Temporal.Now.instant().epochSeconds; Temporal.Now.instant().epochMilliseconds; Temporal.Now.zonedDateTimeISO(); Temporal.Now.timeZone(); Temporal.Now.zonedDateTimeISO('Europe/London'); Temporal.Instant.from('2022-03-04T05:56:78.999999999+02:00[Europe/Berlin]'); Temporal.Instant.from('2022-03-04T05:06+07:00'); Temporal.Instant.fromEpochSeconds(1.0e8); Temporal.ZonedDateTime.from('2025-09-05T02:55:00+02:00[Africa/Cairo]'); Temporal.Instant('2022-08-05T20:06:13+05:45').toZonedDateTime('+05:45'); Temporal.ZonedDateTime.from({ timeZone: 'America/New_York' year: 2025, month: 2, day: 28, hour: 10, minute: 15, second: 0, millisecond: 0, microsecond: 0, nanosecond: 0 }); It seems like the style is to provide these factory functions for almost every use-case except "build me a specific object with these exact values", which you need to use `new Constructor(...)` for. Where is the noise? As a side note, there are concrete performance reasons to use class/prototypes over plain objects or closure-over-hidden state in your own code: - Class and/or constructor functions helps ensure your long-lived objects instantiate their properties in a regular order. V8 has trouble unifying objects with the same properties but different property orders into the same hidden class. Each hidden class costs JIT time and memory space, so minimizing both is nice. You can fix this yourself by forcing instantiation of a value to go through a factory function to normalize property order, but at that point, why not use a class to take advantage of `instanceof`? - Closures over hidden state, eg long-lived closures you would use to implement your own object system over enclosed local variables, cost more than plain objects or class instances. It's more straight-forward to define classes at the top level and wrap them in sugared factory functions than to code-review factories that manually build objects with function properties.
- olliej 5y agoThe problem with relying on JITs inlining things is that you're hoping that an opaque optimizer will come to the same conclusion as you, and historically there have been all sorts of things that spontaneously kill optimizations (I recall there being a point where one of the JS VMs made decisions based on source text size, so comments could change optimization behaviour). There's also all sorts of optimizations you have to depend on to avoid excess object allocations, etc. That said object allocation in JSC and V8 (not sure about spider monkey) is essentially a pointer increment in the common case, so its not exactly expensive.
- bern4444 5y agoThe Temporal API supports a `from` method/function on most of the options - that's very much the functional approach to avoid `new`. Often objects that are class based will expose a `of` or `from` method that abstracts over the `new` keyword `Array.from()` is one example instead of `new Array()` (of course the most common way is `x = [1, 2, 3]` but these other options exist too). This seems fairly functional to me.
- olliej 5y agoWhat do you mean by ordinary functions? There has to be some value to represent a date or time that you pass around, so something somewhere has to return it. If your concern is `whatever(date)` vs `date.whatever()` then the issue you run into is pollution of the global object, which is bad, and frequently not possible due to naming collisions with existing sites (fun story, `let` required some exciting rules to deal with existing content using variables named `let`). Given an inability to add a million new functions to the global object your concise `whatever(date)` becomes `Temporal.whatever(date)`
- 323 5y agoOne good reason is to avoid polluting the global namespace. Otherwise you end up like Mathematica, which has ten thousand or so functions in a single namespace. Or PHP which had (has?) a similar problem (but on a smaller scale).
- adam_arthur 5y agoBut they are on the global namespace. E.g. new Set() is same as new window.Set()
- 323 5y agoRight, but just the constructor. Everything else is a method of Set/Temporal.
- paavohtl 5y agoYou can easily split stuff into modules without getting classes involved. For example the `Math` module which has been in JS basically forever.
- LAC-Tech 5y ago> The community seems to be moving towards functional programming/away from classes.. Anecdotal observation follows: In the span of my career so far it looks like the community moved from imperative programming with a few classes/prototypes sprinkled in there, to imperative programming with a few lambdas and HOF sprinkled in there. I've yet to come across anything significantly functional or OO in the wild, it's still mostly plain old data mutable structures and for loops.
- schwartzworld 5y agoCould be with typescript in mind. TS works well with classes.
- brundolf 5y agoFor one: core types like Temporal and Promise are nominally-identified. i.e. they have an identity that goes beyond just plain objects with a particular set of properties. Oftentimes when people advocate for "plain functions" over constructors in JS, what they're really advocating for is using "plain data"(objects, arrays, primitives, etc). There are lots of good reasons to use plain data in JS - transparency, serializability/deserializability, duck-behavior, etc - but all of that is irrelevant for these special objects. Now of course you could provide a plain function that returns a special, nominally-identified object. But at that point there are basically no advantages over using a constructor (unless you need multiple such functions that take different kinds of arguments); the difference is almost entirely superficial, and constructors do a slightly better job of advertising that their return values are special, and also have precedent and therefore familiarity
- PeterWhittaker 5y ago> Anyone know why all new Ecmascript APIs are implemented to be used as "newable classes" rather than ordinary functions? My guess, and it is only and just that, is because classes, being a newer part of the language, allow the developers and designers to work with a cleaner environment, if you will, avoiding some of the pitfalls that have lingered since ES1. Classes are strict, by default, e.g. I can imagine other benefits, but I am too little caffeinated to articulate them right now. Basically, if one sticks with “the old way”, one runs the risk of one’s users using poor idioms that could cause support grief, but by using the new way one avoids these. Note also that JS classes aren’t pure OO, at least not yet, but are really a different and arguably better approach to function/object (in the JS sense) design and implementation, and that JS is slowly pulling in FP concepts as well.
- newlisp 5y agoBecause they are obsessed in converting JS into Java.
- adam_arthur 5y agoIt's been a bit of a headscratcher for me. I was quite against adding the class keyword to begin with to be honest. Think a lot of that came at the tail end of the "JavaScript is terrible" generational mentality, and the idea was to copy other languages rather than improve what they had independently. To be clear, having additional functionality is fine in and of itself, but now the waters are mudied between function oriented and class oriented approaches, and by and large community seems to be settling on function oriented. In the end, class just created unnecessary fragmentation.
- egeozcan 5y agoI was also happy with prototypes, now when I use them, and I use them rarely, other developers look at my code like black magic and my "others should be able to understand my code" circuit breaker kicks in and I convert it to a class. Is it more readable? Yes. Do I feel like we are dumbing JS down? Yes. Is it worth it when we consider that it helps onboarding hundreds of thousands of developers who want to choose JS? Oh f* yes!
- adam_arthur 5y agoCreating prototypes in general is not necessary for 99% of JS code. Using completely cloned/closured functions will perform just fine for the majority of use cases... And the code there reads very simply... Just a function that returns an object that has functions. Or you can simply separate out functions into utility objects rather than attaching to the specific data object, and performance should be the same as using prototype, without any of the special syntax This model of programming is also much simpler, I'd argue. There is no use of "this", everything is explicit. Especially in JS, "this" sucks because the semantics change depending on whether you use function declarations or arrow functions. And "this" will also reference the caller, so changes if the function gets passed around. Much more obvious to pass an explicit argument to a function than to rely on "this" I do agree there are use cases where prototypes are more useful, performance sensitive games/server code etc. But pretty rare that you can't take one of the approaches described above. And people writing performance sensitive code aren't generally also going to be the newbies. Additionally, could have had syntax sugar for functions that wired prototype without the new/class paradigm, which I argue just adds unnecessary verbosity to the language.
- wruza 5y agoFirst, how do you tell a date from a string or a number easily in the code. Second, the functional thing happens for many reasons, and biggest ones are practical. You can’t DCE (treeshake in hipsterish) classes in js due to highly dynamic semantics, but builtin code doesn’t suffer from that. If it was a non-special type, then maybe it was worth just Foo(), but usually there are some methods attached to it as well as introspection capabilities like toJSON(), toString(), etc. The community seems to be moving towards functional programming/away from classes The community moves to where React is, no real CV choice. Other parts of the community are non-functional, e.g. new WebpackPlugin, etc.
- adam_arthur 5y agoThe community was in the functional space before react. Angular 1 was very function oriented too, and relied heavily on ordinary functions and closure scope rather than class. And then everybody latched onto class in the phase of time between angular 1 and next gen frameworks. Class was something pushed by all the next gen framework authors at the time. Then most next gen frameworks abandoned class, because it proved to be worse in many ways than ordinary functions did. Which was the position I held at the time, and seem to have been borne out. The easy way to tell types apart in ordinary JS is to have consistent naming conventions. If you always name things such that the name implies the type (not Hungarian style, but more natural language oriented), you can infer the type from the name correctly 90% of the time. E.g. always write arrays as plurals, map-like objects as keyToValue, and so on. But the easier answer is to just use Typescript. Inferring type from instantiation point is pretty meaningless anyway. The type needs to be inferrable at the consumption site to be readable, not the production site. Understanding a type only matters in the context where it's used
- chipsa 5y ago> (not Hungarian style, but more natural language oriented) Apps Hungarian wasn't actually a stupid idea. It just got bastardized into Sys Hungarian, which was. Apps Hungarian could tell you whether the string was unsafe (and needed to be sanitized), or safe to use. Or tell you if the integer type you were using was an offset, or a row number, or whatever. It was supposed to give you semantic info that wasn't necessarily obvious from the given type. Sys Hungarian told you that you were using a long pointer to a zero terminated string. But not what the string actually meant. It just regurgitated the type of the object again.