JavaScript, unlike many languages, has a very diverse code style. This is probably a combination of not only ASI (Automatic Semicolon Insertion) code style, but also if quotes should be used or not, and type safety.
So without further ado, lets talk about my favorite code style!
Coder100's code style
Semicolons
Semicolons are always required, but beware of ASI.
ASI automatically inserts a semicolon if the program errors. You can read up here
Quotes
Due to how often we're using single quotes, it is very recommended to use double quotes. Also in lower level languages, double quotes signify a string, and I believe it is wise to adapt that into your coding as well.
console.log("Let's go outside!"); // <-- good!
console.log('Let\'s go outside!') // <-- avoid!
However, the exception is when you require double quotes:
console.log("\"That's what she said\""); // <-- avoid!
console.log('"I am Coder100"'); // <-- good!
console.log('"That\'s what she said"'); // <-- edge case
console.log(`"That's what she said"`); // <-- good!
Try to minimize escaping, and if there is no need for escaping, just use ".
Spacing
I have always used two spaces, but sometimes I use 4, and sometimes I use tabs. So which one should we use? I flipped a coin between two spaces and tabs and got two spaces, so that's what we are using.
function hi() {
return 5; // <-- good!
}
function hi() {
return 5; // <-- avoid!
}
Resolving Paths
Use path.join, not only is it going to make your life easier, it also allows you to make more complex paths.
if (true) {
// this is what we want
} else {
console.log("POG UR COMPUTER IS BROKE");
}
function poggers(a) {
return 5;
}
let myArr = [1, 2, 3];
myArr.push(10);
poggers(10);
Arrow functions
I only use arrow functions as lambdas. You should too. It allows you to define functions anywhere!
const bad = () => console.log("bad"); // <-- avoid!
hi();
function hi() { // <-- good!
console.log("goodbye");
}
[1, 2, 3].map(n => n ** 2); // <-- good!
[1, 2, 3].map(function square(n) {
return n ** 2;
}); // <-- uh ew (avoid)
Also anonymous functions should always be converted to arrow functions, except for when binding.
Hopefully you like this code style. Finally I have peaked in good coding practices! In the next tutorial, I will be showing you how to use eslint to enforce this code style :))
@Baconman321 Okay, your understanding of TCO (not TCI) is a little incorrect. Let me try to clear things up:
All that tail call optimization (TCO) does is convert a recursive call into a loop. That's it. So if you have this code:
function x() {
// do things
x();
}
x(); // this is the call
TCO will convert that x(); at the end (the one by the // this is the call comment) into this:
while(true){
// do things
}
This is how TCO prevents the call stack from being overflowed — it converts the function call into a standard loop, which doesn't need a function call and therefore doesn't overflow the call stack.
Uhm, I think TCI is not in use, but basically JavaScript throws a callstack error because you are calling something over and over without doing anything
IDK why anything inside a recursive function prevents a callstack error either
This has nothing to do with it. If you waited long enough, your original example (with a console.log in it) would throw a callstack error. It's just that it takes wayyyyy longer to run because it's having to actually do something (rather than just an empty loop), so it doesn't hit that callstack error as quickly. Because writing to a buffer is incredibly slow, console.loging every loop means it would take a long time. But here's an example that will hit a callstack error quickly, and it does do something in the loop:
let i = 0;
function x(){
if(i % 100 === 0) console.log("e");
i ++;
x();
}
x();
This simply logs e every 100th iteration, instead of every single iteration, so it doesn't do as much buffer writing and therefore isn't as slow. But, it still gets a callstack error.
As for a resource, I can't really give you one since I'm not sure. These are some very fundamental misunderstandings of how function calls work, so I'm not really sure of a good resource to clear it up. Really, if you're determined enough and you have access to google you'll be able to find anything. (It's how I've done it.)
So, just remember: a function call is not "linked" to what's inside a function. If you recursively call a function over and over, it will run into a call stack overflow. It just depends how long it will take.
However, if TCO is being used, then your function call will actually be converted into a loop and therefore it won't run into a callstack error (loops don't use the callstack). Note, however, that not all functions can be converted into a loop: the function call must be at the very end of the function ("tail call") to be actually made into a loop. If you're wondering why, I'd suggest reading up on tail call optimization. A google search should suffice.
JavaScript, unlike many languages, has a very diverse code style. This is probably a combination of not only ASI (Automatic Semicolon Insertion) code style, but also if quotes should be used or not, and type safety.
So without further ado, lets talk about my favorite code style!
Coder100's code style
Semicolons
Semicolons are always required, but beware of ASI.
ASI
ASI automatically inserts a semicolon if the program errors. You can read up here
Quotes
Due to how often we're using single quotes, it is very recommended to use double quotes. Also in lower level languages, double quotes signify a string, and I believe it is wise to adapt that into your coding as well.
However, the exception is when you require double quotes:
Try to minimize escaping, and if there is no need for escaping, just use
"
.Spacing
I have always used two spaces, but sometimes I use 4, and sometimes I use tabs. So which one should we use? I flipped a coin between two spaces and tabs and got two spaces, so that's what we are using.
Resolving Paths
Use
path.join
, not only is it going to make your life easier, it also allows you to make more complex paths.Destructuring
I haven't found much use for destructuring, but just make sure you add spaces:
Spacing
This code snippet will tell you everything.
Arrow functions
I only use arrow functions as lambdas. You should too. It allows you to define functions anywhere!
Also anonymous functions should always be converted to arrow functions, except for when binding.
Function Definition
I normally call functions before defining them,
however, what you want is really up to you. However, never define functions within loops.
Mutability
Like rust, make sure to define everything as constant. Only when you need to change mutability should you change the
const
keyword tolet
.Objects and Arrays
These can be prefixed with
let
if you are going to change them.const
isn't only for the interpreter, it is also for programmers.If you want objects to be constant, consider also adding
Object.freeze()
Same goes with arrays.
Member Access
The final code style is with member access. Try to use
.
as much as you can, but use[]
for variables and numbers.Conclusion
Hopefully you like this code style. Finally I have peaked in good coding practices!
In the next tutorial, I will be showing you how to use
eslint
to enforce this code style :))See you then!
Credits
Special thanks to @fuzzyastrocat and @Baconman321 for pointing out some edge cases
@Baconman321 Okay, your understanding of TCO (not TCI) is a little incorrect. Let me try to clear things up:
All that tail call optimization (TCO) does is convert a recursive call into a loop. That's it. So if you have this code:
TCO will convert that
x();
at the end (the one by the// this is the call
comment) into this:This is how TCO prevents the call stack from being overflowed — it converts the function call into a standard loop, which doesn't need a function call and therefore doesn't overflow the call stack.
This has nothing to do with it. If you waited long enough, your original example (with a
console.log
in it) would throw a callstack error. It's just that it takes wayyyyy longer to run because it's having to actually do something (rather than just an empty loop), so it doesn't hit that callstack error as quickly. Because writing to a buffer is incredibly slow,console.log
ing every loop means it would take a long time. But here's an example that will hit a callstack error quickly, and it does do something in the loop:This simply logs
e
every 100th iteration, instead of every single iteration, so it doesn't do as much buffer writing and therefore isn't as slow. But, it still gets a callstack error.As for a resource, I can't really give you one since I'm not sure. These are some very fundamental misunderstandings of how function calls work, so I'm not really sure of a good resource to clear it up. Really, if you're determined enough and you have access to google you'll be able to find anything. (It's how I've done it.)
So, just remember: a function call is not "linked" to what's inside a function. If you recursively call a function over and over, it will run into a call stack overflow. It just depends how long it will take.
However, if TCO is being used, then your function call will actually be converted into a loop and therefore it won't run into a callstack error (loops don't use the callstack). Note, however, that not all functions can be converted into a loop: the function call must be at the very end of the function ("tail call") to be actually made into a loop. If you're wondering why, I'd suggest reading up on tail call optimization. A google search should suffice.