DEV Community

Gökalp Gürbüzer
Gökalp Gürbüzer

Posted on

A Humble Guide to Javascript Development for Backend Developers

A Humble Guide to Javascript Development for Java Developers

Türkçe okumak isteyen okurlar, yazının Türkçesi'ne https://medium.com/@ajitatif/back-end-geliştiriciler-için-javascript-geliştirme-üzerine-fe658ef4b5e7 adresinden erişebilir

Disclaimer: The guide below is simply the way I, as a seasoned back-end developer coding in Java, saw things when coding front-end. It may change from person to person, and if you feel offended in any way; know that it is not my intention, only the way I saw things from my perspective

Foreword: Even though this guide has some Java references to it, I'm pretty sure it will be easy enough to understand for people coding in other OOP languages as well)

As software developers, our goal is to create measurable value out of bits and bytes. As the internet began to spread all over the globe (and also near space), we introduced web development to software development to create dynamic content in web. Since the change is continuous, evolution had to follow. World became increasingly more connected as mobile took
off and more technologies had to be introduced to follow (and competently lead) this evolution.

Where we stand now, in 2020, the software development is split to utilize different groups of methodologies. We have front-end, mobile, AI, data science and so many more perspectives on software development methodologies. And of course, we have back-end developers who utilize the ways of eldest practices of the modern software development.

Thanks to the technological evolution, we now live in a time when most of the boundaries are virtual; and it is most unfortunate sometimes.

Like when you are in a position that even though you do not want to, you have to embrace learning a new discipline like front end development.

In my humble opinion, a software developer's main role and focus should always be adding value to a product; in any way he/she can. It is true that a seasoned back-end developer cannot be as effective as a front-end developer in web development, but certainly there are times that he/she would see through some tasks in the other end.

This is the motivation behind writing this humble guide. As a back-end developer, I had to assume a role of front-end developer for my company and add value in any way I can. There were, and there still are times that I tumbled in my motivation to go through this, not because it was way out of my safe zone; but rather I did not truly want it. Still, I try to keep myself motivated with getting-the-job-done sense, and my promise to myself that I will see this through.

It was pretty hard for me to go this far without a real help; and since I know not everyone is willing to self-motivate themselves as I did to myself, I wanted to make your job easier just a little bit if I can. So that, you give yourself a better chance, and a more effective time trying to actually solve the problems rather than trying to figure out your
way into the code.

Some theory and terminology before we start

As a back-end developer, I always tend to try to figure out how a sh*t works, before actually writing a piece of code. Don't get me wrong, but it's mostly painful to do so when working with front-end code. But it is always good to start with the basics...

Javascript

This is the language, you'll most likely to be coding on the front-end. Its syntax is seemingly based on Java, but those days are way past now, and you'll most likely have to deal with the most unreadable code you've ever seen. But that's not always the front-end party's fault; Javascript is hell to read for someone who's been following OOP
for a time.

Javascript is an interpreted language, meaning that it's not compiled; it's evaluated by the interpreter on-the-go. So you are not likely to find out an error before you hit that line of code. Since there is no compilation, there's no compile-time errors. Some JS developers may say "a-aa" here, but I'll revisit this compilation issue later.

Ecmascript

As if learning Javascript was not enough, you'll have to deal with the Ecmash*t, right? Well no. Good news is, Ecmascript (or shortly ES) is the standard specification for Javascript. So, JS is an ES implementation; but as far as I can tell, both are used interchangibly most of the times, in JS projects.

ES (and JS) has its versions as all living programming languages. ES5 is the oldest one that is used in production and dates all the way back to 2009 (the bad-old IE days). Most projects still use ES5 as the final code for wider browser support. (I'll get to this later)

ES6 and above support classes, which you will feel more comfortable writing, and has a better way in module handling. Most projects I've worked on use ES6 (yes, I said ES5 is mostly used in production, and I also said I'll get to that later).

It's also notable that every successive version of ES is actually a superset of its older vesion.

Typescript

Well that's where it gets strange. Typescript is an object oriented "superset of Javascript" that complies with Ecmascript standards. The typescript code is compiled (using tsc command) into Javascript before running on browser, or Node. So everyting that's written in TS is actually a JS in the production.

Typescript supports various OOP principals - inheritance, type-safety, even interfaces. Would it be good for a back-end developer to start with typescript? Maybe, maybe not. Because it depends on the current situation of the project code. I would not suggest introducing TS to a project that is not already developed in TS.

Transpiling

