paulgorman.org/technical

JavaScript

(2015, 2018)

JavaScript is a loosely typed language with objects as containers and prototypal inheritance. It supports anonymous functions. Except in name, JavaScript has nothing to do with Java.

JavaScript in the Browser

Link to external Javascript file from HTML doc:

<script src="script.js"></script>

Inline Javascript:

<script>
    <!--
    my script
    -->
</script>

All scripts on a page share one global namespace. Watch out for name collisions. A common work-around is to minimize the possibility of collisions by wrapping a script in one outer function:

var myScript = (function () {
	var myPrivateVar = 'Thing';
	function myPrivareFunction() {
		// Do stuff
	}
	return {    // Exposed global/public stuff
		myPublicFunction: function () {
			// Stuff
		}
	}
})();

Locate scripts as close to the closing body tag as possible (i.e. — at the end of the document, not inside the head section as is commonly done).

JavaScript from the Command Line

SpiderMonkey is a Mozilla JavaScript engine which can be used outside the browser. The environment varies slightly from the browser; you get print() instead of console.log(), for example.

Run scripts from the command line like:

$  js myScript.js

On macOS:

$  ln -s /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc ~/bin/js

Comments, Variables, Flow Control

// A comment.

/*
   A
   multi-line
   comment.
*/

var myArray = ['red', 'blue', 'green'];

if (bar == 0) {
	doStuff();
} else if (bar == 1) {
	something();
} else {
	panic();
}

for (var i = 0, limit = oldThings.length; i < limit; i++) {
    newThings.push(oldThings[i]);
}

var i = 0;
while (i < 6) {
	doStuff();
	i++;
}

var i = 0;
do {
	i += 1;
	console.log('Increment is ' + i);
} while (i &lt; 3);

switch (color) {
	case 'red':
		document.write('The color is red.');
		break;
	case 'blue':
		document.write('The color is blue');
		break;
	default:
		document.write("It's not red or blue.");
}

Variable scope

Variable scope is either global or function. Beware. (In Javascript 1.7, variables declared with let have block scope, but it’s not widely supported yet.)

var x = 0;

function foo1() { // Global score.
	alert(x); // 0
}

function foo2(x) { // Local scope.
	alert(x); // 0
}

function foo3() {
	var x = 3; // Local scope.
	alert(x); // 3
}

function foo4() {
	if (true) {
		var x = 4; // No block scope in pre-1.7 Javascript!
	}
	alert(x); // 4, because x is function scope.

	if (true) {
		let x = 1000; // Javascript 1.7 and later has 'let' for block scope.
		alert(x); // 1000, block scope
	}
	alert(x); // 4, function scope
}

var foo5 = function () {
	var x = 5;
	return function() { // Closure still (parent's) function scope.
		alert(x); // 5
	}
}

function Foo6() {
    this.x = 6; // Object property is function scope
}
alert(new Foo6().x); // 5

Foo6.prototype.x = 666; // No assignment; x fixed in constructor.
alert(new Foo5().x); // 5 still.
Foo.prototype.y = 777; // Property y established by composition.
alert(new Foo5().y); // 777

Strict Mode

ECMAScript 5’s strict mode lets us opt in to language restrictions that eliminates certain errors and enables some performance optimizations (see MDN on strict mode). Strict mode can apply either to whole script or a single function.

function myFunction() {
	'use strict';
	doStuff();
}

Primitives and Wrapper Objects

Everything in JavaScript is an object, except these types:

Unlike objects, they are immutable. When we assign a new value to and existing string, the new value of the string resides at a different address than the old value (and the old string remains in memory until it’s garbage collected). However, even these primitives sometimes appear to behave like objects.

var n = 3;
var m = n + 1;    // m is 4
var a = n.toString() + ' blind mice.';    // A number primitive has a method?!
print(3 + ' wise men');    // Concatenating an immutable number with a string!?
print(a.length);    // 10; strings primitives have a length method?
print (n + 3);    // 6

JavaScript primitives have corresponding wrapper objects (e.g. Number, Boolean, String). Those wrapper objects have methods. When we try something like concatenating a number with a string, JavaScript briefly instantiates a Number object, and uses its toString() method to complete the concatenation; the original number value remains unchanged.

True or False Comparisons

The == and != operators do type coercion before comparing, which masks type errors by evaluating expressions like ' \t\r\n' == 0 as true. Instead, use === or !== when comparing against: 0 '' undefined null false true.

Functions

// Call a function:
var result = add(2, 2)    // Returns 4

// A function declaration:
function add(n1, n2) {
	return n1 + n2;
}

// A function expression:
var add = function (n1, n2) {
	return n1 + n2;
};

Function declarations and function expressions work the same, except that function declarations get hoisted to the top of their scope (function or global scope). The JavaScript interpreter looks ahead, so that functions can be used before they’re declared. Functions are first-class objects. They can be assigned, passed, or returned like any other object. JavaScript functions accept any number of arguments (and JavaScript doesn’t throw an error if we supply the wrong number of arguments). Functions have a length property, which is the number of arguments the function declaration defines. Functions have an array-like property called arguments, which includes the argument with which it was invoked.

function foo(a, b, c) {
	return arguments[0];
}
foo('alpha', 'bravo', 'charlie');    // Returns 'alpha'

Immediate functions

var flowers = [
    'Roses',
    'Lilies',
    (function () {
        var otherFlowers = ['Possies', 'Dandellions', 'Blue Bells'];
        return otherFlowers[Math.floor(Math.random() * (otherFlowers.length - 1))];
    }()),    // Executes immediately.
    'Carnations'
];
print(flowers);

Arrow Functions

Introduced in ECMAScript 6, arrow functions make some small anonymous functions more concise. However, arrow functions are not merely syntactic sugar; and arrow functions share the this of their enclosing score, rather than an automatically created this of their own. Arrow functions also share the arguments object of the enclosing scope.

