Despite years of Republican alarmism, and despite Trump's repeated lies about "millions of people who voted illegally", state voting officials reported only 73 'credible' allegations of voter fraud in 2016, many of those still unconfirmed.

137.7 million people voted. Is there any lie Republicans won't tell in order to manipulate our democracy?

Think about the millions of hours that Americans waste on football. Now imagine a world where the same Americans spend even half that time studying history or economics, or reading a reputable news source, or doing any of the things it takes to be an effective voter.

Don't wave that goddamn flag if you're not willing to do the work.

JavaScript Notes

Language notes — ECMAScript 2019

JavaScript Notes

Compiled by Jeremy Kelly
www.anthemion.org

These are my JavaScript notes, mostly covering ES5 through ES2019. The notes are here primarily for my convenience, but you are welcome to use them, subject to the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. If you find a mistake, please let me know.

As far as possible, example code follows the Split Notation. Some creative adaptations have been employed.

This page includes a two-column print style. For best results, print in landscape, apply narrow margins, and change the Scale setting in your browser's print options to 60%. Firefox and Chrome each have their own set of printing bugs, but one of them usually works.

Contents

I am still compiling these notes. Check back later for more.

Syntax

It is possible to end some lines without semicolons. If a line does end without one, and if the next line begins with a token that is not valid for the start of a line, the preceding line will be interpreted as if a semicolon had been provided. This is called automatic semicolon insertion or ASI. It should be avoided outside of minifiers.

Like other C-style languages, single-line comments in Javascript begin with //. Multi-line comments begin with /* and end with */.

Identifiers

Identifiers must begin with letters, underscores, or dollar signs. After the first letter, digits can be added.

Along with JavaScript keywords, the following reserved words are forbidden for use as identifiers:

  • class
  • const
  • enum
  • export
  • extends
  • import
  • super

When strict mode is enabled, these are also reserved:

  • arguments
  • eval
  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static
  • yield

Primitive types

Primitive types include boolean, number, BigInt, string, and symbol, plus the 'special' types that implement undefined and null. These are copied and compared by value.

Most primitives contain properties, but, unlike objects, they do not allow these to be modified, nor can new properties be added. Primitives also have constructors, and these are typically used for explicit conversions:

let oNum = Number("10.0");

JavaScript does allow primitive constructors to be invoked within new expressions. Doing so creates an object, however, not a primitive instance. The object will have some qualities of the primitive, but it will not be strictly equivalent:

let oObjNum = new Number(10.0);
gAssert(oNum !== oObjNum);

There is generally no reason to use primitive constructors with new.

Booleans

Boolean values are represented with true and false. When evaluated as booleans, falsy values like these are also considered to be false:

  • undefined
  • Zero
  • NaN
  • The empty string
  • null

All other values are considered to be truthy.

Numbers

Number values are represented with double-precision IEEE 754 floats. Until ES2019, JavaScript did not provide an integer type, but all integer values between -253 and 253 retain full precision in the number type. JavaScript defines global variables Infinity and NaN to represent infinite values and undefined numeric results. As in other languages, NaN is usually unequal to every value, including itself. However, Object.is performs a strict comparison that returns true if both arguments are NaN.

Values can be checked for Infinity or NaN with one of several global functions:

isNaN(num)
Returns true if num is NaN, or something other than a number or boolean. Type conversion causes isNaN('') to return false.
isFinite(num)
Returns true if num is neither positive nor negative infinity, and if it is not NaN.

Numbers can be specified with scientific notation. The mantissa and the exponent are separated by E or e:

const gTol = 1.1E-3;

A different base can be specified by prefixing the number:

Prefix Base
0b or 0B Binary
0o or 0O Octal
0x or 0X Hexadecimal

However, non-decimal literals cannot have fractional components.

Strings can be converted to number instances with several global functions:

parseFloat(text)

Trims text of leading whitespace and trailing non-numeric characters, then converts it to a number. Returns NaN if the text is not a valid number. Unlike parseInt, this function does interpret scientific notation.

The Number constructor, by contrast, trims leading and trailing whitespace, but not other non-numeric characters.

parseInt(text)
parseInt(text, base)

Trims text of leading whitespace and trailing non-numeric characters, discards any fractional component, then converts it to a number. Accepts hexadecimal input if the string begins with 0x or 0X, or decimal input if it begins with a non-zero number, or another base between two and 36, if specified. When no base is provided, some implementations interpret strings beginning with zero as octal, while others treat them as decimal. Returns NaN if the text is not a valid number.

Because they mark the exponent with a letter, strings containing scientific notation are not converted properly.

Number class

Number primitives are wrapped by the Number class. Its static members include:

MIN_VALUE
MAX_VALUE
The least and greatest values that can be represented in a double-precision float.
MIN_SAFE_INTEGER
MAX_SAFE_INTEGER
The least and greatest integers that can be represented in a double-precision float without losing precision.
isInteger(num)
Returns true if num is an integer.
isSafeInteger(num)
Returns true if num is an integer and if it can be represented in a double-precision float without losing precision.

Non-static members include:

toString()
toString(base)
Returns a string representation of the number, in decimal, or in the specified base.
toFixed(ct)
Returns a string representation that rounds the number to ct digits after the decimal point.
toPrecision(ct)
Returns a string representation that rounds the number to ct significant digits.
toExponential()
toExponential(ct)
Returns a string representation of the number, in scientific notation. Optionally rounds the mantissa to ct digits after the decimal point.

Math class

The static Math class contains many constants and methods, including those that handle rounding:

floor(num)
ceil(num)
Returns num rounded toward negative or positive infinity.
trunc(num)
Returns num rounded toward zero.
round(num)
fround(num)
Returns num rounded toward the nearest integer or the nearest single-precision float.

Exponents and roots:

E
Euler's number e.
SQRT1_2
SQRT2
The square roots of one-half and two.
exp(exp)
pow(base, exp)
Returns e or base to the power of exp.
sqrt(num)
cbrt(num)
Returns the square or cube root of num.
log(num)
log2(num)
log10(num)
Returns the natural, base-2, or base-10 logarithm of num.

Trigonometry:

PI
cos(rad)
sin(rad)
tan(rad)
Returns the cosine, sine, or tangent of an angle, measured in radians.
acos(num)
asin(num)
atan(num)
Returns the inverse cosine, sine, or tangent of num, in radians, or NaN if num is out of range.
atan2(y, x)
Returns the inverse tangent, in radians, of the slope specified by y and x.

and other functions:

sign(num)
Returns negative one if num is negative, zero if it is zero, and one if it is positive.
abs(num)
Returns the absolute value of num.
min(num, ...)
max(num, ...)
Returns the minimum or maximum of one or more numbers.
random()
Returns a random number greater than or equal to zero, and less than one.

BigInt

In ES2019, true integer values can be represented with BigInt, which has arbitrary length. A BigInt literal is created by appending n to the value:

const oGoogol = 10n ** 100n;

