JavaScript is a very... a very special language. Even when you think you know all its secrets, it can still surprise you.
Like most things in this World, not all of JS' secrets are really useful. In fact most of them aren't, as everything not well-known by others only leads to confusion. Using secret tips and tricks can make your code run faster or maybe even be smaller, but it'll certainly degrade its readability, which is bad for all the potential reader of your code - including yourself from the future.
But, with this little warning out of the way, there's nothing wrong with knowing these secrets - either to be able to read other's cryptic code, or just to show off.
So, without further ado, let's explore 5 of what I think are some of the least-known JavaScript secrets!
Void operator
Did you know that there's a void
operator in JS? What does it do you might ask? Well, it takes an expression (either simple or complex one wrapped in ()
) you provide right after it, evaluates it, and... always returns undefined
.
void 0; // undefined
void "text"; // undefined
void {}; // undefined
void (() => {}); // undefined
// ... you get the point
So, what kind of use-case is there for such a mind-boggling operator? Well, as it turns out - there's one. Believe it or not undefined
can actually be defined!
(() => {
const undefined = "foo";
console.log(undefined, typeof undefined); // "foo", "string"
console.log(void 0, typeof void 0); // undefined, "undefined"
})();
For your information, in JS, undefined
isn't a reserved keyword and can be used as a variable name, effectively overriding the global in a certain scope.
Using the void
operator with random expression (usually 0
) assures you that you'll get the proper unchanged undefined
no matter what.
Optional parentheses
Parentheses are omnipresent in JavaScript - just as in almost any other programming language. But did you know that you don't have to always write them, even in places where you normally do?
Class constructor
The safest place to omit parentheses is in a parameter-less class constructor call. That's right - you can just remove your parentheses, save those 2 bytes of space, and have your code working just fine!
new Date();
new Date;
new Date().getYear();
(new Date).getYear(); // parentheses needed in a different place
IIFE
The second and last (from what I know) place where you can omit parentheses is in IIFEs or Immediately-Invoked Function Expressions.
Typically, to use IIFE you need to wrap the function expression with parentheses and follow that with another pair of parentheses to actually call the function.
(() => {
// ...
})();
(function () {
// ...
})();
But in reality, the wrapping parentheses aren't always required... at least under certain conditions. You can do so with function expressions defined with the function
keyword and only when the IIFE result is being assigned to a variable or when it's preceded by an unary operator (like void
, !
, etc.).
void function () {
// ...
}();
const result = function () {
// ...
}();
The operator or assignment is necessary so that the parser knows that the following function is actually a function expression.
Sadly, this method doesn't work with arrow functions, so if your goal is just to shave off some bytes, I suggest you use the arrow and discussed parentheses after all.
Comma operator
Next up we've got another operator - this time shorter and much more useful! It's a comma (,
) - yeah, you've read that right - which in JS allows you to execute multiple expressions, one by one while "returning" the value retrieved from the last expression. Take a look:
// parentheses are required for proper assignment
const x = (1, 2, 3, 4); // 4
const y = ((() => "a")(), (() => "b")()); // "b"
const test = () => {
return console.log(y), y;
};
test(); // logs "b" and returns "b"
So, as you can see, the possibilities of the comma operator are truly impressive. You can use any expression and pretty much anything else with a little bit of help from the IIFEs. And when you combine that with arrow functions or console.log()
for debugging you've got some impressively-short lambdas or better debugging experience without an additional line of code!
In operator
And while we're in the topic of operators, why not discuss yet another under-appreciated constructs from this category - the in
operator. The only use-case for this operator is to check whether an object contains certain property, like so:
const obj = { a: 1, b: 2, c: 3 };
"a" in obj; // true
"d" in obj; // false
delete obj.a;
obj.b = undefined;
"a" in obj; // false
"b" in obj; // true
So, why would you use a fancy operator, instead of simply checking for a property with an obj[prop]
syntax like a normal person?
Well, there are some specific differences and thus advantages to this approach. First off, it's very convenient to use it for checking for the existence of properties that could hold falsy values. In such cases, typeof obj[prop] === "undefined"
would be required, which is certainly much more verbose than "prop" in obj
.
With that said, the drawback (or a feature depending on how you look at it) of the in
operator is that it returns true
even for properties that have been directly assigned the undefined
value. If this is what you want, then I guess it's fine, but it also means that you'd have to use the delete
operator to delete properties, instead of simply assigning undefined
(which is a bit slower).
Labels
Lastly, we've got a feature that's also rarely used - labels. In JS, labels (like name:
) can be used to effectively name blocks of code and different loop statements (e.g. for
). Having such names assigned to specific parts of your code allows you to later reference these parts for use with statements like continue
and break
.
outer: for (let i = 0; i < 10; i++) {
inner: for (let j = 0; j < 10; j++) {
if (i === j) {
continue outer;
}
console.log(i, j);
}
}
block: {
// Yup, code blocks are a thing
console.log("You'll see this");
break block; // You can break form code blocks when they're labelled
console.log("But not that");
}
Labels are especially useful when dealing with complex control flow within nested loops. Sure, you can use them to break
out of code blocks, but I really don't know why would you do that - just use a functions or even IIFEs like a normal person.
Summary
So, that's my list of some of the most interesting and lesser-known JavaScript secrets. Let me know in the comments how many of these you did/didn't know about before reading this article. Oh, and also - if you like this kind of stuff, I highly encourage you to check out 2 of mine web dev tricks articles, and all the other stuff on this blog where I cover secrets of the Console API, Object API, and many more! Really cool stuff!
For more up-to-date web dev content, follow me on Twitter, Facebook, or through my personal blog. Thanks for reading and happy coding!
Top comments (7)
You said that we can omit parentheses in a parameter-less class constructor. This has gotchas.
You addressed this in your blog post by saying
But why are those parentheses needed at a different place? Why doesn't it just work without parentheses? How will I remember that I need to use this confusing syntax?
instead I could simply do
This is what I am used to and it works.
Not giving you a hard time. There is a reason why you have to put the parentheses at a different place. It should be explained.
There is a reason why this works:
And this does not
It's because
new Date.toString()
is not equal tonew Date().toString()
. *There is an extremely subtle difference. They have different precedence. *Check out: developer.mozilla.org/en-US/docs/W...
new Date.toString()
throws an error because.
has higher precedence thannew Date
so the expression becomes (or it is resolved as)(new (Date.toString))()
. They might look the same but they are evaluated differently!In short, if you would like to invoke the constructor and chain it with a method in the object, the correct syntax is:
I think this post should have a disclaimer at the top: "Please don't use this in production code"
On the other hand, void is useful when you are writing one-liners for hooks in React, because React will throw an error if you don't return a function or null:
useEffect(() => void setTimeout(myfunction, sometime))
void is also useful if you write switch statements like this:
You can reduce a couple of lines using them this way while maintaining the exact behavior:
once I learned about
labels
, I thought they are so awesome, but since, i used them only twice, and one of those times was in go lang βΊWow! I have used the comma operator in python, but I never realized it worked in JavaScript! That's going to save a ton of space in my code now, thanks!
I must agree with above comments. I learned about comma operator, which is nice :-) Ty for the writeup.
Known about iifes and comma operator but in operator looks great similar usage like python, great stuffπ