var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

elements.map(function(element) {
  return element.length;
}); // [8, 6, 7, 9]

// The above written as an arrow function:
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// With only one parameter, omit the surrounding parentheses:
elements.map(element => {
  return element.length;
}); // [8, 6, 7, 9]

// In an arrow function with only a `return`, omit even the `return` (and braces):
elements.map(element => element.length); // [8, 6, 7, 9]

// Where we need only a single property (`length`), use a destructuring parameter:
elements.map(({ "length": lengthFoo }) => lengthFoo); // [8, 6, 7, 9]

// The desctructuring parameter, even more concisely than seen above:
elements.map(({ length }) => length); // [8, 6, 7, 9]

Arrow functions can’t act as constructors; trying to call them with new throws an error.

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

Object Literals

An object literal is a group of name:value pairs surrounded by curly braces and separated by commas. An object literal can be used anywhere an expression is legal.

var colorThings = {
	red: 'The fire engine',
	blue: 'The cloudless sky',
	'blue-green': 'A bowl of limes'
};

The name need not be quoted if it contains only letters, numbers, or underscores and is not a reserved word (i.e. — a legal JavaScript name).

Object Value Retrieval

colorThings.red    // Returns "The fire engine"; dot notation for legal names
colorThings["blue-green"]    // Use bracket notation for non-legal names

colorThings.magenta    // Returns `undefined`
var c = colorThings.indigo || "A default value";

The bracket notation accepts any valid expression. For example, we can get an object property with a variable:

colorThings[myColor];

Object Value Updating, Property Setting

colorThings['blue-green'] = 'Murky algae';
color.violet = 'Tiny flowers';    // Set new property`

Pass by Reference

Objects are passed by reference (the value is never copied).

var x = {}, y = {}, z = {};    // x, y, z refer to different objects
x = y = z = {}    // x, y, z refer to the same object

Pseudo-Classical Inheritance

Although JavaScript was designed to use prototypal inheritance, it supports an object inheritance method that resembles class-based inheritance:

// Pseudo-class constructor function.
// The constructor name is capitalized by convention.
var Vehicle = function (make, model) {
	this.make = make;
	this.model = model;
	this.speed = 0;
};

// Objects created by the constructor inherit from prototype.
Vehicle.prototype.accelerate = function (n) {
	n = typeof n !== 'undefined' ? n : 1;    // Default value.
	this.speed = this.speed + n;
};

// Make a sub-class constructor.
var Car = function (make, model) {
	this.wheels = 4;
	Vehicle.call(this, make, model);    // Invoke super-class constructor
};

// Make it inherit from super-class.
Car.prototype = new Vehicle();

// Instantiate an instance with 'new'.
var myCar = new Car('Ford', 'Prefect');

this

this refers to the object in or on which the function was called. When creating objects with new, this refers to the new object. Methods like call(), apply(), and bind() let us override what this points to. See the MDN documentation on this.

Prototypal Inheritance

In prototypal inheritance a new object is created by being copied from another object. No constructors, no classes.

var vehicle = {
	make: 'Boeing',
	model: '747',
	speed: 0,
	accelerate: function (n) {
		n = typeof n !== 'undefined' ? n : 1;
		this.speed = this.speed + n;
	}
};

// New object inherits from vehicle:
var car = Object.create(vehicle);

Until we re-assign values to the properties car inherited from vehicle, those properties continues to point to vehicles values.

print(car.speed);    // 0
vehicle.speed = 25;
print.(car.speed);    // 25; car.speed points to vehicle.speed value
car.speed = 99;
print(cars.speed);    // 99; points to new value, memory address
print(vehicle.speed);    // 25; still points to its own value`

If we need to do some object initialization/construction for new objects, one solution is to create the objects using the factory pattern. It’s also possible to set some values during object creation:

var ferrari = Object.create(car, {
    make: {
        value: 'Ferrari',
        enumerable: true,
        configurable: true,
        writable: true
    },
    fast: {
        value: true,
        enumerable: true,
        configurable: true,
        writable:true
    }
});

Regular expressions

Regex objects can be written as regular expression literals:

var re = /^Foo.*/gmi;
console.log("Foo bar bar".replace(re, "Bar"));    // Bar bar bar

Array Extras

// Array.map(myFunction) runs function on every array element, returns results in new array.
var myNumbers = [1, 4, 9]
var sqRoots = myNumbers.map(Math.Sqrt);    // [1, 2, 3]

// Array.forEach(myFunction) runs function on every array element
myNumbers.forEach(console.log);    // 1 4 9

// Array.filter(myFunction) runs function on every array item.
// Returns new array with all items for which function returned true.
myNumbers.filter(oddNumber);    // [1, 9]

// Array.every(myFunction) runs function on array items as long as function returns true.
// It returns true if function returned true for every array item.
myNumbers.every(lessThanTen);    // true

// Array.some(myFunction) runs function on array items as long as function returns false.
// It returns true if function returns true for any array items.
myNumbers.some(evenNumber);    // true, because 4 is even

// Array.indexOf() returns index of first occurrence
var myArray = ['a', 'b', 'c', 'd']
console.log('Index of c in myArray is ' + myArray.indexOf('c'));    // 2

// Array.lastIndexOf() returns index of last occurrence
var myArray = ['a', 'a', 'b', 'c', 'a', 'b']
console.log('Last occurrence of a at ' + myArray.lastIndexOf('a'));    // 4

String Methods

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/prototype#Methods

JSON

JSON serializes JavaScript objects.

// Prepare the data out as a string:
var jsonStructuredData = "{color: 'red', size: 'large'}";
// Read it back in:
var myData = JSON.parse(jsonStructuredData);`