ECMAScript

As the web was colonized by commercial and community users in the mid nineties, there was a recognition of a need to make websites more interactive. Two competing technologies - Microsoft’s JScript and Netscape’s JavaScript - stepped in to fill this need.

The Development of ECMAScript

JavaScript - originally named Mocha, then LiveScript, and finally JavaScript in a marketing move (it has nothing to do with Sun’s Java, other than the license of the name) - was developed by Brendan Eich in just 10 days in May, 1995. It was a multi-paradigm language - it uses prototype-based objects, and blended functional and imperative approaches.

In 1996, Netscape took their JavaScript implementation to ECMA (which originally was an acronym for the European Computer Manufacturers Association, though it is now called Ecma International, and no longer considered an acronym) with the intent of creating a standard that could be used by all browser manufacturers. The result was the ECMAScript 1 standard.

JScript was Microsoft’s implementation of the ECMAScript standard, so named to avoid copyright issues. Another common implementation of the ECMAScript standard is ActionScript 3. Most modern implementations use the ECMAScript 3.0 standard (JavaScript 2), and are transitioning to ECMA Script 6.

ECMAScript 4 and 3.1

However, the reasonably smooth creation and adoption of ECMAScript standards halted when it became clear that Microsoft, who had transitioned JScript to a .NET based engine, had no interest in supporting the new version, and it was mothballed until 2005 when Brendan Eich, Mozilla, and Macromedia restarted the effort, intending to merge new features from ActionScript 3.

A counter-movement to extend the ECMAScript version 3 with less sweeping changes was supported by Microsoft and Yahoo!. And, at the same time open-source JavaScript libraries like Prototype, JQuery, Dojo, and Mootools were exploding in popularity.

ECMAScript 5

In July, 2008, a meeting in Oslo led to the disparate parties coming to an agreement to develop ECMAScript 5 (aka Harmony), and JavaScript 6 - leapfrogging both the ECMAScript 3.1 and ECMAScript 4 efforts. ECMAScript 5 is the basis for Node.js’ interpretation engine, and is fully supported there. In Browser support is more spotty, but many transcompilation tools have been created to allow client-side development in ECMAScript 5 as well.

ECMAScript Language Features

ECMAScript and its implementation, JavaScript is an interpreted, dynamically-typed, event-based, multi-paradigm programming language with prototype-based objects. It has syntax that is very reminiscent of C, but there are several important differences from C and C-like languages that can trip up an experienced C programmer. We call these gotchas and I’ll do my best to bring them to your attention.

Interpreted

Much like Java and C#, JavaScript is an interpreted language - it is converted (by the JavaScript engine in the browser or Node) into an Abstract Syntax Tree, which is then converted into bytecode, which is run on an interpreter - a program that converts the bytecode into machine instructions (specific to the machine it is running on) and executes them.

This is in comparison to compiled languages, which are converted into machine code for a specific platform at compile time. Interpretation takes time, which is why interpreted languages are typically slower than compiled ones. However, this has been a major focus of research in Computing Science, and most modern interpreted languages use Just-in-Time compilation and other tools to speed up execution.

You can learn more about interpreted vs. compiled languages, interpreters, and compilers in CIS 570 - Introduction to Formal Language Theory, CIS 705 - Programming Languages, and CIS 706 - Translator Design I.

Dynamically Typed

JavaScript is a dynamically-typed language, which means that types are associated with values, not variables. In fact, all variables in JavaScript are declared with the var keyword. This allows for duck typing - i.e. if it walks like a duck, quacks like a duck, it is, for all intents and purposes, a duck.

This is a powerful language feature in that it reduces the amount of code necessary to carry out some tasks. Consider the use of interfaces in Java and C# - an interface is a contract that different types of objects agree to in order to be used interchangeably. i.e.

public interface Printable { string print(); } class Foo implements Printable { public string print() {return “It’s me!”} } class Bar implements Printable { public string print() { return “I was in the cake the whole time!” } }

In contrast, in JavaScript we only need to define a print method on all classes we want to have that functionality - i.e.:

class Foo { print() { return “It’s me!”} } class Bar { print() { return “I was in the cake the whole time!” } }

GOTCHA: The downside of dynamically-typed languages is it pushes more of the responsibility for program correctness on the programmer - statically typed languages can use a static type checker to ensure that every class in your code that is calling a print() method has one defined - and raise a compilation error if you don’t. For dynamically-typed languages, this problem won’t be apparent until you run your code, and it causes a runtime error.

