Modern JavaScript for the Impatient - Book Review
Contents
This blogpost is a brief review of the book titled, Modern JavaScript for the Impatient, written by Cay S. Horstmann
My intention of going through this book is to crank out a quick dashboard that
involves Java Script, React JS, Node components that interact with GBQ. I have
always worked with R
, Python
and have been limited by the one page app
restriction of shiny
, streamlit
. Of course, there are ways to make a one
page look like a multiple page app but at its core, shiny
and streamlit
apps
are single page apps. Transition to an app based on JavaScript and React JS will
not be easy as one needs to spend considerable time understanding each of these
tech stack elements as well as understand the way to make them work with each
other. There are people who dedicate their entire working lives to master this
part of tech stack. My attempt is more humble as I want to churn out merely a
bare minimum working dashboard that involves JavaScript stack on front end and
back end
Preface
The book is mainly targeted towards a programmer who wants to learn JS without
getting bogged down with obsolete JS that one comes across in many videos,
tutorials and books. Someone like me who has coded in R
, Python
, C++
might
be put off, if they had to learn the historical baggage that comes with JS. In
that spirit, the author mentions five golden rules that one must keep in mind to
avoid “classic” features of JS
- Declare variables with
let
orconst
, notvar
- Use strict mode
- Know your types and avoid automatic type conversion
- Understand prototypes, but use modern syntax for classes, constructors and methods
- Don’t use
this
outside constructors or methods
About the author
From Cay S. Horstmann’s homepage
I grew up in Northern Germany and attended the Christian-Albrechts-Universität in Kiel, a harbor town at the Baltic sea. I received a M.S. in computer science from Syracuse University, and a Ph.D. in mathematics from the University of Michigan in Ann Arbor. I taught computer science at San Jose State University for almost thirty years and held visiting appointments at universities in Germany, Switzerland, Vietnam, and Macau. I was a “serial entrepreneur” before that was a thing, including a stint as VP and CTO of a dot com startup that went from three people in a tiny office to a public company. In my copious spare time I write books and develop online courses for beginning and professional programmers.
Values and Variables
Every value in JavaScript is one of the following types:
- A number
- The Boolean values
false
andtrue
- The special values
null
andundefined
- A string
- A symbol
- An object
The non-object types are collectively called primitive types
JS has two kind of comments. Single line comments start with //
. Multiple line
comments start with /*
and end with */
You can store a value in a variable with the let
statement
|
|
In JS, variables do not have a type. You are free to store values of any type in
any variable. If you do not initialize a variable, it has a special value
undefined
Avoid two obsolete forms of variable declarations, with the var
keyword and
with no keyword at all
The name of a variable must follow the general syntax of identifers. An identifier consists of Unicode letters, digits and the underscore and dollar characters.
JS has no explicit integer type. All numbers are double-precision floating-point numbers.
JS has both functions and methods.
JS has two ways to indicate the absence of a value. When a variable is declared
but not initialized, its value is undefined
. This commonly happens with
functions. When you call a function and fail to provide a parameter, the
parameter variable has the value undefined
. The null
value is intended to
denote the intentional absence of value.
String literals are enclosed in single or double quotes.
Template literals are strings that can contain expressions and span multiple lines. These strings are delimited by back-ticks
JavaScript objects are very different from those in class-based languages such as Java and C++. A JavaScript object is simply a set of name/value pairs or properties like this :
|
|
Such an object has only public data and neither encapsulation nor behavior. The object is not an instance of any particular class. In other words, it is nothing like an object in traditional OOPS.
A property name is always a string. If the name doesn’t follow the rules of an identifier, quote it in an object literal
In JS, an array is simply an object whose property names are strings ‘0’, ‘1’, ‘2’ and so on. Strings are used because numbers can’t be used as property names. Let’s say you declare
|
|
This is an object with five properties : '0', '1', '2', '3' and 'length'
. One
needs to use the bracket notation to access the first four properties. For
convenience, the argument inside the brackets is automatically converted to a
string. This gives one the illusion of working with an array in a language such
as Java or C++
An array can have missing elements:
|
|
A trailing comma does not indicate a missing element. For example [1,2,7,9,]
is an object with four elements.
When an array needs to be converted to a string, all elements are turned into strings and joined with commas
JS like Java, has no notion of multidimensional arrays, but you can simulate them with arrays of arrays
JSON is a lightweight text format for exchanging object data between applications. It uses JavaScript syntax for object and array literals with a few restrictions:
- Values are object literals, array literals, strings, floating point numbers
and the values
true, false
andnull
- All strings are delimited by double quotes, not single quotes
- All property names are delimited by double quotes
- There are no trailing commas or skipped elements
The JSON format per se doesn’t support object references (although an IETF draft exists), hence JSON.stringify() doesn’t try to solve them and fails accordingly.
Object Literals from a medium article: In plain English, an object literal is a comma-separated list of name-value pairs inside of curly braces. Those values can be properties and functions. Here’s a snippet of an object literal with one property and one function.
|
|
All members of an object literal in JavaScript, both properties and functions, are public. The only place you can put private members is inside of a function.
Assigning an object literal to another variable only performs a shallow copy, which means you get the reference of the object instead of the actual value.
You cannot copy an object literal without manually copying all the values.
Lastly, you can mutate members of an object literal, meaning you can add and remove them as you please.
Object literal should be used if you want to create objects on the go with no intention of copying values to another object or maybe mapping one object to another.
If you will need to create multiple instances of a structure and perform operations based on predefined values, then you should use a function constructor
Destructuring is a convenient syntax for fetching the elements of an array or values of an object.
The left-hand side is not an object literal. It is a pattern to show how the variables are matched with the right-hand side. While destructuring, one can provide a default that is used if the desired value is not present in the object or array.
Control Structures
JavaScript, like Java and C++ differentiates between expressions and statements. An expression has a value. A statement never has a value. Instead, it is executed to achieve some effect.
|
|
The above is a statement whose effect is to declare and intialize the x
variable. Such a statement is called a variable declaration. The simplest form
of a statement is an expression statement. It consists of an expression
followed by a semicolon. An expression statement is only useful for an
expression that has a side effect.
Statements don’t have values, but the JavaScript REPL displays values for them anyway.
In JS, certain statements must be terminated with semicolons. The most common
ones are variable declarations, expression statements, and nonlinear control
flow. However JS will helpfully insert semicolons for you. The basic rule is
simple. When processing a statement, the parser includes every token until it
encounters a semi colon or an “offending token” - something that could not be
part of the statement. If the offending token id preceded by a line terminator,
or is a }
, or is the end of input, then the parser adds a semicolon.
Never start a statement with (
or {
. Then you don’t have to worry about the
statement being considered a continuation of the previous line.
A semicolon is inserted after a nonlinear control flow statement( return, break, continue, throw, yield
) that is immediately followed by a line terminator.
Since semicolons are only inserted before a line terminator or a }
, it is
important that you specify a line terminator if you have multiple statements on
the same line.
Equality of objects means that the two operands refer to the same object. References to different objects are never equal, even if both objects have the same contents
How does JS evaluate x==y ?
- If the two operands have the same type, compare them strictly
- The values
undefined
andnull
are loosely equal to themselves and each other but not to any other values - If one operand is a number and the other a string, convert the string to a number and compare strictly
- If one operand is a Boolean value, convert both to numbers and compare strictly
- If one operand is an object but the other is not, convert the object to a primitive type, then compare loosely
There are three ways in which for
loop can be used
- plain old
for
loop like one uses in Java for of
construct to look over an iterablefor in
construct to loop over the keys
Functions and Functional Programming
One declares a function by providing the name of the function, name of the parameters and body of the function. One does not need to specify the types of function parameters.
A function may choose not to specify a return value, in which case the purpose of the function is mainly to create a side effect
One can use define a function literal using arrow
|
|
You provide the parameter variables to the left of the arrow and the return value to the right. If there is a single parameter, one need not enclose it by parenthesis
|
|
If arrow function is more complex, one can place the entire logic in a block
A function with free variables is called a closure. Free variables are the variables that are used in the code but are not declared as parameters or local variables
JS has its share of unusual features, some of which have proven to be poorly
suited for large-scale software development. Strict mode outlaws some of these
features. To enable strict mode, place the line use strict
as the first
non-comment line in your file
The spread operator spreads out the elements as if they had been provided separately in the call. The rest declaration causes a sequence of values to be placed in to an array
Every declaration is hoisted to the top of its scope. That is, the variable of
function is known to exist even before its declaration, and space is reserved to
hold its value. With let
and const
declarations, accessing a variable before
it is declared throws a ReferenceError
. In strict mode, named functions can
only be declared at the top level of a script of function, not inside a nested
block. As long as you use strict mode and avoid var
declarations, the hoisting
behavior is unlikely to result in programming errors
In Java, you can catch exceptions by their type. Then you can handle errors of
certain types at a low level and others at a higher level. Such strategies are
not easily implemented in JS. A catch
clause catches all exceptions, and the
exception objects carry limited information. In JS, exception handlers typically
carry out generic recovery or cleanup, without trying to analyze the cause of
failure.
The purpose of the finally
clause is to have a single location for
relinquishing resources that were acquired in the try
clause, whether or not
an exception occurred.
JavaScript, by default, does not support named parameters. However, you can do something similar using object literals and destructuring. You can avoid errors when calling the function without any arguments by assigning the object to the empty object, {}, even if you have default values set up.
|
|
the var
keyword only supports function scope. let
on the other hand, allows block-scoped variables
A good example of variable hoisting is
|
|
Object Oriented Programming
In JS, all properties are public, and they don’t seem to belong to any class other than Object.
An example of an object declaration is
|
|
A prototype collects properties that are common to multiple objects.Here is a prototype object
|
|
When creating an employee
object, we set its prototype. The prototype is an
“internal slot” of the object. That is the technical term used in the ECMAScript
language specification to denote an attribute of an object that is manipulated
internally without being exposed to JavaScript programmers as a property. One
can read and write the internal slot with the methods Object.getPrototypeOf
and Object.setPrototypeof
Lookup in the prototype chain is only used for reading property values. If you write to a property, the value is always updated in the object itself.
One can write a factor function that creates new object instances with a shared
prototype. There is a special syntax for invoking such functions, using the
new
operator. By convention, functions that construct objects are named after
whta would be the class in a class-based language.
|
|
When a call to the function is done via new
operators, there are a bunch of
this that take place:
-
The
new
operators creates a new object -
The
[[Prototype]]
internal slot of that object is set to theEmployee.prototype
object -
The
new
operator calls the constructor function with three parameters,this, name, salary
-
The body of the
Employee
function sets the object properties by using thethis
parameter -
The constructor returns, and the value of the
new
operator is the now fully initialized object -
The variable
z3
is initialized with the object referenceThe
new
operator looks just like a constructor call, but it isn’t a class. It’s just a function. Every once in a while, you should remind yourself that a JS class is nothing more than a constructor function, and that the common behavior is achieved with prototypes.
Nowadays, JS has a class syntax that bundles up a constructor function and prototype methods in a familiar form.
|
|
One must definitely use the class
syntax. The syntax gets a number of fiddly
details right that you do not want to manage manually. Just realize that a JS
class
is a syntactic sugar for a constructor function and a prototype object
holding the methods
Classes, unlike functions, are not hoisted. You need to declare a class before you can construct an instance.
A field is private when its name starts with #
. A method is private if its
name starts with a #
In a class
declaration, you can declare a method as static
. Such a method
does not operate on any object. It is a plain function that is a property of the
class.
In JS, as in Java, you use the extends
keyword to express the relationship
among the Employee
and Manager
classes.
|
|
Behind the scenes, a prototype chain is etablished. The prototype of
Manager.prototype
is set to Employee.prototype
. That way, any method that is
not declared in the subclass is looked up in the superclass.
In Java and C++, it is common to define abstract superclasses or interfaces so that you can invoke methods that will be defined in subclasses. In JS, there is no compile-time checking for methods applications, and therefore, there is no need for abstract methods.
In a subclass constructor, you must invoke the superclass constructor, using
super
function. However if you do not supply a subclass constructor, a
constructor is automatically provided.
To avoid trouble don’t use this
inside functions defined with function
. It
is safe to use this
in methods and constructors, and in arrow functions that
are defined inside methods and constructors.
If you are using classic JS notation to create objects, one must specify everything as properties.
One thing that is rather peculiar about JS is that class is a cobbled up from a function by setting its prototype slot. If you want to add a bunch of common functions to class function, one must add them to the prototype slot
It is always better to use class
syntax as it gets a number of fiddly details
right that you do not want to manage manually. In any case JS class
is a
syntactic sugar for a constructor function and a prototype object holding the
methods
If you want to add a method to a class, there are several ways one can do in JS. Here are a few ways
|
|
In the above example, one adds a property to the object literal. The issue with this is that the function is replicated in memory for all objects that instantiate the object literal. A better way is to use prototypes. There are two ways to add something to an existing prototype slot of any object
|
|
|
|
Finally one can use the modern JS syntactical sugar
|
|
Numbers and Dates
All JavaScript numbers are “double precision” values, with a binary
representation that occupies eight bytes. You can place underscores anywhere
between digits to make the number more legible. The global variables Infinity
and NaN
denote the “infinity” and “not a number” values.
To format an integer in a given number to a string, one can use toString
method
|
|
One can use parseInt
and parseFloat
functions to convert strings to integers
and floats.
A few of the number parsing functions are parseInt
, parseFloat
, Number.parseInt
,
Number.parseFloat
Number.MIN_SAFE_INTEGER
is $2^{53}+1$ and Number.MAX_SAFE_INTEGER
is
$2^{53}-1$. The rationale behind the above number is that there are 52 digits in
the mantissa and one signed bit, a total of 53 bits, that can be used to
represent integers in JavaScript.
|
|
One of the ways I tend to remember max safe integer is by linking it to one light year. One light year is about 9 trillion km, 9000 trillion meters. If you forget the units part, the 9000 trillion is the approximately the max safe number in JavaScript.
A big integer is an integer with an arbitrary number of digits. A big integer
literal has a suffix n
.
In JavaScript, time is measured in smoothed milliseconds from the epoch with a valid range of 100 million days in either direction.
Strings and Regular Expressions
A string is a sequence of Unicode code points. Each code point is an integer
between zero and ox10FFF
.
UTF-16 (16-bit Unicode Transformation Format) is a character encoding capable of encoding all 1,112,064 valid character code points of Unicode (in fact this number of code points is dictated by the design of UTF-16).
JavaScript stores strings as sequences of UTF-16 code units. The offset in a call refers to the UFT-16 encoding.
Template literals insert the values of the embedded expressions into the template string. One can customize the behavior of template literals with a tag function.
Calling str.split('')
with an empty separator splits the string into strings
that each hold a 16-bit code units, which is not useful if str
contains
characters above \u{FFFF}
. Hence use a spread operator [...str]
instead.
A regular expression literal is delimited by slashes
Arrays and Collections
- It is better to stay away from
Array
constructor and use array literals - Property keys in an array are always strings
- If you decrease the length, any element whose index is at least the new length get deleted
- There is no requirement that an array has an index property for every index
between 0 and
length-1
To visit all elements of an array, you can use a for
loop to visit all
elements in order, or the for in
loop to visit all index values. One can
produce elements from a given array using slice
method.
JavaScript API provides a Map
class that implements the classic map data
structure: a collection of key/value pairs. Every JavaScript object is a map,
but there are advantages of using Map
- Object keys must be strings or symbols, but
Map
Keys can be of any type - A
Map
instance remembers the order in which elements were inserted - Unlike objects, maps do not have a prototype chain
- You can find out the number of entries with the
size
property
Maps, like all JavaScript collections, have methods keys
, values
and
entries
that yield iterators over the keys, values and key/value pairs.
A Set
is a data structure that collects elements without duplicates. A set is
considered as a map of [value, value]
pairs. Both the keys
and values
methods yield an iterator over the values, and the entries
method yield an
iterator over [value, values]
pairs.
Weak Maps and Sets are mentioned in this chapter but I have a better understanding of these objects after referring to this online post
Weak maps have no traversal methods, and the map objects are not iterable. If
the property you want to monitor is binary, you can use a WeakSet
instead of a
WeakMap
. The keys of weak maps and elements of the weak sets can only be
objects and not primitive type values.
Internationalization
Dates, times, currencies, even numbers are formatted differently in different parts of the world. JavaScript provides convenient functions to handle such requirements.
In US, dates are displayed as month/day/year
; Germany uses day/month/year
whereas China uses year/month/day
format.
A locale specifies the language and location of a user, which allows formatters to take user preferences into account. A locale consists of up to five components
- A language specified by two or three lowercase letters
- Optionally, a script specified by four letters with an initial uppercase that
- further specifies the usage
- Optionally, a country or region, specified by two uppercase letters or three digits
- Optionally, a variant. Optionally, an extension. Extensions describe local preferences for calendars, numbers, and so on.
Let’s say you have some text written by author in Switzerland. It is written in German language but it has currency mentioned as Swiss Franc and not Euros. JavaScript provides all locale-sensitive functions to handle all such text encodings.
toLocaleString
function available in various JavaScript objects such as
Number
, String
can be invoked to obtain localization.
When formatting date and time, there are many locale-dependent issues:
- The names of months and weekdays should be presented in the local language
- There will be local preferences for the order of year, month and day
- The Gregorian calendar might not be the local preference for expressing dates
- The time zone of the location must be taken into account
toLocaleDateString
and toLocaleTimeString
are additional functions that
can be used to choose the type of display format one needs to.
For comparing strings that include unicode characters across languages, a
simple comparison based on UTF-16 blocks is not enough. Hence JavaScript
provides localCompare
method of the String
class.
Asynchronous Programming
JavaScript program runs in a single thread. In particular, once a function starts, it will run to completion before any other part of your program starts running. You know that no other code will corruput the data that your function uses. No other code will try to read any of the data until after the function returns. You never have to worry about deadlocks. However there are issues with having single threaded frameworks. If a program needs to wait for something else - it cannot do anything else. JavaScript provides asynchronous framework to work with all such situations. You specify what you want, and provide callback functions that are invoked when data is available or when an error has occurred.
Let’s say you fire an AJAX request to fetch an image, you can associate callback. Once the image is added, you do not have to worry about corruption by concurrent callbacks. The callbacks are never intermingled. They run one after another in a single JavaScript thread. However, they can come in any order.
In more complex situations, that involve some a callback that scans the contents of the image, another callback that adds the image to an object, etc. Each retrieval requires error handling, which leads to more callbacks. EWith a few levels of processing, this sort of programming style turns into “callback hell”
The Promise
constructor has a single argument, a function that has two
arguments: handlers for success and failure outcomes. This function is called
the “executor” function
|
|
The programming style of using promises
is very time consuming and is it
better to use await/async
syntax that makes working with promises much more
natural. The await
operator can only occur in a function that is tagged with
the async
keyword.
I think the relevant section for a JavaScript newbie like me is to go over the
sections that cover async/await
syntax and not worry about Promises
One can apply the async
keyword to arrow functions, methods, named and
anonymous functions and object literal methods.
I found a good good tutorial on Fetch API from which I understood the way to use
async/await
syntax. The only way to get a good idea of Promises
is to
practice and in that sense, the exercises given at the end of this chapter serve
as a good starting point.
A promise is essentially an improvement of callbacks that manage all asynchronous data activities. A JavaScript promise represents an activity that will either be completed or declined. If the promise is fulfilled, it is resolved; otherwise, it is rejected.
JavaScript promises can have three states, pending, resolved and rejected. The
pending state is the initial state that occurs when a promise is called. While a
promise is pending, the calling function continues to run until the promise is
completed, returning whatever data was requested. When a promise is completed,
it ends in either the resolved state or the rejected state. The resolved state
indicates the promise was successful and that the desired state is passed to the
.then()
method. The rejected state indicates that a promise was denied, and
the error is passed to the .catch()
method.
The expression
|
|
waits for the promise to settle and yields its value.
Of course it is terrible idea to keep waiting in a JavaScript function. Indeed
one cannot use await
in a normal function. The await
operator can only occur
in a function that is tagged with the async
keyword
The compiler transforms the code of an async
function so that any steps that
occur after an await
operator are executed when the promise resolves.
This chapter took the longest to go over as I was not familiar with the nuts and bolts of async programming at all. There is a ton of practice I need to put in, before I can understand how this all works.
Modules
A module provides features for programmers, called the exported features. Any features that are not exported are private to the module. A module also specifies on which other modules it depends. When a module is needed, the JavaScript runtime loads it together with its dependent modules. It is important to understand the difference between module and class. A class can have many instances, but a module doesn’t have instances. It is just a container for classes, functions or values.
Node.JavaScript implements a module system that managed module dependencies. When a module is needed, it and its dependencies are loaded. That loading happens synchronously, as soon as the demand for the module occurs. The Asynchronous Module Definition standard defines a system for loading modules asynchronously, which is better suited for browser-based applications.
ECMAScript modules improve on both of these systems. They are parsed to quickly establish their dependencies and exports, without having to execute their bodies first.
Modules are different from plain “scripts”:
- The code inside a module always executes in strict mode
- Each module has its own top-level scope that is distinct from the global scope of the JavaScript run time
- A module is only processed once even if it is loaded multiple times
- A module is processed asynchronously
- A module can contain
import
andexport
statements
There is so much to modules than covered in the chapter. However the content in this chapter should make any reader curious about the way modules work in other JavaScript based run time environments. I have also had to go through gone through a quick course on Udemy that talks about the details around NPM package manaer
I have skipped the last three chapters of the book and will probably revisit and reread the concepts mentioned, whenever a need arises.
Takeaway
This book is a great resource for someone who has some experience working with other programming languages and wants to get a quick understanding of JavaScript. By removing the excessive focus on browser based examples, the author has made it easy for others to transition to that world of JavaScript.
The book is easy on eyes and can be quickly consumed in a few sittings. Having said that, one gets the best out of the book by working through the exercises at the end of each chapter.
There are so many online courses on JavaScript but nothing beats the learning of reading a good book and soaking up the written material.