JavaScript and Node - Webbprogrammering/websoft GitHub Wiki
We will take the first steps to build a program with JavaScript that we can run with Node.js. We start with the interactive interpreter and then put the code into a file and execute it using the Node.js intepretator.
We also see how we can add functions and classes in separate modules that are included in our main program.
When we are done we have built a small script, our first "Hello World" with JavaScript and Node.js and hopefully we have a basic structure in which we can write our programs.
Write all your code in work/s04/node
.
Prerequisite
You can use one or more programming languages before.
You are familiar with the terminal and you have access to a text editor.
You have installed Node.js, version 12 or later, and the package manager npm.
You have read about the basics of the programming language JavaScript and you know that the website MDN contains information about the language's syntax and semantics.
You know that Node.js has its own documentation for the API that Node.js provides.
The files used in the example can be found under course repository database in example/nodejs/javascript
. You can peek at the source code there, or you can write your own code as you work through the article.
Interpreter
JavaScript is an interpretive language, there is an interpreter who reads and interprets the code. It works similarly in the programming languages Python and PHP.
When you have access to an interpreter you can write JavaScript code that is executed directly.
All browsers have such a built-in interpreter, it is the browser's devtools and console that offer this opportunity. You usually open devtools with F12
in any browser.
But, now we should use Node.js instead.
We can start that interpreter directly in the terminal via the node
command. Then we can type similar commands that I used above. Here you have some commands you can play with.
console.log("Hello World");
for (let i=0; i < 9; i++) {
console.log(i);
}
let a = 1;
let b = a + 1;
console.log(a, b);
If I start node
and then run all the commands above into the interpreter it may look like this in the printout.
$ node
> console.log("Hello World");
Hello World
undefined
>
> for (let i=0; i < 9; i++) {
... console.log(i);
... }
0
1
2
3
4
5
6
7
8
undefined
>
> let a = 1;
undefined
> let b = a + 1;
undefined
>
> console.log(a, b);
1 2
undefined
>
You can cancel the interpreter by pressing ctrl-d
, which often means "end of input".
Sometimes it is easy to quickly open an interpreter to test some code sample, but often you want to save the code in a file instead and run the file via the interpreter.
Save to file
We take the commands I had above and save them in a file interpretator.js
.
Now we can run all the commands in the file with the command node
, it can look like this.
$ node interpretator.js
Hello World
0
1
2
3
4
5
6
7
8
1 2
Great, then we can run our first file with Node.js and JavaScript.
A main function
Let's make a better structure of the program, we put the code in a main function and look at it a bit. I save the code in hello.js
.
This is what my code looks like.
/**
* A sample of a main function stating the famous Hello World.
*
* @returns void
*/
function main() {
console.log("Hello World");
for (let i=0; i < 9; i++) {
console.log(i);
}
let a = 1;
let b = a + 1;
console.log(a, b);
}
main();
If we now run the program with node hello.js
then we get the same printout as before. The difference is that we now have a function that we have named main()
and that it is called at the end of the script.
We now have a starting point for our programs.
Structure the code in main
Let's take a look at the code we just wrote and restructure it a bit. We have a scope here that is block-scope inside the function and we can create the variables at the beginning of the function, it becomes clearer that way.
The function console.log()
is actually for debug printing and there is an equivalent of console.info ()
which is for real printing. They work the same way so it's more of a semantic difference.
To add some code to the code, I update the for loop and save the digit sequence in a variable that I then print, excluding the last comma character, with a built-in function substring()
.
This is what my update looks like, I saved it in hello1.js
.
/**
* A sample of a main function stating the famous Hello World.
*
* @returns void
*/
function main() {
let a = 1;
let b;
let range = "";
b = a + 1;
for (let i=0; i < 9; i++) {
range += i + ", ";
}
console.info("Hello World");
console.info(range.substring(0, range.length - 2));
console.info(a, b);
}
Can you now update the code and add an if statement and a while loop? Do it to show that it is not that difficult.
Can you also search MDN and find out how to print today's date? Try Google on "mdn date".
JavaScript has some built-in objects that offer some built-in methods to call. Check out the JavaScript reference on MDN for a quick overview.
Strict mode
In JavaScript there is something called "strict mode" which is a stricter handling of what can be done in the code. The code becomes a little more brutal and tells off when you write questionable code.
It is common to see the construction "use strict";
at the top of files where you want to make sure that the code is interpreted according to the strict mode.
I update my program and leave a comment at the top of the file and make it look like this.
/**
* A simple test program.
*
* @author Mitt Namn
*/
"use strict";
// Hee is my main function...
I save my updated program in hello2.js
. When I run it it looks the same as before. It's a bit more about order and structure so get into the habit of adding a file comment and "use strict";
.
It is a good idea to add your own name to all files. At least when we have it as schoolwork.
Run your program and see that it works as before.
To learn more about strict mode, read about Strict Mode in MDN.
Creating functions in files
In Node.js we can work with modules that are included into the main files. It makes it easy to distribute the code in different files which makes it easier to test, troubleshoot, maintain and further develop the code.
Let's say we want to create a function that counts from a to b and prints a string with all numbers between a and b.
Such a function may look like below. The function takes two arguments and returns the string. I rename the function to stringRange(a, b)
and put the code in a file stringRange.js
.
Let's take it step by step and make the file with the function and at the same time test it, before we integrate it so it is called from the main function.
Here's how to get my code, including two test cases at the end of the code that shows that it works as expected.
/**
* A collection of useful functions.
*
* @author Mitt Namn
*/
"use strict";
/**
* Return the range between a and b as a string, separated by commas.
*
* @param {integer} a Start value.
* @param {integer} b Last included value.
* @param {string} sep Separator, defaults to ", ".
*
* @returns {string} A comma separated list of values.
*/
function stringRange(a, b, sep = ", ") {
let res = "";
let i = a;
while (i < b) {
res += i + sep;
i++;
}
res = res.substring(0, res.length - sep.length);
return res;
}
console.log(stringRange(1, 10));
console.log(stringRange(1, 10, "-"));
I can now run the file with node
. It will be an easy way to test the file separately.
This is what the printout looks like.
$ node stringRange.js
1, 2, 3, 4, 5, 6, 7, 8, 9
1-2-3-4-5-6-7-8-9
So far, this is just a function in the file.
Create a module of functions
Let's create a module of the function above, a module that exports the function stringRange()
so that it can be used by main()
.
I place these changes in a copy of the file stringRange.js
which I rename to stringRange1.js
.
I need to comment out my test calls so they are not left when I now do a module to be imported by other files.
The first is to define what the module exports. I am now starting the file like this.
/**
* A collection of useful functions.
*
* @author Mitt Namn
*/
"use strict";
module.exports = {
"stringRange": stringRange
};
// Here follows the definition of the function stringRange()...
What I do is create an object module.exports
, objects are constructed with curly brackets {}
. Inside the object I say what to export and there I put my function and export it with the same name. I can export the function with a different name if I want.
If I want to export more functions from the same file, I add what is exported, something like this.
module.exports = {
"stringRange": stringRange,
"exportedAsName": anotherFunction
};
I am now creating a new file import.js
for my main program.
This time I write the code at module level and refrain from using a main function, it works fine too. The code is scoped at the module level, that is, the scope of the file, so there will be no problems with us scraping down into a global scope, otherwise we want to avoid that.
/**
* A simple test program.
*
* @author Mitt Namn
*/
"use strict";
let utils = require("./stringRange1.js");
let res;
res = utils.stringRange(1, 10);
console.info(res);
res = utils.stringRange(1, 10, "-");
console.info(res);
console.log("\nWhat does the imported 'utils' consists of?");
console.log(utils);
Now when I run the program it looks like this.
$ node import.js
1, 2, 3, 4, 5, 6, 7, 8, 9
1-2-3-4-5-6-7-8-9
What does the imported 'utils' consists of?
{ stringRange: [Function: stringRange] }
The last part is the output of the object utils
that contains the imported function. Sometimes, when troubleshooting, it can be helpful to know that even such complex objects can be printed.
So, we have now divided our code into files and we have shown how these files, or modules, can be imported into our main program.
It is a good plan to handle all code, except the main program, as modules that are in their own files.
Creating classes in files
JavaScript offers the ability to create classes that contain methods and member variables.
Classes can be organized into modules where a module is a class. You can then include the classes in their main program, just as we did with functions.
When you then have the class you can create an object with the class as a template. You create an instance of the class.
This is what a class file looks like, with a couple of methods and a member variable. The class is prepared to be a module. I name the file to dice.js
.
/**
* A module for game Guess the number I'm thinking of.
*/
"use strict";
class Dice {
/**
* @constructor
*/
constructor() {
this.dice = null;
}
/**
* Roll the dice and remember tha value of the rolled dice.
*
* @param {integer} faces The number of faces of the dice, defaults to 6.
*
* @returns {integer} The value of the rolled dice min=1 and max=faces.
*/
roll(faces=6) {
this.dice = Math.floor(Math.random() * faces + 1);
return this.dice;
}
/**
* Get the value of the last rolled dice.
*
* @returns {integer} The value of the last rolled dice.
*/
lastRoll() {
return this.dice;
}
/**
* When someone is printing this object, what should it look like?
*
* @returns {string} The value of the last rolled dice.
*/
toString() {
return this.dice;
}
}
module.exports = Dice;
The class represents a dice where you can roll the dice and optionally specify how many sides the dice has. The class remembers the last roll of the dice and you can retrieve its value.
I make a new main program in the file yatzy.js
where I create five dice that I throw and print their value.
/**
* A simple test program importing a class Dice.
*
* @author Mitt Namn
*/
"use strict";
let Dice = require("./dice.js");
// Prepare a dice hand to hold the dices (its an array)
let hand = [];
// Add the dices to the dice hand and roll them once
for (let i=0; i<5; i++) {
hand[i] = new Dice();
hand[i].roll();
}
console.info("Here is the dices you have rolled.");
// Print out the whole datastructure
console.info(hand);
// Join the elements and print out as a string.
// This construct is using the builtin class method toString.
console.info(hand.join());
When I run the program I get a printout that looks like this.
$ node yatzy.js
Here is the dices you have rolled.
[ Dice { dice: 4 },
Dice { dice: 6 },
Dice { dice: 3 },
Dice { dice: 2 },
Dice { dice: 4 } ]
4,6,3,2,4
The first printout prints the entire data structure for my dice hand, it is an array containing five objects and each object has a member variable 'dice'.
The last line is a string created by joining the array elements and using the built-in toString()
function that I defined in the class. It provides a custom string representation of an object and the method can be customized to your needs.
It was a class, like a module. Try to organize your code into functions and classes and divide functions and classes into files and modules.
Finally
You have now received a review of how JavaScript works with Node.js and you have a basic structure in how you can organize your program together with main programs, functions, classes and files with modules.
Hopefully you can build on the basic structure, even with limited knowledge of JavaScript and Node.js. It is always good to have a good foundation to build on.
Create an issue if you have any questions or improvements to this article.