Learn to Code via Tutorials on Repl.it!

← Back to all posts
JavaScript Code Style
h
Coder100 (12881)

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.

console.log("hey :smirk:"); // <-- good!
console.log("hey :smirk:") // <-- avoid!

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.

const path = require("path");

path.join(__dirname, "./index.html"); // <-- good!
__dirname + "/index.html"; // <-- avoid!

Destructuring

I haven't found much use for destructuring, but just make sure you add spaces:

const {log} = console; // <-- avoid!
const { log } = console; // <-- good!
const { log, debug, dir, table, error, warn } = console; // <-- good!
const { log,
        debug,
        dir,
        table,
        error,
        warn } = console; // <-- consider using no wrap (avoid)

Spacing

This code snippet will tell you everything.

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.

const x = {
  key: "value",
  func: () => this.key // good!
};
x.func(); // => undefined

const y = {
  key: "value",
  func: function () { return this.key; } // good!
};
y.func(); // => "value"

Function Definition

I normally call functions before defining them,

a();

function a() {
  console.log("hi");
}

however, what you want is really up to you. However, never define functions within loops.

for (let i = 0; i < 10; i++) {
  function a() { return i; }
  console.log(a);
}

Mutability

Like rust, make sure to define everything as constant. Only when you need to change mutability should you change the const keyword to let.

const a = 5; // <-- good!
const b = 10; // <-- good!
let g = 10; // <-- good!
g++;

let j = 3; // <-- avoid!

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.

const a = {
  b: 6
};

a.b = 6; // <-- avoid!

If you want objects to be constant, consider also adding Object.freeze()

const a = Object.freeze({
  b: 6
});

a.b = 10; // <-- will silently fail

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.

let a = { b: 6 };
a.b = 10; // <-- good!
a[3] = 10; // <-- good!
a["b"] = 10; // <-- bad!

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

Commentshotnewtop
fuzzyastrocat (1473)

Pretty good tutorial! However:

Also anonymous functions should always be converted to arrow functions.

That's not the case! They have two different roles and arrow functions will not work in some situations due to their this scoping. (Otherwise anonymous functions would have no purpose in the language at all.) Example:

const x = {
  key: "value",
  func: () => this.key
};
x.func(); // => undefined

const y = {
  key: "value",
  func: function () { return this.key; }
};
y.func(); // => "value"

Also, I have to disagree with the first part on arrow functions. For the example:

const bad = () => console.log("bad");

hi();

function hi() {
  console.log("goodbye");
}

For such a trivial example, the function bad is much more succinct. And, such a trivial function should not require a circular dependency, which is the only reason function hoisting is useful. So, I feel that simple one-liners (function = evaluate a single expression) should be expressed as arrow functions, while multiline functions (functions which would require () => { ... statements ... }) should be expressed as function name () { ... }.

Coder100 (12881)

thanks! yeah, ig I didn't consider many edge cases in this tutorial, thanks for pointing those out, I'll add them @fuzzyastrocat

Coder100 (12881)

wait a second why doesn't y require binding? It's an anonymous function right? @fuzzyastrocat

fuzzyastrocat (1473)

@Coder100 It works just fine like that, no binding needed. That's the point of (and my point with) the function () { } expression. (Try it yourself).

realTronsi (871)

@Coder100 why did you make a tutorial on your own opinions -_-

cycle farmer

also for example:

for (let i = 0; i < 10; i++) {
  function a() { return i; }
  console.log(a);
}

defining functions in loops is fine if you want to programmatically define functions for some reason, just that it's not commonly needed

const a = {
  b: 6
};

a.b = 6; // <-- avoid!

why is the avoid there, as you stated afterwards, for objects to be constant use Object.freeze()

const != Object.freeze() at all

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!

purely personal preference, using escape characters is arguably even better in most situations since it allows you to utilize all three

Coder100 (12881)

@realTronsi well the thing is this tutorial was inspired by the standard code style, which is absolutely trash

realTronsi (871)

@Coder100 I just don't know what the purpose of this tutorial is lmao, like it doesn't really teach you anything??? Otherwise decently entertaining ig

fuzzyastrocat (1473)

@Coder100 @realTronsi Note that while systematically defining functions in loops is possible, it's very inefficient.

well the thing is this tutorial was inspired by the standard code style, which is absolutely trash

If it's absolutely trash, why did you make a tutorial on it? Shouldn't you make a tutorial on the best way to try to educate people? (Also if it was inspired by the standard code style then everyone's already doing this, so a tutorial isn't needed...)

realTronsi (871)

@fuzzyastrocat I mean if you want to systematically mass define functions that's the way to go, you don't want to do something like

let i = 1;
functions.push(function(){
  return i;
});
i++;
functions.push(function(){
  return i;
});
i++;
//etc

