JavaScript Notes

Software tools –

high and low level languages, editors, compilers, interpreters, IDEs

Computers

This text is about programming computers. That includes mobile phones, tablets, laptops and desktop computers, and also devices like smart TVs, automobile engine management systems, printers, scanners and so on.

To think about all these different types of devices at the same time, it is useful to have an abstract model of computing. This means to just think about what they have in common, and leaving out the details of exactly how they work.

One abstract model is a Turing machine (Alan Turing, 1936). This has a ‘tape’, on which symbols are written, and a head which can read and write on it. The tape head is connected to a finite state machine. This is something which switches from its current state to another state, depending on its current state and the symbol under the tape head. We can think of the finite state machien as being like a processor and memory, and the tape is like a file store on a hard disc or cloud storage.

A different abstract model is the lambda calculus (Alonzo Church and Stephen Kleene, 1935). This sees a computer as a function, like a mathematical function such as square root. This has input (say, 9) and produces output (3). The lambda calculus is a totally general version of this. It can be proved that Turing machines and the lambda calculus are equivalent.   

We will take a computer to be more practical. The stored program concept has the program, as a sequence of instructions, held in memory while it is being executed, alongside data also in memory. The memory is organised as a set of cells or locations, each with a different address, and holding one byte ( eight bits, binary digits, 0s or 1s, like 0110 1010 ).  That means all program instructions and all data must be encoded as binary patterns.

The computer also has a processor. This can read program instructions out of memory, decode and execute them, and move on to the next instruction. A processor has a small number of registers, which are very fast storage locations inside the processor, which program instructions often refer to.

In practice a computer may have a multi-core processor, containing several processors which can run in parallel, and a maybe a graphics processor and memory just used for graphics.

Low and high level programming languages

The instructions the processor can recognise and execute are called machine code or native code.  Different processors have different machine code instruction sets. Typically a processor has around 200 instructions it can execute.

Those instructions are represented by binary patterns – so 1011 1010 might mean ‘add’.

It is possible in theory to write programs in machine code, but is totally impractical. Instead we use languages like JavaScript or C, which use more natural language words like ‘while’ and ‘if’.

Low and high level languages are a range. At the low level end we have native code and assembler, with instructions about processor registers and memory locations. At the high level end languages like JavaScript use a small set of English words as instructions (typically aound 50 words).

Compilers and interpreters

Only native code actually executes in a computer. So the high level language rogram we write must be changed into machine code. One software tool to help with this is called a compiler. This is software which reads in a high level language program (called source code) and outputs the equivalent, changed into a different language (called object code). Usually object code is native code. An example of a compiler in common use today is gcc,  the open source Gnu Compiler Collection.

So the use of a compiler is a two-stage process:

1. Compile the source into native code

2. Execute the resulting object code.

Different things can happen at these two stages. The first is called compile-time. The second is run-time.

Another type of tool is an interpreter. This inputs a high level language program (often called a script) and executes a loop:

repeat for each instruction

.. work out how to do it

.. do it

until end of script reached

Different languages are usually treated in different ways. C, for example, is usually compiled to native code, while JavaScript is usually interpreted. But in theory any language can be interpreted or compiled.

Editors

Source code is just text – like for example

var x=5;

var y=4;

var z=x+y;

console.log(z);

We need a way of ‘writing’ this text, and also reading it in from a text file, editing it, and writing a new version to a text file. A software tool to do this is called a text editor.

This is different from a word processor. This includes formats like font styles, size, colour, layout and so on. But source code is text, and nothing else.

Text editors are pretty simple applications.On Windows, notepad is a text editor provided with the operating system. Other options are notepad++ and atom and textpad. Linux text editors include nano and geany and gedit.

IDEs

An IDE is an ‘integrated development environment’. When programming we need to:

 An IDE is a software tool which makes for quick and easy access to an editor and a compiler. It will also offer other features like

Common IDEs are Netbeans, Eclipse, Intellij IDEA, Microsoft Visual Studio and Android Design Studio.

JavaScript tool setup

JavaScript code is usually interpreted, using the interpreter insode a web browser. So all we need is a text editor and a web browser, used like this:

  1. Use a text editor to edit the JavaScript source code

  2. Open it as a local file in  a web browser (how depends on the browser, but its CTRL-O on Chrome, Firefox and Opera)

  3. Go back to step 1 if needed

 That’s all folks.

The DOM

The DOM is the Document Object Model.

Usually JavaScript runs in a browser, and needs to refer to the displayed web page. The DOM is how it can refer to the web page and the parts in it. We have a chapter about the DOM later.

Security and the sand-box

JavaScript code is usually embedded in a web page and executes when the user downloads it from the web. The user has no idea what the code is doing.

This is therefore a potential security risk. Suppose the code reads all the personal data stored on the user’s device and sends it somewhere? Or deletes everything on the user’s device?

To prevent this, JavaScript in a browser runs in a restricted sand-box. It cannot read or write anything on the user’s machine, on any drive or in the memory of other processes. An exception to this is html5 ‘local storage’, but this is restricted to storage for that particular domain, and it cannot send any data from it anywhere elsewhere.

This is not true for JavaScript executing outside the browser. For example, node.js can read and write local files.

Operating systems and application software

An operating system (OS) is software which makes the hardware usable. It does things like user authentication through paswords or otherwise, allowing rograms to run at the same time, handling memory so processes do not affect each other, organise the fiel system and so on. The common OS at the moment are Microsoft Windows and versions of Linux like Ubuntu and Debian, and Android on mobile devices.

Application software runs on an OS for specific purposes, like a word processor or spreadsheet or web browser.

 

 

 

 

 

 

 

 

 

 

 

 

Basic ideas

– variables, assignments, expressions, I/O, conditionals, loops

How to write JavaScript practically

Usually JavaScript code is executed by the interpreter in a web browser, and it is embedded in a web page. So you need to:

For example, here is a web page being edited in Visual Studio:

Image12

The JavaScript is lines 10 to 15. The rest is html for the web page. This is saved as temp.html

Then this is opened locally in Chrome:

Image13

 In Chrome the console is opened by clicking the 3 dots top right, then More tools.. Developers tools.

Do not attempt to type out that file. Copy-paste this:

 <!DOCTYPE html>

 

<html>

 

<head>

  <title>The title</title>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <script>

    'use strict';

 

    var x = 4;

    var y = 5;

    var z = x + y;

    console.log(z);

 

  </script>

</head>

 

<body>

 

</body>

 

</html>

Most of this is html – hypertext markup language. This is made of elements, which start and end with tags, like <head>..</head> and <title>..</title>. One of these tags is <script>, enclosing JavaScript code. When the browser sees this, it executes it using the interpreter. We will usually just list the JavaScript, like

    'use strict';

 

    var x = 4;

    var y = 5;

    var z = x + y;

    console.log(z);   

but in fact this needs to be embedded in the html web page.

Variables

A variable is

Assignment statements

Programs mostly consist of statements, which are usually either instructions to the computer to do something, or supply some information needed.

In many languages (Java, C, C++, JavaScript) statements should be separated by a semi-colon – a ;

In JavaScript, if the programmer misses out the ; the interpreter will in effect insert it automatically. This is an unfortunate feature, since it encourages a habit which will not work in other languages.

One type of statement is an assignment, such as

var x=4;

This assigns the value 4 to the variable x. The direction of data movement is right to left – the 4 moves to x. Any previous value of x is lost.

In an expression, the terms left-hand and right hand side are used. In this example 4 is the right-hand side and x is the left. There are rules about what can appear on the left and right. These are mostly logical. For example

var 4 = x;

would mean take the value of x, and store it at 4. In other words take the value of 4 and change it to x. But this makes no sense, since we cannot change the value of 4.

Expressions

Often the right hand side is an expression – something for the interpreter to calculate the value of. For example

var z = x+y;

is an expression. The interpreter will calculate the value of this, at run-time.

An expression consists of operators and operands. An operator is something to do. Common ones are + for add, - for subtract, * for multiply and / for divide. The precedence rules – which to do first – are the same as in normal maths – sometimes called BODMAS. So for example 3*4+5 is 12+5 = 17, and not 3*9 = 27. We can use round brackets, so 3*(4+5) is 27.