Alright, remember you read "I'll get to that later" a few times already? Here's the deal: Most Javascript project codes are trans-piled into earlier versions of Javascript to ensure support for a wider range of browsers. So you write your code in ES19, which is the latest version, and use its full advantage. But before the code is run on the browser (or
maybe node), it is transpiled into an earlier version (like ES5 in most cases) and you don't lose the browser support. So basically, there's always (or mostly always) a way to write what you wrote in another ES version.

The most used transpiler is babel, at the moment.

Development Environment

Most development environments in the front-end development consists of these tools:

  • NodeJS and NPM
  • Webpack and its helluva plugins
  • ESLint
  • Gulp/Grunt
  • Jest/Mocha

NodeJS and NPM

What would front-end devs do if it were not for NodeJS... They'd certainly find their way out of course, but Node has become the defacto standard environment for front-end development. NodeJS (or Node) is essentially a server-side technology which utilizes a Chrome V8 engine to interpret and run Javascript code, and is used all-over the globe for microservices. It's pretty lightweight and requires no configuration. And even better, it comes with its own package manager - NPM.

So, why do front-end developers use Node as the environment? First, it's Javascript; this means whatever process your application has to go through, you can mostly just code it in Javascript as if you're coding the front-end. And secondly, NPM is helluva package manager.

All Node projects have a package.json file; which is the equivalent of pom.xml or *.gradle files in Java development. You can find out the dependencies here. But there are a few dependency types:

  • devDependencies, which are not included in the bundles (including test scope of backend code)
  • peerDependencies, which the project/library depends on, but does not include in its package (like provided scope in a pom.xml)
  • dependencies, runtime dependencies which will be included in the bundle

Running the front-end application

More often than none, what you'll need to do just after you checkout a front-end project, is installing dependencies.

npm install
Enter fullscreen mode Exit fullscreen mode

This will make NPM read package.json, download the dependencies and put them in node_modules directory. And most of the times, running below command will open a development server on port either 3000, or 8080:

npm start
Enter fullscreen mode Exit fullscreen mode

If it doesn't, check back package.json file - there must be some hints under scripts. When you find your script run:

npm run <script_name>
Enter fullscreen mode Exit fullscreen mode

Adding dependencies

So you've made some changes to the code but require an extra library to get the thing done. You guessed it right, the package.json file is where to go, but resist your urge to change that file. Instead, get to the terminal and into the source root directory. Then run;

npm install <package_name>[@semVer] --save
# or
npm i <package_name>[@semVer] -S
Enter fullscreen mode Exit fullscreen mode

This will tell NPM to fetch the library, in the version if you asked a specific one, and save it to package.json. So you don't have to run npm i again after you modify the file.

The switch is --save-dev, or -D if you want it to be a devDependency.

There's also a --global or -G switch, which installs the package to a common place where you can use everywhere (as in /usr/local/lib/node_modules in Linux/Mac). This is usually used for CLI packages like angular-cli.

The versioning, is one of NPM's strongest suits. You can tell NPM exactly which versions you support with a notation called Semantic Versioning ("semver" for short). The caret (^) sign is used to say what major version you support. For instance, library:^2.3.1 means your application can work with versions 2.3.1 to 3.0.0 (excluded) of the library. It will be hard to take all in at once, so feel obliged to take a quick look into https://www.npmjs.com/package/semver after you're done here.

Webpack

I'm not sure whether there's a simple answer to the question "What is Webpack?" Event its web page does not say what it is, but it states what it does. It puts your application together and forms a bundle, a package fit for distribution.

Webpack's first responsibility is putting all your source and assets together. It does it by using rule sets (rules) and loaders. A loader is basically a plugin that receives a file and transforms it to fit the bundle (yet, a webpack loader is not a webpack plugin, plugin means something else for webpack) . One of the favorite loaders used in webpack
is babel-loader. Why? Because it transpiles the Javascript source code to ES5 or ES6 while bundling.

When you get a front-end code, you will most likely see at least two webpack files: webpack-bundle.config.js and webpack-devserver.config.js. One is for production code distribution, and one is for local development. Before we get to the differences between them, take a look at one of the webpack config files in your project, and find rules and loaders. You'll most likely find more than one rule and/or loader, but you'll understand what they are now. Also, most
loaders require small configuration snippets in options fields; like babel-loader.

OK, back to webpack devserver: devserver configuration mostly differs in two ways:

  1. It uses devServer, the webpack development server which supports auto-refresh when source code changes.
  2. It creates source maps while making the bundle.