obviously systematically defining functions will be rarely useful though (I can't even think of a great example atm)

frissyn (404)

I flipped a coin between two spaces and tabs and got two spaces, so that's what we are using.

Bruh -_-

Highwayman (1443)

I flipped a coin between two spaces and tabs and got two spaces, so that's what we are using.

Ahh I see so for every line I flip and...

function main() {
          console.log("prepare");
  console.log("for");
  console.log("the");
  console.log("BEST");
          console.log("CODE STYLE");
  console.log("EVER");
          console.log("AWWW YEAAHH");
}
fuzzyastrocat (1473)

@Highwayman

function  main          ()          {
          console.log("everyone"          );
  console          .log(  "should")  ;
  console.log  ("use"          );
          console.log(          "this"  )          ;
  console          .  log(  "style!"          )  ;
          }
Coder100 (12881)
console	.log("indeed!"  );
	console  .log("Sorry for the bad tutorial!"  )	;

@fuzzyastrocat

Baconman321 (619)

Cool, this is pretty much the same for me (well, the const part no, because a lot of stuff I have to change sooner or later). You forgot hoisting tho. Since JavaScript renders functions first, you don't have to worry too much for functions (and sometimes it's actually very useful), but I recommend staying away from hoisting as much as possible. Also, I recommend putting all the global variables and functions (unless otherwise needed somewhere else) at the top of the program. Makes code cleaner. Also, if you have a function (like a loop) that gets called over and over again, and generate new instances a whole lot, I recommend after you're done with the function to assign it to null, so the garbage collector will collect it.

Coder100 (12881)

@Baconman321 ah yes, I did forget a lot of things lol

Coder100 (12881)

@Baconman321 so um I don't really care for hoisting as I normally split functions into many files, and otherwise they don't get called over and over again.

I rarely put functions inside loops, as I usually use things like .map and .foreach

thanks for your advice!

Baconman321 (619)

@Coder100 Typically you would do looping if you do game design or anything like that because you have to constantly update the canvas (or whatever you are using).

Coder100 (12881)

true, typically you would use recursion to achieve that @Baconman321

fuzzyastrocat (1473)

@Coder100 @Baconman321

typically you would use recursion to achieve that

What?!?! You'd quickly blow the JS call stack if you used recursion for your main draw loop! Typically, you'd use requestAnimationFrame.

Baconman321 (619)

@fuzzyastrocat Yeah. Or it goes really really fast (depends on how you implement it). I think coder100 meant that.

fuzzyastrocat (1473)

@Baconman321

depends on how you implement it

No matter how you implement recursion in JS, it's going to blow the call stack if it's unbounded. There is no tail call optimization in almost all standard JS implementations (barring Safari), so if it doesn't blow the stack then it's not recursion.

Baconman321 (619)

@fuzzyastrocat I have been able to requestAnimationFrame with the function call, so IDK why then. Yeah, the call stack is a big frustration, but quite easy to avoid.

fuzzyastrocat (1473)

@Baconman321 requestAnimationFrame is not recursion, which is why it doesn't blow the stack. (If this isn't what you meant then please tell me.)

Coder100 (12881)

wait is requestAnimationFrame not just recursion but without stack overflows? @fuzzyastrocat

fuzzyastrocat (1473)

@Coder100 No, it's not, since with recursion a function call can return but requestAnimationFrame cannot. (Now, they may be implemented in a similar way, but that's neither here nor there. requestAnimationFrame itself is not recursion.)

Coder100 (12881)

@fuzzyastrocat hm alright
ill read up on its implementation xD

Baconman321 (619)

@fuzzyastrocat I tested.

function e(){
console.log("e");
e();
}
e();

does not return a stack error. It just gets called.... really fast. I don't know if that's recursive, but that's what I've heard.

fuzzyastrocat (1473)

@Baconman321 A few things here. One, try removing the console.log and see if you get a stack overflow. It could be that your console.log's buffer is slow to write and so therefore it's just prolonging the stack overflow for quite a while.

If not, what browser are you using? If you're using Safari, as I mentioned before then that would make sense. Safari is an anomaly in that it performs tail-call optimization — standard JS implementations do not, and therefore that code will not work on most browsers. For example, I tried this on Chrome (removing the console.log so that it will run quicker), and it quickly runs into a stack overflow.

Coder100 (12881)

be patient, the threshold is like 10k or smth @Baconman321

Baconman321 (619)

@fuzzyastrocat I'm using chrome. Still, it goes too fast for me, so I prefer to use requestanimationframe.

fuzzyastrocat (1473)

@Baconman321 Let's not just skip away from this issue though, I'm interested. Do you get a callstack error when you remove the console.log? If not, what version of chrome do you use?

EpicRaisin (238)