We should not write assignments like

var z=3*(4+5);

but instead should write

var z=27;

The first version just wastes time and computation.

Input and output

Output means sending the results of a program somewhere. For JavaScript, that often means changing a web page and display a result that way – maybe as a number, but maybe changing the colour of something, altering page layout, mving to a new page location or whatever. Output might go to a printer, or to a file on local storage, or cloud storage or whatever.

 JavaScript output often uses the DOM. For simplicity we will start by outputting values to the console, by statements like

console.log(x,y,z);

 Input includes

and so on.

Usually input in JavaScript comes through the DOM.

Input and output is usually referred to as I/O.

OS usually provide the ability to do I/O/ In other words they contain code to do I/O, and that code is available to application software. Most OS treat I/O as through files, and this includes keyboard in text out. So the OS sets up a standard input file usually connected to the keyboard, and output connected to the screen for character output.

Conditional statements

A conditional statement executes another statement only if an expression is true. This is often called an if statement..

Loops

..

 

 

 

 

 

 

Debugging

Debugging is the process of finding and correcting errors in code. The ability to debug is just as important as the ability to write program code.

There are three broad classes of errors.

The first are errors that break the rules of the language – that is, syntax errors. These should be detected by the compiler or interpreter.

The second are errors that occur at runtime. These might produce an exception,

The third are logic errors, soemtimes called algorithm errors. The method being used in the program is incorrect, and does not do what the program is intended to do.    

Strict mode

In the earliest days of the web, when Javascript was new, code was being produced by people who were new to programming, so errors were very common. So most interpreters would attempt to ignore syntax errors. This is non-strict mode. In strict mode, the interpreter will signal errors.

A consequence of non-strict mode means that errors can be difficult to find. For example, in non-strict mode variables do not need to be defined. But that means if you mis-type a variable name, a new variable just appears. For example

var singleton=false;

..

simpleton=true;

We meant to have a variable named singleton. But we mis-typed it as ‘simpleton’. Now we have another variable named simpleton, and singleton is still false. There is no error message, and it will take us an hour to spot the mistake.

You tell the interpreter to use strict mode by putting ‘use strict’ as the first line in a script. So

         <script >

            'use strict';

            x = 1;

            console.log(x);

        </script>

works as expected – x is not declared so we get a syntax error.

But

        <script >

            x = 1;

            'use strict';

            console.log(x);

        </script>  

in fact is not in strict mode, and we get no syntax error.

You can also set a individual function to be in strict mode. But a better option is to make the entire script be strict.

Modules (see later) are automatically in strict mode.

Debugging strategies

1. Read the error message – what the error is and where it is. Try to make sense of it.

2. Be clear what should happen. What is the correct output, and how does it differ from the actual output?

3. Print debugging. This means outputting important variables at key points in the code. For JavaScript, this probably means console.log

4. Comment debugging. The idea is to find which part of the code causes the error, without deleting it, then re-typing it. So you comment out sections of code /* like this */ If the error remains, that code was OK, and you can remove the commenting

5. Copy the error message and Google it.

6. Use a debugger. This is software which lets you do things like stepping through codeone statement at a time, adding ‘watch variables’ to display values, run to cursor which means run at full speed up to a point in the code, and so on. Not commonly available in JavaScript.

7. Does the code actually execute? You hav esoem code which does not do something – is it because it simply never executes? Put a console.log or an alert just to see if that code executes.

 

 

 

Modularity

Managing scale - decomposition

One of the tasks of software engineering is managing scale. Dealing with 10 or a 100 lines of code is not a problem – but a million is.

The usual solution is some type of decomposition – splitting a problem, and the code which solves it, into smaller problems. This means that

JavaScript functions

How code is split depends on the language. In JavaScript the standard technique is to use a function. For example:

 

<!DOCTYPE html>

 

<html>

    <head>

        <title>The title</title>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <script>

            function average (a,b)

            {

                var result = (a+b)/2;

                return result;

            }

 

            var x=3;

            var y=4;

            var z=average(x,y);

            console.log(z); // outputs 3.5

        </script>    

    </head>

    <body>

 

    </body>

</html>

Control flow

In other words, what happens when? Execution does not start at the function definition, which is

            function average (a,b)

            {

                var result = (a+b)/2;

                return result;

            }

It starts at the first code not in a function, which is

            var x=3;

At

            var z=average(x,y);

the function is called. That makes execution switch to the start of the function, and it carries on until it hits the return. The return instruction means ‘return to where you were called from’. Finally it does

console.log(z);

Data flow into the function

Data values are passed into a function through parameters – sometimes called arguments. So in

            function average (a,b)

the function named average has two formal parameters, named a and b. When the function is called, at

var z=average(x,y);

the actual parameters are x and y. These are matched up in order So the value of x (3) is copied to a, and the value of y (5) is copied to b.  So inside the function,

                var result = (a+b)/2;

result will work out to be 3.5.

Data flow out

A value can flow out using the return statement. So

                return result;

does 2 things. It returns control flow to where the function was called from, and it passes back there its value – in this case, 3.5. Then in

var z=average(x,y);

the returned value is assigned to variable z.

We can call a function many times, such as:

            var x=3;

            var y=4;

            var z=average(x,y);

            console.log(z);

            z=average(8,9);

            var another = average(z,x);

control and data flow works as described.

Pass by value

Look at this code:

            function average (a,b)

            {

                var result = (a+b)/2;

                a=99;

                return result;

            }

 

            var x=3;

            var y=4;

            var z=average(x,y);

            console.log(x, z); // outputs 3, 3.5

Here the value of the parameter a is changed inside the function. But this does not alter the matching actual parameter, x. Why not?

These are numbers, and in JavaScript, numbers are primitives. And primitives are ‘passed by value’. That means a copy is passed to the function. So a=99 alters the copy – it does not change the original, x.

Scope

Look:

            var x=3;

            var y=4;

 

            function average (a,b)

            {

                var x;

                var result = (a+b)/2;

                x=99;

                return result;

            }

 

 

            var z=average(x,y);

            console.log(x, z);

We declare 2 variables named x. We assign 3 to one, and 99 to the other. But the x inside the function does not change the x outside the function.

Scope means the part of a code sequence where a name has the same meaning – if any (if it is not declared, it has no meaning).

In JavaScript there are 2 scopes – inside a function, which is called local,  and outside, which is called global.

In this example we have a local x and a global x. They have the same name – but no connection. Assigning to one does not alter the other.

This is good, because if we are writing a function and want to use a local variable, we do not need to worry whether we have used the same variable name elsewhere. Even if we have, local variables will not alter each other.

Each function has its own, separate, scope:

            var x=3;

            var y=4;

 

            function average (a,b)

            {

                var x;

                var result = (a+b)/2;

                x=99;

                return result;

            }

 

            function another()

            {

                y=100; // the global y

                var x=89; // a different local x

            }

 

 

            var z=average(x,y);

            another(); // execute another

            console.log(x, y, z); // 3,100,3.5

User interface – the DOM

Standards

Objects and classes

Functions

Data structures

Algorithms

 

How to get more help

You can Google or search youtube, but this is a bad idea. JavaScript can seem like a simple language, but it is not. There are many things on the web about JavaScript written by people who have little understanding of it. Many are mis-leading or simply incorrect. Be wary of simply becoming more confused. Here are some suggestions for checking your understanding

 

 

Write and run your own code snippets

You get to see actual output. But it might be different with different browsers or non-browser interpreters and so on.

Current ECMAScript standard

This sets out the actual rules of the language. But it is not a tutorial and does not try to teach the ideas, so for reference only. Also most browsers will not keep up with this latest standard

Previous language standards

5.1, 2016, 2017, 2018, 2019

https://esdiscuss.org/

ECMAScript discussion archive

https://es.discourse.group/

ECMAScript standard community discussion

https://caniuse.com/

Guidance about what you feature you can use, in what browser, in what version

https://developer.mozilla.org/en-US/docs/Web/JavaScript

From Mozilla, MDN provides reference material and tutorials at a range of levels  