Try to see source maps this way: You're running a code that's been generated (transpiled) from the code you originally wrote, but it's not the same code now, because it went through bundling process. Now you cannot debug effectively, because the code you wrote may have changed and concatenated into a bundle file along with other Javascript files. To match the source code with the code running on browser, a source map file can be generated to map the original code lines with
the generated ones. You can think of them as debug symbols in old C days.

A good example to a webpack plugin, is the DefinePlugin. I rely on this specific plugin a lot when building a multi-tenant application. It simply creates constants at build time (when webpack is doing its work); and since those specific constants do not change, webpack hard-wires the constant to its value while bundling. Then you get a hard-coded
value. Webpack evaluates all the code in the build time and removes unnecessary code in the bundle. A good example could be beneficial here:

Here's a snippet from my webpack config:

plugins: [ new webpack.DefinePlugin({ DEBUG: true }) ]
Enter fullscreen mode Exit fullscreen mode

And here's a code that checks the DEBUG flag:

if (DEBUG) {
  this.children.add(new DebugButton());
}
Enter fullscreen mode Exit fullscreen mode

So, DefinePlugin ensures DEBUG constant is set to true on build time, when webpack evaluates the JS file. And when webpack does evaluate the file, it will transpile the code into something like:

this.children.add(new DebugButton());
Enter fullscreen mode Exit fullscreen mode

Because the DEBUG is always true! And it was set to false, webpack would remove these lines altogheter, since the value is always false. So you don't have to worry about shipping a code that you don't want to be seen.

Building your application

Well, you've been through quite a hell for yourself and finally want to see whether the code will actually run on a server rather than the development server.

Most front-end projects have a build script in package.json and they build mostly into dist or build directories in the project root. If not, you'll have to dig thru package.json for the script, and webpack*.config.js files for bundle output directory.

Task runners

I've seen two task runners while working on front end projects: Grunt and Gulp. And I'm more familiar with Gulp, so I'll tell more about it:

Gulp

Gulp tasks are based on Javascript, so every task you write will be coded in JS. This means you'll have to understand the Gulp API (or DSL, whatever you want to call it).

When working with Gulp, you will most likely hear "vynl" concept. You can think of it as a "Stream" in Java 8+.

You run your gulp tasks with gulp command in terminal if you've installed it globally with npm; or in npm scripts if not. Gulp runs your default task if no other task name is given. And of course, if a task depends on another one, gulp will resolve the dependencies and run them in correct order.

Grunt

Grunt is a newer, and for many a better, alternative task runner. Grunt tasks are also based on Javascript, but it's more "declarative" in design. I didn't mingle with Grunt much as I did with Gulp so I'll leave it here before I screw up :)

Unit testing

Front end environment also has the concept of unit testing and it's quite similar to what we do in back-end. The more often used unit test libraries are Jest, Mocha and Chai. Each of these libraries can be used to assert either a library method output, or an HTML element state in the browser.

There's also a library called Sinon, to mock the dependencies when required.

The package.json scripts will tell you which test library/libraries your project is using and how to run the tests. It's generally npm run test, though.

Even though you're not in back-end development now, make sure you run your tests to success before you commit anything to source control. Some practices differ, some don't :)

Javascript Hell: WTAF moments

It had been a while since I last coded Javascript, before starting my current project. And most of the times I simply couldn't read a few lines of code. Not because the code was not ordered well, but because Javascript went to some other space in syntax quickly in a few years.

let, var, or const?

Well, let and const are relatively new and stick to those most of the times.

let is used to define a variable in a limited scope, as we do in Java. You can think of it like var keyword in Java 11+.

const is the same way, but is a constant. You cannot change the value assigned to a const symbol. And remember, if you do not assign a value to a symbol, it's undefined by default (will get to this a bit later) so you cannot do below:

const a;
a = 12; // error
Enter fullscreen mode Exit fullscreen mode

Lastly, the old var: It's also used to define a variable, but vars are scope-indefinite. You declare a var and it lives on forever. No way, right? Yes way.

Equality

First, I'll tell you about the equals signs. The equality is a little different in Javascript than on Java. Javascript's equals sign (==) is number/string invariant. This means that a string with a value of '9' is equal to a number value of 9. WTF, right? I told you, Javascript is not type-safe and shouldn't expect it to be so. The same goes for != sign, it doesn't check whether the operands are number or string.