BigInt values cannot be mixed with numbers without explicit conversion, using the Number and BigInt constructors:

let oVal = Number(oGoogol) / 100.0;
return BigInt(oVal);

A BigInt value may lose precision when converted. If it is outside the number range, an infinite value will result.

Strings

JavaScript strings are sequences of 16-bit character values. There is no character type. Strings are immutable.

String literals can be surrounded by single or double quotes. Quote characters are escaped with a single backslash. Long strings can be split across lines by ending each line with a backslash inside the string:

const gWarn = "No people ever recognize \
their dictator in advance";

Single backslashes in other positions have no effect, and are excluded from the string. String instances can be concatenated with the addition operators + and +=.

Array syntax can be used to read characters, just like charAt. Because strings are immutable, the characters cannot be modified:

let oCh = gWarn[0];

Other types define toString methods that are used during string conversions. Overridding this method allows custom output to be produced when the String constructor is used to convert the type:

tPt.prototype.toString = function () {
  return "[" + this.X + ", " + this.Y + "]";
}

let gPt = new tPt(1, 2);
let gText = String(gPt);

Special characters

The usual escape sequences are supported:

Sequence Character
\0 Null
\b Backspace
\f Form feed
\n Newline
\f Form feed
\r Carriage return
\t Tab
\v Vertical tab
\\ Backslash
\' Single quote
\" Double quote

An arbitrary character can be specified by combining \x or \u with a number of hex digits:

Sequence Character
\x XX An element from the Latin-1 character set
\u XXXX A Unicode code point

If \0 is followed by a digit, the digit sequence will be interpreted as an octal number, producing an error if strict mode is enabled, or a possibly unexpected result if it is not. It is safer to specify the null character with \x00.

Template literals

Introduced in ES2015, template literals are strings surrounded by backquotes. They function much like interpolated strings in C#:

let oCt = cRead_Int();
let oText = `Max value: ${(1 << oCt) - 1}`;

Within the literal, a placeholder is defined by surrounding a JavaScript expression with curly braces, and prefixing these with a dollar sign. The expressions are evaluated when the string is defined, their results are converted to strings, and they are inserted within the text.

Template literals can themselves be nested within placeholder expressions:

let oCd = `U${
  (oCt == 1)
  ? "U"
  : `X${(oCt < 0) ? "N" : oCt}`
}`;

Backquote characters can be included by prefixing each with a single backslash. Template literals can also include tabs and line breaks, which are then stored within the string:

const oQuote = `So little pains
  do the vulgar take
  in the investigation
  of truth`;

Unlike C#'s verbatim strings, escape sequences are processed as usual. This processing can be partially avoided by tagging the string with String.raw():

let oPath = String.raw`C:\Temp`;

This does not allow a trailing backslash to be included, however, as that would be interpreted as an attempt to escape the closing backquote.

Tagged literals

A template literal is tagged by prefixing it with the name of a function:

function oAdd_Dist(aStrs, ...aVals) {
  let oSqs = 0.0;

  let oText = aStrs[0];
  for (let o = 0; o < aVals.length; ++o) {
    oText += (aVals[o] + aStrs[o + 1]);
    oSqs += Math.pow(aVals[o], 2.0);
  }

  oText += `  DIST: ${Math.sqrt(oSqs)}`;
  return oText;
}

let oX = 3, oY = 4;
let oLine = oAdd_Dist`X: ${oX}  Y: ${oY}`;

This function is invoked when the template literal is evaluated. The function's first argument is an array containing the static portions of the literal, before and after each placeholder. If there are n placeholders, this array will contain n+1 elements, some of them possibly empty strings. Following the array, the function receives n arguments representing the placeholder results before they are converted to strings. The tag function can use this data to generate its result, which need not be a string.

The tag function's first argument also contains a raw property that shows the string content before escape sequences are processed.

String class

String primitives are wrapped by the String class. Its properties and methods include:

length
Returns the number of code units in the string. Most characters are represented with a single code unit, but some require more.
charAt(index)
Returns a string containing the character at the specified index.
substr(start, len)

Returns the substring that begins at start and has length len. If start is negative, it wraps once back around the end of the string.

Note that this method specifies the substring length, unlike the similarly-named substring.

substring(start)
substring(start, next)

Returns the substring that begins at start and stops at the end of the string, or just before next. If either argument is negative, it is considered to equal zero. If either argument is greater than the length, it is considered to equal the length. If start is greater than end, substring acts as if the arguments were reversed.

Note that these methods specify the substring end point, unlike the similarly-named substr. They differ from slice in that arguments do not wrap around the end of the string, and may be reversed. slice is generally preferred.

slice(start)
slice(start, next)
Returns the substring that begins at start and stops at the end of the string, or just before next. If either argument is negative, it is wraps once back around the end of the string. The extracted string never wraps forward past the end, so arguments greater than the length are treated as if they were equal to the length.
includes(sub)
includes(sub, start)
Returns true if sub is found within the string, searching from the start of the string, or from index start.
startsWith(sub)
endsWith(sub)
Returns true if the string starts or ends with sub.
indexOf(sub)
indexOf(sub, start)
Returns the index where sub first occurs, searching from the start of the string, or from index start. Returns -1 if it is not found.
lastIndexOf(sub)
lastIndexOf(sub, start)
Returns the index where sub last occurs, searching from the end of the string, or from index start. Returns -1 if it is not found.
search(regex)
Returns the index of the first regular expression match within the string, or -1 if no match is found. The global search flag in the expression is ignored.
match(regex)
Returns an array containing substrings matched by a regular expression. If the global search flag is set in the expression, all matches are returned in the array. If the flag is not set, the first array element contains the first match, if any, and the following elements contain substrings matched by capturing parentheses in the expression, if such are defined. If no match is found, the method returns null.
split(delim)
split(delim, max)
Returns an array containing all substrings delimited by delim, which may be a substring or a regular expression. If max is specified, no more than that number of elements are returned.
replace(orig, new)

Returns a new string that replaces string or regular expression orig with substring new. If orig is a string, only the first match is replaced. If it is a regular expression, and if the expression's global flag is set, all matches are replaced.

It is also possible to specify a replacement function for new. The function accepts the matching substring, the substrings matched by capturing parentheses, if any, the match position, and the original string as arguments, and returns the replacement string.

repeat(ct)
Returns a new string that repeats the original ct times.
trim()
trimStart()
trimEnd()
Returns a new string with whitespace removed from one or both ends.
toLowerCase()
toUpperCase()
Converts to a new string containing all lowercase or uppercase characters.
padStart(len)
padStart(len, pad)
padEnd(len)
padEnd(len, pad)
Returns a new string that extends the original's length to len by adding spaces or iterations of pad to the beginning or end. If pad is two or more characters in length, the last iteration may be truncated to fit.

Symbols

ECMAScript 2015 provides the new symbol primitive type. A symbol can be created by invoking the Symbol function:

let gFlagRun = Symbol("Flag");

