Sync'ing from Memory

JS Quick Restart for the Impatient

For polyglots who can't keep too many languages in their heads and need a deep refresher.

Why this document?

The intention is not to teach Javascript, or cover it completely. This is for programmers (like myself) who work in multiple languages, and after every break from a language (in this case, Javascript) want a quick refresher to get up to speed. And, as of this writing, Javascript.info is a pretty handy collection of explanations and code. And so is MDN.

Quick reminders

  • It's functional.

  • It's imperative.

  • It's object oriented.

  • It's mutable all over the place. (This is a general remark, but an important reminder when coming from Clojure)

  • It's class-oriented. Not!

The Key Syntax Highlights

Declarations

Do not forget let (and var)

And you probably certainly want to use let.

  function foo() {
      // Hoisted to the top!
      bar = 10;

      // Safe-r
      let baz = 20;
      console.log("The value of bar is " + bar);
  }

  foo();

  // 10. The bar is so low, you'll run into it here.
  console.log("The bar outside is a low " + bar);

  // But, we can clean up!
  delete bar;
The value of bar is 10
The bar outside is a low 10
  • var is global scope, or if within a function, all of the function-scope.

  • let is safer. It's always block scope. And let is also not hoisted to the top of the block, unlike var.

  function noop() {
      // ok, though undefined
      console.log(bar);

      // blow up! ReferenceError
      try {
          console.log(blow);
      } catch (e) {
          console.log("Error: " + e.message);
      }
      var bar = 10;
      let blow = 0;
  }
noop();
undefined
Error: Cannot access 'blow' before initialization

Prefer const

const is nothing but immutable reference.

  const some = {};
  console.log(some);

  // Now you can NOT reassign some to anything else
  try {
      some = {"another": "object"}; // Assignment error!
  } catch (e) {
      console.log("Error: " + e.message);
  }

  // But!
  some.message = "hey there!"; // ok
  // some is NOT immutable.

  console.log(some);
{}
Error: Assignment to constant variable.
{ message: 'hey there!' }

Literals

Arrays

  let arr = [1, 2, 3, 'four', 'five']

Strings

  let name = "Ram" // String
  let greeting = `Hello, ${name}` // interpolation
  console.log(greeting)
Hello, Ram

Basic types

  true
  false
  null
  undefined
  42
  31.24
  9911008195436579n // BigInt

Regular Expressions

  /^\d{5}-\d{4}$/

  /^[a-z]/i.test("AbCdEf") // case-insensitive

There are five flags

i

case-insensitive

g

match all instances instead of just the first one

m

match across multiple lines

y

sticky-matching

u

enable the use of Unicode point escapes like so \u{...}

Useful Core Data and Structure Types

MDN Reference

Data Types

  • Boolean

  • Number

  • String

  • BigInt

  • Symbol

Structural Types

  • Array

  • Map

  • Set

  • WeakMap

  • WeakSet

  • Date

  • Object (!)

Others

  • Date

Default Arguments

  function greet(greeting, entity = "World") {
      return `${greeting}, ${entity}!`
  }

  console.log(greet("Hello"))
  console.log(greet("नमस्ते", "ब्रम्हाण्ड"))
Hello, World!
नमस्ते, ब्रम्हाण्ड!

Destructuring

Arrays

Arrays represent a collection of items, and offer a wide variety of methods to work on them. These methods are extremely central to any data processing activities with collections. A good reference is at MDN - Array.

Unfortunately, most methods on the array mutate it, so it is important to plan your code accordingly.

General Ops

Part One

  // Helper - as array-methods mutate, we get fresh ones
  let f = () => ["Apple", "Banana", "Cherry", "Dragonfruit"];  

  let demo = (message, array, op) => {
      console.log(`Performing: ${message}. Array = ${array}`)
      let retval = op()
      console.log(`Return value of op: ${retval}`)
      console.log(`State of array after op: ${array}`)
      console.log("-----------------------")
  }

  a = f()
  demo("Pop", a, () => a.pop())

  a = f()
  demo("Push", a, () => a.push("Mango"))

  a = f()
  demo("Shift", a, () => a.shift())

  a = f()
  demo("Unshift", a, () => a.unshift("Mango"))
