IMPORTANT:
Some of the content here is a personal summary/abbreviation of contents on the Mozilla JavaScript Guide and Runoob JavaScript Guide. Feel free to refer to that site if you think some of the sections written here are not clear.
Introduction to JavaScript
A brief history of JavaScript is omitted, but some key points are listed here.
ECMAScript
ECMAScript (ES) is a standard defined for scripting languages, and JavaScript is an implementation of it.
- so this means that JavaScript is like Python, it does not need a compiler
Browser Engines
Here I refer to engines for executing JavaScript. Different browsers execute JavaScript differently, if they have a different engine. For example:
- Chrome uses v8 (currently the fastest)
- Firefox uses SpiderMonkey
- …
Components of JavaScript
On a high level, JavaScript implements the following three components:
where:
- ECMAScript - the standard of scripting languages
- DOM - controls webpage activities
- BOM - controls browser activities
Object Oriented Programming
Similar to programs such as Java, it is a OOP.
Basics
This guide assumes some prior knowledge of HTML and CSS, as well as any another programming language, and it will only lists summaries/key points of what I have learnt.
Basic Usages
For controlling website related events/content, JavaScript manipulates the document
object:
JavaScript is like pieces of code that gets injected into your html during the “compile” time. Therefore, upon testing, the precedence is as from top to bottom of your code:
for example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- JavaScript goes into the script tag -->
<script>
document.write("This comes first");
</script>
</head>
<body>
<script>
document.write("This comes second");
</script>
</body>
</html>
JavaScript can be inserted in any position (
head
,body
) of the html file, using the<script>
tagupon testing, precedence of running related JavaScript is:
first it searches from current line towards the bottom
then it searches in the
<body>
tagfinally, it searches in the
<head>
tagfor example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function myfunction() {
document.getElementById("1").innerHTML="Hi,1";
}
</script>
</head>
<body>
<script>
function myfunction() {
document.getElementById("0").innerHTML="Hi,0";
}
</script>
<p id="0">
test
</p>
<p id="1">
test
</p>
<button type="button" onclick="myfunction()">点击这里</button>
</body>
</html>where:
only
1
2
3
4
5<script>
function myfunction() {
document.getElementById("0").innerHTML="Hi,0";
}
</script>will be executed
Using
document.write()
during the HTML stream/“compilation”, then it is equivalent of appending to the current html content. However, usingdocument.write()
after your page is loaded, it will overwrite the entire webpage.for example:
1
2
3
4
5
6
7
8
9
10
11<script>
function myfunction(){
document.write("This will overwrite the entire page");
}
document.write("<h1>This is a paragraph</h1>");
document.write("<p>This is another paragraph</p>");
</script>
<p >
You only use <strong>document.write during the HTML output stream</strong>。
</p>
<button type="button" onclick="myfunction()">Click here</button>
Outputting Data
window.alert()
弹出警告框。document.write()
方法将内容写到 HTML 文档中。innerHTML
写入到 HTML 元素。console.log()
写入到浏览器的控制台。
Basic Syntax
Variable Identifiers are case-sensitive and uses the Unicode character set.
For example, the word Früh is legal as variable name.
1
let Früh = "foobar";
However, similar to Java and C, you can only use characters, numbers and symbols
_$
.
Statements are separated by semicolons (;
).
- For example, the variable declaration above.
- A semicolon is not necessary after a statement if it is written on its own line. However, it is considered the best practice to use a
;
for every statement.
Comments is pretty much the same in Java/C++ and other languages.
An example is the easiest:
1
2
3
4
5
6
7// a one line comment
/* this is a longer,
* multi-line comment
*/
/* You can't, however, /* nest comments */ SyntaxError */
Variable Declarations - JavaScript has three kinds of variable declarations.
var
Declares a variable, optionally initializing it to a value.
- For example,
var x = 42;
. This syntax can be used to declare both local and global variables, depending on the execution context.
- For example,
var
declarations, wherever they occur, are created (but not assigned) before any code is executed. This is called hoisting
let
Declares a block-scoped, local variable, optionally initializing it to a value.
- For example,
let y = 13
. This syntax can be used to declare a block-scope local variable.
- For example,
const
Declares a block-scoped, read-only named constant.
however,
const
only protects the assignment of the variable. Fields of theconst
will not be protected (similar toconst
inC
):1
2const MY_OBJECT = {'key': 'value'};
MY_OBJECT.key = 'otherValue'; // is allowed
Undeclared Global Variable
- For example,
x = 42
. This form creates an undeclared global variable. It also generates a strict JavaScript warning
- For example,
Undefined Variables
A variable declared using the
var
orlet
statement with no assigned value specified has the value ofundefined
.- An attempt to access an undeclared variable results in a
ReferenceError
exception
- An attempt to access an undeclared variable results in a
undefined
can be used to determine whether a variable has a valuefor example:
1
2
3
4
5
6var input;
if (input === undefined) {
doThis();
} else {
doThat();
}
undefined
behaves the same asfalse
when used in a Boolean contextfor example:
1
2var myArray = [];
if (!myArray[0]) myFunction();where:
myFunction
executes ifmyArray
element isundefined
undefined
value converts toNaN
when used in numeric context.for example:
1
2var a;
a + 2; // Evaluates to NaN
null
Variable
null
behaves as0
in numeric contexts and asfalse
in Boolean contexts.this means it’s “safer” than
undefined
for example:
1
2var n = null;
console.log(n * 32); // Will log 0 to the console
Variable Scope
global variable is available to any other code in the current document
- variable declared outside of any function
block-scope variable is available to any other code in the current block
a variable declared with
var
is available within the function/global scopefor example:
1
2
3
4
5if (true) {
var x = 5; // available to the entire outer function.
// In this case, it will be global context
}
console.log(x); // x is 5
a variable declared with
let
is available within the current code blockfor example:
1
2
3
4if (true) {
let y = 5; // only available within this if block
}
console.log(y); // ReferenceError: y is not defined
In short:
- variable declaration
var
follows the rule specified above- variable declaration with
let
orconst
have a more limited scope and behaves in the same way
- except that
const
is ready only and requires a value at initialization time- cannot declare a
const
with the same name as a function or variable in the same scope.
Variable Hoisting
Hoisting refer to the ability of referring to a variable declared later, without getting an exception.
- Variables in JavaScript are, in a sense, “hoisted” (or “lifted”) to the top of the function or statement.
However, variables that are hoisted return a value of undefined
.
In ECMAScript 2015,
let
andconst
are hoisted as well but not initialized.for example:
1
2console.log(x); // ReferenceError
let x = 3;
Variable hoisting example:
1 | var myvar = 'my value'; |
can be interpreted as:
1 | var myvar = 'my value'; |
Function Hoisting
For functions, the idea is the same:
- only declaration/initialization are hoisted, but the actual value/function expressions are not.
For example:
1 | baz(); // TypeError: baz is not a function |
would be the same as
1 | var baz; |
Using Global Variables
Global variables are in fact properties of the global object.
- In web pages, the global object is
window
(and inside that browserwindow
, you have your HTML page)
This means:
- The
window
object is the object representing the entire browser window- all JavaScript global variables are
fields
of thewindow
object
- in fact, this includes the HTML DOM
document
objectwindow.document.getElementById("header");
is the same asdocument.getElementById("header");
- all JavaScript global functions are
fields
of thewindow
object
Consequently, you can access global variables across scripts loaded inside the same browser window
Data Types
There are seven primitive types, and Object
boolean
.true
andfalse
. (case sensitive)null
. A special keyword denoting a null value (case sensitive).undefined
. A top-level property whose value is not defined.Number
. An integer or floating point number. For example:42
or3.14159
.[BigInt
. An integer with arbitrary precision. For example:9007199254740992n
.String
. For example: “Howdy”Symbol
(new in ECMAScript 2015). A data type whose instances are unique and immutable.
Data Type Conversion
JavaScript is a dynamically typed language. This means:
- you don’t have to specify the data type of a variable when you declare it.
- data types are automatically converted as-needed during script execution.
For example:
You can do the follows:
1
2
3var answer = 42;
// some other code
answer = 'Thanks for all the fish...';
String Concatenation with +
Operator
This is similar in other languages, except that:
- numbers are only converted to strings with the
+
operator. In the other cases, strings will be converted to number
For example:
'37' + 7 // "377", using the + operator '37' - 7 // 30, not using + operator <!--19--> An integer between `2` and `36` that represents the *radix* (the **base** in mathematical numeral systems) of the `string`. Be careful—this does ***not*** default to `10`! - **returns** the parsed number or `NaN` when - the `radix` is smaller than `2` or bigger than `36`, or - the **first non-whitespace character** cannot be converted to a number.
parseFloat()
parseFloat(string) <!--20-->
Floating-point
A floating-point literal can have the following parts:
[(+|-)][digits].[digits][(E|e)[(+|-)]digits]
for example:
1
2
3
43.1415926
-.123456789
-3.1E+12
.1e-23
Boolean
- Boolean type has two literal values:
true
andfalse
.
- Boolean type has two literal values:
String
both single quote and double quote works
for example:
1
2
3"John Doe"
'John Doe'
Template
it can be used as interpolation like
%s
inside a string forC
for example:
1
2var name = 'Bob', time = 'today';
var interpolated = `Hello ${name}, how are you ${time}?`
or, it can be used for forming a multiline strings
for example:
1
2
3
4
5var poem =
`Roses are red,
Violets are blue.
Sugar is sweet,
and so is foo.`
Array
element inside an array can be of any type, and different elements of the same array can be of different types
empty value in an array is automatically made
undefined
(trailing comma will be ignored)for example:
1
[40, "Hi", 1, 5, , 25, true] // undefined between 5 and 25
RegEx
A regex literal is a pattern enclosed between slashes.
for example:
1
var re = /ab+c/;
Object
a list (map) of zero or more pairs of property names and associated values of an object, enclosed in curly braces (
{}
).for example:
1
{firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
or:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
// method of type person
function changeName(name)
{
this.lastname=name;
}
}
myMother=new person("Sally","Rally",48,"green");
myMother.changeName("Doe");
Objects
The idea is similar with most OOP, yet:
Properties of JavaScript objects can also be accessed or set using a bracket notation
- note that all keys in the square bracket notation are converted to string unless they’re Symbols
you can enumerate the properties of an Object
For Example
1 | // four variables are created and assigned in a single go, |
where:
- in the above code, when the key
obj
of typeObject
is added to themyObj
, JavaScript will call theobj.toString()
method, and use this result string as the new key.
For Example: Iterating Over All Properties
1 | function MyObj(){ |
and the output is:
1 | myObj.name = Jason |
Creating new Objects
In summary, you can create a new object using:
- object initializers
- constructor functions
Object.create
method
Object Initializers
This would basically be the same as C
and C++
1 | var obj = { property_1: value_1, // property_# may be an identifier... |
where:
obj
is the name of the new object,- each
property_i
is an identifier (either a name, a number, or a string literal) - each
value_i
is an expression whose value is assigned to theproperty_i
.
Note
- Identical object initializers create distinct objects that will not compare to each other as equal. In fact, those objects are created as if a call to
new Object()
were made; that is, objects made from object literal expressions are instances ofObject
.
Constructor Functions
This would be the same as most OOP:
- Define the object type by writing a constructor function.
- There is a strong convention, with good reason, to use a capital initial letter.
- Create an instance of the object with
new
.
For Example
1 | function Car(make, model, year) { |
Note
- since the assignment does not check types, you can in fact pass in anything you want.
- for example,
make
could refer to an Object
Object.create
Method
This is useful in the sense that:
- it allows you to choose the prototype object for the object you want to create, without having to define a constructor function.
- The object being inherited from is known as the prototype, and the inherited properties can be found in the
prototype
object of the constructor. (see [Prototype Chain](#Prototype Chain))
- The object being inherited from is known as the prototype, and the inherited properties can be found in the
1 | // Animal properties and method encapsulation |
Enumerate the Properties of an Object
there are three native ways to list/traverse object properties:
for...in
loopsThis method traverses all enumerable properties of an object and its [prototype chain](#Prototype Chain).
Object.keys(o)
This method returns an array with all the own (not in the prototype chain) enumerable properties’ names (“keys”) of an object
o
.Object.getOwnPropertyNames(o)
This method returns an array containing all own properties’ names (enumerable or not) of an object
o
.
For Example: Object.keys(o)
1 | // simple array |
For Example: Object.getOwnPropertyNames()
1 | var obj = { 0: 'a', 1: 'b', 2: 'c' }; |
Prototype Chain
A prototype of an object can be understood as the “parent” of the object (like in Java)
- so that properties to be inherited goes in the
prototype
field
In JavaScript, functions are able to have properties/fields. All functions have a special property named prototype
.
- There is one exception that arrow function doesn’t have a default prototype property
For Example
1 | function doSomething(){} |
and the output is:
1 | constructor: ƒ doSomething() |
At this point, this means you can add properties to a function in two ways:
- add the property to the function directly
- instantiate it as an Object and add a property
- Calling a function with the
new
operator returns an object that is an instance of the function. Properties can then be added onto this object.
- Calling a function with the
1 | function doSomething(){} |
this produces the following:
1 | prop: "some value" |
This tells us three things:
the
__proto__
ofdoSomeInstancing
isdoSomething.prototype
.constructor and its “superclass“ information is stored in its
__proto__
field- in the above, the constructor is
doSomething()
, and its “superclass” isObject
- in the above, the constructor is
There is a prototype chain that holds property of things, which is used for property/field look up
If
doSomeInstancing
does not have the property, then the browser looks for the property in the__proto__
ofdoSomeInstancing
(a.k.a.doSomething.prototype
). If the__proto__
ofdoSomeInstancing
has the property being looked for, then that property on the__proto__
ofdoSomeInstancing
is used.for example:
1
2
3
4
5
6function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log(doSomeInstancing.foo); // returns "bar"
Defining Methods
Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object.
For Example: Object Created using Initializer
1 | var person = { |
For Example: Objects Created using Constructor
1 | function MyObj(){ |
Combing the above two, it means you can attach any function to any type as you want, by simply assigning the function as a property (hence becoming a method).
- provided that you know something about the object
Getters and Setters
In summary, getters and setters are functions with a special keyword that can be either
- defined using object initializers, or
- added later to any object at any time using a getter or setter adding method.
However, there is one extra restriction on getter/setter:
- the getter method must not expect a parameter
- the setter method expects exactly one parameter
Note
- in a sense, getter and setters are more like “computable” fields rather than methods when executing.
For Example: Getter and Setter for Object Initializers
1 | var o = { |
which would be the same as:
1 | var o = { a: 0 }; |
Deleting Properties
You can remove a non-inherited property by using the delete
operator. The following code shows how to remove a property.
1 | var myobj = new Object; |
Comparing Objects
Two distinct objects are never equal, even if they have the same properties. Only comparing the same object reference with itself yields true
.
Reminder
- Strict equal (
===
) Returnstrue
if the operands are equal and of the same type.
For Example
1 | // Two variables, two distinct objects with the same properties |
and only:
1 | // Two variables, a single object |
Control Flows
Since for most languages, the idea for control flows are the same, this section only discusses some important/interesting ones that JavaScript has.
Conditional Statements
The first thing to know are the expressions that evaluate to true/false
:
False Values
false
undefined
null
0
NaN
- the empty string (
""
)
True Values
- All other values, including all objects
In this sense, it is more similar to languages like
C
rather thanJava
Note
There is a non-trivial difference between the object
Boolean
and the values oftrue
andfalse
:
1
2
3 var b = new Boolean(false);
if (b) // this condition evaluates to true
if (b == true) // this condition evaluates to false
If-Else
This is basically the same across most languages:
1 | if (condition_1) { |
Switch
Again, this is the same in languages like Java:
1 | switch (expression) { |
The program first looks for a
case
clause with a label matching the value of expression and then transfers control to that clause, executing the associated statements.If no matching label is found, the program looks for the optional
default
clause:- If a
default
clause is found, the program transfers control to that clause, executing the associated statements. - If no
default
clause is found, the program resumes execution at the statement following the end ofswitch
.
- If a
Note that:
- Same as Java, the optional
break
statement associated with eachcase
clause ensures that the program breaks out ofswitch
once the matched statement is executed.
- If
break
is omitted, the program continues execution inside theswitch
statement (and will evaluate the nextcase
, and so on).
Handling Exceptions
There are some differences between JavaScript and languages like Java:
- Just about any object can be thrown in JavaScript.
- though it is more frequent to throw:
For Example: Throwing any Object
1 | throw 'Error2'; // String type |
For Example: Throwing a Simple Custom Exception
1 | // Create an object type UserException |
where:
- in general, it is a good practice to have a
name
andmessage
field (see [Utilizing Error Objects](#Utilizing Error Objects))
Catch
and Finally
Since JavaScript is a weakly typed language (as compared to Java), it only makes sense to have at most one catch
block:
1 | openMyFile(); |
Note that:
Same as in languages like Java, using
finally
means you might “mask the exceptions” thrown in the originaltry
block:
1
2
3
4
5
6
7
8
9
10
11
12 function f() {
try {
throw 'bogus';
} catch(e) {
console.log('caught inner "bogus"');
throw e; // this throw statement is suspended until
// finally block has completed
} finally {
return false; // OVERWRITES/MASKS the previous "throw"
}
// "return false" is executed now
}where:
- in fact, in this case, you will never be able to throw an exception from the function
f()
Utilizing Error Objects
In general, two fields are often used:
- the
name
property provides the general class ofError
(such asDOMException
orError
) - the
message
generally provides a more succinct message than one would get by converting the error object to a string.
A simple example would be:
1 | try { |
Loops and Iteration
In summary, you have:
for
loopsimilar to the Java and C
for
loop.1
2
3
4
5for (let i = 0; i < selectObject.options.length; i++) {
if (selectObject.options[i].selected) {
numberSelected++;
}
}where:
let
is often used because the variable would be then local scoped
do...while
loopagain, similar to Java
1
2
3do
statement
while (condition);
while
loopwhile (condition) statement <!--59-->
to break:
1
2break;
break [label];
For Example
1 | let x = 0; |
Enhanced for
Loops
There are “two” enhanced for loops:
for...in
- iterates a specified variable over all the enumerable properties of an object
for...of
- iterates over iterable objects (including
Array
,Map
,Set
,arguments
object and so on)
- iterates over iterable objects (including
For Example: for...in
Loop
1 | function dump_props(obj, obj_name) { |
For an object car
with properties make
and model
, result
would be:
1 | car.make = Ford |
Note: If you want to iterate over an array:
- In most cases, it is better to use a traditional
for
loop with a numeric index when iterating over arrays, because thefor...in
statement iterates over user-defined properties in addition to the array elements, if you modify the Array object (such as adding custom properties or methods).
For Example: for...of
Loop
1 | const arr = [3, 5, 7]; |
Functions
Defining Functions
A function definition (also called a function declaration, or function statement) consists of the function
keyword, followed by:
- The name of the function.
- A list of arguments
- The JavaScript statements that define the function, enclosed in curly brackets,
{...}
.
Note
- Same as Java and C, JavaScript passes parameters by value. So that
- primitive passes by value of the primitive
- object passes y the value of the address
For Example
1 | function functionName(arguement1, arguement2) { |
Function Expressions
This basically treats functions as expressions, and you can also pass them around like variables.
A function expression:
- can be anonymous
- assigned to a variable to pass around
For Example: Function Statement
1 | const factorial = function fac(n) { return n < 2 ? 1 : n * fac(n - 1) } |
Reminder
In
C
, you have the equivalent like this:
1 const int (*factorial)(int n) = &fac;
For Example: Passing in a Function
1 | function map(f, a) { |
Function returns: [0, 1, 8, 125, 1000]
.
Function Hoisting
Function declaration, like variables, are hoisted.
However:
- function expressions are not hoisted.
For Example:
1 | console.log(square(5)); |
However:
1 | console.log(square) // square is hoisted with an initial value undefined. |
Recursion
A function can refer to and call itself. There are three ways for a function to refer to itself:
- The function’s name
arguments.callee
- An in-scope variable that refers to the function
For example
1 | var foo = function bar() { |
where, within the function body, the following are all equivalent:
bar()
arguments.callee()
foo()
Closure
Closure describes the property of nested functions such that:
- the inner function full access to all the variables and functions defined inside the outer function (and all other variables and functions that the outer function has access to)
- the outer function does not have access to the variables and functions defined inside the inner function.
As a result
- since the inner function has access to the scope of the outer function, the variables and functions defined in the outer function will live longer than the duration of the outer function execution, if the inner function manages to survive beyond the life of the outer function
For Example
1 | var createPet = function(name) { |
Variable Hiding
This talks about the phenomena that:
- If an enclosed function defines a variable with the same name as a variable in the outer scope, then there is no way to refer to the variable in the outer scope again.
For Example
1 | var createPet = function(name) { // The outer function defines a variable called "name". |
Arguments Object
The arguments of a function are maintained in an array-like object.
- it is strictly speaking not an
Array
(see [Rest Parameters](#Rest Parameters))
Within a function, you can address the arguments passed to it as arguments[i]
.
- in a sense, this means
varargs
comes naturally to JavaScript without specification
For Example:
1 | function easy(){ |
For Example: Using Arguments Object
1 | function myConcat(separator) { |
and calling it:
1 | // returns "red, orange, blue, " |
Arrow Functions
Basically, this idea is analogous to the lambda expressions in Java.
One common usage of arrow function is its combination with Array.prototype.map()
- The
map()
method creates a new array populated with the results of calling a provided function on every element in the calling array.
For Example:
1 | const array1 = [1, 4, 9, 16]; |
Function Parameters
Starting with ECMAScript 2015, there are two new kinds of parameters:
- default parameters
- rest parameters
Default Parameters
In JavaScript, parameters of functions default to undefined
. However, in some situations it might be useful to set a different default value.
For Example:
1 | function multiply(a, b = 1) { |
Rest Parameters
This is basically varargs
- in particular, the rest parameters behaves exactly like an
Array
For Example:
1 | function multiply(multiplier, ...theArgs) { |
Difference between Rest Parameters and
Argument
Object
- Arguments object includes all arguments passed to the function, whereas rest parameters are those, which are not given another name.
- The rest parameters are Array instances, whereas arguments object isn’t an array. Array instances can use the following methods:
map
,sort
,pop
, etc
Predefined Functions
JavaScript has several top-level, built-in functions (for a full list, please visit this link):
- The
eval()
method evaluates JavaScript code represented as a string. - for example,
eval("2+2")
gives4
- The
uneval()
method creates a string representation of the source code of anObject
- The
isNaN()
function determines whether a value isNaN
or not. Note: coercion inside theisNaN
function has interesting rules; you may alternatively want to useNumber.isNaN()
, as defined in ECMAScript 2015, or you can usetypeof
to determine if the value is Not-A-Number.
- The
parseFloat()
function parses a string argument and returns a floating point number.
- The
parseInt()
function parses a string argument and returns an integer of the specified radix (the base in mathematical numeral systems).
- The
decodeURI()
function decodes a Uniform Resource Identifier (URI) previously created byencodeURI
or by a similar routine.
- The
decodeURIComponent()
method decodes a Uniform Resource Identifier (URI) component previously created byencodeURIComponent
or by a similar routine.
- The
encodeURI()
method encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two “surrogate” characters).
- The
encodeURIComponent()
method encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two “surrogate” characters).
Prototype and Inheritance
In Java, suppose you want to have something like:
1 | public class Manager extends Employee { |
In JavaScript, you need to do:
1 | function Manager() { |
Reminder
- remember that constructor and its superclass information of a custom type is stored in the
<CustomeType>.prototype
field (see [Prototype Chain](#Prototype Chain)).
Prototypes
In short, a prototype of an Object defines what gets inherited. (It is not to be seen as “prototype object of the current object”).
- this means that any functions defined in
Object.prototype
will get inherited in any other Object, and other functions defined only withObject
will not be inherited
As a result
- So
Object.prototype.toString()
,Object.prototype.valueOf()
, etc., are available to any object types that inherit fromObject.prototype
, including new object instances created from thePerson()
constructor.
For Example:
1 | function Person(first, last, age, gender, interests) { |
then, compare:
1 | Person.prototype; |
and:
1 | Object.prototype; |
Note:
- In the above example, the
constructor
automatically becomes a property of theprototype
.- This means that all Objects from
Person
will haveconstructor
available
Revisiting create()
In summary, Object.create()
lets you create an Object with a custom prototype:
1 | let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']); |
then:
1 | person2.__proto__ |
Using the constructor
property
As shown in the example in the Prototypes section above, a constructor property is automatically placed in prototype
, so that
- it is available to all instances of the object
- and all children inheriting from this object
For Example:
1 | let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']); |
and more interestingly:
1 | let person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']); |
works as well.
Modifying Prototypes
In summary, I show:
- how to add a method/function to the
Person
‘s prototype, so that all instances of it/children of it can use the function
For Example:
1 | // adding a function `farewell()` to prototype |
then call it with either:
1 | person1.farewell(); |
where both would output the same.
Note that:
be careful when defining fields using
this
. For instance, below would not work:
1
2 // `this` refers to the global scope
Person.prototype.fullName = this.name.first + ' ' + this.name.last;and only function scope works properly
1
2
3
4 Person.prototype.farewell = function() {
// this refers to the Object calling the function (function scope)
alert(this.name.first + ' has left the building. Bye for now!');
};
Inheritance with Prototype
In short, to inherit Teacher
from another object Person
, you need to:
use
Person.call(this, ...paremeters)
- this sets up the
constructor
to inherit properties of another object
- this sets up the
use
Teacher.prototype = Object.create(Person.prototype)
- this sets up the
prototype
field of theTeacher
object - however, this also sets the
prototype.constructor
toPerson
(might cause further inheritance issues)
- this sets up the
use
Object.defineProperty()
to reset the constructor toTeacher()
- so that now
prototype.constructor
will beTeacher
, and inheriting fromTeacher
would lead to the correct constructor being inherited
- so that now
Recall that:
the
Person.prototype
would basically have the following content:
1
2
3 farewell: ƒ ()
constructor: ƒ Person(first, last, age, gender, interests)
__proto__: Object
For Example:
Consider the same Person
object we had before:
1 | function Person(first, last, age, gender, interests) { |
- to create another object
Teacher
that inherits its fields/properties:
1 | function Teacher(first, last, age, gender, interests, subject) { |
- and then:
1 | Teacher.prototype = Object.create(Person.prototype); |
where:
- In this case we are using it to create a new object and make it the value of
Teacher.prototype
.
- Finally, we reset the
prototype.constructor
to make sure future correct behavior for inheriting fromTeacher
:
1 | Object.defineProperty(Teacher.prototype, 'constructor', { |
Inheritance with Class
This is applicable since ECMAScript 2015.
In short, this basically allows syntaxes like Java/C++.
For Example:
1 | class Person { |
instead of the function constructor and prototype definitions.
Additionally, inheritance becomes:
1 | class Teacher extends Person { |
Getters and Setters
In both prototype and class definitions, the idea is basically the same:
- use
get <property>()
for getters in a class - use
set <property>()
for setters
For Example: Class Based Getter and Setter
1 | class Teacher extends Person { |
For Example: Prototype Based Getter and Setter
1 | Object.defineProperties(Teacher.prototype, { |
Dynamic Client Side Programming
This section talks about the building blocks of programming a dynamic website/application.
Working with JSON
JavaScript Object Notation (JSON) is a standard text-based format for representing structured data based on JavaScript object syntax.
- therefore, all you need to do is to parse the JSON string using
JSON.parse()
JavaScript provides a global JSON object that has methods available for converting between the two.
Array as JSON
Basically arrays are formatted using the []
symbol.
For Example:
1 | let heroes = JSON.parse(`[ |
JSON Structure/Object
In general, an object in JSON uses the format:
1 | { |
For Example
1 | '{ |
Using XMLHttpRequest
In short, you can obtain data with with JavaScript using a XMLHttpRequest
to a backend server.
The API called
XMLHttpRequest
(often called XHR). This is a very useful JavaScript object that allows us to make network requests to retrieve resources from a server via JavaScript (e.g. images, text, JSON, even HTML snippets), meaning that we can update small sections of content without having to reload the entire page.
To do this, you usually need to:
- prepare the
requestURL
- create a
new XMLHttpRequest()
- open the request and set the request methods and request URL
- set the expected response type
- send the request
- format the response in the
request.response
into your HTML- here you will use the
onload
function
- here you will use the
For Example
For step 1-5:
1 | let requestURL = |
For step 6:
1 | request.onload = function() { |
where:
- The
XMLHttpRequest.onload
is the function called when anXMLHttpRequest
transaction completes successfully.
Lastly, to format the data into the web page:
1 | <body> |
and to populate the header
and section
:
1 | const header = document.querySelector('header'); |
Converting between Objects and Text
The above section worked with a piece of JSON data directly. However, sometimes data sent are in the forms of a String
, or that you need to send data across in String
. As a result, you need to convert between a String
to a JS Object
.
To do this, use the functions of the JSON
object:
parse()
: Accepts a JSON string as a parameter, and returns the corresponding JavaScript object.stringify()
: Accepts an object as a parameter, and returns the equivalent JSON string.
For Example:
If instead of JSON
, you receive text
:
1 | request.open('GET', requestURL); |
and to use stringify()
1 | let myObj = { name: "Chris", age: 38 }; |
Asynchronous JavaScript
This section assumes some prior knowledge of threading and blocking, so that you need to know:
- a browser blocks user’s inputs when the (JS) application has not been computed/rendered
- programs by default are single threaded (main thread)
Therefore, the solution is avoid blocking is to:
- do some expensive task in another thread:
1 | Main thread: Task A --> Task C |
- use asynchronous programming with a
Promise
1 | Main thread: Task A Task B |
For Example: Simple Asynchronous JavaScript
Consider the case when you are downloading an image, and then need to do something (e.g. apply a filter) to that image:
1 | let response = fetch('myImage.png'); // fetch is asynchronous |
where:
- because
fetch
isasync
, it immediately goes on the next line of code without the promise that the download has finished.- as a result, the second line will throw an error if the data isn’t ready
Async Callbacks
Reminder: Callback functions
Callback functions are just functions passed in as an argument into another function. When the background code finishes running, it calls the callback function to let you know the work is done, or to let you know that something of interest has happened.
for example:
1
2
3
4
5
6
7 btn.addEventListener('click', () => {
alert('You clicked me!');
let pElem = document.createElement('p');
pElem.textContent = 'This is a newly-added paragraph.';
document.body.appendChild(pElem);
});where:
'click'
is the first argument, specifying the type of event thatbtn
to listen for- the second argument is the callback function, so that it is invoked after the event is fired
A more complete and relevant example would be:
1 | function loadAsset(url, type, callback) { |
where:
callback
argument in theloadAsset()
serves as a callback function that is waiting on the XHR call to finish downloading the resource (using theonload
event handler) before it passes it to the callback.
Promises
Promises are the new style of async code that you’ll see used in modern Web APIs. A good example is the fetch()
API, which is basically like a modern, more efficient version of XMLHttpRequest
.
In short, a Promise
is:
it’s the browser’s way of saying “I promise to get back to you with the answer as soon as I can“
technically, it’s an object representing the completion or failure of the async operation (i.e. it is on its way, but it could succeed or fail)
For Example: Using fetch
1 | fetch('products.json').then(function(response) { // the first promise is the response |
where:
fetch()
taking a single parameter — the URL of a resource you want to fetch from the network — and returning apromise
then, after the async
fetch
comes back with result, you have:
- Two
then()
blocks. Both contain a callback function that will run if the previous operation is successful, and each callback receives as input the result of the previous successful operation, so you can go forward and do something else to it.- In fact, each
.then()
block returns another promise, meaning that you can chain multiple.then()
blocks onto each other, so multiple asynchronous operations can be made to run in order, one after another.
- In fact, each
- The
catch()
block at the end runs if any of the.then()
blocks fail- in a similar way to synchronous
try...catch
blocks, an error object is made available inside thecatch()
, which can be used to report the kind of error that has occurred. Note however that synchronoustry...catch
won’t work with promises, although it will work with async/await, as you’ll learn later on.
- in a similar way to synchronous
In essence:
Promise
s are essentially a returned object to which you attach callback functions (with.then()
), rather than having to pass callbacks into a function.
Promise.all
For the code before, we are just waiting for a single promise, and then deal with it asynchronously with .then()
. But what if you want to run some code only after a whole bunch of promises have all fulfilled?
To do this, you can use the Promise.all()
static method:
1 | Promise.all([a, b, c]).then(values => { |
where:
- the
.then()
block runs only after all three Promisesa
,b
,c
returned. - the returned
values
will be an array of three Promises, in the order of[resultA, resultB, resultC]
For Example:
Below will send three requests, and render the webpage only when all three request’s data have been received.
Step 1, send the three requests async:
1 | let coffee = fetchAndDecode('coffee.jpg', 'blob'); |
Step 2: wait for all three Promises async, and then render the page:
1 | Promise.all([coffee, tea, description]).then(values => { |
where:
- basically now
values[0]
will be the result fromcoffee
request,values[1]
will be fromtea
request, andvalues[2]
will be fromdescription
request
finally
for Promises
In short, this is the same as the try...catch...finally
logic in languages like Java.
- the
.finally()
method can be chained onto the end of your regular promise chain, and is guaranteed to be run in the end
For Example:
1 | function fetchAndDecode(url, type) { |
where:
fetch attempt for "${url}" finished.
will be printed in console no matter the requests failed or succeeded.
Creating Own Promises
In short, we know that a promise “does two things”:
- resolve/fulfill the task
- the underneath
resolve()
function
- the underneath
- failed to resolve/fulfill the task
- the underneath
reject()
function
- the underneath
Therefore, this is the common usage of a Promise()
constructor:
1 | new Promise(resolveFunction, rejectFunction); |
so that you will have code like this:
1 | const myFirstPromise = new Promise((resolve, reject) => { |
For Example:
1 | function timeoutPromise(message, interval) { |
where:
- if successful, the data Promise will be a string of
"Success"
(in analogy of what we have done withfetch
, the success result is a theresponse
) - if failed, the error will be the string error message inside the
reject()
clause.
For Example:Mimicking fetch
1 | function myAsyncFunction(url) { |
where:
- this basically mimics what happens under a
fetch
API
Introduction to Events
In the case of the Web, events are fired inside the browser window, and tend to be attached to a specific item that resides in it — this might be a single element, set of elements, the HTML document loaded in the current tab, or the entire browser window.
There are many different types of events that can occur. For example:
- The user selects a certain element or hovers the cursor over a certain element.
- The user hits a key on the keyboard.
- The user resizes or closes the browser window.
- A web page finishes loading.
- A form is submitted.
- A video is played, paused, or finishes.
- An error occurs.
There are even more in the MDN Event reference.
Now, to interact with the events, you typically use the event handler
.
- Each available event has an event handler, which is a block of code (usually a JavaScript function that you as a programmer create) that runs when the event fires.
- When such a block of code is defined to run in response to an event, we say we are registering an event handler.
Note:
- Event handlers are sometimes called event listeners — they are pretty much interchangeable for our purposes, although strictly speaking: the listener listens out for the event happening, and the handler is the code that is run in response to it happening.
For Example: A simple Example
First, in the html
, there is a button element:
1 | <button>Change color</button> |
then, we would like to interact with the click
event of the button:
1 | const btn = document.querySelector('button'); |
where:
- first we store a reference to the button inside a constant called
btn
, using theDocument.querySelector()
function - then we defined a function
random()
that returns a number - finally, we have the event handler: the object
button
has a number of events that can fire on it, and therefore, event handlers available.- Here, we are listening for the
click
event firing, by setting theonclick
event handler property to equal an anonymous function containing code that generates a random RGB color and sets the<body>
background-color
equal to it. - Therefore, the event handler code runs/handles whenever the
click
event fires on the<button>
element, that is, whenever a user selects it.
- Here, we are listening for the
Event Handler Properties
In short, those are properties that exists to contain the event handler code.
An example would be the above code:
1 | const btn = document.querySelector('button'); |
where:
- The
onclick
property would contain the code for handling the eventclick
In fact, some other common event handler properties include:
btn.onfocus
andbtn.onblur
- The color changes when the button is focused (selected) and unfocused (unselected);
btn.ondblclick
- the color changes when the button is double clicked
window.onkeypress
,window.onkeydown
,window.onkeyup
- The color changes when a key is pressed on the keyboard.
- note that we register it on the window object, which represents the entire browser window.
btn.onmouseover
andbtn.onmouseout
- The color changes when the mouse pointer hovers over the button, or when the pointer moves off the button, respectively.
Note that:
- Some events are general and available nearly anywhere (e.g. an
onclick
handler can be registered on nearly any element), whereas some are more specific and only useful in certain situations (e.g. it makes sense to use onplay only on specific elements, such asvideo
).
For Example: Using foreach
with handler property
Sometimes, when you want to set a number of buttons on the page to simultaneously, you can do:
- select all the button elements
1 | const buttons = document.querySelectorAll('button'); |
- Iterate through the array of buttons and assign them:
1 | // either |
where:
- The
forEach()
method of theNodeList
interface calls the callback given in parameter once for each value pair in the list
Using addEventListener()
Instead of setting the event handler/listener inside the property, you can use the addEventListener()
directly to the object:
1 | target.addEventListener(type, listener [, options]); |
For instance, converting the previous example into addEventListener()
code, you will have:
1 | const btn = document.querySelector('button'); |
where:
- Inside the
addEventListener()
function, there are two parameters:- the name of the event we want to this
target=btn
to listen to - the code that comprises the handler function we want to run in handle it.
- the name of the event we want to this
There are some advantages of this over the mechanism of using property:
- there is a counterpart function,
removeEventListener()
which removes a previously added listener. - you can perform various functions at the same time with
addEventListener()
For Example: Using addEventListener
and removeEventListener
To register multiple handlers to execute simultaneously when an event is fired:
1 | myElement.addEventListener('click', functionA); |
then, to remove one of the handlers, use:
1 | myElement.removeEventListener('click', functionA); |
The Event Object
The event object is automatically passed to event handlers to provide extra features and information.
- one useful property of the
event
object is theevent.target
- a reference to the element the event occurred upon
For Example: Changing the color of the button itself
To get the button, you can use the e.target
instead of the btn
Object.
1 | function bgChange(e) { |
where:
- notice that the handler function now takes the argument
e
/evt
/event
object, which is automatically passed in when an event occurs
Most event handlers you’ll encounter have a standard set of properties and functions (methods) available on the event object; see the
Event
object reference for a full list.
Preventing Default Behavior
In short, this uses the function preventDefault()
of the event
Object
- this is useful when, for example, a user has filled the form incompletely, and you want to prevent the action of submitting the incomplete form.
For Example:
First, you have the form html
:
1 | <form> |
then, you need to alter the event
object so that its default action of submitting (for a form) is prevented:
1 | const form = document.querySelector('form'); |
Event Capturing and Bubbling
This talks about the phenomena that, since some elements have a layered/nested structure, (e.g. the video
element below is inside the div
), clicking the video
will have also clicked the div
:
(source: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events)
As a result, some unwanted action might happen. If you want to only trigger the onclick
of the video
, then you first need to understand the bubbling and capturing phase:
In the capturing phase:
- The browser checks to see if the element’s outer-most ancestor (
html
) has anonclick
event handler registered on it for the capturing phase, and runs it if so. - Then it moves on to the next element inside
<html>
and does the same thing, then the next one, and so on until it reaches the element that was actually selected.
In the bubbling phase, the exact opposite occurs:
- The browser checks to see if the element selected (innermost) has an
onclick
event handler registered on it for the bubbling phase, and runs it if so. - Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the
<html>
element.
In modern browsers, by default, all event handlers are registered for the bubbling phase.
- this means that it is the
Event
object that bubbles up from handlersIf you really want to register an event in the capturing phase instead, you can do so by registering your handler using
addEventListener()
, and setting the optional third property totrue
.
Therefore, the solution here is to stop the bubbling propagation with the stopPropagation()
, so that when the innermost video
is clicked, the outer div
and so on are not triggered.
- technically: the standard
Event
object has a function available on it calledstopPropagation()
which, when invoked on a handler’s event object, makes it so that first handler is run but the event doesn’t bubble any further up the chain, so no more handlers will be run.
In code, you will need:
1 | // `videoBox` is the outer `div` element |
Event Delegation
This talks about utilizing the bubbling propagation to gain information on its immediate child.
- since the
Event
object gets bubbled up, we can check the value of theevent.target
to know the child value
Consider the case where you have a list of items:
1 | <ul id="parent-list"> |
then you want to have an event listener for each item if it gets clicked. The most efficient way is to utilize the bubbling and the event
object:
1 | // Get the element, add a click listener... |
where:
- the
Event
object is bubbled up, soEvent.target
contains the innermost clicked information
Introduction to APIs
This section assumes some prior knowledge/experience of using APIs. So that you know what APIs are, and have used them at least for once.
APIs in Client-Side JavaScript
Client-side APIs include:
- Browser APIs
- APIs built into your web browser and are able to expose data from the browser and surrounding computer environment and do useful complex things with it.
- For example, the Web Audio API provides JavaScript constructs for manipulating audio in the browser — taking an audio track, altering its volume, applying effects to it, etc.
- Third-party APIs
- APIs not built into the browser by default, and you generally have to retrieve their code and information from somewhere on the Web.
- For example, the Twitter API allows you to do things like displaying your latest tweets on your website.
And importantly, the distinction between JavaScript Library and JavaScript Frameworks
- JavaScript libraries — Usually one or more JavaScript files containing custom functions that you can attach to your web page to speed up or enable writing common functionality.
- Examples include jQuery, Mootools and React.
- JavaScript frameworks — The next step up from libraries, JavaScript frameworks (e.g. Angular and Ember) tend to be packages of HTML, CSS, JavaScript, and other technologies that you install and then use to write an entire web application from scratch.
- The key difference between a library and a framework is “Inversion of Control”. When calling a method from a library, the developer is in control. With a framework, the control is inverted: the framework calls the developer’s code. (i.e. you are just “configuring the framework’s behavior”)
Common APIs to Know
APIs for manipulating documents loaded into the browser.
- The most obvious example is the DOM (Document Object Model) API, which allows you to manipulate HTML and CSS — creating, removing and changing HTML, dynamically applying new styles to your page, etc
- see more in [Manipulating documents](#Manipulating documents)
APIs that fetch data from the server to update small sections of a webpage on their own are very commonly used.
- Some common examples include
XMLHttpRequest
and the Fetch API. You may also come across the term Ajax, which describes this technique. - see more in [Fetching data from the server](#Fetching data from the server)
- Some common examples include
APIs for drawing and manipulating graphics are now widely supported in browsers — the most popular ones are Canvas and WebGL, which allow you to programmatically update the pixel data contained in an HTML
canvas
element to create 2D and 3D scenes.Audio and Video APIs like
HTMLMediaElement
, the Web Audio API, and WebRTC allow you to do really interesting things with multimedia- For example, creating custom UI controls for playing audio and video, displaying text tracks like captions and subtitles along with your videos
Device APIs are basically APIs for manipulating and retrieving data from modern device hardware in a way that is useful for web apps.
- Examples include telling the user that a useful update is available on a web app via system notifications
Client-side storage APIs are becoming a lot more widespread in web browsers — the ability to store data on the client-side is very useful.
- There are a number of options available, e.g. simple name/value storage with the Web Storage API, and more complex tabular data storage with the IndexedDB API.
In terms of Third-party APIs, some common ones are:
The Twitter API, which allows you to do things like displaying your latest tweets on your website.
Map APIs like Mapquest and the Google Maps API allows you to do all sorts of things with maps on your web pages.
The Facebook suite of APIs enables you to use various parts of the Facebook ecosystem to benefit your app, for example by providing app login using Facebook login, accepting in-app payments, rolling out targeted ad campaigns, etc.
etc.
Entry Points of Common APIs
When using an API, you should make sure you know where the entry point is for the API.
In The Web Audio API, this is pretty simple — it is the
AudioContext
object, which needs to be used to do any audio manipulation whatsoever.The Document Object Model (DOM) API also has a simple entry point — its features tend to be found hanging off the
Document
object, or an instance of an HTML element that you want to affect in some wayThe Canvas API also relies on getting a context object to use to manipulate things, although in this case, it’s a graphical context rather than an audio context.
- Its context object is created by getting a reference to the
canvas
element you want to draw on, and then calling itsHTMLCanvasElement.getContext()
method
- Its context object is created by getting a reference to the
For Example: Using Web Audio API
The entry point is usually the AutioContext
Object:
1 | const AudioContext = window.AudioContext || window.webkitAudioContext; |
and then you could add functions such as:
1 | const audioElement = document.querySelector('audio'); |
For Example: Using DOM APIs
1 | const em = document.createElement('em'); // create a new em element |
For Example: Canvas APIs
To get the entry point for a Canvas, you usually use the context
of the canvas
object:
1 | const canvas = document.querySelector('canvas'); |
then to put some custom shapes into the canvas, you can use the context
object:
1 | Ball.prototype.draw = function() { |