3 ms·
> 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
by 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.
- adam_arthur 5y agoYou've covered a lot here, but just want to comment on a few things. Factory functions can return objects that have a prototype just the same as "new", there is no inherent reason object constructors should require "new". Given that most of modern, idiomatic JS relies on factory functions, it seems very inconsistent to me to then randomly require invoking "new" to construct an object. Why not simply Set([1, 2, 3])? The noise comes when you have a nested expression with random "new" statements sprinkled in which disrupts the flow. Though I agree that overly complex expressions are an anti-pattern My underlying point is that "new" does not provide any clarity, given that objects are created and returned all the time without "new", so represents an inconsistency in design to me. The presence or absence of "new" basically tells you nothing, except implying that the object likely has prototype functions. But whether or not they are prototyped is more of an implementation detail anyway. I agree with you that most of Temporal has factory functions that obviate "new". To your point on performance of closures, this isn't going to materially matter for majority of use cases. Yes, prototype is more efficient than closures functions, but benefits of prototypes can also be achieved through separation of data and function (singleton utility functions that operate on data). Also syntax sugar for prototype could have been done in an (IMO) more consistent way through some syntax addition to functions rather than creating class. Generally modern userland code shouldn't really be using prototype and "this" anyway, as "this" has tons of footguns in JS, and I would guess majority of developers don't understand the intricacies of "this" when passing functions around, or when using arrows vs classical function declarations.