GOTCHA: A second downside (and strength) is that the type of the value held by a variable can change during runtime - i.e:

var foo = 5 foo = “now I’m a string!”

Is perfectly legal in JavaScript, and can allow us to re-use variable names. However, if you later use the variable expecting it to be an integer, this clearly can cause problems.

Event-Based

JavaScript, due to its tight coupling with browsers, is perhaps the most event-based programming language in existence. The following figure illustrates the browser’s use of an event loop to process events. The JavaScript code is initially parsed by the browser (and executed while it is processed), which defines callbacks, functions that are triggered by specific events (typically UI or timing related). These are generated by the browser (or passed on by the OS through the browser), and placed on a queue, where the event loop process them one at a time:

JavaScript Event Loop

All browsers currently use a single thread to process a page’s JavaScript (though WebWorkers, which we’ll cover later in the semester, offer some possibilities of multithreading), so only one event can be processed at a time. One workaround is to use asynchronous events - basically to trigger some action that can be processed separately - typically by the browser - which creates an event when it finishes (or errors out), which can then be processed at a later time, freeing the event loop to handle new events. For example:

setTimeout( function () { console.log(“Tick”), 2000 }); console.log(“Starting...”);

This code tells the browser that at a future point (>= 2 seconds in the future) it should print the word Tick to the console. The JavaScript interpreter, after notifying the browser to do this, immediately starts executing the next line of code - and prints Starting... to the console. Thus, setTimeout is an example of an asynchronous function.

Node uses a similar event loop mechanism, but provides far greater options for asynchronous actions- which is necessary to enable its servers to scale. A synchronous action blocks execution until it finishes. Consider the following webserver written using Node:

var http = require('http'); handleRequest(request, response) { var data = fs.readFileSync(“index.html”);response.send(data); }); var server = new http.Server(handleRequest); server.listen(80)

This server begins listening at port 80 (the standard port for HTTP requests), and when it receives a request, opens and reads the contents of the index.html file. Once loaded, the data is send as part of the response to the requesting browser.

While the file is being read from the hard drive, Node waits patiently for it to finish... and HTTP requests from other clients pile up in our event queue. The result is a painfully slow website for many of our users.

Instead, Node encourages us to use asynchronous actions, for example, we could re-write our server's handleRequest() function with:

handleRequest(request, response) { fs.readFile(“index.html”, function(err, data) { if(err) { response.statusCode = “404”; response.end(err); } else { response.end(data); }); });

Now, we pass a callback function to the asynchronous readFile() method, which will operate on another thread, allowing the main node event loop to immediately move on to the next request. When the callback finishes (either because of an error or because the file was loaded), it sends the response.

Asynchronous functions are such a built-in idea to Node that asynchronous method names don’t even contain "async" - only the synchronous methods get a special call-out.

