0

Here is the doc I am confused with.

When using pre-ES6 style constructors

const { Transform } = require('stream');
const util = require('util');

function MyTransform(options) {
  if (!(this instanceof MyTransform))
    return new MyTransform(options);
  Transform.call(this, options);
}
util.inherits(MyTransform, Transform);
  1. Why do we need to check this instanceof MyTransform? As far as I know, as long as we invoke new MyTransform(), evaluation of this instanceof MyTransfrom will always return true. Maybe using MyTransform() to create a Transform instance can be found in many code bases? This is the only reason I could guess.

  2. What is the purpose of util.inherits(MyTransform, Transform); ? Just to ensure that new MyTransform() instanceof Transform returns true?

Thank you for your time in advance!

krave
  • 1,629
  • 4
  • 17
  • 36

1 Answers1

1
  1. MyTransform is just a function like any other function. There's nothing special about it, and so calling it as a function (without new) will work perfectly fine. So what should happen in that case? According to this code: fall through to a constructor call and returning the resulting instance object.
  2. As per the documentation for that function it enforces prototype inheritance, because again, you wrote MyTransform as just a plain function: while you can use new with any function, you didn't write any of the code necessary for proper prototype inheritance so using new will give you a completely useless object. That means either you add the code necessary to set set up prototype inheritance yourself, or you ask a utility function to do that for you.
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • From my perspective, I will assume that `MyTransform` is a constructor and should be invoked with `new` because the capitalized initial character `M`. I am confused with `util.inherits(MyTransform, Transform)` because I will get what I need no matter how I invoke the function - `MyTransform()` or `new MyTransform()`. In the end, `Transform.call(this, options)` will do the job. So I will not say it will give a completely useless object. But yes, it is completely without prototype inheritance. Just wondered does `util.inherits(MyTransform, Transform)` do anything else beyond the inheritance? – krave Feb 05 '22 at 20:38
  • It isn't: as per the JS spec _what you wrote is a function_ and the only thing that makes it a constructor is the use of `new`, and because JS lets you call any function as a function, the code you're asking about in (1) is there _because_ it can be called without `new`, in which case you want to return a new instance anyway. The JS spec says you should be able to call a function as a function, so unless you're using `class` syntax, a function that can be used as constructor should always have a check to see how it's getting invoked. As for (2): you have the link to the official docs =) – Mike 'Pomax' Kamermans Feb 05 '22 at 23:36
  • Not only we have JS spec to reference but also naming conventions to follow. :) – krave Feb 06 '22 at 03:43
  • Not really, no? The answer to _your question_, namely "why is this code here", has a JS spec answer: it's there because functions are functions. Conventions are nice, but they don't _prevent_ people from calling a function that starts with a capital letter as a function. In fact, there are some web APIs with "capital first letter" functions that throw warnings/errors if you try to call them with `new`, and some that throw warnings/errors if you _don't_ call them with `new`. You asked why we need the instanceof check: this answer explains why. – Mike 'Pomax' Kamermans Feb 06 '22 at 04:33
  • So always checking how a function being invoked is a best practice then? The implementation of a function can also go the way to throw a warning/error if it is not called with `new` just like the web APIs you've mentioned. At least this can force a constant usage for a specific function which is single purpose. And yes, my question is answered. Just would like to discuss it further. Thank you! – krave Feb 06 '22 at 07:56
  • 1
    If you write code that you intend for others to work with in _any_ way, always making sure your code accounts for all valid code paths so that it does the _objectively_ right thing even if it runs in a _subjectively_ wrong way is good software engineering. – Mike 'Pomax' Kamermans Feb 06 '22 at 17:39