Always bet on JavaScript.
ECMAScript is the official name for JavaScript. A new name became necessary because there is a trademark on JavaScript (held originally by Sun, now by Oracle).
Created by Brendan Eich in the year 1995
It is said that Brendan Eich created the first version of the langauge in around 10 days.
JS is a dynamically-typed, interpreted (or JIT-compiled), multi-paradigm, prototype-based high level language.
StackOverflow
2020 Dev Survey
maps.google.com
facebook.com
Instagram app (earlier)
Google Meet
Syntax
Printing to the console
Variables and Scopes
basic operators (JS has a lots of them 🙃)
Looping constructs
Control flow constructs
Can be declared using
var let const
Scopes
The part of the program where a name has valid meaning or value.
Mutable | Either be global or functional in scope
Mutable | Either global or block in scope
Immutable | Either global or block in scope
The identifier can be any text except keywords and strings starting with special chars (such as numbers)
var foo = 6;
foo = 4; // change variable `foo`
x += 1;
x = x + 1; // Compound Assignment Operators
Identifiers are names that play various syntactic roles in JavaScript.
"set" a value to a variable (after declaration)
Compound assignment
+=
-=
*=
/=
%=
Syntax
Printing to the console
Variables and Scopes
basic operators
Looping constructs
Control flow constructs
Statements “do things.” A program is a sequence of statements.
var x;
Expressions produce values.
3 * 6
Example
var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
var x = y >= 0 ? y : -y;
Primarily 3️⃣ ways
(will not be discussed today)
Function Declaraion
function NAME_OF_FUNCTION(parameters) {
// list of statements
// optional return
}
var foo = function () {
...
};
var foo = function _foo() {
...
};
Optional name
Functions can be executed (or called) by using two parentheses, passing arguments.
function sum(a, b) {
return a + b;
}
sum(4, 5);
a
b
One interesting thing about JS ✨
JS won't give an error if you pass fewer or more number of arguments to a function
sum(); // ✅: a = undefined, b = undefined
sum(5); // ✅: a = 5, b = undefined
sum(5, 4) // ✅: a = 5, b = 4
sum(5, 6, 7, 8, 9)// ✅: a = 5, b = 6,
// rest can be accessed using a special object
function sum(a, b) {
return
a + b
}
> sum(4,5)
9
function sum(a, b) {
return
{
sum: a+b
}
}
Example
Expected Output
> sum(4,5)
{ sum: 9 }
// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { }; // function expr. inside var decl.
The mechanism is called ASI (Automatic Semicolon Insertion)
JS values can be classified in two categories
JS has these types of values
no different type for ints or floats
everything (except the primitives) is object
that means arrays and functions are also objects 🤯
available for >=ES6
available for >=ES11
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
true
Differences
Since JS is dynamically-typed,
You can use typeof operator to find the type of a value.
> var foo;
> foo
undefined
> function f(x) { return x }
> f()
undefined
> var obj = {}; // empty object
> obj.foo
undefined
Number Literal ➡️ Just type the number
Only one number type (apart from bigint)
No integers
64-bit floating point (aka double)
Special Numbers are
NaN
Infinity
Does not map well to common understanding of arithmetic:
0.1 + 0.2 = 0.30000000000000004
NaN (Not a Number)
It is (generally) produced by error conditions. Example:
// trying to convert to numbers
> Number('abc')
NaN
> Number(undefined)
NaN
NaN is the only value that is not equal to itself:
> NaN === NaN
false
⚠️ Note ⚠️
typeof NaN is 'number' 😅
> typeof NaN
"number"
Infinity and -Infinity ♾️
Bigger and (smaller) than any numeric value in JS
1 / 0 === Infinity -1 / 0 === -Infinity any + number * Infinity === Infinity any + number / Infinity === 0
Number.MAX_SAFE_INTEGER
represents the maximum safe integer. == 2 ** 53 - 1
const x = Number.MAX_SAFE_INTEGER + 1;
const y = Number.MAX_SAFE_INTEGER + 2;
console.log(x === y); // 🤦 true
Number.MIN_SAFE_INTEGER
== -Number.MAX_SAFE_INTEGER
Number.MAX_VALUE | MIN_VALUE
represents the maximum (or minimum) numeric value representable in JavaScript.
Value Type | Result |
---|---|
undefined | NaN |
null | 0 |
Boolean | True -> 1, false -> 0 |
number | same number |
string | try to convert the string to number. Empty string is 0. |
object | ToPrimitive algorithm |
'abc'
"abc"
'Did she say "Hello"?'
"Did she say \"Hello\"?"
'That\'s nice!'
"That's nice!"
'Line 1\nLine 2' // newline
'Backlash: \\'
> var str = 'abc';
> str[1]
'b'
Concatenation ( + )
> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'
var str = '';
str += 'Multiple ';
str += 'pieces ';
str += 'are concatenated.';
console.log(str)
'Multiple pieces are concatenated.'
Find length (number of characters)
let name = ''
> name.length
0
let name = 'arfat'
> name.length
5
Comparison (“dictionary” or “lexicographical” order)
'a' === 'a' // true
'a' === 'b' // false
'a' > 'b' // false
'A' > 'a' // false
'1' > 'a' // false
'Bee' > 'Be' // true: longer strings are greater if all else is equal
There are two boolean values in JS.
true
false
Truthy values: Non-boolean values which evaluate to true
Falsy values: Non-boolean values which evaluate to false
These values are falsy (or false) in JS
Number Zero
Negative zero -- 🤔 (an artifact of IEEE 754)
Empty String
The n denotes bigint number. 0n is bigint 0.
Array (as Objects)
Difference between Java arrays
Arrays are maps in JS
JS Arrays can contain any element type simultaneously
No need to specify the size upfront. Can extend dynamically.
Array Methods
map
filter
forEach
...
mutating and non-mutating methods
An array is a map from indices (natural numbers, starting at zero) to arbitrary values.
The values are called the array’s elements.
The most convenient way of creating an array is via an array literal. ✅
> var arr = [ 'a', 'b', 'c' ]; // array literal
> arr[0] // get element 0
'a'
> arr[0] = 'x'; // set element 0 to 'x'
> arr
[ 'x', 'b', 'c' ]
The ECMAScript standard specifies arrays as maps (dictionaries) from indices to values.
Arrays Can Also Have Properties
Those are not considered part of the actual array; that is, they are not considered array elements.
> var arr = [ 'a', 'b' ];
> arr.foo = 123;
> arr
[ 'a', 'b' ]
> arr.foo
123
Since arrays are just objects
The .length property returns the number of elements currently present in the array.
Array Constructor ☠️
> var arr = new Array(2);
> arr.length
2
> arr // two holes plus trailing comma (ignored!)
[ , ,]
// The same as ['a', 'b', 'c']:
var arr1 = new Array('a', 'b', 'c');
// ☠️ AVOID this.
An empty array with a given length has only holes in it!
> new Array(2) // alas, not [ 2 ]
[ , ,]
> new Array(5.7) // alas, not [ 5.7 ]
RangeError: Invalid array length
> new Array('abc') // ok
[ 'abc' ]
Array.of() ✅
Creates a new Array instance from a variable number of arguments, regardless of number or type of the arguments.
Array.of(7); // [7]
Array(7); // array of 7 empty slots
Array.of(1, 2, 3); // [1, 2, 3]
Array(1, 2, 3); // [1, 2, 3]
You can't use typeof, since the output will be 'object'
instanceof ☠️☠️
if (arr instanceof Array) {
console.log('arr is an array')
}
Array.isArray ✅
if (Array.isArray(arr)) {
console.log('arr is an array')
}
In some rare cases, instanceof can give wrong answers.
Broadly two kinds of methods
💡 Pro tip 💡: It's a good programming practice to minimize the use of destructive methods.
Array#push
Red means destructive
let livestock = ["🐷", "🐮", "🐔"];
livestock.push("🐴", "🐮");
// ["🐷", "🐮", "🐔", "🐴", "🐮"]
Array#pop
let livestock = ["🐷", "🐮", "🐔"];
let lastElem = livestock.pop();
console.log(lastElem) // "🐔"
console.log(livestock) // ["🐷", "🐮"]
Array#shift
const array1 = [1, 2, 3];
const firstElement =
array1.shift();
console.log(array1); // [2, 3]
console.log(firstElement); // 1
Array#unshift
let train = ["🚃", "🚃"];
train.unshift("🚂");
// ["🚂", "🚃", "🚃"]
Array#forEach
const numbers = [1, 2, 3, 4, 5, 6];
numbers.forEach(function (el) {
console.log(el);
});
for of loops (Not really an Array method)
const numbers = [1, 2, 3, 4];
for (const number of numbers) {
console.log(number);
}
Array#indexOf ❌ ☠️
let array = ['a', 1, '2', NaN];
console.log(array.indexOf('a')); // 0
console.log(array.indexOf(NaN)); // -1
returns index of the element or -1
let array = ['a', 1, '2', NaN];
console.log(array.includes('a')); // true
console.log(array.includes(NaN)); // true
console.log(array.includes('b')); // false
console.log(array.includes('a', 2)); // false
Array#includes ✅
Array#lastIndexOf ⚠️
const animals = [
'Dodo',
'Tiger',
'Penguin',
'Dodo',
NaN
];
animals.lastIndexOf('Dodo') // 3
animals.lastIndexOf('Tiger') // 1
animals.lastIndexOf(NaN) // -1
Array#reverse
let arr = [ 'a', 'b', 'c' ];
arr.reverse()
// [ 'c', 'b', 'a' ]
arr // reversing happened in-place
// [ 'c', 'b', 'a' ]
Array#fill
[1, 2, 3].fill(4); // [4, 4, 4]
[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3); // [1, 2, 3]
changes all elements in an array to a static value, from a start index (default 0) to an end index (default array.length).
Array#sort ⚠️⚠️
> let arr = ['banana', 'apple', 'pear', 'orange'];
> arr.sort()
[ 'apple', 'banana', 'orange', 'pear' ]
> arr // sorting happened in place
[ 'apple', 'banana', 'orange', 'pear' ]
let array = [1, 10, 101, 2, 3, 1001];
array.sort();
console.log(array);
// [1, 10, 1001, 101, 2, 3];
> [3, 4, 5].join('-')
'3-4-5'
> [3, 4, 5].join()
'3,4,5'
> [3, 4, 5].join('')
'345'
> [undefined, null].join('#')
'#'
> [ 'a', 'b', 'c', 'd' ].slice(1, 3)
[ 'b', 'c' ]
Array#map
originalArray.length === newArray.length
const hungryMonkeys = ['🐒', '🦍', '🦧'];
const fedMonkeys = hungryMonkeys.map(function (monkey) {
return monkey + '🍌';
});
// ["🐒🍌", "🦍🍌", "🦧🍌"]
const numbers = [1, 2, 3, 4, 5, 6];
const squares = numbers.map(function (el) {
return el * el;
});
console.log(squares);
// [ 1, 4, 9, 16, 25, 36 ]
Array#filter
0 <= newArray.length <= originalArray.length
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(function (el) {
return el % 2 === 0;
});
console.log(evens);
// [ 2, 4, 6 ]
Exceptions are anomalous or exceptional conditions that require special processing during the execution of a program.
throw statement
The throw statement throws a user-defined exception.
Important Note
throw expression;
function a() {
b();
}
function b() {
c();
}
function c() {
d();
}
function d() {
throw 'error in d';
}
a();
try {
try_statements
}
catch (exception_var) {
catch_statements
}
finally {
finally_statements
}
function a() {
b();
}
function b() {
try {
c();
console.log('after c()')
} catch (ex) {
console.log('caught this exception -- ', ex);
}
}
function c() {
d();
}
function d() {
throw 'error in d';
}
a();
It's not a good idea to throw random expressions. You should always throw an object of the Error class (or something similar).
function a() {
b();
}
function b() {
try {
c();
} catch (ex) {
console.log('caught this exception -- ');
console.log(ex);
}
}
function c() {
d();
}
function d() {
throw new Error('error in d');
}
a();
The finally block always executes when the try block exits.
It always executes, regardless of whether an exception was thrown or caught.
openMyFile();
try {
// tie up a resource
writeMyFile(theData);
} finally {
closeMyFile(); // always close the resource
}
Static Versus Dynamic
Static Typing Versus Dynamic Typing
Static Type Checking Versus Dynamic Type Checking
JS Types
Coercion
== vs ===
In the context of language semantics and type systems, “static” usually means “at compile time” or “without running a program”, while “dynamic” means “at runtime”.
In JavaScript, the main way of dealing with a value whose type doesn’t fit is to coerce it to the correct type. Coercion means implicit type conversion. Most operands coerce:
> '3' * '4'
12
JS has 6 (5 primitive + 1 object in ES5) types that are dynamically typed and (mostly) dynamically typed-checked.
Strict equality (===) and strict inequality (!==) consider only values that have the same type to be equal.
Normal (or “lenient”) equality (==) and inequality (!=) try to convert values of different types before comparing them as with strict (in)equality.
> undefined == null
true
> 1 == true
true
> 0 == false
true
> '' == false
true
> '1' == true
true
true + false
1
12 / "6"
2
"number" + 15 + 3
"number153"
15 + 3 + "number"
"18number"
[1] > null
true
"foo" + + "bar"
"fooNaN"
'true' == true
false
false == 'false'
false
null == ''
false
!!"false" == !!"true"
true
"Why am I a " + typeof + "";
"Why am I a number"
Functions can act in various ways in JavaScript. Let’s look at them —
Traditional Functions (that is, outside any object)
function add1(num1, num2) { // function declaration
return num1 + num2;
}
const add2 = function(num1, num2) { // function expression
return num1 + num2;
}
Object’s methods (inside an object)
const details = {
name: 'Arfat',
getName: function () {
return this.name;
}
}
details.getName(); // 'Arfat'
Constructor Functions
function Person(name) {
this.name = name;
}
const person1 = new Person('Arfat');
console.log(person1);
// Person { name: 'Arfat' }
Factory (object-oriented programming)
In object-oriented programming (OOP), a factory is an object for creating other objects – formally a factory is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be "new".
var e = 10;
function sum(a) {
return function(b) {
return function(c) {
return function(d) {
return a + b + c + d + e;
}
}
}
}
sum(1)(2)(3)
function a() {
let fn;
{
var x = 5;
fn = function() {
console.log(x);
}
}
return fn;
}
var x = 10;
const b = a();
b();
var a = 100;
function outer(x) {
var a = 10;
return function (y) {
return a + y;
}
}
a = 50;
var inner = outer(20);
function abc() {
var a = 30;
console.log(inner(5));
}
abc();
An Environment Record records the identifier bindings that are created within the scope of its associated Lexical Environment. It is referred to as the Lexical Environment's EnvironmentRecord.
For example, if a FunctionDeclaration contains two nested FunctionDeclarations then the Lexical Environments of each of the nested functions will have as their outer Lexical Environment the Lexical Environment of the current evaluation of the surrounding function.
As ECMAScript code is executed, additional properties may be added to the global object and the initial properties may be modified.
Environment Records
Execution Contexts
Execution contexts for ECMAScript code have the additional state components listed in Table.
Component | Purpose |
---|---|
LexicalEnvironment | Identifies the Lexical Environment used to resolve identifier references made by code within this execution context. |
VariableEnvironment | Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context. |
First-class functions
A first-class function is one that may participate as a normal data, i.e. be created literally at runtime, be passed as an argument, or be returned as a value from another function.
Free variable
A free variable is a variable which is used by a function, but is neither a parameter, nor a local variable of the function.
Closure
A closure is a pair consisting of the function code and the environment in which the function is created.
function f() {
this.name = 'arfat';
}
f();
function f() {
'use strict'
this.name = 'arfat';
}
f();
const obj = {
name: 'Arfat',
f: function() {
console.log(this.name);
}
};
obj.f();
const obj = {
name: 'Arfat',
f: function() {
console.log(this.name);
}
};
var g = obj.f;
g();
setTimeout(obj.f, 2000);
var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
this.friends.forEach(
function(friend) { // (1)
console.log(this.name+' knows '+friend); // (2)
}
);
}
};
obj.loop();
An object is a
collection of properties
A
property is a
named container for a
value
w/ some additional attributes
The
name of a property
is called
a
key
;
thus,
an object
can be considered as
a
collection of key-value pairs
.
There are similar concepts in other programming languages,
e.g.,
Map, Dictionary, Associative Array, Symbol Table, Hash Table
, ...
Property Accessors (Dot and square notations)
this in objects
Other gotchas when using this.
Functions Inside Methods Shadow this
Losing this When Extracting a Method
Inheritance
Javascript inheritance by examples
Setting and Deleting Affects Only Own Properties
Sharing Data Between Objects via a Prototype
Prototypal Chain
all objects in JavaScript are maps (dictionaries) from strings to values.
A (key, value) entry in an object is called a property . The key of a property is always a text string.
Properties (or named data properties)
Accessors (or named accessor properties)
Internal properties
Exist only in the ECMAScript language specification.
Dot Notation
var jane = {
name: 'Jane',
'desc.func': function () {
return 'Person named ' + this.name;
},
};
$ jane.name
// 'jane'
$ jane['desc.func']
// [Function]
Bracket Notation
this refers to the object on which the method has been invoked
> var obj = { method: returnThisStrict };
> obj.method() === obj
true
Normal functions in sloppy mode
function returnThisSloppy() {
return this
}
> returnThisSloppy() === window
true
Normal functions in strict mode
function returnThisStrict() {
'use strict';
return this
}
> returnThisStrict() === undefined
true
var counter = {
count: 0,
inc: function () {
this.count++;
}
}
We have called the value of counter.inc as a function.
Hence, this is the global object and we have performed window.count++ .
window.count does not exist and is undefined . Applying the ++ operator to it sets it to NaN.
Use strict mode for avoiding this.
> var func = counter.inc;
> func()
> counter.count // didn’t work
0
> var func3 = counter.inc.bind(counter);
> func3()
> counter.count // it worked!
1
function callIt(callback) {
callback();
}
> callIt(counter.inc)
❌
✓
> callIt(counter.inc.bind(counter))
var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
'use strict';
this.friends.forEach(
function (friend) { // (1)
console.log(this.name+' knows '+friend); // (2)
}
);
}
};
> obj.loop()
What to do?
loop: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' knows '+friend);
});
}
loop: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}.bind(this)); // (1)
}
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}, this);
Global Context
In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.
Function context
Inside a function, the value of this depends on how the function is called.
Simple Call
Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object, which is window in a browser.
function f1() {
return this;
}
// In a browser:
f1() === window; // true
// In Node:
f1() === global; // true
Arrow functions
In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object:
As an object method
When a function is called as a method of an object, it's this is set to the object the method is called on.
this on the object's prototype chain
var o = {f: function() { return this.a + this.b; }};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
If the method is on an object's prototype chain, this refers to the object the method was called on, as if the method were on the object.
As a constructor
When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.
As a DOM event handler
When a function is used as an event handler, its this is set to the element the event fired from (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener()).
In an inline event handler
When the code is called from an inline on-event handler, its this is set to the DOM element on which the listener is placed:
<button onclick="alert((function() { return this; })());">
Show inner this
</button>