@fuzzyastrocat @Coder100
lol I've always just done setInterval('draw()', 1)

Baconman321 (619)

@EpicRaisin That has problems, as it really depends on CPU usage (if the CPU usage is higher, it goes slower because of lag). It's better to use requestAnimationFrame(draw). If not, use setTimeout recursively, as that can be used as a better setInterval.

fuzzyastrocat (1473)

@Baconman321 @EpicRaisin Keep in mind that a recursive setTimeout cannot be relied upon to run forever. See this article for more. So therefore, it is not a better setInterval for the purpose @EpicRaisin described.

Just use requestAnimationFrame and everything will be good :D

(Also @Baconman321 did you see my above comment?)

Baconman321 (619)

@fuzzyastrocat I believe that's TCI (tail call optimization). Yes, I do get a callstack error. Any code whatsoever inside a recursive loop like that prevents a callstack error (according to stackoverflow).

Baconman321 (619)

@fuzzyastrocat What above comment? Also, you can implement a clearTimeout to prevent the callstack from growing too large. Also, I see you like looking into the very depths of programming languages (like how they actually execute code and stuff), I would love to do that too... but it's pretty complex right now :(

fuzzyastrocat (1473)

@Baconman321

Any code whatsoever inside a recursive loop like that prevents a callstack error (according to stackoverflow)

Hmm, I'm confused here. First of all I'm not sure what you mean by "recursive loop", that's kinda an oxymoron, but I assume you mean a recursive call which functions the same as a loop.

Now, if you had tail call optimization you would not get a callstack error. So I'm not sure what your point is there... perhaps you have it backwards, since you say that recursive loops prevent callstack errors when they are really the cause of them?

Also, the above comment was my one asking about continuing the issue. ("Let's not jump away from this though...")

Also, you can implement a clearTimeout to prevent the callstack from growing too large.

But then the loop stops. So now you have to implement some kind of restarting mechanism, and that's just way too complex.

Baconman321 (619)

@fuzzyastrocat Yeah, but it would work. Also, settimeout doesn't work well over 1 hour for some reason (or not at all). 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 (which is like TCI, otherwise IDK from what I heard it's basically something that prevents the call stack from going oof... ok yea I saw TCI doesn't throw an error nvm). I mean recursive loop as in recursive function (sorry). Also, is there a resource that you use to learn this? I would like to read up about this myself...
IDK why anything inside a recursive function prevents a callstack error either...

fuzzyastrocat (1473)

@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.

Baconman321 (619)

@fuzzyastrocat IDK what you mean by writing to a buffer, why does console.log need a buffer? Could it be that it needs to wait a bit before accessing the i/o stream (to the console, or is I/O stream not in a console)? I'm confused...

fuzzyastrocat (1473)

@Baconman321 The "buffer" I speak of is "the place where console.log outputs everything". No, it does not need to wait before writing (that would be waiting for no reason). It's just that console.log is a very slow operation compared to, say, math (console.log("e") will run very slowly compared to 1 + 2 * 3 - 4 / 5 + 6 * 7 - 8). As for exactly why it's slow, that's getting very deep down and I won't explain it here because I think it would just make things more confusing.

Now, everything is relative — this "slowly" is only on the scale of milliseconds (or even microseconds), so you won't notice it if you just console.log once. But since you're console.loging hundreds and thousands of times in your recursive example, the slowness compounds and becomes extreme.

Baconman321 (619)

@fuzzyastrocat Oh ok. I can't find anything explaining what a buffer is used for in console.log... so IDK how I'm gonna know that.

fuzzyastrocat (1473)

@Baconman321 The terminology "buffer" might not be used specifically, but "buffer" itself means "a temporary area of memory to transfer or process data". So while that might not have been the perfect terminology to describe it, I think you can probably get the idea from it: the buffer I'm talking about is where all the things that console.log outputs are stored.

That's something that I think might help a lot — don't necessarily look for things exactly as they are, think about how the connect. For instance, console.log outputs things from your code. But where does it output? Well, there must be some kind of temporary memory that stores the things which console.log outputs. This could be called a buffer, it could be called a stream — it depends on the implementation, but both can convey the same meaning.

Coder100 (12881)

i've never seen buffer outside of C and C++ that's a first for me lol

I know that a buffer can be used for higher level things, but I never considered the dom console a real 'terminal' lol @fuzzyastrocat

Baconman321 (619)

@fuzzyastrocat Yeah. I know chrome has a "preserve-log", so it must be something with more memory than something meant to be very "temporarily" reserved...

Baconman321 (619)

@Coder100 I highly doubt buffer will only be in low (or lower) level languages. In fact, there is Buffer() in nodejs (I believe). A lot of times, you can make your own buffer (in concept).

fuzzyastrocat (1473)