Note that this is not a constructor, and it cannot be called with new. A description string can be passed to the function, and if it is, that string will be included in the output when the symbol itself is converted to a string. The description has no other effect.

A symbol created this way is unequal to every other symbol, even one with the same description:

let gFlagCache = Symbol("Flag");
gAssert(gFlagRun !== gFlagCache);

Whereas the Symbol function always returns a new symbol, the static Symbol.for method fetches one from the global symbol registry. This method accepts a string parameter to be used as a key within the registry. If a symbol with that key is found, that symbol is returned. If not, a new symbol is created for the key, it is added to the registry, and then returned:

let oCkA = Symbol.for("Ck");
let oCkB = Symbol.for("Ck");
gAssert(oCkA === oCkB);

The static Symbol.keyFor method returns the key that was used to create a registry symbol, or undefined if the symbol is not in the registry.

Additionally, the Symbol class statically defines a number of well-known symbols that are used by JavaScript itself.

Unlike most types, symbols are not converted automatically to strings. Such conversions must be performed explicitly, with the String constructor, or with the Symbol.prototype.toString method. Nor can they be converted to numbers, even explicitly. They can be converted to booleans, but their value is always true.

Symbol-keyed properties

Symbols can be used to add symbol-keyed properties to an object. Each symbol is placed within square braces:

let oMsg = {
  [gFlagRun]: true,
  [gFlagCache]: false
};

The property is also dereferenced with the array syntax:

let oCk = oMsg[gFlagCache];

Because the symbol is unique in value, the resulting property is unique within the object. Assuming the symbol is not shared, this ensures that the symbol-keyed property will never collide with another property, even if the object is handled by a second party. If the symbol is reused in the same object, the second value will take precedence, as would happen if an ordinary property name were reused.

Symbol-keyed properties are not enumerated by for/in loops, or by methods like Object.keys. The static Object.getOwnPropertySymbols method returns an array containing the symbols that have been used to define properties in a particular object.

Special primitive types

undefined and null are unique instances of their own, dedicated types.

undefined is assigned to variables that have been declared but not initialized, among other things. If an attempt is made to read a variable that has not been declared, the browser will produce a ReferenceError that describes the variable as 'not defined'. This should not be taken to mean that the variable is undefined. Instead, the variable does not exist at all.

null represents the absence of a value, as it does in languages with pointers. null is equal to undefined when compared with the == operator.

Objects

Technically, every instance that is not a primitive type is an object. Even functions are objects, and these can define properties, including other functions. Object variables are references, and objects are copied and compared by reference.

Many global values are members of the global object, which is created when the interpreter starts. This includes global properties like undefined and NaN, functions like isNaN, constructors like String, and custom globals defined in the script with var. Within a browser, the Window instance is the global object. In ES2019, globalThis can be used to reference the global this value, which often references the global object.

An object can be serialized by passing it to JSON.stringify, which returns a string containing a JSON representation. The object can be deserialized with JSON.parse. JSON represents data with a subset of the JavaScript object literal syntax.

Properties

Superficially, properties resemble class or structure members in other languages. Each property associates one datum with an object instance:

let oRack = {
  Num: oNumNext,
  Cap: 8
};
...
let oCtAvail = oRack.Cap - oCtUsed;

A property can be added to an object by assigning to it. There is no need to declare it first:

oRack.Name = "WEST";

Property names need not qualify as identifiers. In fact, if it is quoted, any string can serve as a name, even the empty string:

let oLook = {
  "Site E": 108,
  "Site F": 90,
  "": 0
};

In this sense, objects are more like associative arrays than traditional class or structure instances. If a particular name is not a valid identifier, it must be dereferenced with the array syntax:

let oCd = oCdsFromName["Site E"];

This syntax also allows the name to be specified with a variable:

let oKey = "Num";
let oNum = oRack[oKey];

When using the array syntax, nested objects:

let gPrefs = {
  Def: { Name: "New", LenMax: 10 }
};

are accessed by concatenating dereference operators, just as a nested array would be:

gPrefs["Def"]["Name"] = "OBSOLETE";

Because arrays and functions are objects, properties can be added to them in the same way. Adding a property to an array does not change its length unless the name is a valid index that is outside the current index range.

Passing an object to with causes identifiers in the statement or block that follows it to be interpreted as properties of that object, if possible:

with (oData) {
  St = "ACT";
  Alloc(++IdxAct);
  ...
}

Using with is generally discouraged, and it is disallowed in strict mode.

Accessor properties

JavaScript can define getters and setters that are read and written like ordinary class variables, but are backed by functions. Properties defined this way are known as accessor properties.

Accessors are declared in object literals by prefixing the backing functions with get and set. Although overloading is normally not supported in JavaScript, both functions are named after the property they define:

let oRef = {
  Num: 0,
  get Cd() { return "F" + this.Num; },
  set Cd(a) { this.Num = a.substring(1); }
}

The setter accepts a single parameter that represents the rvalue in a property assignment:

oRef.Cd = "F10";

As expected, omitting the getter produces a read-only property, and omitting the setter produces one that is write-only. Accessors are inherited like other methods. If a setter is called from a child object, any property it sets will be added to the child, thus hiding the parent value.

Accessors can be added to existing objects with Object.defineProperty or Object.defineProperties.

Property attributes

Properties have attributes that determine whether they are enumerable, whether they can be reconfigured or deleted, and whether their values can be changed. Attributes are also used to add accessor properties to existing objects.

The attributes for a single property can be set with Object.defineProperty, which accepts the object, the name of the property, and a property descriptor:

Object.defineProperty(oRef, "Cd", {
  get: function () { return "F" + this.Num; },
  set: function (a) { this.Num = a.substring(1); },
  enumerable: true,
  configurable: true
});

If the property already exists, and if it is configurable, it will be modified. If it does not exist, it will be created.

Multiple properties can be configured by replacing the property name and descriptor with a second object that associates names with descriptors:

Object.defineProperties(oRef, {
  Cd: {
    get: function () { return "F" + this.Num; },
    set: function (a) { this.Num = a.substring(1); },
    enumerable: true,
    configurable: true
  },
  Rank: {
    get: function () {
      return (this.Num <= 3 ? "A" : "B")
    }
  }
});

A descriptor can be retrieved by passing the object and property name to Object.getOwnPropertyDescriptor. The object must be the one that originally defined the property, not a descendent.

A descriptor is an object with up to four properties, each defining a specific attribute. A data descriptor configures an ordinary, non-accessor property:

Attribute Value
value The property's starting value.
writable Set to true if the value can be changed. When false, the only way to change the value is to reconfigure it with Object.defineProperty or Object.defineProperties. Even setters in the same object cannot change read-only values. Normally, writing to an inherited property creates a new property in the child, leaving the parent unchanged, but even this is disallowed for read-only inherited properties.
enumerable Set to true if the property can be enumerated by for/in loops or functions like Object.keys.
configurable Set to true if the property can be configured by another call to Object.defineProperty or Object.defineProperties, or if it can be deleted. Attempting to reconfigure a non-configurable property produces a TypeError.