https://developer.mozilla.org/en-US/docs/Web

Mozilla MDN material on many aspects of web technology

https://html.spec.whatwg.org/

Html5 – ‘the living standard’

https://developer.mozilla.org/en-US/docs/Web/CSS

Mozilla MDN on CSS

 

Primitive types

Type

Examples of data types are integers (whole numbers), numbers with fractional parts, characters, dates, times, colours, and so on. We can do different things with different types – for example we can multiply numbers but we cannot multiply colours.  Programming languages use the idea of data type.

Some language extend the idea of type to include function type – C does this. Other languages treat functions as data – JavaScript does this.

Strict typing

In some languages, variables have a type, and cannot change. In Java for example we say things like

int x;

which means the type of the variable x is an integer. These languages are strictly typed.

In JavaScript variables do not have type.

But values have type.

So we can change a variable’s value, and so change the type:

        <script>

            var x;

            x=8;

            console.log(typeof x); // number

            x="Hello";

            console.log(typeof x); // string

 

        </script>   

 

Primitive types

JavaScript has 6 primitive types : number, string, boolean, bigint, undefined and symbol. The primitive types are just values.

It also has some object types. There are some built-in objects, and the programmer can define others. An object has data parts, called fields,  and code parts, called methods.

For each primitive type (like number) there is a matching wrapper type (like Number) which is an object. JavaScript is case sensitive, so number and Number are different.   

Number

This primitive type is a number, positive or negative, whole number or not:

            var x;

            x=8;

            console.log(typeof x); // number - primitive

            x=new Number(8);

            console.log(typeof x); // object

            console.log(x instanceof Number);  // true

The Number object is said to be a wrapper object – it wraps a primitive value of type number, and adds relevant methods. The Number and other objects is examined later.

The JavaScript number primitive is held in 64 bits floating point IEEE 754 format. That means they are always stored in 64 bits, always have fractional parts, and are held in a format described in the IEEE 754 standard.

Strings

A string is a sequence of 1 or more characters. And a character is a like a b c A B C * ^ $ Σ π ± and so on. We can ‘add’ strings by concatenating them – just joining them together:

            var x;

            x="A";

            x=x+1;

            console.log(x); // x1

            console.log(typeof x); // string

            x=x+" a b c d";

            console.log(x); // x1 a b c d

Note a space character is just another character. So “a b” is a string of 3 characters.

JavaScript uses Unicode:

            var x;

            x="αβγ ∈∃ℂ";

            console.log(x); // αβγ ∈∃ℂ

Boolean

The boolean type has just 2 possible values – true and false:

            var x;

            x=( 5 === 6);

            console.log(x); // false

BigInt

A BigInt primitive is whole number which can be indefinitely large. A BigInt literal – an actual value – is written with an n after it. So for example

            var x;

            x= 10n**300n;

            console.log(x);

 That makes x 10300, when the largest number safe integer is about 1015 – safe in the sense that claculations with it will not go wrong.

BigInt may be slower than a number.

BigInt is a fairly new type, and old browsers will not understand it.

Undefined

The undefined type ha sjust one value : undefined. This is the value variables have if they have not been assigned a value:

            var x;

            console.log(x); // undefined

 

Objects and protoypes

In computer science an object is a bundle (or a packet or group or bunch or collection) of data values and code. Usually objects have names, as identifiers, and are held in memory during program execution.

JavaScript is an object-oriented programming language (OOP) and can use an objected-oriented paradigm. That means it has a syntax for dealing with objects, and JavaScript code is often about objects.

ES6 introduced the keyword ‘class’. This section is about objects and pprototypes, which are a key idea about how JavaScript objects work. The next section covers classes.  

How to make an object

There are several ways to make a new object in JavaScript. Here is one way:

        var person=

        {

        name: "John",

        dateOfBirth: "1.3.94",

        payrollNumber: 423

        };

This declares a variable, named person. But this is not just one data value. It has three fields, named name, dateOfBirth and payrollNumber. These fields are string and number primitives.

This is called an object literal.

Representing a date as a string is not a good method – we do it here to keep things simple.

The object.property dot notation

We refer to one of the fields within an object by saying what the name of the object is, then a dot ( . ) then the field name. For example

        console.log(person.name); // John

        console.log(person.payrollNumber); // 423   

What is the point of OOP?

OOP is not essential. Complete operating systems, like Unix, have been written without objects.

But OOP has several advantages. One is that we often want to code about things which have several aspects. The above example shows this – one employee has a name, a date of birth, a payroll number and so on. Primitive types can handle these as separate values, but an object can bring these together as a single thing.

Methods

An object is a bundle of data values and code. The code parts are called methods. We can tell an object to execute one of its methods. For example

        var person=

        {

        name: "John",

        dateOfBirth: "1.3.94",

        payrollNumber: 423,

        toString: function()

          {

          return "person: "+this.name+"\nDOB "+this.dateOfBirth+

          "\nPayroll number "+this.payrollNumber;

          }

        };

Now we have 3 data fields, plus another field named toString. This is different in that it is defined as a function. The syntax is similar to that used in our function definitions before, except that we are not giving it a name as function toString()… Instead we are saying toString: function()

Within the function we use ‘this’ to refer to the object which will execute it. So this.name means the name field of the object carrying out the toString method.

 

We can tell the person object to do its toString method like this:

console.log(person.toString());

and this outputs

person: John

DOB 1.3.94

Payroll number 423

Objects often have a toString method, to produce a string version of the object for output and debugging purposes.

Objects as associative arrays

In computer science, an associative array is a set of key-value pairs. That means we have a data values, and each value is linked with some key. We can use the key to access the value. This is sometimes called a map – it connects each key with some value.

We can do that as follows in JavaScript:

        var person=[];  // create an empty associative array

        // then add fields:

        person['name']= "John";

        person['dateOfBirth']= "1.3.94";

        person['payrollNumber']=423;

        // access with dot notation..

        console.log(person.name);

        // or as associative array..

        console.log(person['name']);

The key must be a data value, or an identifier with a suitable value:

        var person=[];  

        person['payrollNumber']= 123;

        // or

        var fieldName='payrollNumber';

        person[fieldName]=123;

        // but not

        person[payrollNumber]= 123; // unless payrollNumber is a variable with a value

Functions are objects

In JavaScript, a function is an object. Function objects only differ from other objects in that they can be called:

        function MyFunction(a,b)

        {

        return a+b;

        }

 

        console.log(MyFunction(2,3)); // 5 - call the function

        MyFunction.x=27; // use it as an object

        console.log(MyFunction.x); // 27  

If we invoke the function using the keyword new, this constructs a new object, as described next.

Patterns of objects – using constructors

The ways of making objects  described above is fine for ‘one of a kind’ situations, where we only want one object of a type. But we often want several objects with the same structure – the same set of fields (but not values)  and the same set of values. That means executing a similar sequence of instructions, repeatedly. That is what a function does.

We can do this using a constructor. This is a function, but invoked using the keyword new, instead of the usual function call.

In that situation the constructor creates a new object, referring to it as this. Fields can then be created on that object and values assigned to the fields. At the function end, the this object is returned:

function Person(n,dob, pn)

        {

        this.name=n;

        this.dateOfBirth=dob;

        this.payrollNumber=pn;

        }

 

        var person1=new Person("John","1.2.99",1234);

        var person2 =new Person("June","2.2.99",1235);

        var anotherPerson = new Person("Jane","3.2.99",1236);  

 

We can include methods as well as data fields:

        function Person(n,dob, pn)

        {

        this.name=n;

        ..

        this.toString = function()

          {

          return "person: "+this.name+"\nDOB "+this.dateOfBirth+

          "\nPayroll number "+this.payrollNumber;

          }

        }

 

        ..

        var anotherPerson = new Person("Jane","3.2.99",1236);  

        console.log(anotherPerson.toString());

Object property attributes

A JavaScript object is a collection of properties. These properties can have attributes.