Don't get too excited though. This must have meant some problems for Javascript developers as well, so Ecmascript introduced new equality check operators, === and !==. These operators first check for type, and then value of the operands. So '9' === 9 yields false.

This is basically why you'll see triple equal signs flying all over.

undefined and null sisters

In Java, like most of the Object Oriented languages, you cannot use a symbol (a method, field, variable or such) before it's defined. But Javascript is different. Since it bears no type safety, nor compilation; it is possible to ask for a symbol that's not there. And you won't know before you hit that line of code...

Javascript uses a special keyword (or a type, an object if you will) to handle this issue, and it's called undefined. Consider the following example which will result in a is not defined error:

const b = a + 2; // a is undefined
Enter fullscreen mode Exit fullscreen mode

You can check if a symbol is defined with if statements:

let b = 0;
if (a !== undefined) { // != is also OK
  b = a + 2;
}
Enter fullscreen mode Exit fullscreen mode

One more thing: A symbol which is defined but have no assigned value will also be undefined, as in below:

var a;
let b;
const c;
// these have their value as undefined
Enter fullscreen mode Exit fullscreen mode

So, undefined is for symbols that are not yet defined, that do not exist, or have not been assigned a value. But does Javascript have null? Yes, it does. And it means that this symbol is defined, but points to a specific place which has no value. And a null value is not an undefined one.

let a = null;
const b;
console.log(a == b); // false, === yields the same result
Enter fullscreen mode Exit fullscreen mode

A null-check can be done with an if statement:

if (a === null) { // == is also OK
  console.log(a);
}
Enter fullscreen mode Exit fullscreen mode

null/undefined checks

most of the times, developers use if (!symbol) for null checking. This checks against null or undefined. So if the symbol is either null or undefined, the if block will be dwelled into.

The opposite goes as well, for a not-null OR not-undefined check, you can use if (symbol).

I've seen some developers use the double exclamation too, like (!!symbol) but that's simply same as (symbol) and is no longer used.

Objects

Leaving inheritance aside, objects in Javascripts are just a little more than Map<Object, Object>s in Javascript. I'm noting down some most astonishing features (to me, anyway) of a Javascript object below:

  • An ojbect is a map of keys and values.
  • You do not have to declare a field in an object before assigning it.
  • You can access a field of an object either with object.field or object['field'] - this includes methods!
  • There are no private fields in ES standard, even though you can effectively create private fields in other ways.
  • Object equality is checked mostly as done in Java, by reference. Note that primitive types are checked by their value, unlike Java
  • Object.keys() returns an array of fields, and you can iterate over this array.
  • An object merge can be done as follows:
const modifiedDevServer = { ...devServer, ...overwritingDevServer };
Enter fullscreen mode Exit fullscreen mode

This will merge overwritingDevServer to devServer, overwriting any existing field on devServer

Arrays

Arrays are one of the nastiest part in Javascript in my opinion. They are so generic and that makes them both powerful and hard to understand.

First of all, an Array in Javascript is not only an array as in Java. It's also a a Queue, a Stack, and is Stream out of the box. So you can both add to its end or start, as well as remove from end or start.

You can find an array be defined in many ways:

const a = [0, 1, 2];
const b = ['hello', 'javacsript', 2019];
const c = [];
c.push(1, 2, 3);
// There's one more which I will get to in the next section
Enter fullscreen mode Exit fullscreen mode

Arrays can be consumed as streams as in Java:

const esVersion = b.filter((it) => !isNan(it))[0]; // 2019
const multiplied = c.map((it) => it * 2); // [ 2, 4, 6 ]
a.foreach((it) => console.log(it)); // console.log for each item
Enter fullscreen mode Exit fullscreen mode

The => notation is referred to as "fat arrow functions" and is basically same as Java's lambda operator.

Arrays have the indexOf method as in Java, but array manupilation differ in a few ways:

b.splice(1); // [ 'hello', 2019 ]
b.splice(1, 0); // same as above
b.splice(1, 0, 'dear'); // [ 'hello', 'dear', 'javascript', 2019 ]
b.splice(1, 1, 'ecmascript']; // [ 'hello', 'ecmascript', 2019 ]
const firstC = c.shift(); // firstC = 1; c = [ 2, 3 ]
const lastA = a.pop(); // lastA = 2; a = [ 0, 1 ]
c.unshift(0); // [ 0, 2, 3 ] (remember we deleted the first element above)
Enter fullscreen mode Exit fullscreen mode

There are soooo many ways to manipulate an array, but I tried to cover some most common used ones.