Also, note the pattern of (err, data) in the callback parameters. This is a common structure for asynchronous callbacks in Node - if the asynchronous request encounters an error, the first parameter will be an error description, error code, or error object. If not, it will be false (or a token that evaluates to false, like undefined, or null, and the second parameter will be the object requested.

Object-Oriented

JavaScript uses prototype-based object-orientation. JavaScript objects are simply a hash of properties, mapping a valid JavaScript name to a value (even a function). The quickest way to create an object is via literal notation:

var cis = { name: “Computing and Information Sciences”, courses: [“CIS 115”, “CIS 200”, “CIS 300”, “CIS 308”, “CIS 450”, “CIS 501”], welcome: function(visitor) { console.log(“Welcome to the CIS Department ” + visitor + “!”); } }

Object Properties

In many ways, a JavaScript object is simply a collection of key-value pairs, known as properties. Properties are typically accessed via dot notation, i.e. object.property, though you can also use bracket notation, i.e. object[“property”]. Thus, JavaScript objects are also associative arrays. When using bracket notation, any string can be used as a property name, though for dot notation only valid JavaScript identifiers will work.

As of ECMAScript 5, there are 3 ways to enumerate through all the properties of an object:

  1. for ... in <obj> loops, which traverse all properties in <obj> and its prototype chain (we’ll cover prototype chains shortly)
  2. Object.keys(<obj>) will return an array all property keys for <obj> (those that it owns, i.e. not in the prototype chain)
  3. Object.getOwnPropertyNames(<obj>) will also return an array of all the property names owned by <obj>

Creating Objects

We’ve already seen creating an object with literal notation (also called an object initializer). The syntax is a collection of key/value pairs (the properties), with colons between the key and value, and the pairs themselves separated by commas, and the entire collection wrapped in curly braces. This is handy when you need a unique or quick object, and won’t need to create similar ones.

A second approach is to use a constructor function. This is defined like any other function, with the exception of the convention of capitalizing the function name, i.e.:

function Bear(color, hungry, pleasant) { this.color = color; this.hungry = hungry; this.year = year; this.growl = function() {console.log("Grr...");} }

The constructor can then be invoked with the new keyword to create a new object:

var pooh = new Bear(“yellow”, true, true);

Methods and Context

Methods in JavaScript objects are simply properties whose value is a function. These can be supplied in the object literal (as was the case for the welcome method of our cis object, above). We can also define a method in the constructor, as we did with the growl method in our Bear class, above.

The this property of object methods and the constructor refers to the object’s context - essentially, the properties that make up its internal state. Suppose we add a method to our pooh object to use the hungry property set in our constructor:

pooh.makeNoise = function() { if(this.hungry) console.log(“Do you have any honey?”); } pooh.makeNoise();

Because the this keyword in both the Bear constructor and in our new makeNoise method is the pooh object's context, invoking pooh.makeNoise() will print Do you have any honey?.

GOTCHA: The this keyword refers to the innermost function's context. So if we have a function nested in our object, i.e.:

pooh.makeNoise = function() { setTimeout( function(){ if(this.hungry) {console.log("Do you have any honey?")}; }, 2000); }

will print nothing, as the this keyword in the anonymous function passed to the setTimeout() function refers to the setTimeout()'s context, not pooh's. In the setTimeout()'s context, we have not defined a hungry property, so when we try to access it, it's value is undefined, which, in JavaScript, evaluates to false. Accordingly, we do not execute the true branch of the if statement.

A common fix is to assign the context to another variable:

pooh.makeNoise = function() { var self = this; setTimeout( function(){ if(self.hungry) {console.log("Do you have any honey?")}; }, 2000); }

Since self is bound to the this of pooh, the revised method prints:

> Do you have any honey?

after a two-second delay.

Object Prototypes

Notice that in the above example we assigned the method makeNoise to the instance of Bear we bound to the variable pooh. If we create another instance of Bear and invoke the makeNoise() method, as in:

var baloo = new Bear(gray, false, true); baloo.makeNoise();

We will get an error message:

> baloo.makeNoise is not a function

By virtue of assigning the makeNoise to the Bear instance pooh, we created an instance method. Only pooh has that method - other Bear instances have no idea it exists.

If we want a method to be shared by all Bears, we’ll need to modify the Bear constructor’s prototype. All objects in JavaScript have a prototype property, which is derived from the constructor's prototype property. Basically, when an object is constructed, its prototype property is bound to that of its constructor's prototype.

Every constructor has a prototype property, which serves as a “blueprint” for the class. Methods and values defined on the object’s prototype are shared by all instances of that object. Returning to our earlier example, if we want all Bears to have a makeNoise() method, we could add it to the prototype once, and invoke it in all children:

Bear.prototype.makeNoise = function() { if(this.hungry) {console.log("Feed me!");} }

Now all bears have a the makeNoise method through the Bear prototype. If we were to invoke this method for the bears we have defined, as well as a new bear instance:

var shardik = new Bear(“black”, true, false); baloo.makeNoise(); shardik.makeNoise(); pooh.makeNoise();

We would see in the console:

> feed me! > Do you have any honey?

The "feed me!" line comes from shardik. Baloo is not hungry, so he says nothing (remember, his constructor supplied a false value to the hungry property). But because we still have a separate instance method makeNoise defined on the Bear instance pooh, pooh uses his own method. And, because it uses a timeout, it prints its result about two seconds after shardik's, even though it is invoked first.

Prototypical Inheritance

The prototype in JavaScript is similar to a class definition in other languages - it allows us to define class methods in one location in memory that all instances of a class share. However, there are important differences. The JavaScript prototype is itself an object instance. As such, it too is a collection of properties.

Moreover, we can assign a different object to be the prototype. This is the basis of inheritance in JavaScript, which works by chaining objects via their prototypes. Consider if we wanted our Bear class to inherit from a Mammal class. We’d do so by setting the Bear’s prototype to an instance of the mammal class:

function Mammal() {} Mammal.prototype.givesMilk = true; Bear.prototype = new Mammal(); console.log(pooh.givesMilk);

Now, all Bears are inheriting from Mammal, and inherit their properties - in this example, true is printed because pooh now has a givesMilk property (but note, by reassigning the prototype our Bear class no longer has a makeNoise() method - so we should bind the prototype first, then define any additional properties.

This creates a prototype chain from Bear all the way to Object (which is the prototype of all objects).

When we access a property, JavaScript looks through the entire prototype chain until it locates a match. If it can’t it returns undefined.

Finally, we can also create an object with Object.create(prototype), passing in the name of the prototype. This is useful when we don’t need to create a constructor.

ES6 Object Syntax

Because of flexibility in creating classes and the ability to access and change properties at any time, there is a wide variety of ways classes and inheritance are declared in existing code. Moreover, for programmers shifting from imperative statically typed object-oriented languages like Java and C#, prototypical inheritance can be very confusing and lead to mistakes. EcmaScript 6 introduces new syntax for declaring classes modeled on more familiar imperative designs that helps with these challenges. Our Bear and Animal classes defined with this syntax look like:

class Animal {} class Bear extends Animal { constructor(color, hungry, pleasant) { this.color = color; this.hungry = hungry; this.pleasant = pleasant; } makeNoise() { console.log(“Grrrr!”); } }

However, the underlying structure of classes has not changed - the method named constructor is the method invoked with the new keyword (i.e. new Bear()), and the created objects still use prototypal inheritance, and we can still modify object properties at the instance (or indeed, at any point in the prototype chain) level.

Additionally, ES6 introduces getters, setters, and static methods.

Functional

This brings us to JavaScript’s nature as a functional language. In JavaScript, functions are first-class objects. They can be created by literals, passed as function arguments, assigned to variables, returned from functions, and be assigned to object properties.

Declaring Functions

JavaScript functions are declared via a literal, consisting of three parts:

  1. The function keyword An optional name (which must be valid). Functions without names are anonymous.
  2. A comma-separated list of parameter names enclosed by parentheses. This may be empty, but must have the ().
  3. The body of the function, enclosed in curved brackets. These may be empty, but the function definition must have the {}.

For example:

function greet(name){ console.log("Hello " + name); }

ECMAScript 5 also introduces the arrow notation for anonymous functions, which consists of three parts:

  1. The parameter list, enclosed with parentheses (which can be empty).
  2. The arrow keyword (=>).
  3. The body, enclosed with curly braces (which can also be empty).

For example:

var greet = (name) => {console.log("Hello " + name)}

In both cases, the body may signal an end to execution and return a value with the return keyword. A function that finishes its execution without encountering a return statement returns a value of undefined.

Function Scope

In JavaScript, functions are the primary module of execution. One of the ways this impacts us is scope - or where variables are accessible. In JavaScript, the only delimiter in scope is the function body - i.e. any variable declared within the function body is accessible within that body. Consider the code:

function foo() { for(var i = 0; i < 10; i++) {} console.log(i); }

GOTCHA: In most languages, the variable i would not exist outside of the for loop - but in JavaScript it does, because the loop body does not create an independent scope as it is not a function. This can cause problems for certain kind of actions.

A second element to this functional scope is the ability to access variables in an outer scope, i.e:

function foo() { var i = 5; setTimeout( () => console.log(i), 2000); }

In this example, the variable i is accessible in the anonymous function declared in the setTimeout() function.

The let keyword (as of EMCAScript 6) introduces block scope to JavaScript - it limits its scope to any block of code (as defined by a set of curly braces), more like traditional imperative languages. As of the current Node.js implementation, using let to limit scope only applies in strict mode.

You can also always attach a value to a global scope by declaring it as a member of the window object (in a browser) or global object (in Node):

window.globalVar = 5; // in a browser, or global.globalVar = 4; // in Node

Function Parameters

As we would expect, the parameters declared in our function definition are available to us in the function body. However, there are some important differences between how JavaScript and other languages handle parameters.

GOTCHA: Most importantly a JavaScript function can be invoked with a different number of parameters than its definition! I.e. the code:

function foo( bar ) { console.log(bar); } foo(); foo(1,2,3,4);

Will execute without complaint, and print:

> undefined > 1

When invoking a javascript any named parameter not in the list will be set to the keyword undefined. Additional arguments can be accessed via the arguments parameter, which is found in all function contexts.

The arguments parameter is an array-like object. It has a length parameter, and its elements can be accessed with bracket index notation. However, array functions will not work with it. Consider the function:

function print() { for(var i = 0; i < arguments.length; i++) { console.log(arguments[i]); } } print(7, “ate”, 9);

Even though it has no parameters defined, it will print:

> 7 > ate > 9 `

Finally, each function has a this parameter defined, which contains the scope of the function. For example:

function foo() { this.bar = 5; console.log(this.bar); } foo();

Will print:

> 5

GOTCHA: The this parameter always refers to the context of the innermost function it appears within. This can cause challenges for programmers who invoke it within nested functions, i.e.:

function foo() { this.bar = 5; setTimeout(function() { console.log(this.bar) }, 2000); } foo();

Will print:

> undefined

Because the this referred to in the setTimeout is the scope of the anonymous function, not the scope of foo. However, the arrow notation does not create a new this, rather it uses the host function’s this, so

function foo() { this.bar = 5; setTimeout(() => console.log(this.bar), 2000); } foo();

Will print:

> 5

Another common strategy to get around scope changes is the use of a user-defined variable, often self, i.e.

function foo() { var self = this; this.bar = 5; setTimeout( function() { console.log(self.bar); }, 2000); }

The this

Invoking Functions

As in most languages, functions can be invoked by placing parenthesis (with optional parameters) after the function’s name.

function foo() { var bar = 5; console.log(this.bar); } foo(); // function invocation

An interesting aside to this principle is that in JavaScript, a function can be invoked immediately after its definition, i.e.:

function foo() { console.log("bar"); }();

Also, as we saw in the discussion of objects, functions assigned can be methods of an object, and can be invoked as methods.

GOTCHA: We often use the terms function and method interchangeably - but they do have specific meanings. A function stands on its own, whereas a method is a function bound to a property of an object.

Interestingly, function in JavaScript are themselves objects, with the Function prototype. This prototype has several methods that are inherited by all functions, call and apply. These also allow invoking functions, but override the this property used in the context of the function. The first argument to either method is the object to use as the this property, and subsequent arguments are the arguments to pass to the function being invoked - a list of comma-separated arguments for call and an array of arguments for apply.

For example, looking to our earlier bears, we can use the instance method defined on pooh for our other bears using call():

pooh.makeNoise.call(baloo); pooh.makeNoise.call(shardik); pooh.makeNoise.call({hungry: true})

Remember that pooh asks for honey, while other bears demand to be fed. In this case, we would see:

> Got any honey? > Got any honey?

Remember, baloo is not hungry, so if we invoke pooh.makeNoise with baloo as the context, nothing is printed. However, shardik is hungry, so when we we invoke pooh.makeNoise with him as context, "Got any honey?" is printed. In the third invocation, we pass an object literal with a hungry property, and again see "Got any honey?" printed. This latter invocation is an example of duck typing as we discussed earlier - as long as the context has the hungry property, we don't care what kind of object it is.

When Can I Use ES6?

Node already uses most ES6 features, though for some you will need to use strict mode (see https://nodejs.org/en/docs/es6/ for details). Generally speaking, it is a good idea to use strict mode in your code - this mode uses a slightly more stringent subset of JavaScript and also makes many silent JavaScript errors (those the browser ignores in order to run poor code) into runtime errors that are easier to catch. To use strict mode, just add the line:

“use strict”;

If you only want to use strict mode for part of your code, wrap it in function scope:

// Not strict out here function() { “use strict”; // Strict in here }(); // Not strict out here!

Notice too, the use of () to immediately invoke the function after it is defined - this will execute the code within the function scope immediately.

However, many browsers lag far behind in adopting ES6 (and many users lag behind in updating their browsers). However, you can already use most of the features of ES6 via transcompilation - using a specialized compiler to convert your ES6 code to an earlier form of JavaScript that will run in the browser. We’ll cover transcompilation in a future lesson.