Performing: Pop. Array = Apple,Banana,Cherry,Dragonfruit
Return value of op: Dragonfruit
State of array after op: Apple,Banana,Cherry
-----------------------
Performing: Push. Array = Apple,Banana,Cherry,Dragonfruit
Return value of op: 5
State of array after op: Apple,Banana,Cherry,Dragonfruit,Mango
-----------------------
Performing: Shift. Array = Apple,Banana,Cherry,Dragonfruit
Return value of op: Apple
State of array after op: Banana,Cherry,Dragonfruit
-----------------------
Performing: Unshift. Array = Apple,Banana,Cherry,Dragonfruit
Return value of op: 5
State of array after op: Mango,Apple,Banana,Cherry,Dragonfruit
-----------------------

Part Deux

Find, remove, copy operations. I couldn't find an "insert at arbitrary index" method.

  let f = () => ["Apple", "Banana", "Cherry", "Dragonfruit"];  

  a = f()
  b = a.slice()

  console.log(`The sliced array is ${b}`)
  // Finding an item index by the item's value
  console.log(`Banana is at position ${a.indexOf("Banana")}`)
  // Or, pass a function
  console.log(`Banana is at position ${a.findIndex(e => e === 'Banana')}`)

  // Removing 2 items starting position 1 (0-based index)
  removed = a.splice(1, 2)
  console.log(`We removed ${removed}, and the remaining array is ${a}`)

  console.log(`The sliced after the above splice operation is ${b}`)
The sliced array is Apple,Banana,Cherry,Dragonfruit
Banana is at position 1
Banana is at position 1
We removed Banana,Cherry, and the remaining array is Apple,Dragonfruit
The sliced after the above splice operation is Apple,Banana,Cherry,Dragonfruit

Operations over Array contents   non mutating

  let f = () => ["Apple", "Banana", "Cherry", "Dragonfruit"];  

  // Find 6-lettered fruits
  const a = f()
  selected = a.filter(fruit => fruit.length === 6)
  console.log(`The selected fruits are ${selected}`)
  console.log(`The input array stands at ${a}`)
The selected fruits are Banana,Cherry
The input array stands at Apple,Banana,Cherry,Dragonfruit

Flatmap

return ["Apple", "Banana"].flatMap(f => f.toLowerCase())
[ 'apple', 'banana' ]

map

return ["Apple", "Banana"].map(f => f.toLowerCase())
[ 'apple', 'banana' ]

reduce

return ["Apple", "Banana"].reduce((a, b) => a + b)
'AppleBanana'

reduceRight

return ["Apple", "Banana"].reduceRight((a, b) => a + b)
'BananaApple'

Objects. Classes.

There are various styles. It's a matter of choice and needs. Some are easy to write, but less memory efficient. Some are a bit involved to code, but end up being more memory efficient. (You guessed it - using the prototype for common methods!)

Objects

Functions are objects too. And can be used as constructors

  function Foo() {
      this.bar = Math.random();
  }

  // foo got no bar.
  let foo1 = Foo();
  try {
      console.log(foo1.bar); // undefined
  } catch(e) {
      console.log("Error: " + e.message);
  }
  console.log(bar); // Global pollution!

  let foo2 = new Foo(); // Note the new - constuctor invocation
  console.log(foo2.bar); // foo2 is an object
Error: Cannot read property 'bar' of undefined
0.5623509638859285
0.2574167748200096

It's the this keyword that needs attention, under the influence of new.

Modify Objects. The Formal Way

// Boring stuff
// But look up

const o = {};

Object.defineProperty(o, 'immutableProperty', {
    value: 10,
    writable: false
});

console.log(o.immutableProperty); // 10
// You can't assign a new value to
// o.immutableProperty. Yay!

// For multiple properties
Object.defineProperties(o, {
    attribute1: {value: function() {return "I am a function - attribute1"}},
    attribute2: {value: "I am another attribute!"}
});

console.log(o.attribute1());
console.log(o.attribute2);
10
I am a function - attribute1
I am another attribute!

Accessor properties

Make function call (getter/setter) appear like attribute access

  let foo = {
      _val: `foo`,
      get val() { return `${this._val}-${this._val}`; },
      set val(new_val) {
          this._val = new_val;
      }
  }

  console.log(foo._val);
  console.log(foo.val); // 'foo-foo'
  foo.val = 'bar';
  console.log(foo._val);
  console.log(foo.val); // 'bar-bar'
foo
foo-foo
bar
bar-bar

Gives you a nice way to ensure sanity checks before setting values.

prototype and __proto__ are different things

