I'm new to node, but I come from an extensive background in programming. Everywhere I looked (both in tutorials and in production code I've seen around), people overwhelmingly use hard coded strings instead of constants to identify events.
I picked one random example out of npm's most depended upon packages list to illustrate what I mean: the «request» library — why do they emit and consume the «data» event by typing the string 'data'
every time, instead of defining a library-wide constant?
Using constants would be considered good practice in any programming language I'm aware of, and yet node developers seem perfectly content with the hard-coded approach. Why?
Const is the variables declared with the keyword const that stores constant values. const declarations are block-scoped i.e. we can access const only within the block where it was declared. const cannot be updated or re-declared i.e. const will be the same within its block and cannot be re-declare or update.
You can assign event handlers to your own events with the EventEmitter object. In the example below we have created a function that will be executed when a "scream" event is fired. To fire an event, use the emit() method.
As a general rule, you should always declare variables with const, if you realize that the value of the variable needs to change, go back and change it to let. Use let when you know that the value of a variable will change. Use const for every other variable.
Constants are block-scoped, much like variables declared using the let keyword. The value of a constant can't be changed through reassignment (i.e. by using the assignment operator), and it can't be redeclared (i.e. through a variable declaration).
Because it doesn't work for Node.js due to the dynamic nature of JavaScript.
Supposed, a module defined a constant data
. Then it would need to export this, as part of its event-emitting API. Something such as:
const data = 'data';
class Api extends EventEmitter () {
constructor () {
setTimeout(() => {
this.emit(data);
}, 2 * 1000);
}
}
module.exports = {
data,
Api
};
Then, from the outside we would have something such as:
const foo = require('./foo');
const { Api, data } = foo;
const api = new Api();
api.on(data, () => {
// ...
});
Basically, something like this. Now what if you mistyped data
in the beginning? What if instead of
const { Api, data } = foo;
you mistyped:
const { Api, Data } = foo;
It would work, without any error. Data
would just be undefined
. You don't get an error in JavaScript when you try to access an object's property that does not exist, you just get back undefined
.
So, you won't get a compiler error, but now you're emitting (under the hood) 'data', but what you're listening for is undefined
. The constant does not help in any way, you still have to make sure that you do not mistype its name.
And that is in no way better or worse than using a string where you are not allowed to mistype anything.
So, to cut a long story short, constants don't improve the situation, they just lead to more typing and make things more complicated - but they don't solve any real problem.
The short answer is that strings are the best constants available to JavaScript. Hiding them behind some static library variable does not make developers' code any more terse, readable (my opinion), or performant; this is why it never became popular convention to do so.
Your question seemed to imply that hard-coded strings are somehow more "fragile" than using variable constants. However, I would argue that this is only true when the "constant" is expected to change over time, and this is hardly the case for any event type because Node.js libraries (and browser APIs, too) strive to maintain some degree of backwards compatibility between releases. Furthermore, the odds that someone would mistype the variable are the same as when using a hard-coded string, because JavaScript has no compile-time checking for property existence or anything else. No guarantees are made either way.
You might have noticed that JavaScript lacks an enum type, which is annoying in a lot of ways.
Older APIs used integer values to fill this void
in the language, similar to the way people used to do it in Java. For example, Node#nodeType
and XMLHttpRequest#readyState
are both designed to be used like so:
var node = document.createTextNode('')
console.log(node.nodeType) //=> 3
console.log(node.nodeType === Node.TEXT_NODE) //=> true
var xhr = new XMLHttpRequest()
console.log(xhr.readyState) //=> 0
console.log(xhr.readyState === XMLHttpRequest.UNSENT) //=> true
This design pattern came to be seen as archaic over time, because people realized (as they did in Java) how little information a number like 4
gives when printed in an error as opposed to the semantic property name UNSENT
.
The best available alternative in JS was, and continues to be, string values. Modern APIs like fetch()
make use of them quite frequently for passing meaningful configuration options to each method.
The JS community seems to be leaning towards this approach over time, and I think there are some very compelling reasons to do so. However, when you come from a more traditional programming background (especially a familiarity with strongly-typed languages), it might take a little time for you to feel comfortable with this approach. Just keep an open mind and read as many books on the language as you can.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With