We can control these directly using Object.defineProperty:

        function MyObject() { // constructor

 

            Object.defineProperty(this, 'b', {

                value: "Hello",

                writable: true, // we can change its value

                enumerable: false, // does not appear in a for..in

                configurable: true // can be deleted

            });

            Object.defineProperty(this, 'c', {

                value: 21,

                writable: false, // we cannot change its value

                enumerable: true, // does  appear in a for..in

                configurable: true // cannot be deleted

            });

 

 

 

            }

 

        var obj1 = new MyObject();

        console.log(obj1.b); // Hello

        obj1.b="bye";

        var p;

        for (p in obj1) // c, not b

        console.log(p);

        delete(obj1.c); // OK

An object can have two kinds of properties. A data property is as above, and can have attributes of value, writable, enumerable and configurable,

The other type of property is an accessor property.  These would be called accessor methods in Java. A accessor property is a getter or setter function, (or both getter and setter). A setter function has one argument, and is used to set  some value. A getter function has no arguments, and is used to read some value:

        function MyObject() {

            ..

            Object.defineProperty(this, 'accessA', {

                set(x) { // setter function

                    this.a = x;

                },

                get() { return this.a; } // getter

 

            })

 

 

            }

 

        var obj1 = new MyObject();

 

        obj1.accessA=77; // call the setter

        console.log(obj1.accessA); // call the getter

The constructor property

All JavaScript objects have a property named constructor. If the object has been made using the new syntax, the value of the constructor property is as expected:

        function CTR() {

            this.x = 99;

        }

 

        var obj = new CTR();

        console.log(obj.x); // 99

        console.log(obj.constructor); // ƒ CTR() { this.x=99; }

 

So the value of the constructor property of obj is CTR, which is a function.

Now a function is itself an object. So it in turn must have a constructor property:

        var obj = new CTR();

        console.log(obj.x); // 99

        console.log(obj.constructor); // ƒ CTR() { this.x=99; }

        console.log(obj.constructor.constructor); // ƒ Function() { [native code] }

So the constructor of CTR is Function. This is as if we had said:


var CTR = new Function..

