Nodejs/Javascript Course Notes

Author: Dave Kuhlman
Contact: dkuhlman (at) davekuhlman (dot) org
Revision: 1.0b
Date: June 22, 2015
Copyright:Copyright (c) 2015 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the MIT License
Abstract:This document provides notes and an outline of an introductory course on programming in Nodejs and Javascript.


1   Introduction

You can think of this document as an extended cheat sheet on programming in Nodejs and Javascript. Hopefully, it can also serve as notes for teaching a class on Javascript/Nodejs.

Nodejs vs. Javascript -- While much of this document likely applies to and is true of Javascript coding, it is specifically focused on Nodejs. The example code snippets in this document were tested with Nodejs.

2   Basics

2.1   The interactive prompt and interpreter

Start the Nodejs interactive interpreter with $ node:

$ node

Exit from the interactive interpreter with a double Ctrl-C:

(^C again to quit)

On Linux in particular, you may get a more friendly interactive prompt by using rlwrap. Try this alias:

alias node="env NODE_NO_READLINE=1 rlwrap node"

For more on this, see:

In many cases, the interactive interpreter will echo back the values you want to see automatically. In other cases, you can use the console.log method:

> console.log("hello")

2.2   Simple, built-in data types

For some built-in data types there is both a primitive and an object type. This is true of String, Number, and Boolean.

Convert from the primitive type to the object with the constructor for that object, and from the object to the primitive type with valueOf:

> a = new Number(25)
[Number: 25]
> b = new String('abcd')
[String: 'abcd']
> c = new Boolean(true)
[Boolean: true]
> a.valueOf()
> b.valueOf()
> c.valueOf()

Notice that equality and identity tests do not work the same on the primitive type and the equivalent object type. For example:

> a = 'abc'
> b = 'abc'
> a == b
> a === b
> a = new String('abc')
[String: 'abc']
> b = new String('abc')
[String: 'abc']
> a == b
> a === b

2.3   Declaring variables

Use the let and var statements to declare variables. See section let and var statements.

2.4   Starting the interactive interpreter

$ node
> console.log('hello');

For simple debugging (when not in the debugger) and to write output to the terminal window, use console.log(...) in your script. For example:

console.log('my_variable: ' + my_variable);

You may find it convenient to use this:

log = console.log
log('my_variable: ' + my_variable);

2.5   Run a script

$ node my_script.js


$ nodejs my_script.js

2.6   Using the debugger

To run a script under the debugger, do:

$ node debug my_script.js

You can also drop into the debugger at a specific location in your script by adding this line to your script:



  • For help with using the debugger, type help at the debugger prompt.
  • To inspect variables etc, type repl at the debugger prompt.
  • For more help with the debugger, see:

2.7   Help with objects

  1. To learn the type of an object, try:;
  2. In the interactive shell, to learn what properties an object has, type the object, then a dot ("."), then press the <tab> key. Node attempts to do tab completion on attribute names.

  3. When you want to inspect and learn more about an object, also consider using the following:

    console.dir(obj);        // print to console
    util.inspect(obj);       // return a string
    var options = {showHidden: true};
    console.dir(obj, options)
    util.inspect(obj, options)

    There are additional options. For more on this, see:

3   Data types

Additional information about the standard, built-in data types can be found here:

3.1   Numbers

Create numbers using the standard literal representations:

> var a = 123
> var b = 12.34
> var c = 1.23e6
> var d = 1.23e-3
> a
> b
> c
> d

Convert a number to a string with the toString method:

> d.toString()

Convert a string to a number with the parseInt and parseFloat methods. Example:

> var a = '123', b = '4.56';
> parseInt(a);
> parseFloat(b);

3.2   Strings

Use single or double quotes to create a string:

> var a = 'one two'
> var b = "three four"
> a
'one two'
> b
'three four'

Get the length of a string with the length property:

> a.length

Get the character at a position (index) in a string with the array index operator ([n]) or the charAt method:

> a[1]
> a[2]
> a.charAt(2)

Other String properties and methods are described here:

3.2.1   String formatting

Formatting strings in JavaScript are a bit limited. However, for padding a string on the left, you could use:

> var s1 = '12'
> var s2 = day = ("0000" + s1).substr(-4,4)
> s2

The Node.js util module has a format method which has a limited amount of sprintf capabilities. Example:

> util.format('name: "%s"  size: %d', 'redwood tree', 100)
'name: "redwood tree"  size: 100'

And, there are implementations of printf/sprintf for JavaScript. Go to and search for "printf" or "sprintf".

3.2.2   Regular expressions