__proto__ was a non-standard property until ECMAScript 2015, although widely defined in various implementations. prototype is typically available on objects like, well, Object, Function, Array, Date – that are used to set the __proto__ objects on newly constructed instances. The correct way to access the prototype of any object are

  • Object.getPrototypeOf() / Reflect.getPrototypeOf(), and

  • Object.setPrototypeOf() / Reflect.setPrototypeOf()

And the use of __proto__ is discouraged in favour of the above - See this.

var F = function() { this.name = "f" }
var f = new F();
console.log(f.__proto__ === F.prototype); // true
console.log(Object.getPrototypeOf(f) === F.prototype) // true
console.log(Object.getPrototypeOf(f) === f.__proto__) // true
console.log(f.prototype) // undefined
true
true
true
undefined

The standard way to denote this object in print is [[Prototype]]

Classes

JS supports the class keyword, and is used as follows.

  class Report {  
      constructor(employee, manager, type) {
          this.employee = employee;
          this.manager = manager;
          this.type = type;
      }
      send() {
          console.log(`Sending ${this.type} report from ${this.employee} to ${this.manager}.`);
      }
  }

  new Report("Peter Gibbons", "Bill Lumbergh", "TPS").send();
Sending TPS report from Peter Gibbons to Bill Lumbergh.

The above is syntax-sugar for the following essentially

  function Report(employee, manager, type) {
      this.employee = employee;
      this.manager = manager;
      this.type = type;
  }

  Report.prototype.send = function() {
      console.log(`Sending ${this.type} report from ${this.employee} to ${this.manager}.`);
  }

  new Report("Peter Gibbons", "Bill Lumbergh", "TPS").send();
Sending TPS report from Peter Gibbons to Bill Lumbergh.

But of course, the class syntax allows for pretty easy to express domain setup. JS classes also support static members (attributes and methods), with a somewhat distinctive syntax.

  class PhoneNumber {
      areaPrefix = '';
      number = '';
      #countryCode = '+91';

      constructor(areaPrefix, number) {
          this.areaPrefix = areaPrefix;
          this.number = number;
      }

      get printable() {
          return PhoneNumber.#format(this.#countryCode, this.areaPrefix, this.number);
      }

      static #format(countryCode, prefix, number) {
          return `(${countryCode}) ${prefix}-${number}`;
      }
  }

  console.log(new PhoneNumber("20", "12345678").printable);
(+91) 20-12345678

Prototype

Javascript uses prototypal inheritance. Object hierarchies chain up via the __proto__ attribute - an attribute which should never be directly accessed. Use the inbuilt (obviously-named) static methods on Object

  • Object.getPrototypeOf

  • Object.setPrototypeOf

  • Object.isPrototypeOf

    Prototypal inheritance is also described in terms of Differential Inheritance. In JS, derived objects are linked to their parents and the lookup of properties is dynamic. Which means, if we modify the prototype of an object at runtime, the effects are noticed on all objects that are linked to it. Which means - objects are linked, unlike class-based OO languages where characteristics are copied at creation time.

Object.create - Prototypal Inheritance

We can use proto objects to create and extend new objects. Note below how we use a "blueprint" object - stateless - and create a new object using Object.create while adding more attributes.


var stateless = {
    someFunc: function() {
        console.log(this.stateVar);
    }
}

var someInstance = Object.create(stateless, {
    "stateVar": {
        value: "Full state honors",
        writable: false, // default
        configurable: false, // default
        enumerable: true // depends
    },
    "moarStateVars" : {
        //
    }
});

someInstance.someFunc()
Full state honors

It gets more interesting. The "blueprint" can be dynamically updated and the derived objects get new data/behaviour. Differential inheritance.

  var stateless = {
      someFunc: function() {
          console.log(this.stateVar);
      }
  }

  var someInstance = Object.create(stateless, {
      "stateVar": {
          value: "Full state honors"
      }
  });

  someInstance.someFunc();

  try {
      someInstance.anotherFunc();
  } catch (e) {
      console.log(e.message);
  }

  stateless.anotherFunc = function() {
      console.log("Another one here - " + this.stateVar);
  }

  someInstance.anotherFunc();

  // But wait, we can *shadow* too
  someInstance.someFunc = function() {
      console.log("I refuse to honor the original contract!");
  }

  someInstance.someFunc();
Full state honors
someInstance.anotherFunc is not a function
Another one here - Full state honors
I refuse to honor the original contract!