An accessor descriptor configures an accessor property:

Attribute Value
get The accessor's getter implementation.
set The accessor's setter implementation.
enumerable Controls enumerability, as above.
configurable Controls configurability, as above.

When creating new properties, value, get, and set default to undefined, while writable, enumerable, and configurable default to false. If neither value, writable, get, nor set are specified, the object is assumed to be a data descriptor.

When reconfiguring properties, unspecified attributes are left unchanged.

Testing properties

The existence of a property can be tested in several ways. The property can be strictly compared with undefined:

let oCkCd = (aParams.Cd !== undefined);

but this fails to distinguish undeclared properties from those that have been explicitly set to undefined.

The in operator accepts a string operand on the left and an object on the right. It returns true if the string is the name of a property, inherited or otherwise, whether its value is undefined or not:

let oCkCd = "Cd" in aParams;

It also accepts a number and an array. It returns true if the number is a valid index for the array:

let oCk10 = 10 in aVals;

The Object.hasOwnProperty method returns true if its string argument names an own property, rather than one that is inherited. The Object.propertyIsEnumerable method also returns true for own properties, as long as they are also enumerable.

Enumerating properties

The for/in loop iterates the names of enumerable properties within some object, including those of inheritied properties. The static Object.keys method also identifies enumerable properties, but it returns an array of names, and it excludes inherited properties. The static Object.getOwnPropertyNames method returns a similar array, but non-enumerable properties are included. None of these methods return symbol-keyed properties.

Deleting properties

The delete operator removes a property from an object. The property can be indicated with the dot notation:

delete aParams.Cd;

or the array notation:

delete aParams["Cd"];

The operator can also target an array element:

delete oEls[10];

Deleting an element does not change the array size as reported by length, it merely sets the element to undefined. However, if the array is iterated with a for/in loop, the deleted element will be skipped.

If the targeted property or element does not exist, delete will fail silently. Inherited properties cannot be deleted through the child. They must instead be deleted through the parent to which they were added.

Creating objects

Object literals

Objects can be initialized with object literals, which contain property/value pairs within curly braces. Each property is separated from its value by a colon. Property/value pairs are separated by commas:

let oRt = {
  Region: gRegionDef,
  Zone: 0
};

Omitting the properties produces an empty object:

let oRack = {};

Constructors

As in other languages, a constructor is a function that initializes new objects. In JavaScript, however, it is not a member of the type. As will be seen, constructors can be used to store class-static variables and methods. They are invoked like functions, but they are preceded by the new keyword:

let oRg = new tRg(2, 8);

This causes an empty object to be created and passed to the constructor, where it is referenced with this:

function tRg(aMin, aMax) {
  this.Min = aMin;
  this.Max = aMax;
}

Unlike other functions, if the constructor has no parameters, its parentheses can be omitted during the call.

The new object's constructor property is also set. Like other functions, the constructor has a prototype property, the prototype typically has a constructor property, and that value is used as the new object's constructor. By default, this will reference the called constructor, but it can be changed in the prototype.

Constructors are not meant to return values. If an object is returned, the first constructed object will be replaced with the returned value. If a non-object value is returned, it will be ignored.

Object.create

Objects can also be instantiated with the Object.create method. This method accepts an argument that specifies the object's prototype:

let oData = Object.create(tData.prototype);

The new object's constructor property will be set to the constructor of the specified prototype, but that function will not be called.

An optional second argument can be used to define one or more properties. Like Object.defineProperties, this parameter accepts an object that maps property names to property descriptors:

oRef = Object.create(Object.prototype, {
  Cd: {
    get: function () { return "F" + this.Num; },
    set: function (a) { this.Num = a.substring(1); },
    enumerable: true,
    configurable: true
  },
  Rank: {
    get: function () { return (this.Num <= 3 ? "A" : "B") }
  }
});

Inheritance

JavaScript uses prototypal inheritance, which only vaguely resembles traditional OOP inheritance. In other languages, classes are compile-time abstractions. In JavaScript, inheritance for a given object is determined by its prototype property, which references a prototype instance, or null if the object does not inherit. A class in JavaScript is simply the set of objects that share a given prototype. The prototype property is private in non-function objects. However:

  • It can be retrieved by passing the object to Object.getPrototypeOf. It can be set by calling Object.setPrototypeOf, but that is discouraged for performance reasons;
  • Most browsers support the non-standard __proto__ accessor property, which allows it to be read or written;
  • The property is public in functions, so the same prototype can be retrieved from the prototype property of the class constructor.

When a property is accessed, it is first sought in the referencing object. If the property has not been added to the object, it is sought within the object's prototype. The prototype is another object; if it does not contain the property, its prototype is checked, and so on, until the property is found, or until a null prototype is encountered. In this way, every object inherits all the properties of its ancestors throughout the prototype chain, while allowing these to be overridden in derived classes. In particular, a function is shared among class instances by adding it to the prototype:

tRg.prototype.Len = function () {
  return this.Max - this.Min;
};

Properties that are not inherited are called own properties.

Note that the child does not inherit copies of the ancestor properties; it links in the most literal sense to the parent and its current state. If a property changes in the parent, the same change will be observed in the child. However, assigning to that property in the child creates a new property that hides the original, unless the inherited property is read-only. Similarly, if the property is an inherited accessor with a setter, that setter will be called, but it will produce a new property in the child that hides the parent value.

Every object also starts with a constructor property, and, typically, all objects in given class will reference the same instance. This allows class-static variables and methods to be defined in and accessed through the constructor. These could not be added to the prototype, as that would cause them to be inherited, and modifying such properties would produce different values in different instances.

To summarize:

  • Non-static variables are added to the object, and this is typically done inside the constructor;
  • Non-static methods are added to the constructor prototype, which becomes the object prototype;
  • Static variables and methods are added to the constructor.

Prototype and constructor setup

An object's prototype is assigned when the object is created. Every function has a prototype property that references a default prototype object. When the function is used as a constructor, the new object is made to reference the prototype object:

function tOpts() {
  ...
};
let oOpts = new tOpts();

Because every function has this property, every function can theoretically be used as a constructor, though seldom to useful effect.

Along with its prototype, every object has a constructor property that is meant to reference the constructor that created it. The default prototype for a given function contains a non-enumerable constructor property that references the function itself, and this value is copied to the new object. Replacing the function's prototype object can break this link:

tOpts.prototype = {
  Ct: function () {
    ...
  },
  ...
};

The constructor property can be restored manually, or the new properties can be added to the existing prototype, which avoids the problem altogether:

tOpts.prototype.Ct = function () {
  ...
};

The Object.create method accepts a prototype instance, which it assigns as the prototype of the returned object. It also sets the object's constructor to match the constructor property in the prototype:

let oOpts = Object.create(tOpts.prototype);

An object can also be created with an object literal, which causes Object.prototype to be assigned as the object's prototype, and Object as its constructor.

Subclassing