Learn more about JavaScript regular expressions here:

A few examples:

> /bbcc/.test('aabbccdd')
> /bbcc/.test('aabbcdd')
> /^bbcc/.test('bbccdd')
> /^bbcc/.test('aabbccdd')

3.3   Arrays

Create an array with square brackets or with the Array constructor. Example:

> a = [11, 22, 'abc', 33]
[ 11, 22, 'abc', 33 ]
> b = new Array(4)
[ , , ,  ]
> c = new Array('abc', 11, 22)
[ 'abc', 11, 22 ]
> a
[ 11, 22, 'abc', 33 ]
> b
[ , , ,  ]
> c
[ 'abc', 11, 22 ]

There is a difference between producing an array with [ ... ] as opposed to new Array(). They produce different kinds of objects, even though for most uses they act the same. The use of [ ... ] to create arrays is recommended for most use cases.

Caution: When the first argument to the Array constructor is a number and the other arguments are not, Nodejs produces an exception:

> b = new Array(1, 2, 'abc' 3)
SyntaxError: Unexpected number
    at Object.exports.createScript (vm.js:44:10)
    at REPLServer.defaultEval (repl.js:118:23)
    at bound (domain.js:254:14)
    at REPLServer.runBound [as eval] (domain.js:267:12)
    at REPLServer.<anonymous> (repl.js:280:12)
    at REPLServer.emit (events.js:107:17)
    at REPLServer.Interface._onLine (readline.js:206:10)
    at REPLServer.Interface._line (readline.js:535:8)
    at REPLServer.Interface._ttyWrite (readline.js:812:14)
    at ReadStream.onkeypress (readline.js:108:10)

Access and change an element of an array with the square bracket indexing operator:

> a
[ 11, 22, 'abc', 33 ]
> a[1]
> a[1a[1] = 99
> a
[ 11, 99, 'abc', 33 ]

Note that there are special array types for each of the following:

- Int8Array
- Uint8Array
- Uint8ClampedArray
- Int16Array
- Uint16Array
- Int32Array
- Uint32Array
- Float32Array
- Float64Array

For more information on arrays, see:

4   Statements

4.1   let and var statements

let and var are used to declare variables.

However, let is experimental and may not be supported by your version of Javascript. In particular, I get an error when I use let with Nodejs. And, when I run jshint on my code containing a let statement, I see:

test01.js: line 32, col 3, 'let' is available in ES6 (use esnext
option) or Mozilla JS extensions (use moz).

The scope of a variable declared with let is different from one declared with var:

  • A variable declared with let has the enclosing block as its scope.
  • A variable declared with var has a function as its scope, if it is used in a function; the variable is global, if it is declared outside of any function.

You can also use var to initialize the value of a variable. Examples:

var a;
var b = 123;
var c = new Map();

4.2   Assignment

The standard assignment operators:

var a = 0
var n = 2
a += n
a -= n
a *= n
a /= n

There are also expressions:

x++        // return the value x, then increment it by 1
++x        // increment x by 1, then return the new value

4.3   if statement

The syntax for the if statement is as follows:

if (condition1)
else if (condition2)
else if (condition3)


  • The else if clauses and the else clause are optional.
  • The conditions must be expressions. The expression is evaluated to determine truth or falsity.

The following explains which values count as true and false for the purposes of the if statement:

  • undefined -- false
  • null -- false
  • Boolean -- The result equals the input argument (no conversion).
  • Number -- The result is false if the argument is +0, −0, or NaN; otherwise the result is true.
  • String -- The result is false if the argument is the empty String (its length is zero); otherwise the result is true.
  • Object -- true.

Note to Python programmers -- An empty Array evaluates to true. This differs from Python.

4.4   for statement

The standard form (inherited from C, C++, and Java):

var a = [11, 22, 33];
var idx;
for (idx = 0; idx < a.length; idx++) {
  console.log("a[" + idx + "]: " + a[idx]);

When we run the above, we'll see:

a[0]: 11
a[1]: 22
a[2]: 33

Iterate over the enumerable properties of an object in arbitrary order with for .. in:

var value;
for (value in a) {
  console.log("value: " + value);

When we run the above, we see this:

value: 0
value: 1
value: 2


  • The values that we iterate over are the indexes of the property, and not the elements contained by the array.
  • The order in which that values are received is undefined.

To iterate over the items in an Array, you can use its forEach method. Example:

var data = [111, 222, 333];
data.forEach(function (item) {
  console.log('item: ' + item);

4.5   while statement

The template/syntax for the while statement is:

while (condition) {

Where condition is an expression as in the i statement.

Here is an example:

function test(container) {
  var idx = 0;
  while (idx < container.length) {
    console.log('item: ' + container[idx]);
    idx += 1;

var container = [11, 22, 33, ];

And, running the above produces this:

$ node test03.js
item: 11
item: 22
item: 33


  • The use of the while statement above is effectively the same as the following for statement:

    function test(container) {
      for (var idx = 0; idx < container.length; idx++) {
        console.log('item: ' + container[idx]);

4.6   break statement

The break statement terminates a loop or switch. Here are several examples:

#!/usr/bin/env node

function test1(max, divisor) {
  var idx;
  for (idx = max; idx > 0; idx--) {
    if ((idx % divisor) === 0) {
    console.log('idx: ' + idx + '  divisor: ' + divisor);

function test_break(code) {
  switch (code) {
    case 'a01':
      console.log('process a01');
    case 'b01':
    case 'b02':
      console.log('process b01 and b02');
      console.log('we do not know about code: ' + code);

function main() {
  test1(20, 15);



  • In the first case handled by our switch statement above, the break is necessary to prevent execution from flowing from one case into the next.
  • However, notice how we handle both cases 'b01' and 'b02' with the same code. In effect, when case 'b01' is matched, it flows into the same code as that used for case 'b02'.

You can also use break with a label. See this:

4.7   continue statement

The continue statement causes control to go immediately to the top of the loop. You can also use the continue statement with a label. See here:

4.8   require statement

require is not actually a statement; it's a function. But, this is a good place to discuss it.

Use require in your code as follows:

var utils = require('my_module');
var tools = require('./my_other_module');


  • Node looks for modules in a set of locations/directories that are in the module search path. You can add to this set of locations with the NODE_PATH environment variable. Type node --help at the command line for information. Example (on Linux):

    $ export $NODE_PATH:/my/other/location
  • You can also specify a path to the module in the argument to require. For example, the example above will load my_other_module from the current working directory even when that directory is not in the module search path.

The objects that are made available through require must be declared as exported. Example:

module.exports = {
  fn1: function () {
    console.log('from fn1');
  fn2: function () {
    console.log('from fn2');
  maximum_val: 999,
  label_val: 'a descriptive string',

var fn3 = function () {
  console.log('from fn3');

Then we can do the following:

> var test_req = require('./test_req');
> test_req.fn1();
from fn1
> test_req.fn2();
from fn2
> test_req.maximum_val;
> test_req.label_val;
'a descriptive string'

But, we cannot do this:

> test_req.fn3();
TypeError: undefined is not a function

5   Functions

Two ways to define functions:

function func1(arg1, arg2) { statements }
var func2 = function(arg1, arg2) { statements }

The second form is important because you will often need to pass a call back function as an argument to a function. So, you can do:

var my_callback = function(x, y) { console.log(x + y); };

or, much more commonly:

a_function(function(x, y) { console.log(x + y); });

Here is a slightly more complete version of the above that might help to make this more clear. Note that we can also capture the value returned by the function to which we pass the call back function:

function a_function(fn) {
  // call the call back function.  It requires 2 arguments.
  var result = fn(4, 5);
  return result;

function test() {
  var result = a_function(function(x, y) {
    var value = x + y;
    return value;
  console.log('result: ' + result);


Which produces the following when run:

$ node test04.js
result: 9


Here is an example of a function that uses both local and global variables:

var globalvar1 = 25;
var z = 20;

function test(x, y) {
  var z = 10;
  console.log('x: ' + x);
  console.log('y: ' + y);
  console.log('z: ' + z);
  console.log('globalvar1: ' + globalvar1);

test(11, 22);

And, when we run the above, we see:

$ node test01.js
x: 11
y: 22
z: 10
globalvar1: 25

Javascript functions support recursion, that is, a function can call itself. In the following example, the walktree function recursively calls itself:

function construct_tree() {
  var tree = {};
  var node1 = {};
  var node2 = {}; = 'node1'; = 'node2'; = 'tree_root';
  node1.children = [];
  node2.children = [];
  tree.children = [node1, node2];
  return tree;

function walktree(node) {
  for (var idx = 0; idx < node.children.length; idx++) {

function test() {
  var tree = construct_tree();


When we run the above, we should see:

$ node test02.js

A function can implement a closure. A nested function can capture values from the scope that encloses it. Example:

#!/usr/bin/env node

function makeAdder(addition) {
  var max = addition * 5;
  function adder(x) {
    x += addition;
    if (x > max) {
      x = max;
    return x;
  return adder;

function main() {
  var f1 = makeAdder(3);


Additional notes on functions arguments:

6   Objects and object oriented programming

Think of an object as being a little like a dictionary. It has named attributes that can be "looked up" within the object. We can create attributes in an object and we can assign values to those attributes in the same way that we do to a dictionary. Maybe an object is a lot like a dictionary.

You can create basic objects in a variety of ways. Here are several:

> var a = {}
> var b = {size: 25, color: 'red'}
> a
> b
{ size: 25, color: 'red' }
> var c = new Object()
> c
> c.width = 12
> c
{ width: 12 }

6.1   A simple object definition

Here is an example that defines an object type and then creates an instance of that object:

var TestObj1 = function (name) { = name; = function (label) {
    console.log(label + ': ' +;

function test() {
  var obj1 = new TestObj1('dave');'the name');


If you study the above simple example, you will notice that an object is just a function that assigns values to its attributes. The function accomplishes that by assigning values to this.attribute_name. When we use the keyword new to create an instance, the JavaScript interpreter will create a new object and bind that newly created object as the value of the property this.

6.2   An object definition with inheritance

Here is an example that does the JavaScript version of inheritance:

#!/usr/bin/env node

var Graph = function () {
  this.vertices = [];
  this.edges = [];

Graph.prototype = {
  vertices: [],
  edges: [],
  addVertex: function (vertex) {
  addEdge: function (edge) {
  show: function () {
    console.log('g:vertices: ' + this.vertices + '  edges: ' + this.edges);

var ColorGraph = function (color, vertex, edge) {, vertex, edge);
  // Alternatively, the following also works.
  //Graph.apply(this, [vertex, edge]);
  this.color = color;

ColorGraph.prototype = Object.create(Graph.prototype);
ColorGraph.prototype.color = 'no-color'; = function () {
  console.log('gc:color: ' + color);;
  // But the following line also seems to work.

function test() {
  var g = new Graph();
  var cg = new ColorGraph('red', 20, 30);


When we run this code, we see:

$ node test05.js
g:vertices: 1,4  edges: 2,5,15
gc:color: red
g:vertices: 20  edges: 30
gc:color: red
g:vertices: 20,0,4,8,12,16  edges: 30,0,3,6,9,12


  • Notice how we create the methods in separate objects and assign them to the .prototype of the class (actually the constructor function). We do this so that when we create instances of this class, we will not create separate and multiple copies of the methods (functions) for each instance.
  • We use Object.create(Graph.prototype) to create the prototype for our derived class because it effectively "copies" the prototype of the base class.

Here is another example with more classes and another level of inheritance. This example implements the following hierarchy of object types:


Here is the code:

#!/usr/bin/env node

var Employee = function (name, dept) { = name;
  this.dept = dept;
Employee.prototype = {
  name: '',
  dept: '',
}; = function () {
  console.log('name: ' +;
  console.log('dept: ' + this.dept);

var AdminEmployee = function (name, dept, responsibility) {, name, dept);
  this.responsibility = responsibility;
AdminEmployee.prototype = Object.create(Employee.prototype);
AdminEmployee.prototype.responsibility = null; = function () {;
  console.log('responsibility: ' + this.responsibility);

var ITEmployee = function (name, dept, specialty) {, name, dept);
  this.specialty = specialty;
ITEmployee.prototype = Object.create(Employee.prototype);
ITEmployee.prototype.specialty = ''; = function () {;
  console.log('specialty: ' + this.specialty);

var SysAdminEmployee = function (name, dept, specialty, system) {, name, dept, specialty);
  this.system = system;
SysAdminEmployee.prototype = Object.create(ITEmployee.prototype);
SysAdminEmployee.prototype.system = ''; = function () {;
  console.log('system: ' + this.system);

var ProgrammerEmployee = function (name, dept, specialty, language) {, name, dept, specialty);
  this.language = language;
ProgrammerEmployee.prototype = Object.create(ITEmployee.prototype); = function () {
ProgrammerEmployee.prototype.language = 'none';;
  console.log('language: ' + this.language);

var Roster = function () {
  this.employees = [];
Roster.prototype = {
  add: function (empl) {
  show: function () {
    var idx;
    for (idx = 0; idx < this.employees.length; idx++) {
      console.log('-------------- Employee record --------------');

function test() {
  var employees = new Roster();
  var empl;
  empl = new Employee('Abel', 'Admin');
  empl = new AdminEmployee('Albert', 'Communications', 'payroll');
  empl = new ITEmployee('Betty', 'Communications', 'network');
  empl = new SysAdminEmployee(
      'Craig', 'CalifOffice', 'repairs', 'Linux');
  empl = new ProgrammerEmployee(
      'Daniel', 'CustomerService', 'office apps', 'Python');

function main() {


When we run the above, we see:

$ node test09.js
name: Abel
dept: Admin
name: Albert
dept: Communications
responsibility: payroll
name: Betty
dept: Communications
specialty: network
name: Craig
dept: CalifOffice
specialty: repairs
system: Linux
name: Daniel
dept: CustomerService
specialty: office apps
language: Python


  • In this example, we only used single inheritance, because JavaScript does not support multiple inheritance, and because single inheritance is less confusing than multiple inheritance.

7   A few common tasks

7.1   Reading and writing files

For synchronous operations use fs.readFileSync and fs.writeFileSync. Example:

var fs = require('fs');
var data = fs.readFileSync('source.txt');
fs.writeFileSync('sink.txt', data.toString().toUpperCase());

For asynchronous operations, use fs.readFile and fs.writeFile. Example:

var fs = require('fs');

function test(infilename, outfilename) {
  fs.readFile(infilename, function (err, data) {
    if (err) {
      throw err;
    data = data.toString().toUpperCase();
    fs.writeFile(outfilename, data, function (err) {
      if (err) {
        throw err;

function main() {
  var args = process.argv,
    infilename = args[2],
    outfilename = args[3];
  test(infilename, outfilename);


We can also use readable and writeable streams. Streams are an abstraction. So, there are a number of different concrete objects that implement the same interface and, therefore, can be used in similar way. Example:

var fs = require('fs');
var t2map = require('through2-map');

function test(infilename, outfilename) {
  var instream = fs.createReadStream(infilename),
    outstream = fs.createWriteStream(outfilename);
  instream.pipe(t2map(function (data) {
    return data.toString().toUpperCase();

function main() {
  var args = process.argv,
    infilename = args[2],
    outfilename = args[3];
  test(infilename, outfilename);



  • Note the use of through2-map in this example. When using streams, the module through2-map may be useful and convenient. You can install it using npm.

7.2   File names and directories

The path module has methods that are useful. Examples:

> var path = require('path')
> path.parse('/path/to/my/file.txt')
{ root: '/',
  dir: '/path/to/my',
  base: 'file.txt',
  ext: '.txt',
  name: 'file' }
> path.extname('/path/to/my/file.txt')
> path.basename('/path/to/my/file.txt')
> path.sep
> path.join(path.sep, 'path', 'to', 'my', 'file.txt')

For more information see:

Process a list of files in a directory with something like the following:

> var fs = require('fs')
> fs.readdir('.', function (err, filenames) {filenames.forEach(function (name) {console.log(name); })})

For more information see:

7.3   JSON -- JavaScript Object Notation

You can convert to and from JSON. Example:

> var a = {aa: 11, bb: [22, 33, 44], cc: 'abcd'}
> var b = JSON.stringify(a)
> b
> JSON.parse(b)
{ aa: 11, bb: [ 22, 33, 44 ], cc: 'abcd' }

8   Writing servers/applications with NodeJS

You can find help with learning how to implement network servers in the tutorials located here:

In particular, exercises that create network servers are at LearnYouNode:

9   jQuery

jQuery is a JavaScript library intended to be used in Web pages. It gives us easy access to the DOM (document object model) and the (HTML) elements in it, enables us to modify those elements, and has a convenient model for responding to events from elements in that DOM. Here are several places where you can learn more about jQuery:

In this document, we are mostly interested in JavaScript itself, rather than creating Web pages. However, the development tools available within a browser (such as Firefox, Chromium, Opera, etc) can be very helpful. Those tools enable us (1) to step through code using and interactive debugger, (2) to inspect objects and values as we go, (3) to view output sent to the console with console.log(msg), and (4) to view the web page as it changes.

So, here is a simple HTML template that you can use to test snippets of JavaScript code:

<!DOCTYPE html>
<!-- If you use a local copy of jQuery, replace this with the
     path to that file.  You can find jQuery at
<script src="JQuery/jquery-2.1.4.js" ></script>
<script src=""></script>
$(document).ready(function() {
    $("p").click(function() {
      var content = $(this).text();
      console.log('clicked: "' + content + '"');
      var content1 = content + content

var TestObj1 = function (name) { = name; = function (label) {
    console.log(label + ': ' +;

function test() {
  var obj1 = new TestObj1('dave');'the name');
  <!-- Provide something to click on so we can trigger our code
  <p>click me 1</p>