Javascript for Impatient programmers
Learning notes form axel Rauschmayer's javascript for impatient programmers
November 6,2022
These are my notes from following the excellent book, javascript for impatient programmers by axel Rauschmayer
Understanding let and var
The let
keyword creates a new variable locally. it is not attached to the global object
let x = 10; //creating a variable
if x == 10 {
let x = 20;
console.log(x); //prints 20
}
console.log(x); //prints 10
// ^^^ This x comes from above
The var
keyword creates a new variable globally.
var a = 10;
let b = 10;
console.log(window.a); //10 :because the variable is available globally
console.log(window.b); //undefined :because the variable is not available globally
One bad thing that happens due to this is.
for (var i = 0; i<5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
/*
prints
5
5
5
5
5
because i was declared as a 'var'and hence
is global so when it is called from the queue.
i has been updated to 5 and only that is accessible
*/
for (let j=1; j<5; j++){
setTimeout(function(){
console.log(i);
}, 1000);
/*
prints
1
2
3
4
5
because i was declared locally when this is pushed to the
stack, it has a local value present with it
*/
Redeclaration
var lets you redeclare variables
redeclaring a variable using the let
keyword result in an error:
let counter = 0;
let counter;
console.log(counter);
This results in an error like,
Uncaught SyntaxError: Identifier 'counter' has already been declared
Temporal Death Zone (TDZ)
A variable declared by the let
keyword has a so-called temporal dead zone (TDZ). The TDZ is the time from the start of the block until the variable declaration is processed.
example that temporal dead zone is time-based, not location-based.
{ //entering new scope, TDZ starts here
let log = function () {
console.log(message);
};
//This is the TDZ and accessing log
//would cause a ReferenceError
let message = 'Hello'; //TDZ ends
log(); //called outside TDZ hence will return some value.
}
Firstly, the curly brace starts a new block scope, therefore, the TDZ. start.
Second the log()
function expression acesses the message
variable. However, the log()
function has not been executed yet.
Third, declare the message
variable and initialize its value to 10. The time from the start of the block scope to the time that the message variable is accessed is called a temporal death zone. When the JavaScript engine processes the declaration, the TDZ ends.
The final call to log()
function accesses the message
variable outside of the TDZ.
It should be noted that if you access a variable declared by the let
keyword in the TDZ, you’ll get a ReferenceError
as illustrated in the following example.
{
console.log(typeOf myVar); //undefined
console.log(typeOf message); //ReferenceError
let message;//TDZ ends here
var myVar;
}
The temporal death zone is there to prevent you from accidently referencing a variable before its declaration.
Default params
function say(message='Hi') {
console.log(message)
}
say(); // 'Hi'
say('Hello'); //'Hello'
Arguments vs Parameters
Parameters - what is specified in the function declaration
Arguments - what is passed to the function
function add(x,y) {
// ^ ^ Parameters
return x+y;
}
add(100, 200);
// ^^^ ^^^ Arguments
JS History
- Founded by Brendan Eich for netscape navigator.
- JavaScript => language and its implementations
- ECMAScript => language standard and language versions
ECMA Technical Committee39 (TC39)
Meetings occur every 2 months, the meeting notes can be found in this github repo
New proposals are tracked through this github repository for proposals
JavaScript rest parameters
The rest parameter, shown as (...)
. A rest parameter allows you to represent an indefinite number of arguments as an array.
function fn(a,b,...args) {
// ^^^^^^^ rest parameter available as an array of elements
for (const a of args){
//do something with a
}
}
rest parameters are available as an array
wrong way to declare a rest parameter
function fn(a,...rest,b) {
//error
}
Using Spread operators
Spread operators are used to combine arrays, maps or set.
const odd = [1,3,5];
const combined = [2,4,6, ...odd]; //valid spread
const combined1 = [2, ...odd, 4, 6]; //valid as well
console.log(combined); // [2,4,6,1,3,5]
console.log(combined1); // [2,1,3,5,4,6]
the syntax is similar to rest params
Using spread params:
var variables = ['a', 'b'];
var newvars = ['c', 'd'];
variables.push(...newvars);
console.log(variables); // ['a','b','c','d']
concatenating arrays
let num = [1,2];
let num2 = [3,4];
let allnums = [...num,...num2]
copying an array
let scores = [80, 70, 90]
let newscores = [...scores]
spread operator and strings
let chars = ['A', ...'BC', 'D'];
console.log(chars); //["A", "B", "C", "D"]
object literal
The goal of this moodely is to make object literals more succinct and powerful by extending the syntax in some ways.
Object property initializer shorthand
function createMachine(name, status) {
//old way of initializing object
return {
name: name,
status: status
}
}
function createMachineNew(name, status){
//new way of initializing object
return {
name,
status
}
}
other ways of initialising
name = 'computer';
status = 'on';
//This works as well
let machine = {
name,
status
};
Computed property name
Prior to ES6, you could use the square brackets ([]
) to enable the computed property names for the properties on objects.
let name = 'machine name';
let machine = {
[name]: 'server', // the key here is 'machine name' and can be accessed as machine['machine name']
["machine hours"]: 10000
}
Concise method syntax
while defining methods, name and full function definition was required. now it’s not
let server = {
name: 'Server',
restart: function (){ //old way of defining
console.log("restart() is running")
},
restartNew() { //new way of defining
console.log("restartNew() is running")
},
'restart NEW'() { //this method can e run by server['restart NEW']()
console.log("this is insane!!!!")
}
}
Basic constructs
Comments:
// Single-line comment
/*
comment wit multiple lines
*/
Primitive values:
Booleans:
true
false
Numbers:
1.141
-123
The number type is used for both floating point numbers and integers. Can only represent integers within a range of 53 bits + sign
Bigints:
17n
-149n
Bigints can grow to arbitrarily large number
Strings:
'abc'
"abc"
`string with interpolated values: ${256} and ${true}`
JavaScript has no extra type for characters. It uses strings to represent characters
Assertions
an assertion describes what the result of a computation is expected to look like and throws excaption when it fails
assert.equal(7+1,8);
assert.equal(7+1,7);
Logging to the console
Console logging can be used with both nodejs and browser. The console API has a lot of customisations possible that can be found here
Operators
//Operators for booleans
assert.equal(true && false, false) //And
assert.equal(true || false, true) //Or
//Operators for numbers
assert.equal(3 + 4, 7);
assert.equal(5 - 1, 4);
assert.equal(3 * 4, 12);
asssert.equal(10 / 4, 2.5);
//Operators for bigints
assert.equal(3n + 4n, 7n);
assert.equal(5n - 1n, 4n);
assert.equal(3n * 4n, 12n);
asssert.equal(10n / 4n, 2.5n);
//Operators for strings
assert.equal('a'+'b', 'ab');
assert.equal('I see' + 3 + 'monkeys', 'I see 3 monkeys');
//Comparison operators
assert.equal( 3 < 4, true);
assert.equal( 3 <= 4, true);
assert.equal( 'abc' === 'abc', true); //AKA strict equal
assert.equal( 'abc' !== 'def', true);
Declaring variables
const
=>
- *immutable variable bindings
- Everything must be initialised immediately, different values cannot be assigned later.
const x = 8;
x = 9; // <== would create issue
let
=>
let create *mutable variable bindings
//Declaring y (mutable binding):
let y = 35;
Ordinary function declaration
//add1() has the parameters a and b
function add1(a,b) {
return a + b
}
//calling function add1()
console.log(ad1(5,2))
Arrow function expressions
These are usually used as arguments of function calls ad method calls
const add2 = (a,b) =>{ return a+b }; //Valid arrow function
const add3 = (a,b) => a+b; //Also valid
console.log(add2(1,2)); //works similar to a normal function
Plain objects
const obj = {
first: 'Jane',
last: 'Doe',
getfullname() {
return this.first + ' ' + this.last;
}
}
console.log(obj.first) //Jane
obj.first = 'Janey';
console.log(obj.getfullname()) //Janey Doe
Arrays
//Array using an Array literal
const arr = ['a','b','c']
arr[0] = '4' //Array can be edited even if it is a const
arr.push('d') //appending an element to an array
Modules
Every module is a file.
Each module is a single file. For example,
file-tools.mjs
main.mjs
The module in file-tools.mjs
exports its function isTextFilePath()
:
export function isTextFilePath(filePath) {
return filePath.endsWith('.txt');
}
The module in main.mjs
imports the whole module path and the function *isTextFilePath():
//Import whole module as namespace object 'path'
import * as path from 'path';
//Import a single export of module file-tools.mjs
import {isTextFilePath} from './file-tools.mjs';
classes
class Person {
constructor(name){
this.name = name;
}
describe() {
return `Person named ${this.name}`;
}
static logNames(persons) {
for (const person of persons) {
console.log(person.name)
}
}
}
class Officer extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
describe() {
return super.describe() + `(${this.title})`;
}
}
const olivier = new Officer('Olivier','CTO')
console.log(olivier) //Person named Jane (CTO)
Exception handling