Function based constructors


  function Contact(name, phone) {
      return {
          whatsYourName() { return name; },
          call() {
              console.log("Calling " + name + " at " + phone);
          }
      }
  }
  // The data in the above case is available in a closure.
  // But not for outside modification.
  // Of course, the usual pass-by-reference/value semantics apply.
  Contact("Veeru", 108).call();

  function EfficientContact(name, phone) {
      this._name = name;
      this._phone = phone;
  }
  // Everyone has access to the name and phone.

  EfficientContact.prototype.whatsYourName = function() {
      return this._name;
  }
  EfficientContact.prototype.call = function() {
      console.log("Efficiently calling " + this._name + " at " + this._phone);
  }
  // But the above methods are shared across derived instances.

  new EfficientContact('Ram', 108).call();
Calling Veeru at 108
Efficiently calling Ram at 108

The Object literal

let foo = {
    attribute: 10,
    someFunction: function() {
        // Can access attributes
        console.log("My attribute = " + this.attribute);
    }
}
foo.someFunction();
My attribute = 10

Modules

The Module Pattern

let myExternalArg = 10;
let someModule = (function (arg) {
    var somePrivate = 20;
    function someOtherPrivateFunction() {
        // I also have access to the arg, okay?
    }
    // Possibly some initializer code

    return {
        // An object that captures the results
        // of initialization, local variables in this
        // scope via the closure, as well as access
        // to the argument 'arg'
        f1: function() { console.log("I know about arg = " + arg) },
        f2: function() { console.log("I also have my private state = " + somePrivate) }
    };
})(myExternalArg);
someModule.f1();
someModule.f2();
I know about arg = 10
I also have my private state = 20

CommonJS

This is a convention used for packaging modules used in certain environments. Like nodejs.

AMD - Asynchronous Module Definition

This is another convention for packaging modules, with dependency declarations. As the name suggests, this is geared towards enabling asynchronous loading of libraries/modules. Useful when you are executing within the browser and JS sources could exist at different locations, and network latencies with linear loading will make the process slow. Especially when browsers can load in parallel.

Implementations which deal with the AMD format include libraries like RequireJS and Dojo.

Asidebrowserify and webpack

browserify can process node-like module definitions, combine sources from various files, and prepare them for use within the browser. webpack helps in creating complex source-code transformation pipelines.

ES2015 Modules

Only mentioned here for the sake for completeness (as of this writing.) The Auth0 article (mentioned in the reference) is a very good read.

Bindings and Closures

Dynamic bindings are a thing. The following is a window into the memory and execution models of JS.

  function foo() {
      console.log("Hello, " + this.name + "!");
  }
  foo(); // Hello, undefined!

  let context = { name: "Ram" };
  // We bind the 'this' in foo to context
  let caller = foo.bind(context);
  caller(); // Hello, Ram!
Hello, undefined!
Hello, Ram!

Async

Comprehensive documents on MDN: Promise.

This is too big a topic by itself. But apart from the fact that you deal with the asynchronous nature of various calls by way of passing callbacks, you can also make use of some constructs/libraries that allow you to lay out your code in a more serial manner.

Use Promise objects, if you'd like to avoid the usual callback style. But remember that Promise objects don't let the computation outcomes escape easy. In a way, it reminds of the core.async way of Clojurescript - only, a bit different and more involved. But it has clear semantics about differentiating between success and error conditions.

To make it a bit easy for you to consume asynchronous computation, there's a more complicated async-await route to structuring your code.

  const slowIdentity =
        (x) => new Promise(resolve => {
            setTimeout(() => {
                resolve(x);
            }, 2500);
        });

  async function awaiter() {
      console.log("I'll wait...");
      console.log("And the value is - " +
                  await slowIdentity(5));
      console.log("Ciao!");
  }

  awaiter();
I'll wait...
And the value is - 5
Ciao!

Metaprogramming

A great reference at MDN: Metaprogramming. The following is a sample, reproduced from the above MDN site.

  let handler = {
      get: function(target, name) {
          console.log(`Attempting to get ${name}`)
          let retval = target[name]
          if (!retval) {
              console.log(`\tDid not find ${name}`)
              retval = 42
          }
          return retval
      }
  }

  let p = new Proxy({}, handler)
  p.a = 1
  console.log(p.a, p.b)
Attempting to get a
Attempting to get b
	Did not find b
1 42