Destructuring

Destructring is one of the top reasons why you can't read a Javascript code. It's a collection of notations to rid yourself of long and boring assignment statements, at the cost of readability (for back-end developers at least).

// destructuring an object
const { view } = data; // is the same as const view = data.view
const { view, game, config } = data; 
// is the same as
const view = data.view;
const game = data.game;
const config = data.config

// destructuring an array
const arr = [ 'hello', 'javascript', 2019 ];
const [ a, b ] = arr;
// is the same as
const a = arr[0];
const b = arr[1];

const [ d, ...e ] = arr; // d = 'hello', e = [ 'javascript', 2019 ]
Enter fullscreen mode Exit fullscreen mode

See? It saves us a lot of lines. I still have trouble reading these though...

Methods, in Detail

A method is basically the same in most languages, and Javascript is not an exception. They can return a value or not, they can have parameters or not.

Methods in Javascript are pass-by value as in Java. And again, as in Java, objects' and arrays' values are their references; meaning you can modify an object or an array in a method - yet you cannot change its reference.

There are a few points to note for back-end developers though:

  1. The very same method may/may not return a value. This means a return; is valid along with return value; in the very same method. Though it's not usually seen in codes
  2. A method can have optional parameters. I'll show an example below
  3. A method's signature is only defined by its name. This means a method can be called with more or less parameters than defined. And it also means overriding a method does not restrict you from using more or less parameters than of the superclass.
  4. Since a method's signature is only defined by its name, there is no method overloading in Javascript as-is. Developers tend to check if a parameter is defined to alter the course of the method flow.
  5. A method can be called using exec built-in function. Though it's rarely used (fortunately)
  6. A method may need binding, especially in callbacks. Will get to this in a bit too.

Optional Parameters

dispatchEvent(event, payload: { data: null }) {
...
}
Enter fullscreen mode Exit fullscreen mode

The dispatchEvent method can be called with or without any payload. If a payload is not sent, the default value will be: { data: null } maybe to ensure the payload itself is not undefined.

You can also call this method with more than two parameters, but the function body will not be aware of the remaining parameters.

Binding

Alright, this was one of the hardest for me to digest. Most of the times, and for callbacks, you'll see something like this:

api.onError = this.onError.bind(this, 'api', 'error');
...

onError(category, level, error) {
  this.log(category, level, error);
}
Enter fullscreen mode Exit fullscreen mode

bind is a built-in method that tells the interpreter to bind method parameters to certain values. The first parameter for bind method is bound to this keyword in the callback. So, when you bind this, the this keyword in method is also this in the binding function. This is mostly used to ensure this refers to the same object accross calls in the same class.

The remaining parameters are bound to the parameters of the bound method. For this instance, category is bound to api, and level to error. Remaining parameters are then passed from the api.onError function call, which in this case is error. A call to api.onError can be like:

try {
...
} catch (exception) {
  api.onError({ code: this.getCode(exception), message: exception.name });
}
Enter fullscreen mode Exit fullscreen mode

Understanding the discipline

It may seem a bit hidden at first glance for us, due to Javascript's syntax; but most of the times, you'll find yourself in a big Observer pattern design. Observer pattern is perfect for self-aware, pluggable components and UI developers take the full advantage of it.

A component you see will most likely register itself to a set of event dispatchers, and update their state and content in compliance with the received event.

This will make it harder for you to see who's depending on whom, since there are no explicit calls among components (or the inter-component calls are scarce). You will need to look to event dispatchers and their events, and see which component interacts with which event. For example, a button's enable state will not be changed by the form it's in, but rather the button will change its state when a ServerCallStart event is received.

This ensures the components are safe to plug in and out, without a change in their parents' code. Well, yes; it usually turns out to be a hell of events, and that's exactly what you will have to figure out.

Conclusion

Front-dend development for a senior back-end developer is a tough pill to swallow; but that doesn't mean it's impossible. We, back-end developers generally use more self-describing ways (even obsolete, for some) and the programming languages we use are generally more explicit. I believe that is why it put such a burden on me. And given that my intention is not staying in front-end development forever, it became to feel as the one ring to Frodo.

As I stated earlier, this is a back-end developer's approach to front-end multiverse; and reflects my struggles and opinions on front-end development. I hope it speeds up someone's path when needed.

I don't know yet if this "guide" will be updated or not, I think it depends on my adventure as a front-end developer.

Special kudos to

ozlemg_ image

ÖzlemG

for initial review and support!

Top comments (0)