which is the effect of the actual

        function CTR() {

We can do this explicitly, as:

        var CTR = new Function( 'this.x = 99;' );

 

        var obj = new CTR();

        console.log(obj.x); // 99

        console.log(obj.constructor);  // ƒ anonymous( ) { this.x = 99; }

Now Function is an intrinsic object, which can be used to construct functions, and other features related to functions. As an intrinsic object, it is there when the interpreter starts. It was not made by other JavaScript code, and this is why it says Function() { [native code] }

If we make an object using the object literal syntax, it still has a constructor property:

        var objLit =

        {

            x: 98

        }

        console.log(objLit.x); // 98

        console.log(objLit.constructor); // ƒ Object() { [native code] }

so the constructor is Object, which is an intrinsic object.

Inheritance

In OOP the idea of inheritance is to avoid coding every new type of object from nothing. Instead we try to use an existing object as the basis for a new type. The idea is to re-use the fields in the old type in the new type. We might change them, or add extra fields as well.

JavaScript does this using the idea of a prototype. An object can have another object as its prototype. Then properties of the protoype are inherited by the other object.

As an example, we will have an object with a field a, and this will be the prototype of another object with a field b, and also the inherited field a:

        function Base() {

            this.a = 101;

        }

 

        function Sub() {

            this.b = 99;

            this.__proto__ = new Base();

        }

 

        var obj = new Sub();

        console.log(obj.a); // 101

        console.log(obj.b); // 99

 JavaScript objects have a property __proto__ (caution -see below). The line

this.__proto__ = new Base();

sets that to be an object with Base as its constructor. So the a field from Base is inherited by the Sub object.

Each Sub object has its own, separate, a field:

        var obj = new Sub();

        var obj2=new Sub();

        obj2.a=200;

        console.log(obj2.a); // 200

        console.log(obj.a);  // 101 - the default value

obj2 has its own a field, different from obj’s a field.

The name __proto__ for the property is a problem. Currently all browsers understand it (old Internet Explorer does not), but it is not part of the ECMAStandard, and it may stop working. A better alternative is Object.setPrototypeOf:

        function Base() {

            this.a = 101;

        }

 

        function Sub() {

            this.b = 99;

            Object.setPrototypeOf(this,  new Base());

        }

and a corresponding Object.getPrototypeOf.

The base object may itself have a prototype, so we have the idea of a prototype chain:

        function BaseBase() {

            this.q = 300;

        }

 

        function Base() {

            this.a = 101;

            Object.setPrototypeOf(this, new BaseBase());

        }

 

        function Sub() {

            this.b = 99;

            Object.setPrototypeOf(this, new Base());

        }

 

        var obj = new Sub();

        console.log(obj.q);  // 300

 

Constructor prototype property

Some objects – those which are functions, and so can be used as constructors using new, have a property named prototype. Confusingly, .prototype is different from .__proto__

         function Ctr() {

            this.b = 99;           

        }

 

        var obj = new Ctr();

        console.log(obj.prototype); // undefined

        console.log(obj.__proto__); // {constructor: ƒ}

        console.log(Ctr.prototype); // {constructor: ƒ}

The idea of .prototype is that it is an object, belonging to a constructor,  from which all its fields are inherited by an object constructed by that constructor. For example:

         function Ctr() {

            this.b = 99;

 

        }

 

        // give Ctr.prototype a function, named func, which sets b

        Ctr.prototype.func = new Function("x", "this.b=x");

 

        var obj = new Ctr(); //b will be 99

        obj.func(9); // call function from prototype, set b to 9

        console.log(obj.b); // 9

Encapsulation

One idea in OOP is that of encapsulation. The idea is that an object bundles together code and data, and access to the data is controlled, to make sure the data in the object is always valid and correct and that the object cannot ‘go wrong’.  

This was intended to solve a problem in structured programming, where there would usually be a set of global data, and this would be accessed by code in functions. The problem was there was no way to control what code could alter what data. A bug in any one function could corrupt the global data, which might make other functions fail. Testing was then very difficult.

The OOP solution was to encapsulate all data into separate objects, so all data was a field in some object. And it was not directly accessible – in Java terms, it would be private. It can only be accessed through public getter and setter methods. And the setter methods would validate any data changes, to ensure the data could never ‘go wrong’.

This is what JavaScript accessor properties offer. For example, suppose the value of ‘a’ in our object cannot be negative. We just need an if in the setter:

     function MyObject() {

            ..

            Object.defineProperty(this, 'accessA', {

                set(x) { // setter function

                    if (x>0) this.a = x;

                },

                get() { return this.a; } // getter

 

            })

 

 

            }

 

References

Why does changing one object alter another object?

        function MyObject() {

 

            this.x=2;

 

            }

 

        var obj1 = new MyObject();

        var obj2 = obj1;

        obj1.x=9;

 

        console.log(obj2.x); // 9

and if JavaScript is pass by value, how come this works?

        function swap(a, b) {

            var t = a.x;

            a.x = b.x;

            b.x = t;

        }

 

        function MyObject(v) {

            this.x = v;

        }

 

        var obj1 = new MyObject(1);

        var obj2 = new MyObject(2)

        swap(obj1, obj2);

 

        console.log(obj1.x, obj2.x); // 2  1

Image10The answer is that if a variable names an object, the variable is a reference to that object. In effect, the variable is a pointer to the object, like this:

obj1 is a pointer to the object. We can think of this as being the address in RAM of where the object is held.

If we say

        var obj1 = new MyObject();

        var obj2 = obj1;  

we have only said ‘new’ once, so we only have one object. We have two pointers, obj1 and obj2, which point to the same object:

 

Image11So if we say obj1.x=9, this changes obj2.x – because they are the same object.      

JavaScript is pass by value. This means when a parameter is passed to a function, a copy is passed. But when the parameter is an object, a copy of a pointer to the object is being passed.

So the function can follow that pointer and alter what is in the object.   

So here:

function swap(a, b) {

            var t = a.x;

            a.x = b.x;

            b.x = t;

        }

the a and b are copies of pointers to two objects. The t=a.x follows one pointer and gets the x field in that object. Then b.x=t follows the b pointer, and writes that x value into the object that b points to.

In the calling code

        var obj1 = new MyObject(1);

        var obj2 = new MyObject(2)

        swap(obj1, obj2);

 we have used new twice, so we just have 2 objects (not 4). And the swap function has exchanged their x fields.

If we had said

function swap(a, b) {

            t = a;

            a = b;

            b = t;

        }

it would have had no effect. The a and b are copies of pointers. Changing those copies (instead of the objects they point at) has no effect.

Static fields

In OOP, a static field is per object type, not per object.

So it is a single value shared between all objects of that type. Instead of all objects having their own value, there is just one static value belonging to the type. In JavaScript we can do this using a field of the constructor function object. A constructor is a function, and a function is an object, so it can have fields.

An example of the use of a static field is in a version of the auto-number feature of some databases:

        Person.lastID=0; // static field initialisation – a field of the function object

 

        function Person(n,dob) // payrollNumber is auto-assigned

        {

 

        this.name=n;

        this.dateOfBirth=dob;

        this.payrollNumber=Person.lastID; // current last ID

        Person.lastID++; // move static field onto next ID

        this.toString = function()

          {

          return "person: "+this.name+"\nDOB "+this.dateOfBirth+

          "\nPayroll number "+this.payrollNumber;

          }

        }

 

        var person1=new Person("John","1.2.99");

        var person2 =new Person("June","2.2.99");

        var anotherPerson = new Person("Jane","3.2.99");  

        console.log(anotherPerson.toString());

 The anotherPerson is

person: Jane

DOB 3.2.99

Payroll number 2

The built-in objects and the Object object

JavaScript has a set of built-in objects, which already exist when the interpreter starts. They include:

Object and Function : the fundamental objects concerned with how OOP works

Number, BigInt, Boolean, String and Symbol : the wrapper classes that wrap primitive values. That means they have a primitive value as a property, and have methods useful in connection with that type

Math, Date, RegExp, JSON : useful utility objects

Array, Set and Map : basic data structures

The complete reference for all built-in objects is at MDN.

Object is one of those pre-built objects  It provides several things useful for manipulating objects, and since JavaScript is an OOP language, is key:

Constructor

Creates a new empty object:

    var obj = new Object();

    obj.f = 99;

    console.log(obj); // {f: 99}

Other methods include:

Methods

Object.assign(to, from);

copies enumerable properties from one object to another

Object.defineProperty(..)

add a property to an object, with given attributes

Object.create

Create an object with a given prototype

Object.freeze

Prevent an object from being changed

Object.getPrototypeOf

 

Object.is

Compares two objects (almost like === )

Object.keys

Return an array of enumerable own property names

Prototype methods

toString

Return a string representation

valueOf

For a wrapper class, returns the value wrapped

The global object

When the interpreter starts, there is a global object in scope. A ‘this’ in global scope refers to it. Anything you declare, like

var x;

in fact becomes a property of that global object.

In a browser, the global object is named window. Th eidentifier ‘globalThis’ refers to the global object in any environment:

console.log(this); // Window

console.log(globalThis); // Window

var x=88;

console.log(globalThis.x); // 88

console.log(globalThis.Date); // ƒ Date() { [native code] }

console.log(Window===window); // false

console.log(window instanceof Window); // true

// var window = new Window(); no - Illegal constructor

var window;

console.log(window); // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

Function objects and concepts

We can think about functions in two different ways – as code, or data:

  1. In C and Pascal and Basic and Algol and Fortran, a function is a code unit. It either just does something (a procedure in Pascal, or a void function in C), or it returns a computed value. A function is a code block which executes. In JavaScript we often have functions which executes when te user clicks a button, for example.

  2. Or, a function is a piece of data, like a number or a string. Code can input, process, store and retrieve, and output data – so it can do all of that with functions. Th eonly special thing about a function as a data type is that it can also be executed. Languages which can handle functions as data which can be manipulated are known as functional languages. JavaScript has an object named Function, which supports this.  

Standard function definition and call

Like this:

   function max(a,b)

   {

     return a>b? a : b;

   }

 

   var bigger = max(7,9);

Functions as data objects

For example:

    function max(a, b) {

      console.log(max.name); // max

      console.log(max.length); // 2 (number of arguments expected)

 

      return a > b ? a : b;

    }

 

    var x = max(8, 9); // calling the function

 

    console.log(max instanceof Function); // true

    var code = max.toString();

    console.log(code); // as above!

Here max is a function – so it is an object, a bunch of data and methods, in an inheritance chain. Functions inherit from the built-in object Function, and this has properties of name and length, as shown above.

So max is an object, an instance of Function. From Function it inherits the toString()method, which produces the code of the function

Function expressions

An expression is code which evaluates to something. A function expression is code which evaluates to a function. This could be assigned to an identifier, and then called, like:

   var bigger = function (a,b) { return a>b? a : b; };

 

   var m = bigger(7,9);

Anonymous functions

If we say

var x=9;

we have an identifier (x) to which is assigned a value (9). A function expression evaluates to a value – a function object – and sometimes we do not need to assign that to an identifier.  So it does not have a name. For example

   function map(f,values) // apply function f to the array values

   {

     for (var index=0; index<values.length; index++)

     values[index]=f(values[index]);

   }

 

   var data = [1,2,3];

   map(function(x) { return 2*x;}, data);

   console.log(data); // [2, 4, 6]

Here function(x) { return 2*x;} is a data value, which is a Function object. But it does not have a name – it has not been assigned to an identifier. This is like x+y is an expression, and sometimes we do not need to assign it to a name to use it. Here we apply it, as a function, to every element of an array.

Arrow functions

 

The Function object

Function is a pre-built object which relates to functions.

It can be used as a constructor. The arguments are an optional list of strings, which are the arguments, and the final one is the source code of the function. For example:

var addUp = new Function('data','var result=0; var p; for(p=0; p<data.length; p++) result+=data[p]; return result; ');

 

var values=[1,2,3];

var total=addUp(values);

console.log(total); // 6

In this example there is no advantage over a conventional function definition. But it would be possible to construct the function programmatically, and then call it. So JavaScript code can construct code, and run the result.

If this is done on the basis of user input, this might be a security risk.

The Function object has properties name and length, and the prototype has methods apply, call, bind and toString.  

The difference between apply and call is that the first takes an array of arguments, and the second a list:

    function max(a, b) {

      return a > b ? a : b;

    }

 

    console.log(max.apply(null,[5,4]));

    console.log(max.call(null,5,4));

 

 Classes

JavaScript implements OOP using prototypes, as described in the previous section. But some OOP languages, such as Java and C++, use the idea of a class, as a type of object. ES5 introduced class into JavaScript, as syntactic sugar – that is, to make code look better, while keeping prototypes as the actual way OOP is implemented.

Class

A class is a type of object. It provides a template, a recipe or cookie cutter for making sets of objects of the same type:

        class Person {

            firstName;

            lastName;

        }

 

        var p1 = new Person;

        p1.firstName = 'John';

        p1.lastName = 'Smith';

        console.log(p1.firstName, p1.lastName);

        var p2 = new Person;

        p2.firstName = 'Jane';

        p2.lastName = 'Jones';

        console.log(p1.firstName, p1.lastName);

It is a convention to have class names starting Upper Case. This is to make it clearer which identifiers are classes and what are not.

Here the p1 object is an instance of the class Person.

Methods

As well as data fields, a class can also define methods bundled into an object:

        class Person {

            firstName;

            lastName;

 

            setFields(f, l) {

                this.firstName = f;

                this.lastName = l;

            }

 

        }

 

        var p1 = new Person;

        p1.setFields('John', 'Smith');

        console.log(p1.firstName, p1.lastName);

So know a Person instance knows how to do the setFields method.

A method can contain any code, not just assignment to fields.

Constructor

As in the previous section, a constructor is code used to initialize an object:

        class Person {

            firstName;

            lastName;

 

            constructor(f, l) {

                this.firstName = f;

                this.lastName = l;

            }

 

        }

 

        var p1 = new Person('John','Smith');

        console.log(p1.firstName, p1.lastName);

Constructors cannot be overloaded, meaning a class can only have one constructor.

Private fields

By default, class fields are public, meaning they can be accessed from outside the class. As a result of accidental bugs, this can produce invalid data:

        class Person {

            firstName;

            lastName;

 

            constructor(f, l) {

                this.firstName = f;

                this.lastName = l;

            }

        }

 

        var p1 = new Person('John','Smith');

        p1.lastName=''; // no last name

        console.log(p1.firstName, p1.lastName);

We can prevent this by making data fields private, by having them start with a #. Then they cannot be accessed from outside the class. We must write public getter and setter methods to access them, and the setter can validate any changes:

         class Person {

            #firstName; // private fields

            #lastName;

 

            constructor(f, l) {

                this.#firstName = f;

                this.#lastName = l;

            }

 

            setLastName(l)

            {

                if (l.length>0) // do not allow zero length last names

                this.#lastName=l;

            }

 

            display()

            {

               console.log(this.#firstName, this.#lastName);

            }

        }

 

        var p1 = new Person('John','Smith');

        // not allowed - p1.#lastName='anything';

        p1.setLastName('Ali');

        p1.display();

Currently (2020) private fields do not work in Firefox and Intrenet Explorer.

Static

Static members are per class not per object. In other words they are data fields or methods for which there is just one copy, shared by all instances of the class, and so common to all objects of that type. For example, suppose we want to express the fact that people have two legs, as distinct from horses that have four and spiders that have eight:

        class Person {

            #firstName;

            #lastName;

 

            constructor(f, l) {

                this.#firstName = f;

                this.#lastName = l;

            }

 

            static legCount=2;

 

            setLastName(l)

            {

                if (l.length>0)

                this.#lastName=l;

            }

 

            display()

            {

               console.log(this.#firstName, this.#lastName);

            }

        }

 

        var p1 = new Person('John','Smith');

        console.log(Person.legCount);

p1.legCount does not work – a static field can only be accessed through the class, not an instance (unlike Java).

Class inheritance

A class can extend another class. The original class might be called a base class, and the extending class a sub-class. Instances of the sub-class inherit properties of the base class:

class Base

{

    baseField=3;

}

 

class Sub extends Base

{

    subField=4;

}

 

var obj=new Sub();

 

console.log(obj.baseField, obj.subField); // 3 4

 

Arrays, maps and other data structures

 

Associative arrays

We have seen that all objects are associative arrays or maps. So they are key-value pairs:

var obj=new Object();

obj['x']=99; // write key-value x-99

console.log(obj['x']); // get value with key x

Typed arrays

These have either 8, 16, 32 or 64 bits per element, and are fixed length. For example:

var arr = new Int8Array(5);

for (var index = 0; index < arr.length; index++)

  arr[index]=index+10;

for (var index = 0; index < arr.length; index++)

  console.log(arr[index]); // 10, 11.. 14

We can iterate as an enumerable:

for (var p in arr)

  console.log(p, arr[p]); // 10, 11.. 14

There are signed and unsigned versions. An Int8Array is signed 8 bits, and so the elements can range for -128 to +127:

 var arr = new Int8Array(5);

arr[2]=-1;

console.log(arr[2]); // -5

If the value is outside this range, the result is ‘wrong’:

var arr = new Int8Array(5);

// range is -128 to +127

arr[2]=+128;

console.log(arr[2]); // -128

but a Uint8Array is unsigned, so it ranges from 0 to 255:

var arr = new Uint8Array(5);

arr[2]=+128;

console.log(arr[2]); // 128

arr[2]=257;

console.log(arr[2]); // 1

arr[2]=-1

console.log(arr[2]); // 255

There is a corresponding Int16Array, Uint32Array and so on.

There are also an 8 bit unsigned ‘clamped’ version, such that if the range is exceeded you just get the maximum or minimum:

var arr = new Uint8ClampedArray(5);

arr[2]=+300;

console.log(arr[2]); // 255

The Array object

In most languages an array is a data structure with elements which are accessed using an index, which is an integer. The Array object provides something similar to this:

var arr = new Array(3); // make an array with 3 slots

arr[0] = 27; arr[1] = 15; arr[2] = -9; // put values in

for (var index = 0; index < 3; index++)

  console.log(arr[index]); // 27 15 9

But there are differences. An array can contain a mix of data types:

var arr = new Array(3);

arr[0] = 27; arr[1] = "Hello"; arr[2] = false;

for (var index = 0; index < 3; index++)

  console.log(arr[index]);

Array indexes start at 0. Array.length is the number of elements in the array. But unlike most languages (C, Java, C++) an array is not fixed in size:

var arr = new Array(3);

arr[3]="Surprise";

for (var index = 0; index < arr.length; index++)

  console.log(index, arr[index]);

outputs

0 undefined

1 undefined

2 undefined

3 Surprise

Neither are they dense. An array which has had a large index written to will be extended to that size if required, meaning there may be elements as some indexes which are undefined:

var arr = new Array(3);

 

arr[5]="Surprise";

for (var index = 0; index < arr.length; index++)

  console.log(index, arr[index]);

outputs

0 undefined

1 undefined

2 undefined

3 undefined

4 undefined

5 Surprise

http

Protocols

This text is mostly about JavaScript, and that is most commonly used in web pages usually sent using a protocol named http. So this section is about http and its background ideas.

The Internet is a set of computing devices which send messages to each other. These messages are used for many purposes.  The Web aka the world-wide web is one such service. Others are email, file transfer, time synchronisation and so on.   

Http stands for Hyper-Text Transfer Protocol. A protocol is a set of rules or conventions which are needed for communication, so that the sender knows what to send, in what order, and the receiver knows what to expect.  

On the Internet messages are sent in data bursts called packets.

Packet sniffing

Image3Using Wireshark

A packet sniffer is a piece of software which reads and displays packets a device can detect. Wireshark (formerly known as Ethereal) is the best known, and is free open source. You may need to run it with root priveldges, so on Linux start it at the command line with sudo wireshark:

 

TCP/IP

The Internet has to handle several issues:

These problems are handled by using a set of different protocols, arranged in layers. This is sometimes called a protocol stack. The Internet uses a set of protocol layers called TCP/IP.

NTP

To illustrate the idea with a practical example, we use ntp. This is Network Time Protocol, a way to get accurate time settings on networked machines. This is Wireshark capturing ntp packets:

Image4

Here, mint2.lan is the desktop I am writing this on, and time.cloudflare.com is a remote time  server which is offering the time check service. Two packets have been captured. One is from the client to the server, asking what the time is, and the other is server to client, supplying the time.

This shows the second packet, server to client:

Image5

The frame was 90 bytes long, on the ‘wire’. Interface 0 is an Ethernet cable connection, so this really is a wire, not wireless or fibre (in the local link). The lower section, starting 74 and ending 94, are the bytes, in hex, in that frame.

In the middle section it also lists Ethernet II, Internet Protocol, User Datagram Protocol and Network Time protocol. The 90 bytes of the frame are made up of these 4 parts, which match the 4 layers of the TCP/IP scheme.

The Ethernet II is the ‘link   layer’:

Image6

This has a destination and source address – for example the destination is 74:d4:35:54:a8:9f. This is  the MAC address of this computer – the address hard-wired into the network interface electronics in this machine.

Image7 The second block of bytes is for the Internet Protocol packet, which is in the second TCP/IP layer:
This also has the source and destination addresses, as IP addresses (not MAC). So the source is 162.159.200.1, which is the IP address of time.cloudflare.com. Another field is ‘Time to live’, value 57. Suppose the packet cannot be delivered, maybe because someone switched off the destination. We need to ensure the packet does not bounce around the Internet forever, and the ‘time to live’ field does this. This (‘the hop count’) is reduced by 1 every time the packet is sent device to device, and when it counts down to 0, the packet is destroyed.

The third layer holds a User Datagram Protocol packet:

Image8

The UDP includes port numbers – through different ports one device can connect to different destinations for different purposes at the same time.

The 4th layer is the actual NTP packet:

Image9

This contains fields relevant to the NTP protocol. For example, the Peer Clock Stratum is 3. Time servers are arranged in a hierarchy. Stratum 0 are very accurate atomic clocks, not directly accessible. They are connected to stratum 1 machines, which also connect to other stratum 1 machines to check time. Stratum 2 machines connect to these, and each other, and 3 to 2. This gives a large number of time servers available, all synchronised to a small number of very accurate clocks. This is not simple, due to time delays in sending and receiving packets. The target is an accuracy of 20 pico-seconds, which is 10-12 seconds – around 1000 times faster than any current device.  

The TCP/IP layers

TCP/IP is a set of many protocols, arranged in four layers:

Link layer – handles different media like cable, wireless and fibre, and protocols like PPP, point-to-point protocol, which many ISPs use to connect routers in home across phone lines to backbone routers. Links do not cross routers.

Internet layer – like Internet protocol versions 4 and 6, connecting routers

Transport layer- like UDP and TCP, connect client to server

Application layer – like NTP and htp and ftp and telnet

So a frame on a medium (like an Ethernet cable) would have link layer bytes, wrapping for example an IP packet, wrapping a UDP packet, wrapping an NTP packet, as in the example above.

Communication can be connectionless, like UDP. Here the client sends out a packet, with a source and destination address. The packet might be delivered – or it might not. NTP uses UDP and is connectionless.

Or the communication might be connection-based, like TCP.  This involves a sequence of messages back and forth, establishing a connection, exchanging messages and closing the connection. Http in the application layer uses TCP in the transport layer and is connection-based.

For application programming, we only need to be concerned with application layer protocols, and can think of the rest as a black box through which client and server are connected.

TCP

UDP is connectionless, but TCP is connection-based – and http uses it, so we look at TCP. The idea is a 3 stage sequence:

  1. Establish a connection

  2. Use the connection

  3. Close the connection

At each stage we need to be sure messages have been set.

For example to establish a connection, 3 packets are sent:

A SYN from the client requesting  connection

A SYN/ACK from the server acknowledging the request, and sending an accept back

An ACK from the client acknowledging the accept.

So for example a packet from the server back to the client is:

Frame 61: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0

Ethernet II, Src: a0:1b:29:67:4e:ca, Dst: 74:d4:35:54:a8:9f

Internet Protocol Version 4, Src: waltermilner.com (87.117.220.232), Dst: mint2.lan (192.168.1.65)

Transmission Control Protocol, Src Port: 80, Dst Port: 56300, Seq: 0, Ack: 1, Len: 0

    Source Port: 80

    Destination Port: 56300

    [Stream index: 3]

    [TCP Segment Len: 0]

    Sequence number: 0    (relative sequence number)

    [Next sequence number: 0    (relative sequence number)]

    Acknowledgment number: 1    (relative ack number)

    1010 .... = Header Length: 40 bytes (10)

    Flags: 0x012 (SYN, ACK)

        000. .... .... = Reserved: Not set

        ...0 .... .... = Nonce: Not set

        .... 0... .... = Congestion Window Reduced (CWR): Not set

        .... .0.. .... = ECN-Echo: Not set

        .... ..0. .... = Urgent: Not set

        .... ...1 .... = Acknowledgment: Set

        .... .... 0... = Push: Not set

        .... .... .0.. = Reset: Not set

        .... .... ..1. = Syn: Set

        .... .... ...0 = Fin: Not set

        [TCP Flags: ·······A··S·]

    Window size value: 28960

    [Calculated window size: 28960]

    Checksum: 0xf958 [unverified]

    [Checksum Status: Unverified]

    Urgent pointer: 0

    Options: (20 bytes), Maximum segment size, SACK permitted, Timestamps, No-Operation (NOP), Window scale

    [SEQ/ACK analysis]

        [This is an ACK to the segment in frame: 57]

        [The RTT to ACK the segment was: 0.013444470 seconds]

        [iRTT: 0.013524670 seconds]

    [Timestamps]

where frame 57 was requesting the connection

Http

Http is one application layer protocol that operates over TCP.

For example a request for a web page sent by a browser to a server would be this TCP packet

Frame 63: 555 bytes on wire (4440 bits), 555 bytes captured (4440 bits) on interface 0

Ethernet II, Src: 74:d4:35:54:a8:9f, Dst: a0:1b:29:67:4e:ca

Internet Protocol Version 4, Src: mint2.lan (192.168.1.65), Dst: waltermilner.com (87.117.220.232)

Transmission Control Protocol, Src Port: 56300, Dst Port: 80, Seq: 1, Ack: 1, Len: 489

Hypertext Transfer Protocol

    GET /test.html HTTP/1.1\r\n

    Host: waltermilner.com\r\n

    Connection: keep-alive\r\n

    Pragma: no-cache\r\n

    Cache-Control: no-cache\r\n

    Upgrade-Insecure-Requests: 1\r\n

    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36\r\n

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n

    Accept-Encoding: gzip, deflate\r\n

    Accept-Language: en-GB,en-US;q=0.9,en;q=0.8\r\n

    \r\n

    [Full request URI: http://waltermilner.com/test.html]

    [HTTP request 1/2]

    [Response in frame: 67]

    [Next request in frame: 75]    

This is requesting test.html from the server at waltermilner.com. The \r\n are control characters for a RETURN and LINE FEED. The http command is GET.

The server sends back:

 Frame 67: 424 bytes on wire (3392 bits), 424 bytes captured (3392 bits) on interface 0

Ethernet II, Src: a0:1b:29:67:4e:ca, Dst: 74:d4:35:54:a8:9f

Internet Protocol Version 4, Src: waltermilner.com (87.117.220.232), Dst: mint2.lan (192.168.1.65)

Transmission Control Protocol, Src Port: 80, Dst Port: 56300, Seq: 1, Ack: 490, Len: 358

Hypertext Transfer Protocol

    HTTP/1.1 200 OK\r\n

    Connection: Keep-Alive\r\n

    Content-Type: text/html\r\n

    Last-Modified: Fri, 11 Apr 2014 13:21:50 GMT\r\n

    Accept-Ranges: bytes\r\n

    Content-Length: 145\r\n

    Date: Wed, 16 Sep 2020 14:42:48 GMT\r\n

    Server: LiteSpeed\r\n

    \r\n

    [HTTP response 1/2]

    [Time since request: 0.015230382 seconds]

    [Request in frame: 63]

    [Next request in frame: 75]

    [Next response in frame: 76]

    [Request URI: http://waltermilner.com/test.html]

    File Data: 145 bytes

Line-based text data: text/html (9 lines)

    <!DOCTYPE html>\n

    <html>\n

        <head>\n

            <title>Title</title>\n

        </head>\n

        <body>\n

            <div>This is a test page</div>\n

        </body>\n

    </html>\n

 The http packet contains a header, in yellow, ended by a \r\n blank line, then the actual web page, in blue. This is http response 1 of 2, because the browser also asks for the favicon:

75 10.264678467 mint2.lan waltermilner.com HTTP 486 GET /favicon.ico HTTP/1.1

which the server sends back as an image:

76 10.277974882 waltermilner.com mint2.lan HTTP 1768 HTTP/1.1 200 OK  (image/x-icon)

http headers

The above examples show how an http request or response packet can contain header fields with values, and these provide the server or client with additional information. Details are documented here. Some notes:

Connection: keep-alive\r\n. The ’connection’ means the TCP connection. Keep-alive means the browser expects it might request other pages, and does not want to go through another connection setup sequence.

Pragma: no-cache\r\n

Cache-Control: no-cache\r\n. A cache means a copy of the requested item. So a server might keep a copy of a requested web page, maybe in memory, to make it faster to find it again for another request. Or a browser might save a copy of a web page locally, client-side, so it does not have to fetch it again on another view request. Pragma is a deprecated version

User-Agent: .. The user agent is the software which is making the request. Usually it is some browser, but it might be a web-searching robot – or anything else. This header supplies information on that.

 Accept: text/html,application/xhtml..;q=0.9,image/avif,.. says what type of data is expected to be returned – html as text, or a graphics image in binary format a dn so on. The q=.. provides an indication of which is preferred.

Last-Modified: Fri, 11 Apr 2014 13:21:50 GMT\r\n – tells the browser when the file on the server was last modified

Date: Wed, 16 Sep 2020 14:42:48 GMT\r\n – tells the browser the date and time on the server

Server: LiteSpeed – tells the browser what server software is being used.   

Http requests and responses

An http request packet will usually be sent from browser to server. It will consist of a request, some headers as above, and a data body, depending on what the request is.

The common requests are GET and POST. These are sometimes confused. The intention was that GET is used to fetch a resource – usually a web page maybe with other things like images, from server to browser, while POST would be used to send something to the server, to do something such as add a record to a database or login. The POST would often send name-value pairs inputted through an html form, such as  

 <!DOCTYPE html>

 

<html>

    <head>

        <title>The title</title>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

    </head>

    <body>

        <form method="POST" action="login.php">

            <input type="text" id="fname" name="fname"><br>

            <input type="text" id="lname" name="lname">

            <input type="submit" value="Send">

        </form>

    </body>

</html>

 

When John Brown is entered for the name and the Send button is clicked, an http packet is sent :


Frame 1: 796 bytes on wire (6368 bits), 796 bytes captured (6368 bits) on interface 0

Ethernet II, Src: 00:00:00:00:00:00, Dst: 00:00:00:00:00:00

Internet Protocol Version 4, Src: localhost (127.0.0.1), Dst: localhost (127.0.0.1)

Transmission Control Protocol, Src Port: 46110, Dst Port: 80, Seq: 1, Ack: 1, Len: 730

Hypertext Transfer Protocol

    POST /login.php HTTP/1.1\r\n

    Host: 127.0.0.1\r\n

    Connection: keep-alive\r\n

    .. other headers..

    Accept-Language: en-GB,en-US;q=0.9,en;q=0.8\r\n

    \r\n

 

    File Data: 22 bytes

HTML Form URL Encoded: application/x-www-form-urlencoded

    Form item: "fname" = "John"

    Form item: "lname" = "Brown"

So this starts POST /login.php, followed by headers, then we have a data block, consisting of names (like fname) and values (like John).

On the server, login.php would be a script which would actually do the login, and output html, which would be sent back to the browser. Login.php would be a PHP script, which is easily able to pick up the name-value pairs and process them as required, with simple database access.

The confusion comes about because GET will be a pretty similar thing. If we change the method to GET, this packet is sent:


Frame 7: 677 bytes on wire (5416 bits), 677 bytes captured (5416 bits) on interface 0

Ethernet II, Src: 00:00:00:00:00:00, Dst: 00:00:00:00:00:00

Internet Protocol Version 4, Src: localhost (127.0.0.1), Dst: localhost (127.0.0.1)

Transmission Control Protocol, Src Port: 46146, Dst Port: 80, Seq: 1, Ack: 1, Len: 611

Hypertext Transfer Protocol

    GET /login.php?fname=another&lname=name HTTP/1.1\r\n

    Host: 127.0.0.1\r\n

    .. other headers

    Accept-Language: en-GB,en-US;q=0.9,en;q=0.8\r\n

    \r\n

 

This also sends the name-value pairs, but in a different way – in the ‘query string’. The URL requested is now login.php?fname=another&lname=name, as a ? followed by name=value pairs, separated by a &.

So GET can also be used to send data – but that was not the intention. GET was intended to fetch data, and POST to send.

It is sometimes said GET is insecure but POST is secure. Clearly this is not true. POST data is sent in the packet body, and can be read by anyone with a packet sniffer. So POST is not secure. We need to use SSL and https to send confidential data over the web.  

A module is automatically in strict mode, without saying ‘use strict’;  

 

            x = 1;

            'use strict';

            console.log(x);

        </script>  

Modules

 

Basic idea

A module is like a script, but global scope is not shared between modules. Instead variables and functions must be explicitly exported from and imported into modules.

Need server not file://

Image1Using file protocol

We often develop html css and Javascript loading web pages directly into a browser, from a local file. In the URL bar the browser displays:

and the protocol in use is file://. So the browser is simply reading the html page, and anything else, just like a word processor loads a document from a disk file.

Image2Using http:// protocol

But JavaScript modules will not allow that. A different protocol (usually http:// ) is required. The browser sends a request to a server. The server finds the html file, and sends it back to the browser, which displays it.

A simple way to achieve that is to download, install and start a local http server, such as Apache ( link ). Then if we fetch pages from the ‘local loop’ with IP address 127.0.0.1, we are using our local server, like this:

Basic example

In a JavaScript file testModule.js we have

var x=99;

var y=101;

 

export {x};

and in a web page:

<html>

    <head>

        <title>The title</title>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

 

        <script type="module">

           import {x}  from "./testModule.js";

           y=4; // error -not found

        </script>    

    </head>

    <body>

        <div>stuff</div>

    </body>

</html>

The relative path "./testModule.js" is for the file testModule.js to be in the same folder as the web page. It could be in a sub-folder, like "./modules/ testModule.js". Just "testModule.js" does not work. Some systems use filename extension .mjs not .js for modules, but some servers (like Apache 2.4.18) do not think .js is a script, and fail to load .mjs as being the wrong mime type.

As well as var, we can import const, let and functions:

 /*

 Filename testModule.js JavaScript file

 */

 

function add(a, b) { return a+b; };

 

var x=1;

const y=2;

 

export {add,x,y};

and

        <script type="module">

           import {add,x,y}  from "./testModule.js";

 

           var z=add(x,y);

           console.log(z); // 3

        </script>

Imports and exports are effectively ‘hoisted’:

 /*

 Filename testModule.js JavaScript file

 */

export {add,x,y};

 

function add(a, b) { return a+b; };

 

var x=1;

const y=2;

and

        <script type="module">

           var z=add(x,y);

           console.log(z); // still works

           import {add,x,y}  from "./testModule.js";

        </script>   

We cannot re-declare an imported variable:

        <script type="module">

           var x; // Uncaught SyntaxError: Identifier 'x' has already been declared

           var z=add(x,y);

           console.log(z);

           import {add,x,y}  from "./testModule.js";

        </script>  

Modules always strict mode

Without saying ‘use strict’; a module is executed in strict mode – another advantage.

Use of default

A module can export one (only) thing as default. Then another module can import the default and re-name it:

/*

 Filename testModule.js JavaScript file

 */

var x=1;

var p=2;

export {p};

export default x;

and

        <script type="module">        

           import {default as y}  from "./testModule.js";

           import {p}  from "./testModule.js";  

           console.log(y, p); // 1, 2

        </script>  

 

Import and export formats

The import and export statements can take various forms, optionally renaming items:

Import Statement Form

Filename imported

Imported name

Name as used

Does what?

import v from "mod";

"mod"

"default"

"v"

Import and rename a default export

import * as ns from "mod";

"mod"

"*"

"ns"

Import a namespace object – see below

import {x} from "mod";

"mod"

"x"

"x"

Import, not re-named

import {x as v} from "mod";

"mod"

"x"

"v"

Import re-named

import "mod";

No actual import –  for side-effects

And

Export Statement Form

What is exported

File requested

What is imported?

Exported as..

export var v;

"v"

 

 

"v"

export default function f(){}

"default"

 

 

"f"

export default function(){}

"default"

 

 

"*default*"

export default 42;

"default"

 

 

"*default*"

export {x};

"x"

 

 

"x"

export {v as x};

"x"

 

 

"v"

export {x} from "mod";

"x"

"mod"

"x"

 

export {v as x} from "mod";

"x"

"mod"

"v"

 

export * from "mod";

 

"mod"

"*"

 

The export .. from..; enables one module to aggregate exports from several files into a single exporting file.

Namespaces and importing objects

There are two namespaces in JavaScript – global and local to a function. So if we have a global variable x in one script, we cannot have another, different global x in another.

Importing items into an object fixes this. For example:

/*

 Filename testModule.js JavaScript file

 */

var a=1;

var b=2;

export {a,b};  

and

        <script type="module">        

           import  * as MyModule from "./testModule.js" ;

 

           console.log(MyModule.a, MyModule.b); // 1, 2

        </script>   

Items are imported as fields of an object (here, MyModule) and referred to as such (like MyModule.a). Thsi way w edo not have to worry that about a global name in one script clashing with the same name  in another  we use the object as a namespace.