@Coder100 Eh I mean, with modern browsers it's basically a real terminal. Like I said though, depends on the implementation.

fuzzyastrocat (1473)

@Baconman321 Yeah, depends on the browser/implementation. Also Chrome might use a buffer, and then move that memory into more permanent memory (the other purpose of buffers).

potatojs (816)

I haven't found much use for destructuring

are u serious

// you can do this
let a=2;
let b=1;
[a, b] = [b, a];
console.log(a);// 1
console.log(b);// 2

that's enough for me

Kasey00 (0)

Scoping is mucked up in functions. Normal function use this in the scope of the caller, however arrow functions use this in the scope of the callee, a.k.a the sane way https://testmyspeed.onl/.

YashasShah (39)

This will really help!

liammandes (0)

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.
http://slitherio.online/

CodingRedpanda (154)
function          cool()  {
          console  .log("this is a cool tutorial"); // <- i used a semi
  console          .log("keep up the gud work") // <- NOOOOOO!
}

@Coder100

CodingRedpanda (154)

cool!
i will try to do these suggestions! after i finish thinking about how stupid i am for using var instead of const and using tabs instead of 2 spaces :{

Edit: i use semis in python
@Coder100

xxpertHacker (641)

Imho, this is gross:

const y = {
  key: "value",
  func: function () { return this.key; } // eww
};

This is better:

const y = {
  key: "value",
  func() { return this.key; } // good!
};

And this is even worse:

const a = Object.freeze({
  b: 6
});

a.b = 10; // <-- will silently fail

You know what, I think even ESLint has recommended this for the last decade:

"use strict";

const a = Object.freeze({
  b: 6
});

a.b = 10; // <-- will throw a runtime exception
Coder100 (12881)

it throws an exception? did not know that lol
and yeah that first one is @xxpertHacker

xxpertHacker (641)

@Coder100 Yeah, just like this does:

const x = 0;
x = 1;

Like, what are you doing with your life if you reassign a const like that, your entire script should crash.

CursorsDev (422)

in strict mode trying to modify a frozen object will result in an error

CursorsDev (422)

Use path.join, not only is it going to make your life easier, it also allows you to make more complex paths.

er, the actual reason is for all platform support since linux and gnu uses / while windows uses \...

Coder100 (12881)

well yeah obviously, but you can also have more complex paths:

path.join("..", "whatev", "whatev");

@CursorsDev

CodeLongAndPros (1539)

Tabs > spaces
8 wide < 2 wide

CodeLongAndPros (1539)

@Coder100 If you need more than 3 levels of indentation you should refactor anyways

programmeruser (441)

@CodeLongAndPros why would you use spaces? Different editors interpret spaces the same but tabs differently. The only languages I know that care about whitespacfe are Python and Makefile. (also, are you still posting on your blog at https://codelongandprosper90.github.io/?)

Coder100 (12881)
using System;

namespace myProg
{
  class MyClass
  {
    public static void Main(string[] args)
    {
       // bruh
    }
  }
}

@CodeLongAndPros

firefish (888)

@Coder100 Why you always use arrow functions:
The function keyword hoists, so don't use that unless in conjunction with let or use strict;,
and scoping is mucked up in functions. Normal function use this in the scope of the caller, however arrow functions use this in the scope of the callee, a.k.a the sane way.

fuzzyastrocat (1473)

@firefish I don't think it's always 100% sane though. I mean, put yourself in the shoes of a newbie JS programmer. What do you expect this to return:

const x = {
  prop: 1,
  func: () => this.prop
};

It seems like that should return 1, but no, it returns undefined since there's no global prop. However using a function() { return this.prop; } will return 1 as expected, so I would argue that sometimes arrow functions can be more confusing.

alexz6359 (0)

When i look at this. Uhhh I dont understand anything. lol

EIG520 (0)

I'm pretty sure it's better to use tabs because it requires less data-space.

Coder100 (12881)

yes, but usually it won't matter because if you were to go to production, your files would ideally be minimized @EIG520

Baconman321 (619)

Ur so lucky! My tutorials barely get recognition, and I've done a few on things like the file reader api for JavaScript, service workers, and even a PHP tutorial (which I think I'm going to heavily improve). The most upvotes I got was 7 (might have changed, but eh... I doubt it). ;(

Oh and also, you forgot the importance of comments...

potatojs (816)

@Baconman321 you don't have 10.000 cycles lol

Baconman321 (619)

@potatojs Doesn't matter. A 0 cycle person could make a tutorial better than a person with 100k cycles. It doesn't depend on cycles, it depends on skill.

potatojs (816)

@Baconman321 lol i didn't mean that your tutorials are bad, i'm sure they are good enough, but when people see a guy with 100k cycles they upvote immediately :)

programmeruser (441)

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.

I always default to const and never use let with objects or arrays...