A type subclasses another when its prototype inherits from the prototype of the superclass:

function tStore(aName) {
  this.Name = aName;
  ...
}

function tStoreFast(aName) {
  tStore.call(this, aName);
  ...
}
tStoreFast.prototype = Object.create(tStore.prototype);
tStoreFast.prototype.constructor = tStoreFast;

tStoreFast.prototype.Write = function (aData) {
  ...

The new prototype stores properties to be shared by subclass instances. Instantiating the prototype with Object.create assigns the superclass prototype without invoking its constructor, which would add unwanted properties to the subclass prototype. Instead, superclass properties are added to subclass instances by the subclass constructor, which uses call to invoke the superclass constructor. Because the default protoype is overwritten by Object.create, the constructor property must be restored manually.

Testing inheritance

An object's class is determined by its prototype. The instanceof operator accepts an instance on the left and a constructor on the right:

let oCkStore = aStore instanceof tStore;

It returns true if the instance is an object, and if it inherits from the object referenced by the constructor's prototype property, whether directly or indirectly. Adding non-class properties to the tested object does not change this result. By extension, the operator typically returns true if the right operand is Object, since all objects inherit from Object.prototype by default. Similarly, arrays can be identified by setting the right operand to Array. The isPrototypeOf function also indicates whether one object is the ancestor of another, though it is invoked on the prototype, and receives the child as an argument:

let oCkStore = tStore.isPrototypeOf(aStore);

Object attributes

A non-extensible object is one that does not allow new properties to be added. A sealed object is one that is non-extensible, with properties that are non-configurable as well. A frozen object is sealed and contains all read-only properties.

These qualities are checked with functions like Object.isExtensible, Object.isSealed, and Object.isFrozen. They are applied with Object.preventExtensions, Object.seal, and Object.freeze. They can also be applied by manually configuring property attributes. A non-extensible object cannot be made extensible again. Neither can sealed or frozen objects be unsealed or unfrozen.

Arrays

JavaScript arrays inherit from Array.prototype. They are typically created with array literals, which are comma-delimited sequences inside square braces:

let oInsPend = [ "A11", "B04", "CXX" ];

When commas are listed without intervening values, succeeding values are indexed (and the array length set) as though values had been provided. The last trailing comma before the closing brace is ignored, however:

let oInsMark = [ , "NUL", , ];
gAssert(oInsMark[1] === "NUL");
gAssert(oInsMark.length === 3);

Though they are counted in the array length, the missing values do not produce real elements. In particular, those indices will not be iterated by for/in loops. They can be dereferenced to produce undefined, but that is true for any invalid index.

Arrays can also be created with the Array constructor. Calling Array without an argument produces an empty array. Passing a single numeric argument creates an array of the specified length, but it does not add real elements. Passing multiple arguments, or a single non-numeric argument to Array assigns those values as elements, much like an array literal:

let oInsPend = new Array("A11", "B04", "CXX");

JavaScript arrays are untyped, so different types can be mixed in the same sequence. Multidimensional arrays are implemented as arrays of arrays.

Arrays are indexed with 32-bit unsigned integers, allowing over four billion elements to be stored. The element count is returned by the length property. Because arrays are objects, and because the array syntax can also be used to reference ordinary object properties, negative numbers, non-integer numbers, and other invalid indices can be used to read or write values, and the resulting properties are enumerable, but they do not change the array length. Dereferencing an index that is out of range produces undefined, like any attempt to read an undeclared property.

An array can be truncated or extended by writing to the length property. Assigning to an element with an index that is greater than or equal to the current length also extends the array. In both these cases, however, the length is increased without adding enumerable elements.

Array class

The Array class includes methods such as:

join()
join(delim)
Returns a string containing the string representation of every element, delimited by commas or by delim.
slice(start)
slice(start, next)
Returns a new array containing the elements that begin at start, and stop at the array's end, or just before next. If either argument is negative, it wraps once back around the end of the array. The extracted array never wraps forward past the end, so arguments greater than the length are treated as if they were equal to the length.
indexOf(val)
indexOf(val, start)
Returns the index of the first element that is equal to val, or negative one if no match is found. If start is specified, the search begins at that position. If start is negative, it is considered to wrap once around the end of the string. If start is greater than the last index, the search fails.
lastIndexOf(val)
lastIndexOf(val, start)
Returns the index of the last element that is equal to val, or negative one if no match is found. If start is specified, the search begins at that position. If start is negative, it is considered to wrap once around the end of the string. If start is greater than the last index, the search begins at the last element.
includes(val)
includes(val, start)
Returns true if any element is equal to val. If start is specified, the search begins at that position. If start is negative, it is considered to wrap once around the end of the string. If start is greater than the last index, the search fails. Added in ES2016.
unshift(el, ...)
Adds one or more elements to the beginning of the array, then returns its new length.
shift()
Removes one element from the beginning of the array and returns it.
push(el, ...)
Adds one or more elements to the end of the array, then returns its new length.
pop()
Removes one element from the end of the array and returns it.
concat(add, ...)
Returns a new array containing the original elements, plus any arguments passed to the function. Unlike splice, if one or more arguments are themselves arrays, their elements are added, rather than the arrays as a whole.
splice(start)
splice(start, len)
splice(start, len, ...)
Modifies the array in place by removing elements, or inserting them, or doing both, then returns any removed elements in a new array. The operation begins at position start. If no other arguments are provided, this element and those that follow it are removed and returned. If a len is specified, that number of elements are removed. If more arguments are provided, those values are also inserted at start. Unlike concat, array arguments are inserted as arrays.
reverse()
Reverses the element order in place, then returns the modified array.
sort()
sort(compare)
Sorts the elements in place, then returns the modified array. By default, elements are sorted by their string representations, so numbers are not sorted in increasing order. To customize the sort, pass a compare function that accepts two values, and returns a positive number if the second should be sorted after the first, zero if they are equal, and a negative number if the first should be sorted after the second.

The following methods pass the elements of an array to a custom function, call, which itself accepts up to three values: an element, its array index, and the array as a whole. These array methods also accept an optional this parameter. When this is provided, it is referenced wherever this is used within call:

forEach(call)
forEach(call, this)
Iterates the array and passes each element to function call.
some(call)
some(call, this)
Iterates the array and returns true if call returns true for any element.
every(call)
every(call, this)
Iterates the array and returns true if call returns true for every element.
filter(call)
filter(call, this)
Iterates the array, passes each element to function call, and returns a new array containing the elements for which call returned true.
map(call)
map(call, this)
Iterates the array, passes each element to function call, and returns a new array containing the values returned by call.

The following methods use a callAcc function that accepts up to four values: an accumulator, which stores an ongoing calculation, an element, its array index, and the array as a whole:

reduce(callAcc)
reduce(callAcc, init)
Iterates the array, passes each element to function callAcc, and returns the last value produced by that function. If init is provided, iteration begins at the first element, and init is used as the first accumulator value. If it is not provided, iteration begins at the second element, and the first is used as the accumulator.
reduceRight(callAcc)
reduceRight(callAcc, init)
Iterates the array in reverse, passes each element to function callAcc, and returns the last value produced by that function. If init is provided, iteration begins at the last element, and init is used as the first accumulator value. If it is not provided, iteration begins at the element before the last element, and the last is used as the accumulator.

Array-like objects

Some objects, like the arguments instance defined within functions, are known as array-like objects. These are not true arrays, but they can sometimes be used as if they were. Every such object:

  • Provides a length property;
  • Associates a number of property values with integer indices.

Though they are not Array instances, many Array.prototype methods can be applied to these objects with Function.call or Function.apply.

Regular expressions

Regular expressions are wrapped by instances of the RegExp class. Instances can be created with the RegExp constructor:

let oRegCd = new RegExp("A[1-3]");

or by assigning regular expression literals, which surround the expression with forward slashes:

let oRegCd = /A[1-3]/;

Most characters are expected to match exactly within the target text. Others have special meanings within the expression:

\ / | . * + ^ $ ? : = ! [ ] { } ( )

To match one of these characters, it is usually necessary to escape it with a single backslash.

Tabs and other non-printing characters are specified with the same escape sequences used in ordinary strings, with the exception of the backspace character, which is matched by [\b]. A control character ctrl-X can be matched with \c X.

The trailing slash in the expression literal can be followed by one or more letters that set flags:

let oRegCmds = /F\d\d/ig;

These letters can also be passed as a second parameter to the RegExp constructor. Flags are used to configure the search:

Flag Effect
i Produces a case-insensitive search.
m Enables multi-line mode, which causes ^ and $ to match the beginnings and ends of lines.
g Produces a global search, allowing some functions to process matches beyond the first.

These RegExp methods perform the search:

exec(text)
Returns an array containing a substring matched by the regular expression, plus substrings matched by capturing parentheses in the expression, if such are defined. The array also defines an index property that gives the position of the match within text, and an input property that returns text itself. If the global search flag is set in the expression, the method also sets the lastIndex property within the expression instance to the position just after the match. This position becomes the starting point for the next search, if exec is called again. If no match is found, exec returns null.
test(text)
Returns true if a substring is matched by the regular expression. If the global search flag is set, the method also sets the lastIndex property within the expression instance to the position just after the match. This position becomes the starting point for the next search, if test is called again.

A number of String methods also use regular expressions, including search, match, split, and replace.

Character classes

Expressions can also use character classes, each of which matches one of a number of characters. Enclosing characters within square braces produces a character set, which matches any one of the contained characters:

let oRegNumLot = /[123]/;

Prefixing the characters with a caret negates the set, causing it to match any one character that is not within the braces:

let oRegCdLot = /[^123]/;

A range of characters is specified by joining the lower and upper limits with a hyphen:

let oRegDigOct = /[0-7]/;

Neither periods nor asterisks are treated as special characters within a set, so they need not be escaped.

Other classes include:

Class Match
. Any character that is not a newline.
\s Any ASCII or Unicode whitespace character.
\S Any character that is not matched by \s.
\d Any ASCII number character.
\D Any character that is not matched by \d.
\w Any ASCII letter, number, or underscore character. Note that accented or non-roman characters are not included.
\W Any character that is not matched by \w.

Entire sequences can be matched against a set of alternatives by delimiting sub-expressions with the pipe character:

let oRegRt = /(MAIN\d|AUX\d\d|OFF) /;

Sub-expressions are checked from left to right, and the first to produce a match is used, even if another would match more completely.

Quantifiers

Characters and sub-expressions can be followed by quantifiers that allow them to repeat in the target text:

Quantifier Effect
? Match once or not at all.
* Match zero or more times.
+ Match one or more times.
{ct} Match exactly ct times.
{min,} Match at least min times.
{min, max} Match anywhere from min to max times.

Because they allow characters to be matched zero times, quantifiers like ? and * can produce expressions that match all strings, since every string contains zero or more instances of a given character.

By default, quantifiers implement greedy matches that consume as much of the target text as possible before the remainder is matched with the rest of the expression. Although ? is itself a quantifier, it can also be added to the end of a quantifier to specify a lazy match that consumes as little of the text as is needed to produce a match.

Capturing parentheses

Surrounding characters with parentheses produces a sub-expression that can be modified as a whole by a quantifier or another function:

let oReg = / (XO)+ /;

These capturing parentheses also store the target substring matched by the sub-expression. The substring can be recalled in another part of the expression by prefixing the sub-expression number with a backslash:

let oRegChQuot = /(["']).\1/;

The recalled substring is matched only if the target text contains an exact repetition of the substring that matched the referenced sub-expression. The characters inside non-capturing parentheses are prefixed by ?:.

let oReg = / (?:XO)+ /;

These do not store the matching substring.

Anchors

Normally, expressions are matched wherever possible within the target text. Matches can be constrained to certain positions in the text with anchors. These are not matched to characters, but to positions between characters:

Anchor Position
^ The beginning of the text, or the beginning of any line, if the multi-line flag is set.
$ The end of the text, or the end of any line, if the multi-line flag is set.
\b The beginning or end of a word, which is any point between a \w character and a \W, or between a \w and the beginning or end of the text. Line breaks are already non-word characters, so there is no need to set the multi-line flag.
\B Any point that is not the beginning or end of a word, as defined by \b.

Enclosing characters with parentheses and prefixing with ?= or ?! creates a lookahead, which also constrains the match relative to its surroundings:

Lookahead Effect
patt(?=post) Matches patt if it is followed immediately by post, without consuming or matching post.
patt(?!post) Matches patt if it is not followed immediately by post.

Operators

Logical operators

JavaScript offers the usual logical operators:

Operator Effect
! Logical complement
&& Logical AND
|| Logical OR

Neither && nor || necessarily returns a boolean value. If the left && operand is falsy, the operator immediately returns that value. If the operand is truthy, it returns the right operand, whether that happens to be truthy or falsy. Conversely, if the first || operand is truthy, it returns that value, otherwise it returns the second operand.

Because undefined is falsy, the || operator can be used to select the first defined value from a set of identifiers:

function gExec(aCt) {
  let oCt = aCt || this.CtDef || 0;
  ...

Because the ! operator always returns a boolean, !! can be used to convert truthy or falsy values to boolean equivalents.

The ternary conditional operator ?: works as in other languages, though it is also capable of returning two different types.

Bitwise operators

The bitwise operators work as they do in other languages, but the operands are treated as 32-bit integers. Integer bits outside this range are discarded, as are fractional components:

Operator Effect
~ Bitwise complement
& &= Bitwise AND
| |= Bitwise OR
^ ^= Bitwise XOR
<< <<= Left shift
>> >>= Right shift with sign
>>> >>>= Right shift with zeros

Right shift with sign conserves the high-order bit, so it effectively divides by powers of two, even if the left operand is negative. Right shift with zeros inserts zeros instead. The right operand of all shift operations must be between zero and 31. Negative right operands cause the operator to return zero, while operands greater than 31 are treated as operand % 32.

JavaScript supports the compound assignment operators found in other languages. These include bitwise operators &=, |=, ^=, <<=, >>=, and >>>=,

Arithmetic operators

The arithmetic operators function mostly as expected:

Operator Effect
+ += Addition
- -= Subtraction
* *= Multiplication
\ \= Division
% %= Modulus
** **= Exponentiation (ES2016)

However:

  • Because of JavaScript's aggressive approach to type conversion, the unary plus operator + can be used to convert non-numeric types to numbers;
  • The modulus operator % also works with float values, and, when it produces a remainder, its sign matches that of the first operand;
  • Unary expressions cannot used on the left side of the exponentiation operator. For this reason, negative literals must be parenthesized:

    let oMask = (-2) ** oExp;
    

JavaScript also offers the increment ++ and decrement -- operators found in other languages. They can be used as prefix or postfix operators.

Equality and comparison operators

The loose equality operators == and != check for general equivalence, so various type conversions are allowed. By contrast, the strict equality operators === and !== return false if the operands have different types. When applied to arrays, functions, and other objects, both varieties compare references, so distinct but otherwise identical instances are not equal. There is no operator that tells whether distinct objects or arrays contain the same properties and values.

Like the loose equality operators, the comparison operators automatically convert their operands.

Other operators

void

The void operator accepts a single operand, which it evaluates. It then discards the result and returns undefined.

Sequence operator

As in other languages, the sequence operator evaluates both its operands and returns the value on the right. Because this operator has the lowest possible precedence, the sequence expression must be parenthesized if its result is to be assigned:

let oYOrig = (++oX, ++oY);

typeof

The typeof operator returns a string that gives the general type of its operand, whether "undefined", "boolean", "number", "bigint", "string", "symbol", "object", or "function". Note that the values are always lowercase. null variables are considered to have the "object" type.

Type conversion

JavaScript is permissive about type conversions, and most values are automatically converted to other types when required:

  • undefined produces NaN when converted to a number, while null produces zero. Both produce false when converted to a boolean;
  • true produces one and false produces zero when converted to a number;
  • Numbers are automatically converted to strings. Zero and NaN values produce false when converted to booleans, while other numbers produce true;
  • The empty string is converted to zero or false, as required. Strings that contain valid decimal numbers are converted to those numbers automatically, while other strings produce NaN. The empty string produces false when converted to a boolean, while non-empty strings produce true;
  • Empty arrays produce the empty string when converted to strings, while non-empty arrays produce comma-delimited lists of their elements. Nested arrays are not represented correctly, however. Empty arrays produce zero when converted to a number. Arrays containing a single number or numeric string produce that number when converted to a number, while multi-element arrays and those containing non-numeric elements produce NaN. All arrays produce true when converted to booleans;
  • Objects produce their toString result when converted to strings, or their toString or valueOf results when converted to numbers. Overriding toString allows custom string output to be produced when the conversion is performed with the String constructor. Objects always produce true when converted to booleans.

These conversions are used when comparing values with the == operator, so undefined is equal to null, "1" is equal to one, and "0" is equal to false. When a string is added to any other type with the addition operator +, the non-string type is converted to a string. In some cases, string conversion occurs even when neither operand is a string. Adding two arrays with the addition operator, for instance, causes both to be converted to strings and then concatenated.

Explicit conversions are performed by passing values to the Boolean, Number, String, or Object constructors, or with functions like parseFloat and parseInt. For values other than undefined and null, a string can also be produced by invoking the value's toString method.

Variables

JavaScript variables have no type, so a value of one type can be overwritten with another type at any time.

In strict mode, a variable must be declared before it is assigned, or an error will result. When strict mode is disabled, assigning an undeclared variable automatically declares it within the global object, even if the assignment occurs in a function. Reading from an undeclared variable always produces an error. Undeclared variables can also be deleted from the global object, while declared variables cannot.

Variables can be declared with var, let, or const.

var

In ES5, variables are declared with the var keyword. Multiple variables can be declared and optionally initialized in the same line by separating them with commas:

var oX = 0.0, oY;

Uninitialized variables have the undefined value. Redeclaring a variable that was created with var has no effect. If the new declaration also initializes the variable, it is simply assigned with the new value.

When declared outside of any function, a var variable is created as a property of the global object.

When declared inside a function, such a variable is hoisted, giving it function scope. This makes it accessible outside the containing block, and even before the variable is declared, where its value is undefined:

function gExec(aCkCalc) {
  gWgt = oWgt;
  if (aCkCalc) {
    var oWgt = 0.0;
    ...
  }
}

var should be avoided in modern JavaScript.

let and const

Variables declared with let behave like those in other languages. When declared in a function, they have block scope, so they are inaccessible outside the containing block, and they cannot be accessed before the declaration. They have global scope when declared outside a function, but they do not add properties to the global object.

When using let, redeclaring a variable in the same scope always produces a SyntaxError. Nothing prevents the same variable from being declared in a contained block, however, and when this is done, the inner declaration hides the outer one.

const behaves as let, but it creates read-only variables that produce errors when reassigned. Objects referenced by const variables can be modified as usual. const variables must be initialized where they are declared.

Control structures

if

The if statement automatically converts its argument to a boolean. Because undefined and null are both falsy, this allows the validity of an object reference to be checked very simply:

if (aObj) ...

switch

switch statements are allowed to branch on conditional values of any type. Strict equality is used when comparing conditional values against case values, and case values can be expressions that are evaluated at run time. case blocks that do not break or return fall through to the next block.

for and for/in

Unlike many languages, JavaScript allows var loop variables defined within the initialization statement of a for loop to be accessed outside the loop:

for (var o = 0; o < oCt; ++o) {
  ...
}
let oIdxMatch = o;

Because they are hoisted, such variables can even be used before they are declared. let index variables cannot be accessed before or after the loop.

The for/in loop iterates the indices within an array, and its var loop variables are also accessible from outside:

for (var o in oEls)
  if (Ck(oEls[o])) break;
let oIdxMatch = o;

for/in can also iterate the enumerable properties of an object, including those that were inherited. In this case too, it iterates property names rather than values:

for (let oProp in oData)
  gOut(oProp + ": " + oData[oProp]);

Labels

Normally, the break statement causes the innermost loop or switch to end, while continue causes the innermost loop to iterate. An outer loop can be targeted by prefixing the loop statement with a label, consisting of a label name followed by a colon:

zMain: while (true) {
  zBls: for (let oIdxBl in oBls) {
    for (let oIdxMsg in oMsgs)
      switch (oMsgs[oIdxMsg]) {
        case "HOLD": continue zBls;
        case "DONE": break zMain;
        ...

If break is followed by a label name, the specified loop will end. If continue is followed by a name, that loop will iterate.

Functions

A function declaration consists of the function keyword, the function name, and a parameter list. Any type can be returned by any function, so there is no type in the signature. Parameters are also untyped:

function gReset(aPos, aCkSync) {
  ...

Defining the function within an expression produces a function expression. Though a function name can be provided, these definitions are typically anonymous:

let gReset = function (aPos, aCkSync) {
  ...

Function instances can also be created with the Function constructor:

let gPow2 = new Function("aExp", "return 1 << aExp;");

The last argument is a string that gives the implemention for the new function. The preceding arguments, if any, are strings containing the names of the new function's parameters. The new function is always executed as if it were defined in the global scope, so closures cannot be created this way.

JavaScript does not allow functions to be overloaded. If a second function is declared with the same name, that function replaces the first. Default parameters are not explicitly supported, but all parameters are optional, so default values can be set within the function.

Function declarations can be nested within other functions, but, in strict mode, they can be placed only at the top level of the containing function, or within another block. Function expressions can appear anywhere. Strict mode also gives block scope to nested functions, rather than function scope. Like variables, nested function declarations are hoisted, allowing them to be called before they are declared, if they are accessible. Unlike hoisted variables, which are undefined before their initializations, hoisted functions are usable anywhere.

A method is a function that has been assigned to a property in some object. Functions assigned to array elements are also treated as methods. In JavaScript, functions are themselves objects that can contain their own properties, including additional methods. Like global var variables, global functions are created as properties of the global object.

Invoking functions

JavaScript allows functions to be called without specifying all or any of their arguments. When this is done, the parameters are undefined within the function. Conversely, when a result is read from a function that has not returned a value, undefined is produced.

If a function is called with extra arguments, they are ignored. Within the function, the array-like arguments object can be used to access these and other arguments. arguments was originally presented as a property of the Function prototype, but that has been deprecated; it is now a local variable within the function. arguments can be used to implement variadic functions. arguments has a length property that gives the actual argument count. The function also has a length property, and this gives the parameter count.

this

Within most methods, this refers to the object or array through which the method was invoked. Within a constructor, this refers to the new instance, even if the constructor is itself a method.

In strict mode, within functions that are neither methods nor constructors, this is undefined. It is also undefined within nested functions, even those nested within methods. Before ES5, or when strict mode is disabled, this references the global object.

Every function inherits call and apply methods that invoke the function itself. If an argument is passed to either method, that value is referenced by this when the function is executed. When strict mode is enabled, this can be made to reference a primitive type, null, or undefined. Before ES5, or when strict mode is disabled, primitive types are replaced with wrapper objects, while null and undefined cause this to reference the global object.

When call is invoked with more than one argument, the additional arguments are forwarded to the function. The second apply argument is expected to be an array. If that argument is specified, its elements are passed as arguments to the function.

Closures

A closure binds one or more functions to a persistent copy of the context in which they were defined. Returning a nested function produces a closure that can access variables or call functions in the containing scope, even after the program exits the containing function. The containing function can also return an object that declares multiple functions, allowing the closure data to be manipulated by different operations. This is another way that function scope can be used to limit access to functions and data. All functions within the closure share the same function-scope data, but each invocation of the containing function creates a new context with a distinct copy of that data.

this is a keyword, not a variable, so its meaning changes when a function is invoked in different contexts. The this value can be copied to a variable, which can then be included in a closure. Alternatively, a bound function can be used, this being a new function that wraps the original. The new function is created with the bind method, which is inherited by all functions. Within the new function, this is set to the value of the first bind argument. If additional arguments are passed to bind, those are forwarded to the original function every time the bound function is invoked. If arguments are passed to the bound function, those are also forwarded to the original, following the permanently bound arguments, if any.

IIFE

Functions are sometimes used as namespaces. A global function is defined, variables and functions are declared and used within it, and the containing function is called immediately after. This avoids the name conflicts that can occur when objects are added to the global scope. The pattern is known as the Immediately-Invoked Function Expression or IIFE:

(function () {
  ...
}());

Without the outer parentheses, the interpreter would read this as a function declaration, which is required to specify a name. Only expressions are allowed within parentheses.

Exceptions

Any type can be thrown, but the JavaScript interpreter only throws Error and its subclasses. Error describes the exception with its name and message properties. The Error function can be used as a constructor:

throw new Error("gExec: Invalid name");

but it also creates and returns an instance without new:

throw Error("gExec: Invalid name");

A try block is followed by a catch block, a finally block, or both. A given catch collects all exceptions in the try block, regardless of type. The catch must define an exception variable, even if it is not needed:

try {
  ...
}
catch (oExcept) {
  ...
}
finally {
  ...
}

Miscellanea

Strict mode

The "use strict" directive is an ordinary string that enables strict mode. This mode makes many improvements to the language, including:

  • Instead of creating a new global variable, writing to an undeclared variable produces a ReferenceError;
  • Instead of referencing the global object, this is undefined within non-class functions;
  • The with statement is disallowed;
  • Instead of failing silently, an error is produced when an attempt is made to change a read-only property, or to add or delete a property from a read-only object;
  • Instead of being interpreted as octal values, integer literals that begin with zero produce SyntaxError;
  • Neither arguments nor eval are allowed to be assigned to another object or function;
  • Variables and functions declared within code passed to eval are not added to the containing scope.

Place the directive in the first non-comment line of the script to enable strict mode globally. Place it in the first line of a function to enable it locally:

function gExec() {
  "use strict";
  ...
}

The directive is ignored altogether in versions before ES5.

eval

The global eval function accepts a single string and executes it as JavaScript code. Generally, the code behaves as if it were run from the calling scope. However:

  • If either the calling code or the eval code uses strict mode, then the eval code will be executed within a temporary scope. If strict mode is not enabled, variables declared within eval code will persist after the function returns;
  • If eval is assigned to another variable, and if the function is called from that variable, its code will be executed within the global scope.

The code can always access and modify existing variables in the calling scope.

eval returns the value of its last statement, or undefined if that statement has no value. It also throws unhandled exceptions that were thrown within its code. If the code cannot be parsed, eval produces a SyntaxError.

debugger

The debugger statement pauses script execution and shows the debugger, like a breakpoint.

Sources

JavaScript for Impatient Programmers
Axel Rauschmayer
2019, Axel Rauschmayer

JavaScript Pocket Reference, 3rd Edition
David Flanagan
2012, O'Reilly Media, Inc.

JavaScript: The Definitive Guide, 6rd Edition
David Flanagan
2011, O'Reilly Media, Inc.

JavaScript & JQuery
Jon Duckett
2014, John Wiley & Sons, Inc.

MDN Web Docs: Arithmetic operators, BigInt type, const, Function.prototype.bind, globalThis, let, Math, Object.defineProperty, Regular Expressions, String, Symbol, Symbol (glossary entry), Template literals, var
Retrieved January 2018 - October 2019

BigInt: Arbitrary precision integers in JavaScript
Ecma TC39
Retrieved October 2019

Explain the encapsulated anonymous function syntax
Stack Overflow
Retrieved February 2018