JavaScript/Print version
This is the print version of JavaScript You won't see this message or any elements not part of the book's content when you print or preview this page. |
The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/JavaScript
Introduction
JS is a programming language that implements the international standard ECMAScript. It is based on the following concepts.
Dynamic data types
JS knows some primitive data types (Number, String, Boolean, BigInt, Symbol, Undefined, Null) and diverse derivates of the data type object (Array, Date, Error, Function, RegExp). [1] [2] If a variable exists, its type is clearly defined. But the type can be changed at any time by assigning a value of a different type to the variable, e.g.: the code fragment let x; x = 'Some text'; x = 2; x = [10, 11, 12];
is perfectly correct. It will not create a compile-time or run-time error. Only the type of the variable x
changes from Undefined to String to Number and lastly to Object/Array.
(Note: JSON is a text-based data format, not a data type. As such, it’s language-independent. It uses the JavaScript object syntax.)
Functional programming
Functions are first-class citizens similar to variables. They can be assigned to variables, passed as arguments to other functions, or returned from functions. The code fragment function sayHello() {return 'Good morning'};let x = sayHello; console.log(x());
creates a function sayHello, assigns it to the variable x, and executes it by calling x().
Object-orientated programming
JS supports object-oriented programming and inheritance through prototypes. A prototype is an object which can be cloned and extended. Doing so, a prototype chain arises. This differs from other OO-languages, e.g. Java, which uses classes for object-oriented features like inheritance. Nevertheless, at the syntactical level, classes are available in JS. But this is only 'syntactical sugar'. Under the hood, JS uses the prototype mechanism.
C-like syntax
The JS syntax is very similar to that of C, Java, or other members of the C-family. But we must always consider that the concepts and runtime behavior are distinctly different.
Relation to Java
JS has no relation to Java aside from having a C-like syntax. To avoid possible confusion, we would like to highlight some distinctions between JS and Java clearly.
In the beginning, Netscape developed JavaScript, and Sun Microsystems developed Java. Java includes classes and object instances, whereas JavaScript uses prototypes. In Java, variables must be declared before usage, which is unnecessary (but not recommended) in JS.
In Java, variables have an immutable static type (int
or String
, for example) that remains the same during the complete lifespan of a running program. In JS they also have a type (Number
or String
, for example), but this type can change during the lifespan of a running program. The type is detected from the environment. Therefore it's not necessary and not possible to define the type explicitly.
int x = 0; // Java: 'name of type', 'name of variable', ...
let x = 0; // JS: 'let' or 'const', 'name of variable', ...
// The type will be 'Number' because of the right side of the equal sign.
let x = String (0); // JS: explicit change from 'Number' to 'String' BEFORE assignment to x
// The type will be 'String'. Test it with: alert(typeof x)
JS engines
JS can run on the client-side as well as on the server-side. First versions of JS have run in Browsers that acted as mere interpreters. Today, the language is handled by just-in-time compilers (JIT). They parse the script, create an Abstract Syntax Tree (AST), optimize the tree, generate a JIT-specific bytecode out of the AST, generate hardware-specific machine code out of the bytecode, and bring the machine code to execution. Such just-in-time compilers exist not only in Browsers. They can also be part of other applications, e.g.: node.js which is written mainly in C++.
Widely used JS engines are:
- V8 from Google: Google Chrome, Electron, Chromium, node.js
- SpiderMonkey from Mozilla, Firefox
- JavaScriptCore from Apple, Safari
- ActionScript from Adobe, Flash
References
- ↑ MDN: Data Types
- ↑ MDN: Details on Data Types
First program
Here is a single JavaScript statement, which creates a pop-up dialog saying "Hello World!":
alert("Hello World!");
For the browser to execute the statement, it must be placed inside a HTML <script>
element. This element describes which part of the HTML code contains executable code. It will be described in further detail later.
<script>
alert("Hello World!");
</script>
The <script>
element should then be nested inside the <head>
element of an HTML document. Assuming the page is viewed in a browser that has JavaScript enabled, the browser will execute (carry out) the statement as the page is loading.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Some Page</title>
<script>
alert("Hello World!");
</script>
</head>
<body>
<p>The content of the web page.</p>
</body>
</html>
This basic 'Hello World' program can then be used as a starting point for any new program that you need to create.
Exercises
1. Copy and paste the basic program into a file, and save it on your hard disk as 'exercise_1-1.html'. You can run it in different ways:
- By going to the file with a file manager and opening it using a web browser.
- By starting your browser and then opening the file from the menu.
- By starting your browser and then specify the URL to 'exercise_1-1.html' with the file protocol. Please note that a) there are 3 slashes after 'file:' and b) replace 'temp' with the name of your directory.
- Windows:
file:///C:/temp/exercise_1-1.html
(it's Windows syntax, nevertheless use slash instead of backslash) - Linux:
file:///temp/exercise_1-1.html
- Windows:
What happens?
A dialog appears with the text: Hello World!
2. Save the above file as 'exercise_1-2.html'. Replace the double quotes in the line alert("Hello World!");
with single quotes, so it reads alert('Hello World!');
and save the result. If you open this file in the browser, what happens?
Nothing changes. A dialog appears with the text: Hello World! Double quotes and single quotes (apostrophes) are equivalents in JS.
Placing the code
The language JavasScript was originally introduced to run in browsers and handle the dynamic aspects of user interfaces, e.g., validation of user input, modifications of page content (DOM) or appearance of the user interface (CSS), or any event handling. This implies that an interconnection point from HTML to JS must exist. The HTML element <script>
plays this role. It is a regular HTML element, and its content is JS.
The <script>
element may appear almost anywhere within the HTML file, within <head>
as well as in <body>
. There are only a few criteria for choosing an optimal place; see below.
Internal vs. external JavaScript
The <script>
element either contains JS code directly, or it points to an external file resp. URL containing the JS code through its src
attribute. The first variant is called Internal JavaScript or Inline JavaScript, the second External JavaScript.
In the case of Internal JavaScript the <script>
element looks like:
<script>
// write your JS code directly here. (This line is a comment in JS syntax)
alert("Hello World!");
</script>
Internal scripting has the advantage that both your HTML and your JS are in one file, which is convenient for quick development. This is commonly used for temporarily testing out some ideas, and in situations where the script code is small or specific to that one page.
For the External JavaScript the <script>
element looks like:
<!-- point to a file or to a URL where the code is located. (This line is a comment in HTML syntax) -->
<script src="myScript.js"></script>
<script src="js/myScript2.js"></script>
<script src="https://example.com/dist/js/externallib.js"></script>
<script src="https://example.com/dist/js/externallib.min.js"></script>
<!-- although there is nothing within the script element, you should consider that the HTML5 spec -->
<!-- doesn't allow the abbreviation of the script element to: <script src="myScript.js" /> -->
Separate Files for Javascript Code
Having your JS in a separate file is recommended for larger programs, especially for such which are used on multiple pages. Furthermore, such splits support the pattern of Separation of Concerns: One specialist works on HTML, and another on JS. Also, it supports the division of the page's content (HTML) from its behavior (JS).
Overall, using External scripting is considered a best practice for software development.
Remote Code Injection vs. Local Library
With the example
<script src="https://example.com/dist/js/externallib.min.js"></script>
you can inject remotely maintained code from the server https://example.com
in your local web project. Remote code updates may break your local project or unwanted code features may be injected into your web project. On the other hand, centralized maintained and updated libraries serve your project due to bugfixes that are automatically updated in your project when the library is fetched again from the remote server.
Minified vs. Non-Minified Code
Minified Javascript code compresses the source code e.g. by shorting comprehensive variables like vImage into a single character variable a. This reduces significantly the size of the library and therefore reduces network traffic and response time until the web page is ready. For development and learning it might be helpful to have the uncompressed libraries locally available.
External JavaScript
For more detailed information you can refer to MDN [1].
The src
attribute
Adding src="myScript.js"
to the opening script
tag means that the JS code will be located in a file called myScript.js in the same directory as the HTML file. If the JS file is located somewhere else, you must change the src
attribute to that path. For example, if it is located in a subdirectory called js, it reads src="js/myScript.js"
.
The type
attribute
JS is not the only scripting language for Web development, but JS is the most common one on client-side (PHP runs on server-side). Therefore it's considered the default script type for HTML5. The formal notation for the type is: type="text/javascript"
. Older HTML versions know a lot of other script types. Nowadays, all of them are graded as legacy. Some examples are: application/javascript
, text/javascript1.5
, text/jscript
, or text/livescript
.
In HTML5, the spec says that - if you use JS - the type
attribute should be omitted from the script element [2], for Internal Scripting as well as for External Scripting.
<!-- Nowadays the type attribute is unnecessary -->
<script type="text/javascript">...</script>
<!-- HTML5 code -->
<script>...</script>
The async
and defer
attributes
Old browsers use only one or two threads to read and parse HTML, JS, CSS, ... . This may lead to a bad user experience (UX) because of the latency time when loading HTML, JS, CSS, images, ... sequentially one after the next. When the page loads for the first time, the user may have the impression of a slow system.
Current browsers can execute many tasks in parallel. To initiate this parallel execution with regards to JS loading and execution, the <script>
element can be extended with the two attributes async
and defer
.
The attribute async
leads to asynchronous script loading (in parallel with other tasks), and execution as soon as it is available.
<script async src="myScript.js"></script>
defer
acts similar. It differs from async
in that the execution is deferred until the page is fully parsed.
<script defer src="myScript.js"></script>
Location of <script>
elements
The script
element may appear almost anywhere within the HTML file. But there are, however, some best practices for speeding up a website [3]. Some people suggest to locate it just before the closing </body>
tag. This speeds up downloading, and also allows for direct manipulation of the Document Object Model (DOM) while it is rendered. But a similar behavior is initiated by the above-described async
and defer
attributes.
<!DOCTYPE html>
<html>
<head>
<title>Example page</title>
</head>
<body>
<!-- HTML code goes here -->
<script src="myScript.js"></script>
</body>
</html>
The <noscript>
element
It may happen that people have deactivated JS in their browsers for security or other reasons. Or, they use very old browsers which are not able to run JS at all. To inform users in such cases about the situation, there is the <noscript>
element. It contains text that will be shown in the browser. The text shall explain that no JS code will be executed.
<!DOCTYPE html>
<html>
<head>
<title>Example page</title>
<script>
alert("Hello World!");
</script>
<noscript>
alert("Sorry, the JavaScript part of this page will not be executed because JavaScript is not running in your browser. Is JavaScript intentionally deactivated?");
</noscript>
</head>
<body>
<!-- HTML code goes here -->
</body>
</html>
JavaScript in XHTML files
XHTML uses a stricter syntax than HTML. This leads to small differences.
First, for Internal JavaScript it's necessary that the scripts are introduced and finished with the two additional lines shown in the following example.
<script>
// <![CDATA[
alert("Hello World!");
// ]]>
</script>
Second, for External JavaScript the type
attribute is required.
Reference
- ↑ MDN: The script element
- ↑ WHATWG: The type attribute
- ↑ Yahoo: Best practices for speeding up your website
Other Script Types
The script
element will work in most browsers, because JavaScript is currently the default scripting language of the world wide web. In HTML5, the current spec says that developers should omit the type
attribute from script tags[1], although previously it was recommended to specify what type of script you are using in case the default scripting language changes.
<!-- Unnecessary, used to be recommended -->
<script type="text/javascript"></script>
<!-- New HTML5 code -->
<script></script>
Instead of specifying the scripting language individually in the script
element itself, you may also use a meta tag in the head of the document to specify a default scripting language for the entire page.
<meta http-equiv="Content-Script-Type" content="text/javascript" />
While the text/javascript
was formally obsoleted in April 2006 by RFC 4329 [2] in favour of application/javascript
, it is still preferable to continue using text/javascript
due to HTML validators' and Internet Explorer web browsers' inability to understand application/javascript
[3]
References
- ↑ https://html.spec.whatwg.org/dev/scripting.html#attr-script-type
- ↑ RFC 4329: Scripting Media Types
- ↑ "application/javascript" and "application/ecmasscript" media types not recognized.
Lexical structure
Summary
- All elements of the language are case-sensitive, e.g.:
const x = 0; const X = 0;
defines two different variables;CONST x = 0;
leads to a syntax error. - Single line comments are introduced by
//
. Multi-line comments are surrounded with/* */
. - Semicolons for ending a statement are optional - with few exceptions.
- The first character of a variable name cannot be a number:
const 1x = 0;
leads to a syntax error.
Case Sensitivity
JavaScript is case-sensitive. This means that all keywords, variable names, and function names must be written consistently. If you create a function Hello()
it is different from the function HELLO()
, hello()
, or hEllo()
. Or, the statement IF (x > 0) { }
leads to a syntax error because the keyword if
is defined in lower-case.
Whitespaces
Whitespace includes spaces, tabs, and line breaks.[note 1] If there is a sequence of multiple whitespaces, JavaScript reduces them to a single whitespace, e.g: ' ' -> ' ', '\n\t' -> '\n'. This single remaining whitespace delimits the language elements like keywords and variable names, ... from each other. The resulting source code is hard to read for people [2] but (a little) easier to parse by the browser. The main advantage is the smaller size of the code which must be transferred between server and client.
The following script is an example with very little whitespace.
function filterEmailKeys(event){
event=event||window.event;
const charCode=event.charCode||event.keyCode;
const char=String.fromCharCode(charCode);
if(/[a-zA-Z0-9_\-\.@]/.exec(char)){return true;}
return false;
}
The following is the same script with a typical amount of whitespace.
The following is the same script with a typical amount of whitespace.
function filterEmailKeys(event) {
event = event || window.event;
const charCode = event.charCode || event.keyCode;
const char = String.fromCharCode(charCode);
if (/[a-zA-Z0-9_\-\.@]/.exec(char)) {
return true;
}
return false;
}
The following is the same script with a lot of whitespaces.
function filterEmailKeys( evt ) {
evt = evt || window.event;
const charCode = evt.charCode || evt.keyCode;
const char = String.fromCharCode ( charCode );
if ( /[a-zA-Z0-9_\-\.@]/.exec ( char ) ) {
return true;
}
return false;
}
Comments
Comments are parts of the source code that will - per definition - not be executed.
They allow you to leave notes in your code to help other people understand it. They also allow you to comment out code that you want to hide from the parser but you don't want to delete.
- Single-line comments
A double slash //
turns all of the following text on the same line into a comment that will not be processed by the JavaScript interpreter.
// Show a welcome message
alert("Hello, World!")
- Multi-line comments
Multi-line comments start with /*
and end with the reverse */
. Multi-line comments don't nest.
Here is an example of how to use the different types of commenting techniques.
/* This is a multi-line comment
that contains multiple lines
of commented text. */
let a = 1;
/* commented out to perform further testing
a = a + 2;
a = a / (a - 3); // is something wrong here?
*/
alert('a: ' + a);
/* This comment has two /* but they're both canceled out by */
Semicolons
In many programming languages, semicolons are required at the end of each code statement. In JavaScript, the use of semicolons is optional, as a new line indicates the end of the statement (with some exceptions). This is called automatic semicolon insertion.
// the line
x = a + b;
// is equivalent to:
x = a + b
But the exceptions can be quite surprising.[3] Automatic semicolon insertion can create hard to debug problems.
a = b + c
(d + e).print()
The above code is not interpreted as two statements. Because of the parentheses on the second line, JavaScript interprets the above as if it were
a = b + c(d + e).print();
when instead, you may have meant it to be interpreted as
a = b + c;
(d + e).print();
Even though semicolons are optional, it's preferable to end statements with a semicolon to prevent any misunderstandings from taking place.
Literals
A literal is a hard-coded value. Literals provide a means of expressing specific values in your script. For example, to the right of the equals sign:
const myLiteral = "a fixed value";
There are several types of literals available. The most common are the string literals, but there are also numeric literals, booleans, undefined, null, regex literals, array literals, and object literals.
Examples of an object, a boolean, and a string literal:
const myObject = { name:"value", anotherName:"anotherValue" };
const isClosed = true;
const mayBeWrong = "true";
Details of these different types are covered in Variables and Types.
Identifiers
An identifier is a name for a piece of data, such as a variable, array, or function. There are rules:
- Letters, dollar signs, underscores, and numbers are allowed in identifiers.
- The first character cannot be a number.
Examples of valid identifiers:
u
$hello
_Hello
hello90
1A2B3C
is an invalid identifier, as it starts with a number.
An example of 'identifiers' are variable names. They obey such rules.
- Uppercase and lowercase letters, underscores, and dollar signs can be used.
- Numbers are allowed after the first character.
- Non-latin characters like "á" can be used in variable names as long as they have the Unicode properties "ID_Start" or "ID_Continue" for the start or rest of the name respectively.[4] Special characters are not allowed.
- Variable names are case sensitive: different case means a different name.
- A variable may not be a reserved word.
Exercises
Notes
References
- ↑ ECMAScript Language Specification, Chapter 12.2 - White Space
- ↑ Khan Academy
- ↑ ECMA-262 ECMAScript Language Specification, Chapter 12.9 - Automatic Semicolon Insertion
- ↑ ECMAScript Language Specification, the IdentifierName production
Automatic semicolon insertion
Automatic Semicolon Insertion (ASI)
In languages of the C-family, the semicolon denotes the end of a statement. Unlike other C-like languages, JavaScript doesn't enforce that. Instead, the semicolon is optional, and the interpreter adds missing semicolons - mostly at the end of a line - to terminate statements. Doing so, it takes complex rules into account. This may conflict with the intended purpose.
If you write your code without semicolons at the end of statements you must take care of problematic situations. Here are some rules-of-thumb to avoid problems. But there are many more rules.
- The expression after one of the keywords
return
,throw
, oryield
must be on the same line as the keyword itself. - The label identifier after
break
orcontinue
must be on the same line as the keyword. - If a line starts with one of
(
,[
,`
,+
,-
, or/
, end the previous line with a semicolon.
Examples
Entered code .. | .. interpreted as | intended code |
---|---|---|
return
2a + 1
|
return;
2a + 1;
|
return 2*a + 1;
|
function getObject() {
return
{
// some lines
}
}
|
function getObject() {
return;
{
// some lines
};
}
|
function getObject() {
return {
// some lines
};
}
|
i
++
|
i;
++;
|
i++;
|
In the first case, the programmer intended 2*a + 1 to be returned; instead, the code returned undefined
. Similarly, in the second case, the programmer intended to return the lines enclosed by the braces {}
, but the code returned undefined
. Due to this oddity in JavaScript, it is considered a best practice never to have lines break within a statement and never have the opening brace on a separate line.
See also
Reserved words
In JavaScript, some tokens (words) have a special semantic (meaning). Therefore they cannot be used as names of variables, functions, classes, etc [1] [2]. Some of them are generally reserved words; others are reserved only in a special context; others are reserved for possible future usage without having a special functionality nowadays; others have been defined in outdated ECMAScript versions of the years 1997 - 99.
The list of such special words as of 2022 follows. For some of the words, we offer further information.
- abstract
- await
- boolean
- break
- byte
- case
- catch
- char
- class
- const
- continue
- debugger
- default
- delete
- do
- double
- else
- enum
- export
- extends
- false
- final
- finally
- float
- for
- function
- goto
- if
- implements
- import
- in
- instanceof
- int
- interface
- let
- long
- native
- new
- null
- package
- private
- protected
- public
- return
- short
- static
- super
- switch
- synchronized
- this
- throw
- throws
- transient
- true
- try
- typeof
- var
- void
- volatile
- while
- with
- yield
Furthermore, there are predefined methods like forEach()
, predefined modules like Math
, or predefined objects like BigInt
whose names should be avoided also.
References
Variables and types
Purpose
Computer languages need to use variables. Why this? In most cases, programs don't solve single problems like the very concrete question: What is the circumference of a circle with a radius of 5 cm? Such a concrete question can be solved without using variables: alert (2 * 5 * 3.14);
Instead, most questions are more general: What is the circumference of a circle with an arbitrary radius? You don't want to write a program for a radius of 5 cm, another program for a radius of 6 cm, another one for a radius of 7 cm, and so on. You want to write a single program that computes the circumference for all possible radii. The program needs input (from a user, from another program, from a database, ...) that tells him for which value it shall run. let r = prompt("How big is the radius of your circle?"); alert (2 * r * 3.14);
, or even better: let r = prompt("How big is the radius of your circle?"); alert (2 * r * Math.PI);
.
Those two examples are flexible. They ask the user for the desired radius, store the given value in a variable with the name r, and compute the circumference using this variable. The variable r is introduced by the keyword let
. And there is a second variable. The module Math has predefined a variable PI with the keyword const
: const PI = 3.141592653589793;
.
In JavaScript, variables can be used similarly to variables in mathematical formulas. During runtime, the values of variables are stored in the main memory of the computer (RAM), from where they can be used at a later moment in the lifetime of the program. You can imagine a variable as a small box where you deposit some value and take it out whenever you need it.
Variables are a cornerstone in the transition from individual problem solving to a strategy, respectively an algorithm.
Declaration and initialization
If you want to use a variable, we recommend declaring them explicitly. This is not mandatory but has strong benefits.
In many cases, the declaration is accompanied by an initialization, e.g. let x = 0;
. The declaration is let x;
, and the initialization is x = 0;
. But it's also possible to omit the initialization part: let x;
which causes the value of the variable to be undefined
.
Keyword let
The keyword let
introduces a variable whose value may be changed multiple times.
let x = 0;
// ...
x = x + 5;
// ...
x = -4;
// ...
Keyword const
The keyword const
introduces a variable that must be initialized immediately. Moreover, this very first value can never be changed. That helps the JavaScript engine to optimize the code for better performance. Use const
as often as possible.
const maxCapacity = 100;
// ...
maxCapacity = maxCapacity + 10; // not possible
let maxCapacity = 110; // not possible
When you work with objects, e.g., with arrays, in combination with the keyword const
, it's the same: you cannot assign another value (object, array, number, or whatever) to the variable. Nevertheless, its elements can be changed.
const arr = [1, 2, 3];
arr = [1, 2, 3, 4]; // not possible
arr = new Array(10); // not possible
arr[0] = 5; // ok: 5, 2, 3
alert(arr);
arr.push(42); // ok: 5, 2, 3, 42
alert(arr);
In some cases const
variables are written in uppercase, e.g., PI
. This is a convention and not mandatory.
Keyword var
At first glance, var
is identical to let
. But the range where such variables are known is different from the variables declared by var
; see chapter Scope below.
Omitting the declaration
You can assign a value to a variable without declaring the variable previously. "JavaScript used to allow assigning to undeclared variables, which creates an undeclared global variable. This is an error in strict mode and should be avoided altogether."[1] In other words: The variable goes to global scope, see below. As long as you don't have good reasons you should avoid using the global scope because its usage tends to create unwanted side effects.
// direct usage of 'radius' without any keyword for its declaration
/* 1 */ radius = 5;
/* 2 */ alert (2 * radius * 3.14);
Sometimes such situations occur by accident. If there is a typing error in the source code, JavaScript uses two different variables: the original one and a new one with the wrongly typed name - even if you use one of the keywords let
, const
, or var
.
let radius = 5; // or without 'let'
alert("Test 1");
// ... later in the code
radus = 1; // typo will not be detected
alert("Test 2");
You can instruct JavaScript to search for such typos by inserting the command "use strict";
as the first line of your scripts.
"use strict";
let radius = 5;
alert("Test 1");
// ... later in the code
radus = 1; // typo will be detected and an error message given
alert("Test 2"); // will never execute
Data types
Programmers who are familiar with (strict) typed languages like Java may miss in the above chapter the possibility of defining the type of variables. JavaScript knows many different data types. But their handling and behavior is very different from that in Java. In the next chapter you will learn about that.
Scope
A scope is a range of consecutive JavaScript statements with a clearly defined start and end. JavaScript knows four types of scopes: block, function, module, and global scope. Depending on the kind of declaration and the location of the declaration, variables are within such scopes. They are 'visible' respectively 'accessible' only within their scope. If you try to access them from outside, an error will occur.
Block scope
A pair of curly brackets {}
creates a block. Variables declared within a block by let
or const
are bound to this block and cannot be accessed outside.
"use strict";
let a = 0;
// ...
if (a == 0) {
let x = 5;
alert(x); // shows the number 5
} else {
alert(x); // ReferenceError (with a different 'a')
}
alert(x); // ReferenceError
The variable x
is declared inside a block (in this simple example, the block consists of only two lines.) It is not accessible behind the end of the block, which is the closing curly bracket }
in the else
line. The same applies to the case that the variable x
is declared with const
instead of let
.
Be careful with the keyword var
; its semantics is different! First, var
is not block-scoped. Second, it leads to a technique called hoisting, which has been used in JavaScript since its first days. Hoisting changes the semantics of different declarations 'under the hood'. Concerning var
, it splits declaration and initialization into two separate statements and shifts the declaration part to the top of the current scope. Hence the variable is declared but not initialized if you use it before the line where it is declared in the source.
The script
"use strict";
alert(x); // undefined, not ReferenceError !
x = 1; // correct, despite of "use strict"
alert(x); // shows 1
var x = 0;
is changed to:
"use strict";
var x;
alert(x); // undefined, not ReferenceError !
x = 1;
alert(x); // shows 1
x = 0;
On the other hand, the keyword let
keeps the declaration in the line where it is written.
"use strict";
alert(x); // ReferenceError
x = 1; // ReferenceError
alert(x); // ReferenceError
let x = 0;
There are more differences. Here is a version of the first example of this chapter replacing let
by var
:
"use strict";
let a = 0;
// ...
if (a == 0) {
var x = 5; // 'var' instead of 'let'
alert(x); // shows the number 5
} else {
alert(x); // ReferenceError (with a different 'a')
}
alert(x); // shows the number 5 !!
We recommend avoiding var
completely because of two reasons:
- JavaScript's hoisting technique is not easy to understand.
- Other members of the C-family languages don't know it.
Instead of using var
, use the keyword let
.
Function scope
A function creates its own scope. Variables declared in the function's scope cannot be accessed from outside.
"use strict";
function func_1() {
let x = 5; // x can only be used in func_1
alert("Inside function: " + x);
}
func_1();
alert(x); // Causes an error
The function scope is sometimes called the local scope because this was the name in older ECMAScript versions.
See also: Closures works the other way round - access of outer variables inside the function.
Module scope
It is possible to divide huge scripts into multiple files and let the functions and variables communicate with each other. Each file creates its own scope, the module scope. The chapter JavaScript/Modules explains more about that.
Global scope
Variables or functions are in global scope if they are declared at the top level of a script (outside of all blocks and functions).
"use strict";
let x = 42; // 'x' belongs to global scope
// define a function
function func_1() {
// use variable of the global context
alert("In function: " + x);
}
// start the function
func_1(); // shows "In function: 42"
alert(x); // shows "42"
x
is declared at the top level, hence it is in the global scope and can be used everywhere. But in the next example the declaration of x
is wrapped by { }
. Hence it is no longer in global scope.
"use strict";
{
let x = 42; // 'x' is not in global scope
}
alert(x); // ReferenceError
Hint: The use of the global scope isn't considered good practice. It tends to create unwanted side effects. Instead, try to modularize your code and let the parts communicate via interfaces.
Exercises
See also
References
- ↑ MDN: Variable declarations
Numbers
Primitive types use a fixed format; some can contain only a limited number of certain values. In contrast, objects are more complex, especially including methods and properties.
With the exception of null
and undefined
, primitive types have a corresponding object wrapper with data type specific methods. Therefore you will find on this page descriptions of some methods.
String
String is a datatype to hold text of arbitrary length. String variables are created by assigning a string literal to them. String literals can be enclosed in " "
or ' '
.
"use strict";
const myName_1 = "Mike"; // double quote
const myName_2 = 'Monica'; // apostrophe
const myName_3 = "naɺ̠ɯçito"; // non-latin characters
If your string literal contains a "
or '
, you can use the other one as the outer delimiter, or you escape them with a \
.
"use strict";
const book_1 = "Mike's book";
const monica_1 = 'Here name is "Monica".';
const book_2 = 'Mike\'s book';
const monica_2 = "Here name is \"Monica\".";
If your string literal is computed out of some fixed text plus some dynamic parts, you can use the template literal technique. Here, the literal is enclosed in backticks ` `
and contains variables and expressions.
"use strict";
const a = 1;
const b = 2;
const resultMessage = `The sum of ${a} and ${b} is: ${a + b}.`;
// same as:
'The sum of ' + a + ' and ' + b + ' is: ' + (a + b);
alert(resultMessage);
The +
operator concatenates two strings, e.g. alert("Hello " + "world!");
. Additionally, there are a lot of methods for strings.
Hint: JavaScript doesn't have something like a 'character' or 'byte' data type.
Properties and methods for strings
We show some methods which are often used. For a complete list, please refer to MDN.
length
length
is a property, not a method. Hence there are no parenthesizes ()
. It returns the length of the string as a whole number.
const foo = "Hello!";
alert(foo.length); // 6
includes(searchText)
The method returns true
if the string contains a specified string, otherwise it returns false
.
let text = "Hello world, hello Wikiversity!";
document.write(text.includes("Hello")); //true
concat(text)
The method returns a string where 'text' is appended to the original string.
const foo = "Hello";
const bar = foo.concat(" World!");
alert(bar); // Hello World!
indexOf(searchText)
The method returns the position of the first occurrence of 'searchText', starting with 0. If 'searchText' cannot be found, -1 is returned. The method acts case sensitive.
const foo = "Hello, World! How do you do?";
alert(foo.indexOf(" ")); // 6
const hello = "Hello world, welcome to the universe.";
alert(hello.indexOf("welcome")); // 13
search(regularExpression)
The method compares the string with a regular expression (=pattern), returning the index of the first match in the string. It is similar to indexOf()
, but much more powerful. E.g.: it can be made case insensitive by replacing the quotation marks in "hello"
with forward slashes and an i
at the end.
let text = "Hello world, hello Wikiversity!";
document.write(text.search(/hello/i)); // 0
lastIndexOf(searchText)
The method returns the position of the last occurrence of 'searchText'. If 'searchText' cannot be found, -1 is returned. The method acts case sensitive.
const foo = "Hello, World! How do you do?";
alert(foo.lastIndexOf(' ')); // 24
replace(text, newtext)
The method returns a string where 'text' is replaced by 'NewText' on the original string. Only the first occurrence is replaced. The method acts case sensitive.
const foo = "foo bar foo bar foo";
const newString = foo.replace("bar", "NEW"):
alert(foo); // foo bar foo bar foo
alert(newString); // foo NEW foo bar foo
As you can see, the replace
method only returns the new content and does not modify the origin string in 'foo'.
slice(start [, end])
The method returns a substring beginning at the 'start' position.
"hello".slice(1); // "ello"
When the 'end' is provided, they are extracted up to, but not including the end position.
"hello".slice(1, 3); // "el"
slice
allows extracting text referenced from the end of the string by using negative indexing.
"hello".slice(-4, -2); // "el"
Unlike substring
, the slice method never swaps the 'start' and 'end' positions. If the 'start' is after the 'end', slice
will attempt to extract the content as presented, but will most likely provide unexpected results.
"hello".slice(3, 1); // ""
substr(start [, number of characters])
The method is deprecated. Use substring
or slice
instead.
substring(start [, end])
The method extracts a substring starting at the 'start' position.
"hello".substring(1); // "ello"
When the 'end' is provided, they are extracted up to, but not including the end position.
"hello".substring(1, 3); // "el"
substring
always works from left to right. If the 'start' position is larger than the 'end' position, substring
will swap the values; although sometimes useful, this is not always what you want; different behavior is provided by slice.
"hello".substring(3, 1); // "el"
toLowerCase()
The method returns the current string in lower case.
const foo = "Hello!";
alert(foo.toLowerCase()); // hello!
toUpperCase()
The method returns the current string in upper case.
const foo = "Hello!";
alert(foo.toUpperCase()); // HELLO!
Number
Number is one of the two numeric types (the other one is BigInt). Number stores integer values as well as floating point values in a unified 64-bit format defined by IEEE 754. That means, that JavaScript doesn't have different data types for integers and float like some other languages.
The possible range for such values is approximate -10300 to +10300 with different precision depending on the distance to 0.
In the range from -(253 − 1) to +253 − 1 there is no uncertainness for integer operations. 253 = 9,007,199,254,740,992 which is a little smaller than 1016.
"use strict";
let counter = 20; // no decimal point
alert(counter + " " + typeof counter);
let degree = 12.1; // with decimal point
alert(degree + " " + typeof degree);
For Number the usual arithmetic operators ('power' is **
and 'modulo' is %
), comparison operators (<
, >
, ...), and bitwise operators are available.
In opposite to some other languages, the division of two whole numbers can result in a number with decimal places, e.g. alert(1/3);
.
Properties and methods for numbers
Working with numbers is supported by many properties and methods. Internally, they are implemented at different areas:
- The built-in object
Math
provides properties that represent common mathematical constants like π or e. Syntax:Math.xyz
(no parenthesis) - The build-in object
Math
provides common mathematical functions like sin or log. Syntax:Math.xyz()
(with parenthesis) - The object
Number
provides properties that characterize the implementation of the data type number, like MAX_VALUE or NEGATIVE_INFINITY. Syntax:Number.xyz
(no parenthesis) - The object
Number
provides static methods that check the relation between numbers and other data types, e.g., isInteger or parseFloat. Syntax:Number.xyz()
(with parenthesis) - The object
Number
provides instance methods that act on concrete number values or variables, e.g., toExponential or toFixed. Syntax:value.xyz()
(with parenthesis)
// some examples
"use strict";
const var_1 = Math.PI;
alert(var_1);
alert(var_1.toFixed(2));
const var_2 = Math.sqrt(3);
alert(var_2);
alert(Number.MAX_VALUE);
alert(Number.isInteger(123)); // true
alert(Number.isInteger(12.3)); // false
We show some properties and methods which are often used. For a complete list, please refer to MDN Math and MDN Numbers.
Properties
Most commonly used constants:
Math.E
Returns the constant e.Math.PI
Returns the constant pi.Math.LN10
Returns the natural logarithm of 10.Math.LN2
Returns the natural logarithm of 2.Math.SQRT2
Returns the square root of 2.
Math.ceil(number)
Returns the smallest integer greater than the number passed as an argument.
const myInt = Math.ceil(90.8);
alert(myInt); // 91
alert(Math.ceil(-90.8)); // -90
Math.floor(number)
Returns the greatest integer less than the number passed as an argument.
const myInt = Math.floor(90.8);
alert(myInt); // 90
alert(Math.floor(-90.8)); // -91
Math.round(number)
Returns the closest integer to the number passed as an argument.
alert(Math.round( 90.8)); // 91
alert(Math.round(-90.8)); // -91
alert(Math.round( 90.3)); // 90
alert(Math.round(-90.3)); // -90
Math.max(number_1, number_2)
Returns the higher number from the two numbers passed as arguments.
alert(Math.max(8.3, 9)); // 9
alert(Math.max(8.3, -9)); // 8.3
Math.min(number_1, number_2)
Returns the lower number from the two numbers passed as arguments.
alert(Math.min(8.3, 9)); // 8.3
alert(Math.min(8.3, -9)); // -9
Math.random()
Generates a pseudo-random number between 0 and 1.
alert(Math.random());
Number.parseInt(string)
Number.parseFloat(string)
The two methods parseInt
and parseFloat
convert strings into numbers. They scan the given string from left to right. When they recognize a character distinct from 0 - 9, they finish scanning and return the converted numbers read so far (parseFloat accepts the decimal point). If the first character is distinct from 0 - 9, they return Math.NaN, meaning Not a Number.
Hint: It's not necessary to specify 'Number.' in the source code.
const x = parseInt("7.5");
alert(x); // 7
const y = parseInt("Five");
alert(y); // NaN
const z = parseFloat("2.8") + 3;
alert(z); // 5.8
// scientific notation is accepted
alert(parseFloat("123.456e6")); // 123456000
BigInt
BigInt is a data type that represents integers of arbitrary length. Hence, it's a variable-length format (conceptually) delimited only by the available RAM.
BigInts are created either by adding a 'n' to the end of an integer literal or by using the BigInt()
function.
"use strict";
let hugeNumber_1 = 12345678901234567890n; // 'n' at the end
alert(typeof hugeNumber_1);
let hugeNumber_2 = BigInt("12345678901234567890"); // no 'n'
alert(typeof hugeNumber_2);
// a 'small' BigInt can be created out of an integer value
let hugeNumber_3 = BigInt(123);
alert(typeof hugeNumber_3);
BigInt supports the arithmetic operators + - * / **
, comparison operators, and most of the bitwise operators.
Boolean
Boolean variables can contain one of two possible values, true
or false
. JavaScript represents false
by either a Boolean false, the number 0, NaN, an empty string, or the built-in types undefined or null. Any other values are treated as true
.
"use strict";
let firstLoop = true;
alert(typeof firstLoop);
Undefined
Variables that have just been declared but not initialized with any value have the data type undefined.
"use strict";
let x;
// ...
alert(typeof x);
if (x == undefined) {
// remedy the missing initialization
x = 0;
}
alert(typeof x);
// ...
Null
In JavaScript, null is marked as one of the primitive values, because its behavior is seemingly primitive. However, when using the typeof
operator, it returns "object". This is considered a bug, but one which cannot be fixed because it will break too many scripts.[1]
Symbol
Symbol represents a unique identifier. Symbols may have a descriptor that is given as a string to its constructor.
"use strict";
// 'person' is a symbol with the description "Olaf"
const person = Symbol("Olaf");
alert(person.description); // Olaf
Symbols are used as keys in objects (embedded in [ ]
) to guarantee uniqueness.
See also
ECMAScript definitions on data types
Exercises
References
Strings
Primitive types use a fixed format; some can contain only a limited number of certain values. In contrast, objects are more complex, especially including methods and properties.
With the exception of null
and undefined
, primitive types have a corresponding object wrapper with data type specific methods. Therefore you will find on this page descriptions of some methods.
String
String is a datatype to hold text of arbitrary length. String variables are created by assigning a string literal to them. String literals can be enclosed in " "
or ' '
.
"use strict";
const myName_1 = "Mike"; // double quote
const myName_2 = 'Monica'; // apostrophe
const myName_3 = "naɺ̠ɯçito"; // non-latin characters
If your string literal contains a "
or '
, you can use the other one as the outer delimiter, or you escape them with a \
.
"use strict";
const book_1 = "Mike's book";
const monica_1 = 'Here name is "Monica".';
const book_2 = 'Mike\'s book';
const monica_2 = "Here name is \"Monica\".";
If your string literal is computed out of some fixed text plus some dynamic parts, you can use the template literal technique. Here, the literal is enclosed in backticks ` `
and contains variables and expressions.
"use strict";
const a = 1;
const b = 2;
const resultMessage = `The sum of ${a} and ${b} is: ${a + b}.`;
// same as:
'The sum of ' + a + ' and ' + b + ' is: ' + (a + b);
alert(resultMessage);
The +
operator concatenates two strings, e.g. alert("Hello " + "world!");
. Additionally, there are a lot of methods for strings.
Hint: JavaScript doesn't have something like a 'character' or 'byte' data type.
Properties and methods for strings
We show some methods which are often used. For a complete list, please refer to MDN.
length
length
is a property, not a method. Hence there are no parenthesizes ()
. It returns the length of the string as a whole number.
const foo = "Hello!";
alert(foo.length); // 6
includes(searchText)
The method returns true
if the string contains a specified string, otherwise it returns false
.
let text = "Hello world, hello Wikiversity!";
document.write(text.includes("Hello")); //true
concat(text)
The method returns a string where 'text' is appended to the original string.
const foo = "Hello";
const bar = foo.concat(" World!");
alert(bar); // Hello World!
indexOf(searchText)
The method returns the position of the first occurrence of 'searchText', starting with 0. If 'searchText' cannot be found, -1 is returned. The method acts case sensitive.
const foo = "Hello, World! How do you do?";
alert(foo.indexOf(" ")); // 6
const hello = "Hello world, welcome to the universe.";
alert(hello.indexOf("welcome")); // 13
search(regularExpression)
The method compares the string with a regular expression (=pattern), returning the index of the first match in the string. It is similar to indexOf()
, but much more powerful. E.g.: it can be made case insensitive by replacing the quotation marks in "hello"
with forward slashes and an i
at the end.
let text = "Hello world, hello Wikiversity!";
document.write(text.search(/hello/i)); // 0
lastIndexOf(searchText)
The method returns the position of the last occurrence of 'searchText'. If 'searchText' cannot be found, -1 is returned. The method acts case sensitive.
const foo = "Hello, World! How do you do?";
alert(foo.lastIndexOf(' ')); // 24
replace(text, newtext)
The method returns a string where 'text' is replaced by 'NewText' on the original string. Only the first occurrence is replaced. The method acts case sensitive.
const foo = "foo bar foo bar foo";
const newString = foo.replace("bar", "NEW"):
alert(foo); // foo bar foo bar foo
alert(newString); // foo NEW foo bar foo
As you can see, the replace
method only returns the new content and does not modify the origin string in 'foo'.
slice(start [, end])
The method returns a substring beginning at the 'start' position.
"hello".slice(1); // "ello"
When the 'end' is provided, they are extracted up to, but not including the end position.
"hello".slice(1, 3); // "el"
slice
allows extracting text referenced from the end of the string by using negative indexing.
"hello".slice(-4, -2); // "el"
Unlike substring
, the slice method never swaps the 'start' and 'end' positions. If the 'start' is after the 'end', slice
will attempt to extract the content as presented, but will most likely provide unexpected results.
"hello".slice(3, 1); // ""
substr(start [, number of characters])
The method is deprecated. Use substring
or slice
instead.
substring(start [, end])
The method extracts a substring starting at the 'start' position.
"hello".substring(1); // "ello"
When the 'end' is provided, they are extracted up to, but not including the end position.
"hello".substring(1, 3); // "el"
substring
always works from left to right. If the 'start' position is larger than the 'end' position, substring
will swap the values; although sometimes useful, this is not always what you want; different behavior is provided by slice.
"hello".substring(3, 1); // "el"
toLowerCase()
The method returns the current string in lower case.
const foo = "Hello!";
alert(foo.toLowerCase()); // hello!
toUpperCase()
The method returns the current string in upper case.
const foo = "Hello!";
alert(foo.toUpperCase()); // HELLO!
Number
Number is one of the two numeric types (the other one is BigInt). Number stores integer values as well as floating point values in a unified 64-bit format defined by IEEE 754. That means, that JavaScript doesn't have different data types for integers and float like some other languages.
The possible range for such values is approximate -10300 to +10300 with different precision depending on the distance to 0.
In the range from -(253 − 1) to +253 − 1 there is no uncertainness for integer operations. 253 = 9,007,199,254,740,992 which is a little smaller than 1016.
"use strict";
let counter = 20; // no decimal point
alert(counter + " " + typeof counter);
let degree = 12.1; // with decimal point
alert(degree + " " + typeof degree);
For Number the usual arithmetic operators ('power' is **
and 'modulo' is %
), comparison operators (<
, >
, ...), and bitwise operators are available.
In opposite to some other languages, the division of two whole numbers can result in a number with decimal places, e.g. alert(1/3);
.
Properties and methods for numbers
Working with numbers is supported by many properties and methods. Internally, they are implemented at different areas:
- The built-in object
Math
provides properties that represent common mathematical constants like π or e. Syntax:Math.xyz
(no parenthesis) - The build-in object
Math
provides common mathematical functions like sin or log. Syntax:Math.xyz()
(with parenthesis) - The object
Number
provides properties that characterize the implementation of the data type number, like MAX_VALUE or NEGATIVE_INFINITY. Syntax:Number.xyz
(no parenthesis) - The object
Number
provides static methods that check the relation between numbers and other data types, e.g., isInteger or parseFloat. Syntax:Number.xyz()
(with parenthesis) - The object
Number
provides instance methods that act on concrete number values or variables, e.g., toExponential or toFixed. Syntax:value.xyz()
(with parenthesis)
// some examples
"use strict";
const var_1 = Math.PI;
alert(var_1);
alert(var_1.toFixed(2));
const var_2 = Math.sqrt(3);
alert(var_2);
alert(Number.MAX_VALUE);
alert(Number.isInteger(123)); // true
alert(Number.isInteger(12.3)); // false
We show some properties and methods which are often used. For a complete list, please refer to MDN Math and MDN Numbers.
Properties
Most commonly used constants:
Math.E
Returns the constant e.Math.PI
Returns the constant pi.Math.LN10
Returns the natural logarithm of 10.Math.LN2
Returns the natural logarithm of 2.Math.SQRT2
Returns the square root of 2.
Math.ceil(number)
Returns the smallest integer greater than the number passed as an argument.
const myInt = Math.ceil(90.8);
alert(myInt); // 91
alert(Math.ceil(-90.8)); // -90
Math.floor(number)
Returns the greatest integer less than the number passed as an argument.
const myInt = Math.floor(90.8);
alert(myInt); // 90
alert(Math.floor(-90.8)); // -91
Math.round(number)
Returns the closest integer to the number passed as an argument.
alert(Math.round( 90.8)); // 91
alert(Math.round(-90.8)); // -91
alert(Math.round( 90.3)); // 90
alert(Math.round(-90.3)); // -90
Math.max(number_1, number_2)
Returns the higher number from the two numbers passed as arguments.
alert(Math.max(8.3, 9)); // 9
alert(Math.max(8.3, -9)); // 8.3
Math.min(number_1, number_2)
Returns the lower number from the two numbers passed as arguments.
alert(Math.min(8.3, 9)); // 8.3
alert(Math.min(8.3, -9)); // -9
Math.random()
Generates a pseudo-random number between 0 and 1.
alert(Math.random());
Number.parseInt(string)
Number.parseFloat(string)
The two methods parseInt
and parseFloat
convert strings into numbers. They scan the given string from left to right. When they recognize a character distinct from 0 - 9, they finish scanning and return the converted numbers read so far (parseFloat accepts the decimal point). If the first character is distinct from 0 - 9, they return Math.NaN, meaning Not a Number.
Hint: It's not necessary to specify 'Number.' in the source code.
const x = parseInt("7.5");
alert(x); // 7
const y = parseInt("Five");
alert(y); // NaN
const z = parseFloat("2.8") + 3;
alert(z); // 5.8
// scientific notation is accepted
alert(parseFloat("123.456e6")); // 123456000
BigInt
BigInt is a data type that represents integers of arbitrary length. Hence, it's a variable-length format (conceptually) delimited only by the available RAM.
BigInts are created either by adding a 'n' to the end of an integer literal or by using the BigInt()
function.
"use strict";
let hugeNumber_1 = 12345678901234567890n; // 'n' at the end
alert(typeof hugeNumber_1);
let hugeNumber_2 = BigInt("12345678901234567890"); // no 'n'
alert(typeof hugeNumber_2);
// a 'small' BigInt can be created out of an integer value
let hugeNumber_3 = BigInt(123);
alert(typeof hugeNumber_3);
BigInt supports the arithmetic operators + - * / **
, comparison operators, and most of the bitwise operators.
Boolean
Boolean variables can contain one of two possible values, true
or false
. JavaScript represents false
by either a Boolean false, the number 0, NaN, an empty string, or the built-in types undefined or null. Any other values are treated as true
.
"use strict";
let firstLoop = true;
alert(typeof firstLoop);
Undefined
Variables that have just been declared but not initialized with any value have the data type undefined.
"use strict";
let x;
// ...
alert(typeof x);
if (x == undefined) {
// remedy the missing initialization
x = 0;
}
alert(typeof x);
// ...
Null
In JavaScript, null is marked as one of the primitive values, because its behavior is seemingly primitive. However, when using the typeof
operator, it returns "object". This is considered a bug, but one which cannot be fixed because it will break too many scripts.[1]
Symbol
Symbol represents a unique identifier. Symbols may have a descriptor that is given as a string to its constructor.
"use strict";
// 'person' is a symbol with the description "Olaf"
const person = Symbol("Olaf");
alert(person.description); // Olaf
Symbols are used as keys in objects (embedded in [ ]
) to guarantee uniqueness.
See also
ECMAScript definitions on data types
Exercises
References
Dates
In JavaScript, a Date is an object. Hence it must be explicitly created with the new
operator.
Date contains a value that represents the number of milliseconds that have elapsed since January 1, 1970 UTC. It's worth mentioning what it does not contain: There is no information about a timezone within the object. Nevertheless, you can convert it to an appropriate string of any arbitrary time zone. Other methods can select parts like the month or the day of the week, or you can use the number for any computation, and more.
Constructor
The default constructor creates the Date object as of the current point in time of your computer.
const currentMilliSeconds = new Date(); // creates a new Date object as of 'now'
alert(currentMilliSeconds); // implicit conversion to string
alert(currentMilliSeconds.toString()); // explicit conversion to string
alert(currentMilliSeconds.valueOf()); // the real value
You can pass parameters to the constructor to generate a certain Date object.
// 1000 ms = 1 second after the beginning of JavaScript time
const pointInTime_1 = new Date(1000);
alert(pointInTime_1);
// begin of last minute in last century
const pointInTime_2 = new Date(1999, 11, 31, 23, 59);
alert(pointInTime_2);
Methods
Some often used methods of Date are:
Static methods
- Date.now(): Returns the number of milliseconds since January 1, 1970, 00:00:00 UTC calibrated (plus/minus some hours) to the timezone of your computer.
- Date.UTC(<parameters>): Returns the number of milliseconds since January 1, 1970, 00:00:00 UTC.
- Date.parse(text): Parsing of strings with Date.parse() is strongly discouraged due to browser differences and inconsistencies.
Instance methods
- toISOString(): Returns a string in ISO 8601 format.
- getFullYear(): Returns the full 4-digit year.
- getMonth(): Returns the current month. [0 - 11]
- getDate(): Returns the day of the month. [1 - 31]
- getDay(): Returns the day of the week. [0 - 6]. Sunday is 0, with the other days of the week taking the next value.
- getHours(): Returns hours [0 - 23] based on a 24-hour clock.
- getMinutes(): Returns minutes. [0 - 59]
- getSeconds(): Returns seconds. [0 - 59]
- getTime(): Returns the time in milliseconds since January 1, 1970.
- valueOf(): Returns the time in milliseconds since January 1, 1970. Equivalent to getTime().
- getTimezoneOffset(): Returns the difference in minutes between UTC and local time.
- setFullYear(year): Stores the full 4-digit year within the Date object.
- setMonth(month, day): Sets the month, and optionally the day within the month. '0' is January, ...
'As Integer'
The Date can be returned as an integer by the method valueOf()
or by prefixing the constructor with a +
sign, e.g., to use it for "seeding" a PRNG (Pseudo Random Number Generator) or to perform calculations.
const dateAsInteger_1 = new Date().valueOf();
alert(dateAsInteger_1);
const dateAsInteger_2 = +new Date();
alert(dateAsInteger_2);
Timezones
The Date object purely contains a whole number (Integer). It does not know anything about timezones. As long as you don't specify timezones in any form, you work in your local timezone.
But sometimes it's necessary to consider timezones.
// Example 1
// Create it in UTC: 27th of January, midnight
const date_1 = new Date(Date.UTC(2020, 00, 27, 0, 0, 0));
alert(date_1);
// show it in another timezone (Jakarta is +7) ...
const jakartaTime = new Intl.DateTimeFormat('en-GB', { timeZone: 'Asia/Jakarta', dateStyle: 'full', timeStyle: 'long' }).format(date_1);
alert(jakartaTime);
// ... the original value has not changed
alert(date_1);
// Example 2
// assume we are in New York timezone (-5 against UTC)
const date_2 = new Date();
console.log(date_2.toString());
// show it in another timezone (Los Angeles is -8 against UTC)
const laTime = new Intl.DateTimeFormat('en-GB', { timeZone: 'America/Los_Angeles', dateStyle: 'full', timeStyle: 'long' }).format(date_2);
console.log(laTime);
// ... the internal value has not changed
console.log(date_2.toString());
New API: Temporal
The object Date has some inconsistencies and shortcomings, especially: weak timezone support, no support for non-Gregorian calendars, missing calendar functions, and more. Possibly they will be fixed in a new Temporal API in one of the subsequent JavaScript versions.
Exercises
Arrays
In JavaScript, an array is an object where you can store a set of values under a single variable name. So far, it's the same as in many other languages. But there are distinctions.
- It's not necessary that the values are of the same data type. You can put everything you want in an array and worry about the data type later. (But there are also typed arrays where all values have the same data type.)
- When you create an array, you do not need to declare a size - but you can. Arrays grow automatically. This makes arrays very convenient to use, but not well-suited for applications in numerical analysis.
- Because arrays are objects, they have methods and properties you can invoke at will. For example, the
.length
property indicates how many elements are currently in the array. If you add more elements to the array, the value of the.length
gets larger. - The element-counting starts at 0, which means, for instance, that the 5th element is located with [4].
(Hint: When using arrays, you should always use the bracket notation with non-negative integers, e.g., arr[3] = 42;
. Technically, it's also possible to use the dot notation, but this leads - at least for beginners - to unexpected behavior.)
Create an array
First, as with all objects, there is a constructor.
"use strict";
const arr_1 = new Array(); // empty array
alert(arr_1.length);
const arr_2 = new Array(0, 2, 4); // 3 elements
alert(arr_2);
Next, the JavaScript syntax supports square brackets when creating or working with arrays.
"use strict";
const arr_1 = []; // empty array
alert(arr_1.length);
const arr_2 = [0, 2, 4]; // 3 elements
alert(arr_2);
You can predefine the size of an array when declaring it.
"use strict";
const arr_3 = new Array(50); // 50 elements
alert(arr_3.length);
Access an array element
Array elements are accessed for reading or writing with the usual bracket notation.
"use strict";
const arr_4 = [0, 2, 4, 6, 8];
alert(arr_4[3]); // 6
arr_4[0] = 9;
alert(arr_4); // 9, 2, 4, 6, 8
When you access an element above the array's actual length, the size of the array will grow, and the new element will be created.
"use strict";
const arr_5 = [0, 2, 4, 6, 8];
arr_5[10] = 9;
alert(arr_5); // 0,2,4,6,8,,,,,,9
alert(arr_5.length); // 11
Varying data types
You can store values of different data types within an array.
"use strict";
const arr_6 = [0, "two", 4]; // number and string
console.log(arr_6); // [0, "two", 4]
// and even values of data type 'array' can be stored
const arr_7 = [10, 11];
arr_7[2] = arr_6; // array in array
console.log(arr_7); // [10, 11, [0, "two", 4]]
console.log(arr_7.length); // 3
Nested arrays
As shown before, an array element may be an array (which itself may contain elements of type array (which itself may contain ...)). This can occur during runtime or during initialization. To access the lower levels directly, you must specify as many bracket pairs []
as necessary to reach this level.
"use strict";
const arr_8 = [ [0, 1], [10, 11, 12, 13], [20, 21] ];
console.log(arr_8[2]); // one level goes to an array of numbers: [20, 21]
console.log(arr_8[1][1]); // two levels go to a number: 11
// same with assignments ...
arr_8[2][0] = "twenty";
console.log(arr_8[2]); // ["twenty", 21]
... and a little more complex
"use strict";
const arr_9 = []; // empty
arr_9[0] = [];
arr_9[0][0] = [];
arr_9[0][0][2] = "Hallo world!";
console.log(arr_9); // [[[undefined, undefined, "Hallo world!"]]]
arr_9[2] = "Third element of first level";
console.log(arr_9);
// [[[undefined, undefined, "Hallo world!"]], undefined, "Third element of first level"]
Properties and methods
length
length
is a property of each array (it's not a method). It represents the number of elements in that array.
alert([0, 1, 2].length); // 3
Please notice that array indices are zero-based. Therefore the array's length is huger than the last index.
concat
The concat
method returns the combination of two or more arrays. To use it, first, you need two or more arrays to combine.
const arr1 = ["a","b","c"];
const arr2 = ["d","e","f"];
Then, make a third array and set its value to arr1.concat(arr2)
.
const arr3 = arr1.concat(arr2); //arr3 now is: ["a","b","c","d","e","f"]
Note that in this example, the new arr3 array contains the contents of both the arr1 array and the arr2 array.
join
and split
The join
method returns a single string that contains all of the elements of an array — separated by a specified delimiter. If the delimiter is not specified, it is set to a comma.
There is also a split
method that performs the opposite of join
: it operates on a string, divides him into elements based on the specified delimiter, and returns an array that contains those elements. (Hint: split
is a method of the data type string, not of array.)
To use join
, first make an array.
const abc = ["a", "b", "c"];
Then, make a new variable and assign it to abc.join()
.
const a = abc.join(); // "a,b,c"
You can also use a dedicated delimiter.
// use 'semicolon' plus 'space' as delimiter
const b = abc.join("; "); // "a; b; c"
Convert it back into an array with the string's split
method.
const a2 = a.split(","); // ["a", "b", "c"]
const b2 = b.split("; "); // ["a", "b", "c"]
push
The push
method adds one or more elements to the end of an array and returns the array's new length.
"use strict";
const arr = [0, 1, 2, 3];
alert(arr); // 0, 1, 2, 3
const len = arr.push(100);
alert(len); // 5
alert(arr); // 0, 1, 2, 3, 100
pop
The pop
method removes the last element of an array and returns the element.
"use strict";
const arr = [0, 1, 2, 3];
alert(arr); // 0, 1, 2, 3
const elem = arr.pop();
alert(elem); // 3
alert(arr); // 0, 1, 2
push
and pop
work at the end of an array; they reverse their respective effects.
unshift
The unshift
method adds one or more new elements to the beginning of an array and returns the array's new length. It works by 'unshifting' every old element from its old index to , adds the new element to index , and adopts the array's length
property. It is comparable with push
but works at the beginning of the array.
"use strict";
const arr = [0, 1, 2, 3];
alert(arr); // 0, 1, 2, 3
const len = arr.unshift(100);
alert(len); // 5
alert(arr); // 100, 0, 1, 2, 3
shift
The shift
method removes the first element of an array and returns the removed element. It works by 'shifting' every old element from its old index to , adopts the array's length
property, and returns the old first element. It is comparable with pop
but works at the beginning of the array.
"use strict";
const arr = [0, 1, 2, 3];
alert(arr); // 0, 1, 2, 3
const elem = arr.shift();
alert(elem); // 0
alert(arr); // 1, 2, 3
unshift
and shift
work at the beginning of an array; they reverse their respective effects.
Exercises
See also
Regular expressions
Overview
JavaScript implements regular expressions (regex for short) when searching for matches within a string. As with other scripting languages, this allows searching beyond a simple letter-by-letter match, and can even be used to parse strings in a certain format.
Unlike strings, regular expressions are delimited by the slash (/) character, and may have some options appended.
Regular expressions most commonly appear in conjunction with the string.match() and string.replace() methods.
At a glance, by example:
strArray = "Hello world!".match(/world/); // Singleton array; note the slashes
strArray = "Hello!".match(/l/g); // Matched strings are returned in a string array
"abc".match(/a(b)c/)[1] === "b" // Matched subgroup is the 2nd item (index 1)
str1 = "Hey there".replace(/Hey/g, "Hello");
str2 = "N/A".replace(/\//g, ","); // Slash is escaped with \
str3 = "Hello".replace(/l/g, "m").replace(/H/g, "L").replace(/o/g, "a"); // Pile
if (str3.match(/emma/)) { console.log("Yes"); }
if (str3.match("emma")) { console.log("Yes"); } // Quotes work as well
"abbc".replace(/(.)\1/g, "$1") === "abc" // Backreference
Compatibility
JavaScript's set of regular expressions follows the extended set. While copying a Regex pattern from JavaScript to another location may work as expected, some older programs may not function as expected.
- In the search term, \1 is used to back reference a matched group, as in other implementations.
- In the replacement string, $1 is substituted with a matched group in the search, instead of \1.
- Example: "abbc".replace(/(.)\1/g, "$1") => "abc"
- | is magic, \| is literal
- ( is magic, \( is literal
- The syntaxes (?=...), (?!...), (?<=...), and (?<!...) are not available.
Examples
- Matching
- string = "Hello world!".match(/world/);
- stringArray = "Hello world!".match(/l/g); // Matched strings are returned in a string array
- "abc".match(/a(b)c/)[1] => "b" // Matched subgroup is the second member (having the index "1") of the resulting array
- Replacement
- string = string.replace(/expression without quotation marks/g, "replacement");
- string = string.replace(/escape the slash in this\/way/g, "replacement");
- string = string.replace( ... ).replace ( ... ). replace( ... );
- Test
- if (string.match(/regexp without quotation marks/)) {
Modifiers
Single-letter modifiers:
g | Global. The list of matches is returned in an array. |
i | Case-insensitive search |
m | Multiline. If the operand string has multiple lines, ^ and $ match the beginning and end of each line within the string, instead of matching the beginning and end of the whole string only:
|
Operators
Operator | Effect |
---|---|
\b | Matches boundary of a word. |
\w | Matches an alphanumeric character, including "_". |
\W | Negation of \w. |
\s | Matches a whitespace character (space, tab, newline, formfeed) |
\S | Negation of \s. |
\d | Matches a digit. |
\D | Negation of \d. |
Function call
For complex operations, a function can process the matched substrings. In the following code, we are capitalizing all the words. It can't be done by a simple replacement, as each letter to capitalize is a different character:
var capitalize = function(matchobj) {
var group1 = matchobj.replace(/^(\W)[a-zA-Z]+$/g, "$1");
var group2 = matchobj.replace(/^\W([a-zA-Z])[a-zA-Z]+$/g, "$1");
var group3 = matchobj.replace(/^\W[a-zA-Z]([a-zA-Z]+)$/g, "$1");
return group1 + group2.toUpperCase() + group3;
};
var classicText = "To be or not to be?";
var changedClassicText = classicText.replace(/\W[a-zA-Z]+/g, capitalize);
console.log(changedClassicText==="To Be Or Not To Be?");
The function is called for each substring. Here is the signature of the function:
function (''<matchedSubstring>[, <capture1>, ...<captureN>, <indexInText>, <entireText>]'') {
...
return ''<stringThatWillReplaceInText>'';
}
- The first parameter is the substring that matches the pattern.
- The next parameters are the captures in the substrings. There are as many parameters as there are captures.
- The next parameter is the index of the beginning of the substring starting from the beginning of the text.
- The last parameter is a remainder of the entire text.
- The return value will be put in the text instead of the matching substring.
See also
- Regular Expressions - a Wikibook dedicated to regular expressions.
- Perl Regular Expressions Reference - a chapter devoted to regular expressions in a book about the Perl programming language.
External links
- JavaScript RegExp Object Reference at W3schools.com
- JavaScript RexExp Tester at regular-expressions.info
- Regular Expressions in Javascript at mozilla.org
- JavaScript RegExp Object at mozilla.org
Operators
String concatenation
The +
operator acts in two different ways depending on the type of its two operands. If one or both of them are strings, it acts as a string concatenator. If both are numeric, it acts as an arithmetic addition.
An example of string concatenation is "one " + "world"
resulting in "one world"
. If one of the two operands isn't a string, it is implicitly converted to a string before the +
operation takes place.
"use strict";
// regular concatenation
const word_1 = "one";
const word_2 = "world";
let result = word_1 + " " + word_2;
alert(result); // "one world"
// implicit type conversion
const arr = [41, 42, 43];
result = word_1 + arr + word_2; // first, the array is converted to a string
alert(result); // "one41,42,43world"
// dito
const x = 1;
result = x + word_2;
alert(result); // "1world"
Arithmetic operators
JavaScript has the arithmetic operators +
, -
, *
, /
, %
(remainder), and **
(exponentiation). These operators work the way you learned in mathematics. Multiplication and division will be calculated before addition and subtraction. If you want to change such precedence, you can use parenthesizes.
Hint: In opposite to some other languages, the division operation may result in a floating point number - it's not always a pure integer.
const a = 12 + 5; // 17
const b = 12 - 5; // 7
const c = 12 * 5; // 60
const d = 12 / 5; // 2.4 Division results in floating point numbers
const e = 12 % 5; // 2 Remainder of 12/5 is 2
const f = 12 - 2 * 5; // 2 Multiplication is calculated first
const g =(12 - 2) * 5; // 50 Parenthesis are calculated first
Some mathematical operations, such as dividing by zero, cause the returned variable to be one of the error values - for example, infinity
, or NaN
(Not a number).
The return value of the remainder operator maintains the sign of the first operand.
The +
and -
operators also have unary versions, where they operate only on one variable. When used in this fashion, +
returns the number representation of the object, while -
returns its negative counterpart.
"use strict";
const a = 42;
const b = +a; // b is 42
const c = -a; // c is -42
As noted above, +
is also used as the string concatenation operator: If any of its arguments is a string or is otherwise not a number, all arguments are converted to strings and concatenated together. All other arithmetic operators works the other way round: They attempt to convert their arguments into numbers before evaluating.
"use strict";
const a = 42;
const b = "2";
const c = "3";
alert(a + b); // "422" (a string)
alert(a * b); // 84 (a number)
alert(b * c); // 6 (a number)
Bitwise operators
There are seven bitwise operators: &
, |
, ^
, ~
, >>
, <<
, and >>>
.
These operators convert their operands to integers (truncating any floating point towards 0), and perform the specified bitwise operation on them. The logical bitwise operators, &
, |
, and ^
, perform the and, or, and xor on each individual bit and provides the return value. The ~
(not operator) inverts all bits within an integer, and usually appears in combination with the logical bitwise operators.
Two bit shift operators, >>
, <<
, move the bits in one direction that has a similar effect to multiplying or dividing by a power of two. The final bit-shift operator, >>>
, operates the same way, but does not preserve the sign bit when shifting.
These operators are kept for parity with the related programming languages, but are unlikely to be used in most JavaScript programs.
Assignment operators
The assignment operator =
assigns a value to a variable. Primitive types, such as strings and numbers, are assigned directly. However, function and object names are just pointers to the respective function or object. In this case, the assignment operator only changes the reference to the object rather than the object itself.
For example, after the following code is executed, "0, 1, 0" will be alerted, even though array_A
was passed to the alert, but array_B
was changed. This is because they are two references to the same object.
"use strict";
const array_A = [0, 1, 2];
const array_B = array_A;
array_B[2] = 0;
alert(array_A); // 0, 1, 0
Similarly, after the next code snippet is executed, x
is a pointer to an empty array.
"use strict";
const z = [5];
const x = z;
z.pop();
alert (x);
If the result of any of the above-shown arithmetic or bitwise operators shall be assigned to its first operand, you can use a shorter syntax. Combine the operator, e.g., +
, directly with the assignment operator =
resulting in +=
. As an example x = x + 5
can be abbreviated as x += 5
.
The abbreviated operator/assignment syntax reads:
Arithmetic | Logical | Shift |
---|---|---|
+= | &= | >>= |
-= | |= | <<= |
*= | ^= | >>>= |
/= | ||
%= |
For example, a common usage for +=
in a for
loop:
"use strict";
const arr = [1, 2, 3];
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i]; // same as: sum = sum + arr[i];
}
alert(sum);
Increment operators
Increment and decrement operators are a particular form of arithmetic operators: ++
and --
. a++
increments a
and returns the old value of a
. ++a
increments a
and returns the new value of a
. The decrement operator acts similarly but reduces the variable instead.
As an example, the next operations all perform the same task:
"use strict";
let a = 1;
alert(a); // 1
a = a + 1;
alert(a); // 2
a += 1;
alert(a); // 3
a++;
alert(a); // 4
++a;
alert(a); // 5
// but this SHOWS something different; see next chapter
alert(a++); // shows 5 again
alert(a); // nevertheless, 'a' was incremented to 6
Pre and post-increment operators
Increment operators may be written before or after a variable. The position decides whether they are pre-increment or post-increment operators, respectively. That affects the operation.
"use strict";
// increment occurs before a is assigned to b
let a = 1;
let b = ++a; // a = 2, b = 2;
// increment occurs to c after c is assigned to d
let c = 1;
let d = c++; // c = 2, d = 1;
Due to the possibly confusing nature of pre and post-increment behavior, code can be easier to read if you avoid increment operators.
"use strict";
// increment occurs before a is assigned to b
let a = 1;
a += 1;
let b = a; // a = 2, b = 2;
// increment occurs to c after c is assigned to d
let c = 1;
let d = c;
c += 1; // c = 2, d = 1;
Comparison operators
Comparison operators determine whether their two operands meet the given condition. They return true or false.
Concerning the 'equal' and 'not-equal' operators, you must take care. ==
is different from ===
. The first one tries to adapt the data type of the two types to each other and then compares the values. The second one compares the types as well as their values and returns only true if type and value are identical: 3 == "0003"
is true and 3 === "3"
is false.
Op. | Returns | Notes |
---|---|---|
== | true, if the two operands are equal | May change an operand's type (e.g. a string as an integer). |
=== | true, if the two operands are strictly equal | Does not change operands' types. Returns true if they are the same type and value. |
!= | true, if the two operands are not equal | May change an operand's type (e.g. a string as an integer). |
!== | true, if the two operands are not strictly equal | Does not change the operands' types. Returns true if they differ in type or value. |
> | true, if the first operand is greater than the second one | |
>= | true, if the first operand is greater than or equal to the second one | |
< | true, if the first operand is less than the second one | |
<= | true, if the first operand is less than or equal to the second one |
We recommend using the strict versions (===
and !==
) because the simple versions may lead to strange and non-intuitive situations, such as:
0 == '' // true
0 == '0' // true
false == 'false' // false (''Boolean to string'')
false == '0' // true (''Boolean to string'')
false == undefined // false
false == null // false (''Boolean to null'')
null == undefined // true
... although you might want:
0 === '' // false
0 === '0' // false
false === 'false' // false
false === '0' // false
false === undefined // false
false === null // false
null === undefined // false
Logical operators
The logical operators are and, or, and not — written as &&
, ||
, and !
. The first two take two boolean operands each. The third takes one and returns its logical negation.
Operands are boolean values or expressions that evaluate to a boolean value.
"use strict";
const a = 0;
const b = 1;
if (a === 0 && b === 1) { // logical 'and'
alert ("a is 0 AND b is 1");
}
if (a === 1 || b === 1) { // logical 'or'
alert ("a is 1 OR b is 1");
}
&&
and ||
are short circuit operators: if the result is guaranteed after the evaluation of the first operand, it skips the evaluation of the second operand. Due to this, the &&
operator is also known as the guard operator, and the ||
operator is known as the default operator.
"use strict";
// declare 'myArray' without initialization
let myArray;
// runtime error!
if (myArray.length > 0) {
alert("The length of the array is: " + myArray.length);
}
// no error because the part after '&&' will not be executed!
if (myArray && myArray.length > 0) {
alert("The length of the array is: " + myArray.length);
}
The !
operator determines the inverse of the given boolean value: true becomes false and false becomes true.
Note: JavaScript represents false by either a Boolean false, the number 0, NaN, an empty string, or the built-in types undefined or null. Any other value is treated as true.
Concerning the precedence of the three operators, !
is evaluated first, followed by &&
, and lastly ||
.
a || b && !c
| | | |
| | └ 1. ┘
| └───── 2. ───┘
└───────── 3. ───────┘
More details on the relationship between precedence and short-circuiting are explained at MDN
Other operators
? :
The ? :
operator (also called the "ternary" operator) is an abbreviation of the if
statement. First, it evaluates the part before the question mark. Then, if true, the part between the question mark and the colon is evaluated and returned, else the part behind the colon.
const target = (a == b) ? c : d;
However, be careful when using it. Even though you can replace verbose and complex if/then/else chains with ternary operators, it may not be a good idea to do so. You can replace
if (p && q) {
return a;
} else {
if (r != s) {
return b;
} else {
if (t || !v) {
return c;
} else {
return d;
}
}
}
with
return (p && q) ? a
: (r != s) ? b
: (t || !v) ? c
: d
The above example is a poor coding style/practice. When other people edit or maintain your code, (which could very possibly be you,) it becomes much more difficult to understand and work with the code.
Instead, it is better to make the code more understandable. Some of the excessive conditional nesting can be removed from the above example.
if (p && q) {
return a;
}
if (r != s) {
return b;
}
if (t || !v) {
return c;
} else {
return d;
}
delete
delete obj.x
unbinds property x
from object obj
.
The delete keyword deletes a property from an object. It deletes both the value of the property and the property itself. After deletion, the property cannot be used before it is added back again. The delete
operator is designed to be used on object properties. It has no effect on variables or functions.
new
new cl
creates a new object of type cl. The cl operand must be a constructor function.
instanceof
o instanceof c
tests whether o
is an object created by the constructor c
.
typeof
typeof x
returns a string describing the type of x
. Following values may be returned.
Type | returns |
---|---|
boolean | "boolean" |
number | "number" |
string | "string" |
function | "function" |
undefined | "undefined" |
null | "object" |
others (array, ...) | "object" |
Exercises
See also
Control structures
Most programming languages are composed of 'bricks' like tokens (keywords, variables, operators, ...), expressions like myArray.length + 1
, statements (delimited by ;
), blocks {...}
, functions, and modules. At first glance, the execution of the program follows the sequence of statements, top down. But in nearly all cases, it is necessary that the program does not run in the strict order of the written statements. Instead, some parts must run only if certain conditions apply, and others will be omitted and run under different conditions. Or, it may become necessary that some parts are executed repetitively. Other parts may run in parallel and get synchronized later. Or, a function of a different module must compute a value before the next statement can be executed.
In this hierarchy of 'language bricks' the term block is essential for the understanding of the program flow. In JavaScript, a block is a sequence of zero or more statements (or smaller blocks) that are surrounded by braces { // zero or more statements }
. The language constructions we discuss here invoke or repeat blocks.
if / else
The if / else
statement (yes, it's a single statement, even though it contains other statements in its blocks) invokes the execution of one of two blocks depending on the evaluation of a condition. The evaluation returns a boolean value. If it is true
, the first block is executed; if it is false
, the second block is executed. The respectively other block is skipped over.
if ( condition ) {
// block of statements
} else {
// block of statements
}
The else
part is optional, i.e. it's possible to use if
without the else
part and its block.
if ( condition ) {
// block of statements
}
An example:
"use strict";
const a = 3;
const b = "3";
if (a == b) {
alert("The two variables contain the same value, but may have different data types.");
} else {
alert("The two variables contain different values.");
}
// an example without 'else'
const c = 6 / 2;
if (a === c) {
alert("The two variables contains the same value and are of the same data type.");
}
If one of the two blocks contains exactly ONE statement, the braces can be omitted. But for the clearness of the code, we recommend the use of a unified syntax with braces.
// same as above; but this abbreviated syntax is not recommended.
"use strict";
const a = 3;
const b = "3";
if (a == b) alert("The two variables contains the same value, but may have different data types.");
else alert("The two variables contain different values.");
const c = 6 / 2;
if (a === c) alert("The two variables contains the same value and are of the same data type.");
In many cases, the situation demands more complex decisions than a simple true/false alternative. For example, you may want to know whether a number is negative, zero, or positive. In such cases, a solution might look like this:
"use strict";
const x = 3;
if (x < 0) {
alert("The number is negative.");
} else {
// x is equal or greater than 0
if (x === 0) {
alert("The number zero.");
} else {
alert("The number is positive.");
}
}
You can shorten this code a bit without losing clarity. Because the first else
block contains only a single statement - namely the second if
- you can omit its braces and combine the first else
and the second if
within one line.
"use strict";
const x = 3;
if (x < 0) {
alert("The number is negative.");
} else if (x === 0) {
alert("The number is zero.");
} else {
alert("The number is positive.");
}
This is a clear and often-used programming style. It's used in situations where you have a manageable number of choices or where you have to make decisions with multiple variables.
switch
If the number of decisions grows significantly, the code gets clearer if you use the switch
statement instead of a long list of else if
conditions.
The switch
statement evaluates an expression and steers the flow of statements based on the comparison of its result with the labels behind the keyword case
.
"use strict";
const myVar = "a";
// evaluation takes simple variables as well as complex expressions
switch (myVar.toUpperCase()) {
case "A":
// …
break;
case "B":
// …
break;
default: // analog to 'else' without any further 'if'
// …
break;
}
If the result of the evaluation matches one of the labels, JavaScript executes the following statements up to the next break
or the end of the entire switch
. If none of the labels match, execution continues at the default
label, or - if none is present - skips the switch
statement entirely.
Labels are literals or expressions; e.g., case (2 + 1).toString():
is possible.
As soon as a break
statement is reached, the execution of the switch
gets terminated. Normally it appears at the end of each case to prevent execution of the code of the following cases. But it can be omitted if you intentionally want to execute them in addition to the current ones. In the following example, the same code will run for i
equal to 1, 2, or 3.
"use strict";
const i = 2;
switch(i) {
case 1:
case 2:
case 3:
// …
break;
case 4:
// …
break;
default:
// …
break;
}
Because the expression to be evaluated as well as the labels can be complex expressions, it's possible to build very flexible constructions.
"use strict";
const i = 2;
switch(true) { // in this example it's a constant value
case (i < 10):
alert("one digit");
break;
case (i >= 10 && i < 100):
alert("two digits");
break;
default:
// …
break;
}
The continue
keyword does not apply to the switch
statement.
try / catch / finally
If there is a possibility that a runtime error might occur, you can 'catch' that error and perform meaningful actions to handle the situation. E.g., a network connection or a database might no longer be available; a user input leads to a division by zero; ... .
try {
// critical block where errors might occur
} catch (err) {
// block to handle possible errors. Normally not executed.
} finally {
// block that will be executed in ALL cases
}
"use strict";
const x = 15;
let average;
try {
// block with critical statements
x = x + 5;
average = x / 0;
alert("The average is: " + average);
} catch (err) {
// block to handle possible errors
alert("Something strange occurs. The error is: " + err);
} finally {
// block that will be executed in ALL cases
alert("End of program.");
}
If one of the statements in the critical block raises a runtime error, the execution of its remaining statements is omitted. Instead, the execution invokes the catch block. Lastly, the finally block is executed.
Please note that the finally block is executed in all cases, regardless of whether a runtime error occurs or not. That even applies if the critical or the catch block executes a return
statement.
throw
In the above example, the JavaScript engine throws an exception by itself. In other situations, the JavaScript engine acts in one way or another, but you may want to see it treated differently. E.g., in the case of a division by zero, the engine doesn't throw an error; it assigns Infinity
to the result and jumps to the following statement. If you want a different behavior, you can create and throw exceptions by your own program.
"use strict";
const x = 15;
let average;
try {
// block with critical statements
average = x / 0;
// or: const z = "abc"; average = z / 0;
if (average === Infinity || Number.isNaN(average)) {
// Throw your own exception with any text
throw "Error during division. The result is: " + average;
}
alert("The average is: " + average);
} catch (err) {
// block to handle possible errors
alert("Something strange occurs. The error is: " + err);
} finally {
// block that will be executed in ALL cases
alert("End of program.");
}
If an exception occurs - generated by the JavaScript engine or by your program - and is not caught by a catch block, the script terminates or - if it is a function - it returns control to the calling function. The error handling may be implemented there or in one of the functions which have been called it.
"use strict";
const answer = prompt("How old are you?");
const age = Number(answer);
if (isNaN(age)) {
throw answer + " cannot be converted to a number.";
// The script terminates with this message (it's not a function)
}
alert("Next year you will be " + (age + 1));
Exercises
Loops
Loops and iterations are other cases where the sequential flow of statements is manipulated by surrounding language constructs. This is described on the next page.
See also
Loops
JavaScript supports the repetitive execution of code blocks via the keywords for
and while
. Similar behavior is offered by the forEach
method of data type Array
(and similar data types).
for (;;) {}
The syntax of the for
statement is: for (<initial expression>; <condition>; <final expression>) { <block> }
. It is executed based on the following rules:
- The
initial expression
is executed - exactly once for the completefor
statement. Usually, it declares and initiates one or more variables - often numbers with integer values. Sometimes the declaration of the variables is made above thefor
statement. - The
condition
is evaluated. If it returnstrue
, step 3 follows. If it returnsfalse
, thefor
statement terminates. - The code block is executed.
- The
final expression
is executed. Usually, it increments or decrements a variable that is part of thecondition
. - The loop repeats at step 2.
for (let i = 0; i < 10; i++) {
// a block of statements
}
An example: Show the series of even numbers multiplied by itself (power 2); show the sum over the numbers from 0 to 10.
"use strict";
const upperLimit = 10;
let sum = 0;
let tmpString = "";
for (let i = 0; i <= upperLimit; i++) {
sum = sum + i;
if (i % 2 === 0) {
tmpString += i*i + "; "
}
}
alert ("The sum is: " + sum + ". The square numbers are: " + tmpString);
Optional syntax parts
The initial expression
, the condition
, and the final expression
are optional. If you omit one or more of them, your script shall perform other useful statements to control the loop. Some examples:
"use strict";
// an infinite loop if you do not terminate it with 'break' (see below)
for (;;) {
// ...
break;
}
const answer = prompt("With which start value shall the loop begin?");
let i = Number(answer);
for (; i >= 0 && i < 10; i++) {
// the start value is computed above the loop
}
for (let i = 0; i < 10; ) {
// ...
if (true) { // an arbitrary other condition to control the increment
i++;
}
}
Nesting
for
loops can be nested. You can use a second (or 'inner') loop within the block of the first (or 'outer') loop.
"use strict";
const maxInner = 10;
const maxOuter = 4;
let myString = "";
for (let o = 1; o <= maxOuter; o++) {
myString = "";
// Be careful. It's easy to confuse inner and outer loop variables.
for (let i = 1; i <= maxInner; i++) {
myString = myString + o*i + ", ";
}
alert(myString);
}
This nesting of loops is also possible in all the below constructs.
continue / break
Sometimes only parts or the block shall run. This can be realized by one or more if / else
statements. If it's appropriate, such conditional statements can be shortened by the keyword continue
. If continue
is reached, the rest of the block is skipped, and the above step 4 final expression
is executed.
"use strict";
for (let i = 0; i <= 6; i++) {
if (i === 5) {
continue; // skip the rest of the block
}
alert(i); // 0, 1, 2, 3, 4, 6
}
This example is very simple. It skips the lower part of the block for the case where i
equals to 5. Of course, it can be expressed differently. A more realistic example would be a loop over the result of a database search which skips parts of a complex handling of rows with a specific status.
The break
keyword is similar but more rigid than the continue
keyword. It skips not only the rest of the block but also terminates the complete loop.
"use strict";
for (let i = 0; i <= 6; i++) {
if (i === 5) {
break; // terminal the loop
}
alert(i); // 0, 1, 2, 3, 4
}
A realistic scenario is a loop over the result of a database search where the connection to the database is broken in the middle of the loop.
You can use continue
and break
in all forms of the here-discussed variants of loops.
do {} while ()
The syntax of the statement is: do { <block> } while (<condition>)
. It is executed based on the following rules:
- The block is executed. Because this is the very first step, the block is executed at least 1 time. This is helpful if you want to be sure that something happens under all circumstances.
- The
condition
is evaluated. If it returnstrue
, step 1 is invoked again. If it returnsfalse
, the loop terminates. Please notice that there is no specific part where you can manipulate a variable that is checked in the condition. This must be done somewhere in the block among the other statements.
"use strict";
let counter = 100;
do {
counter++;
alert(counter); // ... or some logging
} while (counter < 10);
while () {}
The syntax of while (<condition>) { <block> }
is very similar to the previous do { <block> } while (<condition>)
. The only difference is that the condition is checked before and not after the block.
for (x in Object) {}
This language construct is intended to iterate over the properties of an object. Its syntax is: for (<variable> in <object>) { <block> }
. The variable receives the key of all properties - one after the next - of the object. For each of them, the block is executed once.
"use strict";
const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};
for (const key in myObj) {
alert(key); // firstName, familyName, born
// alert(myObj[key]); // if you want to see the values
}
Arrays are specialized objects. Hence it is possible to use the for..in
on arrays. The keys for arrays are integers starting with 0 - and that is precisely what is extracted from the array.
"use strict";
const myArray = ["certo", "uno", "dos", "tres"];
for (const key in myArray) {
alert(key); // 0, 1, 2, 3
// alert(myArray[key]); // if you want to see the values
}
Arrays also accept non-numeric keys, e.g., myArray["four"] = "cuatro";
. The for..in
loop handles such non-numeric keys - in opposite to the below for..of
loop, and in opposite to the traditional for
loop at the top of this page.
See also: MDN: for..in
for (x of Array) {}
This language construct is intended to iterate over an array. Its syntax is: for (<variable> of <iterable object>) { <block> }
. The variable receives all values - one after the next - of the array. For each of them, the block is executed once.
This definition sounds similar to the above definition of the for..in
. But there are significant differences:
- It returns the values, not the keys resp. index of the array.
- It works only on all iterable objects (Array, Map, Set, ...). The data type 'object' is not iterable.
- It works only on such keys which are numeric. Non-numerical keys resp. index are silently ignored.
"use strict";
const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";
for (const myValue of myArray) {
alert(myValue); // cero, uno, dos, tres. No 'cuatro' because the key is a string.
}
See also: MDN: for..of
for..in vs. for..of
The differences between the two language constructs are summarized in an example.
"use strict";
const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};
const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";
// for..of not allowed on true objects; only for..in is possible.
for (const x of myObj) {}; // error
for (const x in myArray) {
alert(x); // 0, 1, 2, 3, four
}
for (const x of myArray) {
alert(x); // cero, uno, dos, tres. NO cuatro!
}
Object.entries() method
If you are looking for a loop that processes both keys and values of an object, there is a more straightforward method than the above-shown combination of the for..in
with myObj[key]
. The data type Object offers the entries()
method that returns an array. Each element of this array contains an array with two values: the property key and the property value. In other words: the return value of the entries()
method is a two-dimensional array. And - as usual with arrays - you should use it in combination with for..of
.
"use strict";
const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};
const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";
for (const [key, val] of Object.entries(myObj)) {
alert(key + ' / ' + val);
}
for (const [key, val] of Object.entries(myArray)) {
alert(key + ' / ' + val); // 'four / cuatro' is included
}
Hint: The Object.entries()
as well as the following Array.forEach()
methods are no 'core' language elements like keywords or the for..in
or for..of
language constructs. They are methods of the Object respectively Array data type.
Array.forEach() method
The data type Array offers the method forEach()
. It loops over the elements of the array, returning one element after the next. The interesting point is that it takes a function as its argument. This makes the difference to the divers for
and while
loops. Such functions are often called callback function.
The method invocation is very easy: myArray.forEach(myFunction)
. The - possibly - confusing part is that function calls can be abbreviated in various ways.
The first example shows the function call explicitly. It defines the function myFunction which also takes a single argument. When myFunction is called by forEach(), the next array element is inserted as the argument to myFunction.
"use strict";
// function definition (the function is not called here!)
function myFunction(element) {
alert("The element of the array is: " + element)
};
// a test array
const myArray = ['a', 'b', 'c'];
// iterate over the array and invoke the function once per array element
myArray.forEach(myFunction);
The following examples show some abbreviations of the function invocation syntax.
"use strict";
// the 'arrow' syntax
const myFunction =
(element) => {
alert("The element of the array is: " + element)
};
// same code without line breaks:
// const myFunction = (element) => {alert("The element of the array is: " + element)};
const myArray = ['a', 'b', 'c'];
myArray.forEach(myFunction);
"use strict";
const myArray = ['a', 'b', 'c'];
// Define the function directly as the argument of the forEach(). Such
// functions are called 'anonymous' functions.
myArray.forEach((element) => {alert("The element of the array is: " + element)});
"use strict";
const myArray = ['a', 'b', 'c'];
// in simple cases, more syntactical elements are optional
myArray.forEach(element => alert("The element of the array is: " + element));
It depends on your preference, which syntax you use.
In many cases, the called function performs side effects like logging. To calculate values over all array elements, it is necessary to use the technique of closures. In the following example, the variable sum
is not defined in the outer context, not in the anonymous function.
"use strict";
const myArray = [3, 0, -1, 2];
let sum = 0;
myArray.forEach(element => sum = sum + element);
alert(sum);
All in all, the following rules apply to the forAll()
method:
- The method can be used for iterable objects like Array, Map, Set. The data type Object is not iterable.
- The method only iterates over such elements which have a numerical key - what is the usual case for arrays.
See also:
Exercises
See also
Functions
A function is a block of code that solves a dedicated problem and returns the solution to the calling statement. The function exists in its own context. Hence, functions divide massive programs into smaller 'bricks' which structure the software as well as the software development process.
// define a function
function <function_name> (<parameters>) {
<function_body>
}
// call a function
<variable> = <function_name> (<arguments>);
JavaScript supports the software development paradigm functional programming. Functions are a data type derived from Object; they can be bound to variables, passed as arguments, and returned from other functions, just as any other data type.
Declaration
Functions can be constructed in three main ways. The first version can be abbreviated further; see below.
The conventional way:
"use strict";
// conventional declaration (or 'definition')
function duplication(p) {
return p + "! " + p + "!";
}
// call the function
const ret = duplication("Go");
alert(ret);
Construction via a variable and an expression:
"use strict";
// assign the function to a variable
let duplication = function (p) {
return p + "! " + p + "!";
};
const ret = duplication("Go");
alert(ret);
Construction via the new
operator (this version is a little cumbersome):
"use strict";
// using the 'new' constructor
let duplication = new Function ("p",
"return p + '! ' + p + '!'");
const ret = duplication("Go");
alert(ret);
Invocation
For the declaration of functions, we have seen 3 variants. For their invocation, there are also 3 variants. The declarations and invocations are independent of each other and you can arbitrarily combine them.
The conventional invocation variant uses the function name followed by parenthesis ( )
. Within the parenthesis, there are the function's arguments, if any exist.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
// the conventional invocation method
const ret = duplication("Go");
alert(ret);
If the script runs in a browser, there are two more possibilities. They use the window
object that is provided by the browser.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
// via 'call'
let ret = duplication.call(window, "Go");
alert(ret);
// via 'apply'
ret = duplication.apply(window, ["Go"]);
alert(ret);
Hint: If you use the function name without the parenthesis ()
, you will receive the function itself (the script), not any result of an invocation.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
alert(duplication); // 'function duplication (p) { ... }'
Hoisting
Functions are subject to 'hoisting'. This mechanism transfers the declaration of a function automatically to the top of its scope. As a consequence, you can call a function from an upper place in the source code than its declaration.
"use strict";
// use a function above (in source code) its declaration
const ret = duplication("Go");
alert(ret);
function duplication(p) {
return p + "! " + p + "!";
}
Immediately Invoked Function
So far, we have seen the two separate steps declaration and invocation. There is also a syntax variant that allows the combination of both. It is characterized by using parenthesis around the function declaration followed by ()
to invoke that declaration.
"use strict";
alert( // 'alert' to show the result
// declaration plus invocation
(function (p) {
return p + "! " + p + "!";
})("Go") // ("Go"): invocation with the argument "Go"
);
alert(
// the same with 'arrow' syntax
((p) => {
return p + "! " + p + "!";
})("Gooo")
);
This syntax is known as a Immediately Invoked Function Expression (IIEF).
Arguments
When functions are called, the parameters from the declaration phase are replaced by the arguments of the call. In the above declarations, we used the variable name p
as a parameter name. When calling the function, we mostly used the literal "Go" as the argument. At runtime, it replaces all occurrences of p
in the function. The above examples demonstarte this technique.
Call-by-value
Such substitutions are done 'by value' and not 'by reference'. A copy of the argument's original value is passed to the function. If this copied value is changed within the function, the original value outside of the function is not changed.
"use strict";
// there is one parameter 'p'
function duplication(p) {
// In this example, we change the parameter's value
p = "NoGo";
alert("In function: " + p);
return p + "! " + p + "!";
};
let x = "Go";
const ret = duplication(x);
// is the modification of the argument done in the function visible here? No.
alert("Return value: " + ret + " Variable: " + x);
For objects (all non-primitive data types), this 'call-by-value' has a - possibly - astonishing effect. If the function modifies a property of the object, this change is also seen outside.
"use strict";
function duplication(p) {
p.a = 2; // change the property's value
p.b = 'xyz'; // add a property
alert("In function: " + JSON.stringify(p));
return JSON.stringify(p) + "! " + JSON.stringify(p) + "!";
};
let x = {a: 1};
alert("Object: " + JSON.stringify(x));
const ret = duplication(x);
// is the modification of the argument done in the function visible here? Yes.
alert("Return value: " + ret + " Object: " + JSON.stringify(x));
When the example runs, it shows that after the invocation of duplication
, the changes made by the function are visible not only in the return value. Also, the properties of the original object x
have changed. Why this; is it different from the behavior concerning primitive data types? No.
The function receives a copy of the reference to the object. Hence, within the function, the same object is referenced. The object itself exists only one time, but there are two (identical) references to the object. It does not make a difference whether the object's properties are modified by one reference or the other.
Another consequence is - and this may be intuitively the same as with primitive data types (?) - that the modification of the reference itself, e.g., by the creation of a new object, will not be visible in the outer routine. The reference to the new object is stored in the copy of the original reference. Now we have not only two references (with different values) but also two objects.
"use strict";
function duplication(p) {
// modify the reference by creating a new object
p = {};
p.a = 2; // change the property's value
p.b = 'xyz'; // add a property
alert("In function: " + JSON.stringify(p));
return JSON.stringify(p) + "! " + JSON.stringify(p) + "!";
};
let x = {a: 1};
alert("Object: " + JSON.stringify(x));
const ret = duplication(x);
// is the modification of the argument done in the function visible here? No.
alert("Return value: " + ret + " Object: " + JSON.stringify(x));
Note 1: The naming of this argument-passing technique is not used consistently across different languages. Sometimes it is called 'call-by-sharing'. Wikipedia gives an overview.
Note 2: The described consequences of JavaScript's argument-passing are comparable with the consequences of using the keyword const
, which declares a variable to be a constant. Such variables cannot be changed. Nevertheless, if they refer to an object, the object's properties can be changed.
Default values
If a function is invoked with fewer arguments than it contains parameters, the surplus parameters keep undefined. But you can define default values for this case by assigning a value within the function's signature. The missing parameters will get those as their default values.
"use strict";
// two nearly identical functions; only the signature is slightly different
function f1(a, b) {
alert("The second parameter is: " + b)
};
function f2(a, b = 10) {
alert("The second parameter is: " + b)
};
// identical invocations; different results
f1(5); // undefined
f1(5, 100); // 100
f2(5); // 10
f2(5, 100); // 100
Variable number of arguments
For some functions, it is 'normal' that they get involved with different numbers of arguments. For example, think of a function that shows names. firstName
and familyName
must be given in any case, but it's also possible that an academicTitle
or a titleOfNobility
must be shown. JavaScript offers different possibilities to handle such situations.
Individual checks
The 'normal' parameters, as well as the additional parameters, can be checked to determine whether they contain a value or not.
"use strict";
function showName(firstName, familyName, academicTitle, titleOfNobility) {
"use strict";
// handle required parameters
let ret = "";
if (!firstName || !familyName) {
return "first name and family name must be specified";
}
ret = firstName + ", " + familyName;
// handle optional parameters
if (academicTitle) {
ret = ret + ", " + academicTitle;
}
if (titleOfNobility) {
ret = ret + ", " + titleOfNobility;
}
return ret;
}
alert(showName("Mike", "Spencer", "Ph.D."));
alert(showName("Tom"));
Every single parameter that may not be given must be individually checked.
The 'rest' parameter
If the handling of the optional parameters is structurally identical, the code can be simplified by using the rest operator syntax - mostly in combination with a loop. The syntax of the feature consists of three dots in the function's signature - like in the spread syntax.
How does it work? As part of the function invocation, the JavaScript engine combines the given optional arguments into a single array. (Please note that the calling script does not use an array.) This array is given to the function as the last parameter.
"use strict";
// the three dots (...) introduces the 'rest syntax'
function showName(firstName, familyName, ...titles) {
// handle required parameters
let ret = "";
if (!firstName || !familyName) {
return "first name and family name must be specified";
}
ret = firstName + ", " + familyName;
// handle optional parameters
for (const title of titles) {
ret = ret + ", " + title;
}
return ret;
}
alert(showName("Mike", "Spencer", "Ph.D.", "Duke"));
alert(showName("Tom"));
The third and all following arguments of the call are collected into a single array that is available in the function as the last parameter. This allows the use of a loop and simplifies the function's source code.
The 'arguments' keyword
In accordance with other members of the C-family of programming languages, JavaScript offers the keyword arguments
within functions. It is an Array-like object that contains all given arguments of a function call. You can loop over it or use its length
property.
Its functionality is comparable with the above rest syntax. The main difference is that arguments
contains all arguments, whereas the rest syntax affects not necessarily all arguments.
"use strict";
function showName(firstName, familyName, academicTitles, titlesOfNobility) {
// handle ALL parameters with a single keyword
for (const arg of arguments) {
alert(arg);
}
}
showName("Mike", "Spencer", "Ph.D.", "Duke");
Return
The purpose of a function is to provide a solution of a dedicated problem. This solution is given back to the calling program by the return
statement.
Its syntax is return <expression>
, where <expression>
is optional.
A function runs until it reaches such a return
statement (, or an uncatched exception occurs, or behind the last statement). The <expression>
may be a simple variable of any data type like return 5
, or a complex expression like return myString.length
, or is omitted entirely: return
.
If there is no <expression>
within the return
statement, or if no return
statement is reached at all, undefined
is returned.
"use strict";
function duplication(p) {
if (typeof p === 'object') {
return; // return value: 'undefined'
}
else if (typeof p === 'string') {
return p + "! " + p + "!";
}
// implicit return with 'undefined'
}
let arg = ["Go", 4, {a: 1}];
for (let i = 0; i < arg.length; i++) {
const ret = duplication(arg[i]);
alert(ret);
}
Arrow functions (=>)
Arrow functions are a compact alternative to the above-shown conventional function syntax. They abbreviate some language elements, omit others, and have only a few semantic distinctions in comparison to the original syntax.
They are always anonymous but can be assigned to a variable.
"use strict";
// original conventional syntax
function duplication(p) {
return p + "! " + p + "!";
}
// 1. remove keyword 'function' and function name
// 2. introduce '=>' instead
// 3. remove 'return'; the last value is automatically returned
(p) => {
p + "! " + p + "!"
}
// remove { }, if only one statement
(p) => p + "! " + p + "!"
// Remove parameter parentheses if it is only one parameter
// -----------------------------
p => p + "! " + p + "!" // that's all!
// -----------------------------
alert(
(p => p + "! " + p + "!")("Go")
);
Here is one more example using an array. The forEach
method loops over the array and produces one array-element after the next. This is put to the arrow function's single argument e
. The arrow function shows e
together with a short text.
"use strict";
const myArray = ['a', 'b', 'c'];
myArray.forEach(e => alert("The element of the array is: " + e));
Other programming languages offer the concept of arrow functions under terms like anonymous functions or lambda expressions.
Recursive Invokations
Functions can call other functions. In real applications, this is often the case.
A particular situation occurs when they call themselves. This is called a recursive invokation. Of course, this implies the danger of infinite loops. You must change something in the arguments to avoid this hurdle.
Typically the need for such recursive calls arises when an application handles tree structures like bill of materials, a DOM tree, or genealogy information. Here we present the simple to implement mathematical problem of factorial computation.
The factorial is the product of all positive integers less than or equal to a certain number , written as . For example, . It can be solved by a loop from to , but there is also a recursive solution. The factorial of is the already computed factorial of multiplied with , or in formulas: . This thought leads to the correspondent recursive construction of the function:
"use strict";
function factorial(n) {
if (n > 0) {
const ret = n * factorial(n-1);
return ret;
} else {
// n = 0; 0! is 1
return 1;
}
}
const n = 4;
alert(factorial(n));
As long as is greater than , the script calls factorial
again, but time with as the argument. Therefore the arguments converge to . When is reached, this is the first time the factorial
function is not called again. It returns the value of . This number is multiplied by the next higher number from the previous invocation of factorial
. The result of the multiplication is returned to the previous invocation of factorial
, ... .
Exercises
See also
Anonymous functions
A function is a block of code that solves a dedicated problem and returns the solution to the calling statement. The function exists in its own context. Hence, functions divide massive programs into smaller 'bricks' which structure the software as well as the software development process.
// define a function
function <function_name> (<parameters>) {
<function_body>
}
// call a function
<variable> = <function_name> (<arguments>);
JavaScript supports the software development paradigm functional programming. Functions are a data type derived from Object; they can be bound to variables, passed as arguments, and returned from other functions, just as any other data type.
Declaration
Functions can be constructed in three main ways. The first version can be abbreviated further; see below.
The conventional way:
"use strict";
// conventional declaration (or 'definition')
function duplication(p) {
return p + "! " + p + "!";
}
// call the function
const ret = duplication("Go");
alert(ret);
Construction via a variable and an expression:
"use strict";
// assign the function to a variable
let duplication = function (p) {
return p + "! " + p + "!";
};
const ret = duplication("Go");
alert(ret);
Construction via the new
operator (this version is a little cumbersome):
"use strict";
// using the 'new' constructor
let duplication = new Function ("p",
"return p + '! ' + p + '!'");
const ret = duplication("Go");
alert(ret);
Invocation
For the declaration of functions, we have seen 3 variants. For their invocation, there are also 3 variants. The declarations and invocations are independent of each other and you can arbitrarily combine them.
The conventional invocation variant uses the function name followed by parenthesis ( )
. Within the parenthesis, there are the function's arguments, if any exist.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
// the conventional invocation method
const ret = duplication("Go");
alert(ret);
If the script runs in a browser, there are two more possibilities. They use the window
object that is provided by the browser.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
// via 'call'
let ret = duplication.call(window, "Go");
alert(ret);
// via 'apply'
ret = duplication.apply(window, ["Go"]);
alert(ret);
Hint: If you use the function name without the parenthesis ()
, you will receive the function itself (the script), not any result of an invocation.
"use strict";
function duplication(p) {
return p + "! " + p + "!";
}
alert(duplication); // 'function duplication (p) { ... }'
Hoisting
Functions are subject to 'hoisting'. This mechanism transfers the declaration of a function automatically to the top of its scope. As a consequence, you can call a function from an upper place in the source code than its declaration.
"use strict";
// use a function above (in source code) its declaration
const ret = duplication("Go");
alert(ret);
function duplication(p) {
return p + "! " + p + "!";
}
Immediately Invoked Function
So far, we have seen the two separate steps declaration and invocation. There is also a syntax variant that allows the combination of both. It is characterized by using parenthesis around the function declaration followed by ()
to invoke that declaration.
"use strict";
alert( // 'alert' to show the result
// declaration plus invocation
(function (p) {
return p + "! " + p + "!";
})("Go") // ("Go"): invocation with the argument "Go"
);
alert(
// the same with 'arrow' syntax
((p) => {
return p + "! " + p + "!";
})("Gooo")
);
This syntax is known as a Immediately Invoked Function Expression (IIEF).
Arguments
When functions are called, the parameters from the declaration phase are replaced by the arguments of the call. In the above declarations, we used the variable name p
as a parameter name. When calling the function, we mostly used the literal "Go" as the argument. At runtime, it replaces all occurrences of p
in the function. The above examples demonstarte this technique.
Call-by-value
Such substitutions are done 'by value' and not 'by reference'. A copy of the argument's original value is passed to the function. If this copied value is changed within the function, the original value outside of the function is not changed.
"use strict";
// there is one parameter 'p'
function duplication(p) {
// In this example, we change the parameter's value
p = "NoGo";
alert("In function: " + p);
return p + "! " + p + "!";
};
let x = "Go";
const ret = duplication(x);
// is the modification of the argument done in the function visible here? No.
alert("Return value: " + ret + " Variable: " + x);
For objects (all non-primitive data types), this 'call-by-value' has a - possibly - astonishing effect. If the function modifies a property of the object, this change is also seen outside.
"use strict";
function duplication(p) {
p.a = 2; // change the property's value
p.b = 'xyz'; // add a property
alert("In function: " + JSON.stringify(p));
return JSON.stringify(p) + "! " + JSON.stringify(p) + "!";
};
let x = {a: 1};
alert("Object: " + JSON.stringify(x));
const ret = duplication(x);
// is the modification of the argument done in the function visible here? Yes.
alert("Return value: " + ret + " Object: " + JSON.stringify(x));
When the example runs, it shows that after the invocation of duplication
, the changes made by the function are visible not only in the return value. Also, the properties of the original object x
have changed. Why this; is it different from the behavior concerning primitive data types? No.
The function receives a copy of the reference to the object. Hence, within the function, the same object is referenced. The object itself exists only one time, but there are two (identical) references to the object. It does not make a difference whether the object's properties are modified by one reference or the other.
Another consequence is - and this may be intuitively the same as with primitive data types (?) - that the modification of the reference itself, e.g., by the creation of a new object, will not be visible in the outer routine. The reference to the new object is stored in the copy of the original reference. Now we have not only two references (with different values) but also two objects.
"use strict";
function duplication(p) {
// modify the reference by creating a new object
p = {};
p.a = 2; // change the property's value
p.b = 'xyz'; // add a property
alert("In function: " + JSON.stringify(p));
return JSON.stringify(p) + "! " + JSON.stringify(p) + "!";
};
let x = {a: 1};
alert("Object: " + JSON.stringify(x));
const ret = duplication(x);
// is the modification of the argument done in the function visible here? No.
alert("Return value: " + ret + " Object: " + JSON.stringify(x));
Note 1: The naming of this argument-passing technique is not used consistently across different languages. Sometimes it is called 'call-by-sharing'. Wikipedia gives an overview.
Note 2: The described consequences of JavaScript's argument-passing are comparable with the consequences of using the keyword const
, which declares a variable to be a constant. Such variables cannot be changed. Nevertheless, if they refer to an object, the object's properties can be changed.
Default values
If a function is invoked with fewer arguments than it contains parameters, the surplus parameters keep undefined. But you can define default values for this case by assigning a value within the function's signature. The missing parameters will get those as their default values.
"use strict";
// two nearly identical functions; only the signature is slightly different
function f1(a, b) {
alert("The second parameter is: " + b)
};
function f2(a, b = 10) {
alert("The second parameter is: " + b)
};
// identical invocations; different results
f1(5); // undefined
f1(5, 100); // 100
f2(5); // 10
f2(5, 100); // 100
Variable number of arguments
For some functions, it is 'normal' that they get involved with different numbers of arguments. For example, think of a function that shows names. firstName
and familyName
must be given in any case, but it's also possible that an academicTitle
or a titleOfNobility
must be shown. JavaScript offers different possibilities to handle such situations.
Individual checks
The 'normal' parameters, as well as the additional parameters, can be checked to determine whether they contain a value or not.
"use strict";
function showName(firstName, familyName, academicTitle, titleOfNobility) {
"use strict";
// handle required parameters
let ret = "";
if (!firstName || !familyName) {
return "first name and family name must be specified";
}
ret = firstName + ", " + familyName;
// handle optional parameters
if (academicTitle) {
ret = ret + ", " + academicTitle;
}
if (titleOfNobility) {
ret = ret + ", " + titleOfNobility;
}
return ret;
}
alert(showName("Mike", "Spencer", "Ph.D."));
alert(showName("Tom"));
Every single parameter that may not be given must be individually checked.
The 'rest' parameter
If the handling of the optional parameters is structurally identical, the code can be simplified by using the rest operator syntax - mostly in combination with a loop. The syntax of the feature consists of three dots in the function's signature - like in the spread syntax.
How does it work? As part of the function invocation, the JavaScript engine combines the given optional arguments into a single array. (Please note that the calling script does not use an array.) This array is given to the function as the last parameter.
"use strict";
// the three dots (...) introduces the 'rest syntax'
function showName(firstName, familyName, ...titles) {
// handle required parameters
let ret = "";
if (!firstName || !familyName) {
return "first name and family name must be specified";
}
ret = firstName + ", " + familyName;
// handle optional parameters
for (const title of titles) {
ret = ret + ", " + title;
}
return ret;
}
alert(showName("Mike", "Spencer", "Ph.D.", "Duke"));
alert(showName("Tom"));
The third and all following arguments of the call are collected into a single array that is available in the function as the last parameter. This allows the use of a loop and simplifies the function's source code.
The 'arguments' keyword
In accordance with other members of the C-family of programming languages, JavaScript offers the keyword arguments
within functions. It is an Array-like object that contains all given arguments of a function call. You can loop over it or use its length
property.
Its functionality is comparable with the above rest syntax. The main difference is that arguments
contains all arguments, whereas the rest syntax affects not necessarily all arguments.
"use strict";
function showName(firstName, familyName, academicTitles, titlesOfNobility) {
// handle ALL parameters with a single keyword
for (const arg of arguments) {
alert(arg);
}
}
showName("Mike", "Spencer", "Ph.D.", "Duke");
Return
The purpose of a function is to provide a solution of a dedicated problem. This solution is given back to the calling program by the return
statement.
Its syntax is return <expression>
, where <expression>
is optional.
A function runs until it reaches such a return
statement (, or an uncatched exception occurs, or behind the last statement). The <expression>
may be a simple variable of any data type like return 5
, or a complex expression like return myString.length
, or is omitted entirely: return
.
If there is no <expression>
within the return
statement, or if no return
statement is reached at all, undefined
is returned.
"use strict";
function duplication(p) {
if (typeof p === 'object') {
return; // return value: 'undefined'
}
else if (typeof p === 'string') {
return p + "! " + p + "!";
}
// implicit return with 'undefined'
}
let arg = ["Go", 4, {a: 1}];
for (let i = 0; i < arg.length; i++) {
const ret = duplication(arg[i]);
alert(ret);
}
Arrow functions (=>)
Arrow functions are a compact alternative to the above-shown conventional function syntax. They abbreviate some language elements, omit others, and have only a few semantic distinctions in comparison to the original syntax.
They are always anonymous but can be assigned to a variable.
"use strict";
// original conventional syntax
function duplication(p) {
return p + "! " + p + "!";
}
// 1. remove keyword 'function' and function name
// 2. introduce '=>' instead
// 3. remove 'return'; the last value is automatically returned
(p) => {
p + "! " + p + "!"
}
// remove { }, if only one statement
(p) => p + "! " + p + "!"
// Remove parameter parentheses if it is only one parameter
// -----------------------------
p => p + "! " + p + "!" // that's all!
// -----------------------------
alert(
(p => p + "! " + p + "!")("Go")
);
Here is one more example using an array. The forEach
method loops over the array and produces one array-element after the next. This is put to the arrow function's single argument e
. The arrow function shows e
together with a short text.
"use strict";
const myArray = ['a', 'b', 'c'];
myArray.forEach(e => alert("The element of the array is: " + e));
Other programming languages offer the concept of arrow functions under terms like anonymous functions or lambda expressions.
Recursive Invokations
Functions can call other functions. In real applications, this is often the case.
A particular situation occurs when they call themselves. This is called a recursive invokation. Of course, this implies the danger of infinite loops. You must change something in the arguments to avoid this hurdle.
Typically the need for such recursive calls arises when an application handles tree structures like bill of materials, a DOM tree, or genealogy information. Here we present the simple to implement mathematical problem of factorial computation.
The factorial is the product of all positive integers less than or equal to a certain number , written as . For example, . It can be solved by a loop from to , but there is also a recursive solution. The factorial of is the already computed factorial of multiplied with , or in formulas: . This thought leads to the correspondent recursive construction of the function:
"use strict";
function factorial(n) {
if (n > 0) {
const ret = n * factorial(n-1);
return ret;
} else {
// n = 0; 0! is 1
return 1;
}
}
const n = 4;
alert(factorial(n));
As long as is greater than , the script calls factorial
again, but time with as the argument. Therefore the arguments converge to . When is reached, this is the first time the factorial
function is not called again. It returns the value of . This number is multiplied by the next higher number from the previous invocation of factorial
. The result of the multiplication is returned to the previous invocation of factorial
, ... .
Exercises
See also
Object-based programming
Object-oriented programming (OOP) is a software design paradigm that first arose in the 1960s and gained popularity in the 1990s. It strives for modularization, reusability, encapsulation and hiding of state (data) and behavior (functions), design in a hierarchy of generalization, inheritance, and more.
It allows the components to be as modular as possible. In particular, when a new object type is created, it is expected that it should work without problems when placed in a different environment or new programming project. The benefits of this approach are a shorter development time and easier debugging because you're re-using program code that has already been proven. This 'black box' approach means that data goes into the object and other data comes out of the object, but what goes on inside isn't something you need to concern yourself with.
Over time different techniques have been developed to implement OOP. The most popular ones are the class-based and the prototype-based approach.
Class-based OOP
Classes are a blueprint that defines all aspects - state as well as behavior - of a group of structurally identical objects. The blueprint is called the class, and the objects instances of that class. Popular members of the C-family languages, especially Java, C++, and C#, implement OOP with this class-based approach.
Prototype-based OOP
In the prototype-based approach, every object stores its state and behavior. In addition, it has a prototype (or null
if it is on top of the hierarchy). Such a prototype is a pointer to another, more general object. All properties of the referenced object are also available in the referencing object. Classes explicitly don't exist in the prototype-based approach.
// define a constructor for the class "Class4Person"
function Class4Person () {
this.firstname = "";
this.lastname = "";
this.age = 0;
return this;
};
// define a method "set_age()" for the class "Class4Person"
Class4Person.prototype.set_age = function (pYears) {
// set age of person
this.age = pYears;
}
// define a method "get_older()" for the class "Class4Person"
Class4Person.prototype.get_older = function (pYears) {
// if pYear parameter is not set, define pYears=1
pYears = pYears || 1;
// increment age with pYears
this.age += pYears;
}
// define a method "get_info()" for the class "Class4Person"
Class4Person.prototype.get_info = function () {
// create an info string about person
return "My name is " + this.firstname + " " + this.lastname + " and I am " + this.age + " old."
}
// create instance of Class4Person
let anna = new Class4Person();
// set attributes of instance "anna"
anna.firstname = "Anna";
anna.lastname = "Miller";
// call methods of instance "anna"
anna.set_age(15); // anna.age = 15
anna.get_older(5); // anna.age = 20
anna.get_older(); // anna.age = 21
// create instance of Class4Person
let bert = new Class4Person();
// set attributes of instance "bert"
bert.firstname = "Bert";
bert.lastname = "Smith";
// call method for instance "bert"
bert.set_age(30); // age of instance "bert" is now set to 30 - bert.age = 30
// create info string for instances and show in console log
console.log(anna.get_info()); // "I am Anna Miller and I am 21 years old."
console.log(bert.get_info()); // "I am Bert Smith and I am 30 years old."
OOP in JavaScript "Two jackets for one body"
One of JavaScript's cornerstones is the provision of objects in accordance with the rules of the prototype-based OOP. Objects consist of properties that are key/value pairs holding data as well as methods. One of those properties is always the prototype property '__proto__'. It points to the 'parent' object and, by this, realizes the relationship.
// relationships are realized by the property '__proto__'
let parent = [];
let child = {
name: "Joel",
__proto__: parent,
};
console.log(Object.getPrototypeOf(child)); // Array []
┌─────────────────────┐ ┌─────────────────────┐ │ child │ ┌──> │ parent │ ├─────────────────────┤ | ├─────────────────────┤ │ name: Joel │ | │ .... : ... │ ├─────────────────────┤ | ├─────────────────────┤ │ __proto__: parent │ ──┘ │ __proto__: ... │ ──> ... ──> null └─────────────────────┘ └─────────────────────┘
If a requested property is missed on any object, the JavaScript engine searches it in the 'parent' object, 'grandparent' object, and so on. This is called the prototype chain.
All of that applies to user-defined objects and system-defined objects like Arrays
or Date
in the same way.
Since EcmaScript 2015 (ES6), the syntax offers keywords like class
or extends
, which are used in class-based approaches. Even though such keywords have been introduced, the fundaments of JavaScript haven't changed: Those keywords lead to prototypes in the same way as before. They are syntactical sugar and get compiled to the conventional prototype technique.
In summary, the syntax of JavaScript offers two ways to express object-oriented features like inheritance in the source code: the 'classical' and the 'class' style. Despite the different syntax, the implementation techniques differ only slightly.
The classical syntax
Since its first days, JavaScript has defined the parent/child relation of objects with the 'prototype' mechanism. If not explicitly notated in the source code, this happens automatically. The classical syntax exposes it quite well.
To define a parent/child relation of two objects explicitly, you should use the method setPrototypeOf
to set the prototype of an object to a dedicated other object. After the method has run, all properties - inclusive functions - of the parent object are known to the child.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
const adult = {
familyName: "McAlister",
showFamily: function() {return "The family name is: " + this.familyName;}
};
const child = {
firstName: "Joel",
kindergarten: "House of Dwars"
};
// 'familyName' and 'showFamily()' are undefined here!
alert(child.firstName + " " + child.familyName);
// set the intended prototype chain
Object.setPrototypeOf(child, adult);
// or: child.__proto__ = adult;
alert(child.firstName + " " + child.familyName);
alert(child.showFamily());
}
</script>
</head>
<body id="body">
<button onclick="go()">Run the demo</button>
</body>
</html>
The 'adult' object contains the 'familyName' and a function 'showFamily'. In the first step, they are not known in the object 'child'. After setPrototypeOf
was running, they are known because the 'child's prototype no longer points to the default 'Object' but to 'adult'.
The next script demonstrates the prototype chain. It starts with the user-defined variables myArray
and theObject
. myArray
is an array with three elements. The assignment operation in line 6 sets theObject
to the same array. A loop shows the prototype of theObject
and assigns the next higher level of the prototype chain to it. The loop finishes when the top level of the hierarchy is reached. In this case, the prototype is null
.
function go() {
"use strict";
// define an array with three elements
const myArray = [0, 1, 2];
let theObject = myArray;
do {
// show the object's prototype
console.log(Object.getPrototypeOf(theObject)); // Array[], Object{...}, null
// or: console.log(theObject.__proto__);
// switch to the next higher level
theObject = Object.getPrototypeOf(theObject);
} while (theObject);
}
As you know, properties are key/value pairs. Hence it is possible to directly use the value of the 'prototype' property to identify and manipulate prototypes. Interestingly the key's name isn't 'prototype' but '__proto__'. This is shown in line 11. Nevertheless, we recommend ignoring this technique and using API methods for prototype manipulations, such as Object.getPrototypeOf
, Object.setPrototypeOf
, and Object.create
instead.
The 'class' syntax
The script defines two classes, Adult and Child with some internal properties, one of them being a method. The keyword extends
combines the two classes hierarchically. Afterward, in line 21, an instance is created with the keyword new
.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
class Adult {
constructor(familyName) {
this.familyName = familyName;
}
showFamily() {return "The family name is: " + this.familyName;}
}
class Child extends Adult {
constructor(firstName, familyName, kindergarten) {
super(familyName);
this.firstName = firstName;
this.kindergarten = kindergarten;
}
}
const joel = new Child("Joel", "McAlister", "House of Dwars");
alert(joel.firstName + " " + joel.familyName);
alert(joel.showFamily());
}
</script>
</head>
<body id="body">
<button onclick="go()">Run the demo</button>
</body>
</html>
The property familyName
and the method showFamily
are defined in the Adult class. But they are also known in the Child class.
Please note again that this class-based inheritance in JavaScript is implemented on top of the prototype-based classical approach.
See also
Objects
In JavaScript, an Object is a collection of properties; properties are key-value pairs. The keys are Strings or Symbols. Values can be of any data type - including functions (in this case, they are called methods).
Objects look like associative arrays implemented as hashtables. But in practice, JavaScript engines might have implemented them in other ways to achieve optimal performance.
Please consider that JavaScript objects are not identical to JSON. JSON is a format for a textual representation ('serialization' / 'de-serialization') of objects, which can also be done in other languages or in a pure text editor.
Create an object
The JavaScript syntax supports different ways to create objects.
'Literal notation' using curly braces { }
"use strict";
// an empty object (no properties)
const obj_0 = {};
alert(JSON.stringify(obj_0));
// 'obj_1' contains a set of 3 properties. The value of key 'c' is
// a second object with an empty set of properties.
const obj_1 = { a: "Any string", b: 42, c: {} };
alert(JSON.stringify(obj_1));
// using variables
const a = "Any string";
const b = 42;
const c = {};
const obj_2 = { a: a, b: b, c: c };
alert(JSON.stringify(obj_2));
// shorthand usage of variables
const obj_3 = { a, b, c };
alert(JSON.stringify(obj_3));
Constructor
The new Object()
constructor creates a new object.
"use strict";
const obj_4 = new Object({ a: 5, b: 42 });
alert(JSON.stringify(obj_4));
Object.create()
The Object.create()
method creates a new object from an existing object.
"use strict";
const obj_5 = Object.create({ a: 5, b: 42 });
alert(JSON.stringify(obj_5)); // ???
alert(JSON.stringify(obj_5.a));
console.log (obj_5);
Read a property
Each property consists of a key-value pair. To read a property's values, you can use its key in one of two syntactical ways, dot notation or bracket notation.
"use strict";
const person = {firstName: "Albert", lastName: "Einstein" };
// dot notation
alert(person.firstName); // no " " or ' '
// bracket notation
alert(person["firstName"]); // must use a string
// bracket notation using a variable
const fn = "firstName";
alert(person[fn]);
What if you don't know the keys? To get a list of all existing keys, use the method Object.keys()
. It returns an array whose elements are strings. Those strings may be used in a loop to access the values. Because you need such loops in many cases, JavaScript offers a special language element for that situation. The for .. in
loop selects the keys and loops over all properties.
"use strict";
const person = {firstName: "Albert", lastName: "Einstein" };
alert(Object.keys(person)); // firstName, lastName
// The for .. in loop selects all existing keys and loops over the properties
for (const k in person) {
alert('The key is: ' + k + '. The value is: ' + person[k]);
}
Analog to the Object.keys()
method there are the two methods Object.values()
and Object.entries()
to get the values respectively the properties (key and value). In the latter case, you get an array of arrays (with two elements each).
"use strict";
const person = {firstName: "Albert", lastName: "Einstein" };
alert(Object.values(person)); // Albert, Einstein
// for .. of loops differ from for .. in loops in syntax AND semantic; see chapter 'Loops'
for (const [k, v] of Object.entries(person)) {
alert('The key is: ' + k + '. The value is: ' + v);
}
Add or modify a property
You can use the dot notation as well as the bracket notation to add or modify properties. There is no difference in the syntax between the above read operations and the write operations.
"use strict";
const person = {firstName: "Albert", lastName: "Einstein" };
// add properties
person.bornIn = "Ulm";
person["profession"] = "Physicist";
alert(JSON.stringify(person));
// modify properties
person.bornIn = "Germany";
person["profession"] = "Theoretical Physics";
alert(JSON.stringify(person));
In the above example, the variable 'person' is created with the keyword const
. So it's not possible to assign a new value to it, but it's possible to manipulate its properties. When manipulating properties, the original object keeps unchanged.
Delete a property
The delete
operator removes the complete property from an object - the key as well as the value. This is different from the case where someone stores null
or undefined
in the value.
Again, you can use the dot notation as well as the bracket notation.
"use strict";
const person = {firstName: "Albert", lastName: "Einstein", bornIn: "Ulm", profession: "Physicist" };
delete person.bornIn;
delete person["profession"];
alert(JSON.stringify(person));
Merge objects
JavaScript offers a 'spread syntax' (3 dots). It expands an Object to its properties (key-value pairs) or an array to its elements. This can be helpful when you want to merge multiple objects into a single one.
"use strict";
const person = {firstName: "Albert", lastName: "Einstein" };
const address = {city: "Ulm" };
// expand the two objects (and merge them)
const newPerson = {...person, ...address};
alert(JSON.stringify(newPerson));
// The result is an object with 3 properties. All values are strings.
// {firstName: "Albert", lastName: "Einstein",city: "Ulm"}
// which is different from the version without the 'spread syntax':
const newPerson1 = {person, address};
alert(JSON.stringify(newPerson1));
// The result is an object with 2 properties. Both values are objects.
// {person: {firstName: "Albert", lastName: "Einstein"}, address: {city: "Ulm"}}
Functions/methods as part of an object
The value part of a property may contain the body of a function; see here. In this case, the function is called a method.
"use strict";
// only definitions here, no execution!
const city = {
showName: function (cityName) {
if (typeof cityName === "undefined") {
alert("Sorry, I don't know my name.");
} else {
alert("The name of the city is: " + cityName);
}
},
// this works too!
showPopulation(count) {
alert(count + " Inhabitants");
},
};
// JSON's 'stringify()' doesn't support the serialization of methods. Use 'console.log()' instead.
console.log(city);
// executions
city.showName();
city.showName("Nairobi");
city.showPopulation(4500000);
Exercises
Constructors and prototypes
Constructor
The new operator creates a new object based on a constructor and a prototype.
The constructor is a function matching the name of the object, and, when called by the new operator, has the keyword this assigned to the newly created instance of the object. The constructor can then assign any initial variables as required.
function CoinObject() {
this.value = 0;
}
var slug = new CoinObject(); // Creates a "slug" - a valueless coin (slug.value = 0).
The constructor can accept parameters - however, it is not possible to overload the constructors by having multiple functions with a different number of parameters.
(The 'new' statement is the most common way to create a new JavaScript object from a constructor function, but a few JavaScript programmers sometimes use alternative techniques.[1][2] )
Prototypes
A prototype for an object is the set of auto-created fields and methods. It cannot operate by itself, and relies on an existing constructor.
When the object is created, the fields initialized in the prototype are copied to the newly created object.
function CoinObject() {
this.value = 0;
}
CoinObject.prototype.diameter = 1;
slug = new CoinObject(); // A valueless coin (slug.value = 0), with diameter of 1 (slug.diameter = 1)
Since the prototype of an object behaves as an object, you can use this to create inheritance.
Dynamically extending objects
Objects within JavaScript are fully dynamic, and fields within an object can instantly be created if they do not already exist. If, for example, you discover that you now need to keep track of the thickness in the coin example, you can simply add the new field to the prototype.
Changing the prototype of an object allows existing instances of the object to gain access to that change.
When a property value has been assigned on a new object, that property takes precedence over the prototype property from its ancestor. If you delete the property from the new object, the chain of inheritance takes you back to the property from the parent object.
To demonstrate, an Animal constructor is used to create an object, a dog called Spot. After that creation, the constructor has a gender property added to it. That gender property is also accessible from the object for Spot.
The gender property can be updated, and if the gender property is deleted from the dog object, it will retrieve the desired property from its ancestor instead, the Animal constructor.
function Animal (type, name) {
this.type = type || 'No type';
this.name = name || 'No name';
}
var dog = new Animal('dog', 'Spot');
// Add gender to the Animal object
Animal.prototype.gender = 'unspecified';
// dog.gender is 'unspecified'
dog.gender = 'male';
// dog.gender is 'male';
delete(dog.gender);
// dog.gender is once again, 'unspecified'
References
- ↑ Greg Tatum. "Killing JavaScript's this". Examples of JavaScript supporting many alternatives to traditional object-oriented programming. One section "typical object-oriented JavaScript".
- ↑ Douglas Crockford. "Crockford on JavaScript -- Act III: Function the Ultimate" pdf "Crockford on JavaScript -- Act III: Function the Ultimate" video. Describes several different ways to make object constructors in JavaScript.
Inheritance
This page or section is an undeveloped draft or outline. You can help to develop the work, or you can ask for assistance in the project room. |
instanceof operator
The instanceof operator determines whether an object was instantiated as a child of another object, returning true if this was the case. instanceof is a binary infix operator whose left operand is an object and whose right operand is an object type. It returns true, if the left operand is of the type specified by the right operand. It differs from the .constructor property in that it "walks up the prototype chain". If object a is of type b, and b is an extension of c, then a instanceof b and a instanceof c both return true, whereas a.constructor === b returns true, while a.constructor === c returns false.
Inheritance by prototypes
The prototype of an object can be used to create fields and methods for an object. This prototype can be used for inheritance by assigning a new instance of the superclass to the prototype.[1]
function CoinObject() {
this.value = 0;
this.diameter = 1;
}
function Penny() {
this.value = 1;
}
Penny.prototype = new CoinObject();
function Nickel() {
this.value = 5;
}
Nickel.prototype = new CoinObject();
Inheritance by functions
function CoinObject() {
this.value = 0;
this.diameter = 1;
}
References
- ↑ Matthew Batchelder. "Object Oriented JavaScript" describes one way to implement inheritance in JavaScript.
Access control
Access Control
JavaScript does not provide a direct means to control access to internal variables, however, it is possible to restrict access to some variables.
By default, variables within an object are public and can be modified anywhere in the code. As such, any programmer that uses the code in the future may change the internal state of the object unexpectedly, which has the potential for problems. While the easiest protection against this is to properly document your code (e.g. comments showing how to use the object), there are cases where you want to block direct access to a variable.
To declare and use a variable as private, there are two steps required:
- Declare a new variable within the constructor using the var statement.
- Create an anonymous function within the constructor, and assign it as a method for an object.
The example below shows a private field being used:
function MyObject() {
this.publicNumber = 10; // Public field.
var privateNumber = 20; // Private variable.
this.getPrivateNumber = function() {
return privateNumber;
}
}
testObject = new MyObject();
var privateNumber is normally a local variable that only exists within the function. As you can see, it is accessed in this.getPrivateNumber(), which is an anonymous function, attempting to access it directly would result in an error. Since this anonymous function is declared in the constructor, it can use and modify the local variables declared in function MyObject, and keeps a reference to the variable even when the function returns. The anonymous function is bound to an instance of the object, and creating a new MyObject will create a new anonymous function that refers to the new privateNumber.
Closures
A Closure is a technique where a function is bundled (enclosed) with its surrounding variables, the lexical environment. Typically, Closures are implemented in functional programming languages like JavaScript where they support Currying.
Lexical environment
First, we show the access of a function to its lexical environment. This alone is not a Closure.
"use strict";
function incrementByCertainValue(param) {
return param + certainValue;
}
let certainValue = 10;
alert(incrementByCertainValue(8)); // 18
certainValue = 100;
alert(incrementByCertainValue(8)); // 108
The function incrementByCertainValue
increments its parameter by the value of the variable certainValue
. Because incrementByCertainValue
as well as certainValue
are defined within the same block, the function has access to the variable. Whenever the function is called, it reads the variable and uses it to compute its return value.
Closure
To extend the above example to a Closure, we have to combine an (inner) function with access to variables of its lexical environment - typically another function -, and save this state by returning this inner function.
"use strict";
function incrementByCertainValue(param) {
// declare a function that will perform the work. (Only declaration, currently not invoked.)
const add = function (certainValue) {
// access to 'param' from lexical environment and to its parameter 'certainValue'
return param + certainValue;
}
return add;
}
const incrementByFive = incrementByCertainValue(5);
alert(incrementByFive(8)); // 13
const incrementBySix = incrementByCertainValue(6);
alert(incrementBySix(8)); // 14
alert(incrementByFive(10)); // 15
The (outer) function incrementByCertainValue
- contains a parameter
param
, - defines an (inner) function
add
that takes another parametercertainValue
, - in addition to its parameter, the (inner) function has access - as usual - to its lexical environment where
param
is defined - returns the (inner) function.
So far, there are exclusively declarations and no running code.
When the variable incrementByFive
is declared in line 12, it is initialized with the return value of the function incrementByCertainValue(5)
. This is the crucial point where code runs and the Closure technique acts. Within incrementByCertainValue
the 5 is known as the parameter/variable param
. Next, the function add
is created using param
from its lexical environment. This function add
accepts one parameter that must be given from the calling routine later on. The return statement of incrementByCertainValue
delivers this function add
that has bound the value '5' into its body. Please note that the function name add
is arbitrary and not seen outside of incrementByCertainValue
.
When incrementByCertainValue
is called a second time with the argument '6', the '6' is bound to a separate, second function.
Exercises
See also
Debugging
JavaScript Debuggers
Firebug
- Firebug is a powerful extension for Firefox that has many development and debugging tools including JavaScript debugger and profiler.
Venkman JavaScript Debugger
- Venkman JavaScript Debugger (for Mozilla based browsers such as Netscape 7.x, Firefox/Phoenix/Firebird and Mozilla Suite 1.x)
- Introduction to Venkman
- Using Breakpoints in Venkman
Internet Explorer debugging
- Microsoft Script Debugger (for Internet Explorer) The script debugger is from the Windows 98 and NT era. It has been succeeded by the Developer Toolbar
- Internet Explorer Developer Toolbar
- Microsofts Visual Web Developer Express is Microsofts free version of the Visual Studio IDE. It comes with a JS debugger. For a quick summary of its capabilities see [1]
- Internet Explorer 8 has a firebug-like Web development tool by default (no add-on) which can be accessed by pressing F12. The Web development tool also provides the ability to switch between the IE8 and IE7 rendering engines.
Safari debugging
Safari includes a powerful set of tools that make it easy to debug, tweak, and optimize a website for peak performance and compatibility. To access them, turn on the Develop menu in Safari preferences. These include Web Inspector, Error Console, disabling functions, and other developer features. The Web Inspector gives you quick and easy access to the richest set of development tools ever included in a browser. From viewing the structure of a page to debugging JavaScript to optimizing performance, the Web Inspector presents its tools in a clean window designed to make developing web applications more efficient. To activate it, choose Show Web Inspector from the Develop menu. The Scripts pane features the powerful JavaScript Debugger in Safari. To use it, choose the Scripts pane in the Web Inspector and click Enable Debugging. The debugger cycles through your page’s JavaScript, stopping when it encounters exceptions or erroneous syntax. The Scripts pane also lets you pause the JavaScript, set breakpoints, and evaluate local variables.[1]
JTF: JavaScript Unit Testing Farm
- JTF is a collaborative website that enables you to create test cases that will be tested by all browsers. It's the best way to do TDD and to be sure that your code will work well on all browsers.
jsUnit
built-in debugging tools
Some people prefer to send debugging messages to a "debugging console" rather than use the alert() function[2][3][4]. Following is a brief list of popular browsers and how to access their respective consoles/debugging tools.
- Firefox: Ctrl+Shift+K opens an error console.
- Opera (9.5+): Tools >> Advanced >> Developer Tools opens Dragonfly.
- Chrome: Ctrl+Shift+J opens chrome's "Developer Tools" window, focused on the "console" tab.
- Internet Explorer: F12 opens a firebug-like Web development tool that has various features including the ability to switch between the IE8 and IE7 rendering engines.
- Safari: Cmd+Alt+C opens the WebKit inspector for Safari.
Common Mistakes
- Carefully read your code for typos.
- Be sure that every "(" is closed by a ")" and every "{" is closed by a "}".
- Trailing commas in Array and Object declarations will throw an error in Microsoft Internet Explorer but not in Gecko-based browsers such as Firefox.
// Object
var obj = {
'foo' : 'bar',
'color' : 'red', //trailing comma
};
// Array
var arr = [
'foo',
'bar', //trailing comma
];
- Remember that JavaScript is case sensitive. Look for case related errors.
- Don't use Reserved Words as variable names, function names or loop labels.
- Escape quotes in strings with a "\" or the JavaScript interpreter will think a new string is being started, i.e:
alert('He's eating food');should be- alert('He\'s eating food'); or
- alert("He's eating food");
- When converting strings to numbers using the parseInt function, remember that "08" and "09" (e.g. in datetimes) indicate an octal number, because of the prefix zero. Using parseInt using a radix of 10 prevents wrong conversion. var n = parseInt('09',10);
- Remember that JavaScript is platform independent, but is not browser independent. Because there are no properly enforced standards, there are functions, properties and even objects that may be available in one browser, but not available in another, e.g. Mozilla / Gecko Arrays have an indexOf() function; Microsoft Internet Explorer does not.
Debugging Methods
Debugging in JavaScript doesn't differ very much from debugging in most other programming languages. See the article at Computer Programming Principles/Maintaining/Debugging.
Following Variables as a Script is Running
The most basic way to inspect variables while running is a simple alert() call. However some development environments allow you to step through your code, inspecting variables as you go. These kind of environments may allow you to change variables while the program is paused.
Browser Bugs
Sometimes the browser is buggy, not your script. This means you must find a workaround.
browser-dependent code
Some advanced features of JavaScript don't work in some browsers.
Too often our first reaction is: Detect which browser the user is using, then do something the cool way if the user's browser is one of the ones that support it. Otherwise skip it.
Instead of using a "browser detect", a much better approach is to write "object detection" JavaScript to detect if the user's browser supports the particular object (method, array or property) we want to use.[5] [6]
To find out if a method, property, or other object exists, and run code if it does, we write code like this:
var el = null;
if (document.getElementById) {
// modern technique
el = document.getElementById(id);
} else if (document.all) {
// older Internet Explorer technique
el = document.all[id];
} else if (document.layers) {
// older Netscape Web browser technique
el = document.layers[id];
}
Further reading
- "JavaScript Debugging" by Ben Bucksch
References
- ↑ "Safari - The best way to see the sites" (HTML). Apple. Retrieved 2015-03-09.
Optimization
JavaScript optimization
Optimization Techniques
- High Level Optimization
- Algorithmic Optimization (Mathematical Analysis)
- Simplification
- Low Level Optimization
- Loop Unrolling
- Strength Reduction
- Duff's Device
- Clean Loops
- External Tools & Libraries for speeding/optimizing/compressing JavaScript code
Common Mistakes and Misconceptions
String concatenation
Strings in JavaScript are immutable objects. This means that once you create a string object, to modify it, another string object must theoretically be created.
Now, suppose you want to perform a ROT-13 on all the characters in a long string. Supposing you have a rot13() function, the obvious way to do this might be:
var s1 = "the original string";
var s2 = "";
for (i = 0; i < s1.length; i++) {
s2 += rot13(s1.charAt(i));
}
Especially in older browsers like Internet Explorer 6, this will be very slow. This is because, at each iteration, the entire string must be copied before the new letter is appended.
One way to make this script faster might be to create an array of characters, then join it:
var s1 = "the original string";
var a2 = new Array(s1.length);
var s2 = "";
for (i = 0; i < s1.length; i++) {
a2[i] = rot13(s1.charAt(i));
}
s2 = a2.join('');
Internet Explorer 6 will run this code faster. However, since the original code is so obvious and easy to write, most modern browsers have improved the handling of such concatenations. On some browsers the original code may be faster than this code.
A second way to improve the speed of this code is to break up the string being written to. For instance, if this is normal text, a space might make a good separator:
var s1 = "the original string";
var c;
var st = "";
var s2 = "";
for (i = 0; i < s1.length; i++) {
c = rot13(s1.charAt(i));
st += c;
if (c == " ") {
s2 += st;
st = "";
}
}
s2 += st;
This way the bulk of the new string is copied much less often, because individual characters are added to a smaller temporary string.
A third way to really improve the speed in a for loop, is to move the [array].length statement outside the condition statement. In face, every occurrence, the [array].length will be re-calculate For a two occurrences loop, the result will not be visible, but (for example) in a five thousand occurrence loop, you'll see the difference. It can be explained with a simple calculation :
// we assume that myArray.length is 5000
for (x = 0;x < myArray.length;x++){
// doing some stuff
}
"x = 0" is evaluated only one time, so it's only one operation.
"x < myArray.length" is evaluated 5000 times, so it is 10,000 operations (myArray.length is an operation and compare myArray.length with x, is another operation).
"x++" is evaluated 5000 times, so it's 5000 operations.
There is a total of 15 001 operation.
// we assume that myArray.length is 5000
for (x = 0, l = myArray.length; x < l; x++){
// doing some stuff
}
"x = 0" is evaluated only one time, so it's only one operation.
"l = myArray.length" is evaluated only one time, so it's only one operation.
"x < l" is evaluated 5000 times, so it is 5000 operations (l with x, is one operation).
"x++" is evaluated 5000 times, so it's 5000 operations.
There is a total of 10002 operation.
So, in order to optimize your for loop, you need to make code like this :
var s1 = "the original string";
var c;
var st = "";
var s2 = "";
for (i = 0, l = s1.length; i < l; i++) {
c = rot13(s1.charAt(i));
st += c;
if (c == " ") {
s2 += st;
st = "";
}
}
s2 += st;
Strict Mode
The strict mode
Strict mode can be enabled by placing '"use strict";' at the beginning of a script, before other statements:
// Dummy comment
"use strict";
var myvar = 4;
It can also be enabled for a single function only:
function myfun(){
"use strict";
var myvar = 6;
}
Strict mode ensures the following:
- New variables need to be declared with "var"; "var" is no longer optional.
- Attempts to write to non-writable variables throw an error rather than silently doing nothing.
- Attempts to delete undeletable properties throw an error rather than silently doing nothing.
- Octal numerals are disallowed.
- Etc.
Strict mode is available since JavaScript 1.8.5, i.e. ECMAScript version 5.
External links
- Strict mode, developer.mozilla.org
- JavaScript Use Strict, w3schools.com
- Strict Mode (JavaScript), msdn.microsoft.com
Shell
You can play around with JavaScript in one of multiple shells, in interactive batch mode. This means that you can enter JavaScript one line at a time and have it immediately executed; and if you enter a statement that returns a value without assigning the value anywhere, the value is displayed.
For a list of shells, see the mozilla.org list referenced from Enternal links.
Keywords: REPL.
Standalone
Mozilla Firefox uses SpiderMonkey JavaScript engine, which is available as a standalone interactive shell for multiple platforms. You can download it from:
- https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/
- Look for jsshell-*.zip
Unzip the file, and run "js" from the command line. A prompt appears:
js>
You can enter statements, one at a time:
js> function incr(i) { return i+1; } js> incr(1) 2 js> function plus2(i) { return i+2; } js> plus2(1) 3 js> incr function incr(i) { return i+1; } js> print ("1+1:"+incr(1)) 1+1:2 js> console.log("Yep.") // Console is available Yep.
Multi-line function definitions can be entered one line at a time, pressing enter after each line.
To run JavaScript snippets that use alert function--since they are intended for web browsers, you can define your own alert function:
js> function alert(message) { print ("Alert: "+message); }
From browser
You can have an interactive mode, entering JavaScript one line at a time and have it immediately executed, directly from your web browser.
In many versions of Firefox, press Control + Shift + K to get a web console window. At the bottom of the console window, there is a separate one-line field into which you can enter JavaScript lines and have them run by pressing Enter. Even multi-line function definitions can be entered, but not by pressing Enter but rather Shift + Enter and pressing Enter only after the whole definition was entered.
External links
- Introduction to the JavaScript shell, developer.mozilla.org
- JavaScript shells, developer.mozilla.org
- JavaScript Engine Speeds, ejohn.org
- JavaScript interactive shell with completion, stackoverflow.com
Introduction to the Document Object Model (DOM)
HTML pages are internally implemented by a tree that contains the HTML elements (and CSS styles) as its nodes. This tree is called Document-Object Model, or DOM. JavaScript has full access to the DOM. It can navigate through the tree, modify the tree and nodes, ranging from simply adding new nodes to rearranging several areas on the page.
A hint about the terminology: Because of its closeness to XML, HTML uses the term element; and because of its structure as a tree, DOM uses the term node.
Nodes
When loading into the browser, the HTML document is broken down into a tree of nodes. For example, take a look at the following HTML snippet:
<div id="exampleDiv">This is an<br>example HTML snippet.</div>
Through DOM, JavaScript sees this snippet as four nodes.
- The
div
, from its start tag through its end tag, is one node. Thisdiv
happens to have a property assigned inside its start tag. This property is named "id" and has the value "exampleDiv".
- The three other nodes in this example are inside the
div
. They are called child nodes of thediv
, because thediv
contains them. Conversely, thediv
is their parent node.
- The first child of the
div
is a text node, with the value "This is an". Text nodes contain only text; they never contain tags, which is why the tree stops here. - The
br
tag is another node. - The rest of the text is another text node.
Since the text nodes and the br
tag all share the same parent, they are said to be sibling nodes.
Accessing nodes
You can access nodes of the DOM tree by various methods. One of them is getElementById
.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
const p = document.getElementById("p2");
alert(p.innerHTML);
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Show the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. It accesses the element with the id 'p2' and shows its content.
Accessing content
If you want to get access to the content of a node, you can use different properties of different classes: Node.textContent
, HTMLElement.innerText
, or Element.innerHTML
. But they are not equal; please consider the differences. To keep our examples clear and easy, we use Element.innerHTML
whenever possible because it is very close to the HTML source code.
const exampleContent = document.getElementById("example").innerHTML;
Changing content
After accessing a node, you can change its content by assigning a new value to its content.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
const p = document.getElementById("p2");
p.innerHTML = "Another text";
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Change the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. Again, it accesses the element with the id 'p2' and changes its content.
Modifying the tree structure
JavaScript can manipulate the structure of the DOM tree.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
// read 'body' node
const b = document.getElementById("body");
// read second 'p' node
const p = document.getElementById("p2");
// moves it to the end of 'body'
b.appendChild(p);
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Move the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. It accesses the elements 'body' and 'p2', then, it moves the 'p' element to the end of the 'body'.
See also
Runtime document manipulation
HTML pages are internally implemented by a tree that contains the HTML elements (and CSS styles) as its nodes. This tree is called Document-Object Model, or DOM. JavaScript has full access to the DOM. It can navigate through the tree, modify the tree and nodes, ranging from simply adding new nodes to rearranging several areas on the page.
A hint about the terminology: Because of its closeness to XML, HTML uses the term element; and because of its structure as a tree, DOM uses the term node.
Nodes
When loading into the browser, the HTML document is broken down into a tree of nodes. For example, take a look at the following HTML snippet:
<div id="exampleDiv">This is an<br>example HTML snippet.</div>
Through DOM, JavaScript sees this snippet as four nodes.
- The
div
, from its start tag through its end tag, is one node. Thisdiv
happens to have a property assigned inside its start tag. This property is named "id" and has the value "exampleDiv".
- The three other nodes in this example are inside the
div
. They are called child nodes of thediv
, because thediv
contains them. Conversely, thediv
is their parent node.
- The first child of the
div
is a text node, with the value "This is an". Text nodes contain only text; they never contain tags, which is why the tree stops here. - The
br
tag is another node. - The rest of the text is another text node.
Since the text nodes and the br
tag all share the same parent, they are said to be sibling nodes.
Accessing nodes
You can access nodes of the DOM tree by various methods. One of them is getElementById
.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
const p = document.getElementById("p2");
alert(p.innerHTML);
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Show the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. It accesses the element with the id 'p2' and shows its content.
Accessing content
If you want to get access to the content of a node, you can use different properties of different classes: Node.textContent
, HTMLElement.innerText
, or Element.innerHTML
. But they are not equal; please consider the differences. To keep our examples clear and easy, we use Element.innerHTML
whenever possible because it is very close to the HTML source code.
const exampleContent = document.getElementById("example").innerHTML;
Changing content
After accessing a node, you can change its content by assigning a new value to its content.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
const p = document.getElementById("p2");
p.innerHTML = "Another text";
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Change the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. Again, it accesses the element with the id 'p2' and changes its content.
Modifying the tree structure
JavaScript can manipulate the structure of the DOM tree.
<!DOCTYPE html>
<html>
<head>
<script>
function go() {
"use strict";
// read 'body' node
const b = document.getElementById("body");
// read second 'p' node
const p = document.getElementById("p2");
// moves it to the end of 'body'
b.appendChild(p);
}
</script>
</head>
<body id="body">
<p id="p1" style="background: aqua">one</p>
<p id="p2" style="background: green">two</p>
<p id="p3" style="background: red">three</p>
<button onclick="go()">Move the second paragraph</button>
</body>
</html>
When clicking on the button, the function go
is called. It accesses the elements 'body' and 'p2', then, it moves the 'p' element to the end of the 'body'.
See also
Finding elements
To work with nodes of the DOM tree, you need to locate them directly or navigate to them beginning at a starting point. DOM's Document interface serves as an entry point into the web page's content. It offers a rich set of properties and methods to reach particular nodes. The methods return single nodes or an array of nodes.
We use the following HTML page to demonstrate the most important methods. |
<!DOCTYPE html>
<html>
<head>
<script>
function show() {
"use strict";
// ...
}
</script>
<style>
.head_2 {
display: flex;
justify-content: center;
}
.text {
padding-left: 1em;
font-size: 1.4em;
}
.button {
height:1.4em;
width: 4em;
margin-top: 1em;
font-size: 1.2em;
background-color: Aqua;
}
</style>
</head>
<body>
<h1>An HTML header</h1>
<h2 class="head_2">An HTML sub-header</h2>
<div id="div_1">
<p id="p1" class="text">Paragraph 1</p>
<p id="p2" class="text">Paragraph 2</p>
<p id="p3" class="text">Paragraph 3</p>
</div>
<div id="div_2">
<h2>Another HTML sub-header</h2>
<div id="div_3">
<p>Another paragraph 1</p>
<p>Another paragraph 2</p>
<p>Another paragraph 3</p>
</div>
</div>
<button class="button" onclick="show()">Go</button>
</body>
</html>
|
Clicking on the button
invokes the function show
. The examples should be included there.
Using ID
An easy-to-use, fast, and exact method to locate an individual element is to mark the element with the id
property in the HTML, e.g., <p id="p2">
, and use this id
as the parameter to getElementById()
. The following code snippet will locate the element and displays its content.
function show() {
"use strict";
const elem = document.getElementById("p2");
alert(elem.innerHTML);
}
The getElementById
method returns one single element (the first with this id
if the id
is not unique).
That is also true if the element is not a text node but a node with child nodes. The return value is a single element with all its child elements included.
function show() {
"use strict";
const elem = document.getElementById("div_3");
alert(elem.innerHTML);
}
// expected output:
// <p>Another paragraph 1</p>
// <p>Another paragraph 2</p>
// <p>Another paragraph 3</p>
Using tag name
Another way to find elements on an HTML page is the getElementsByTagName
method. It accepts a tag name, e.g., 'h1', 'div', or 'p', and returns all such elements in an array.
Here, we use the method to retrieve an array of all 'div' elements.
function show() {
"use strict";
// if you want to search in the complete document, you must specify 'document'
let elemArray = document.getElementsByTagName("div");
// loop over all array elements
for (let i = 0; i < elemArray.length; i++) {
alert(elemArray[i].innerHTML);
}
alert("Part 2");
// if you want to search only a sub-tree, you must previously locate
// the root of this sub-tree
const elem = document.getElementById("div_2");
elemArray = elem.getElementsByTagName("div");
for (let i = 0; i < elemArray.length; i++) {
alert(elemArray[i].innerHTML);
}
}
Using class name
Next, elements can be located by an associated CSS class selector. Class selectors can have a complex syntax. Here, we use only the simple form of class names.
The example retrieves all elements that use the CSS class text - what is done by the 3 paragraphs of the first div
. Please note, that the other paragraphs are not retrieved.
function show() {
"use strict";
let elemArray = document.getElementsByClassName("text");
// loop over all array elements
for (let i = 0; i < elemArray.length; i++) {
alert(elemArray[i].innerHTML);
}
}
Using a query selector
The shown locating methods use specialized semantics to locate elements. But there is also a general method that combines all of that - plus more.
Query selectors use a complex syntax consisting of HTML element ids, HTML element names, HTML attributes, CSS classes, positions, and more. They locate single elements or a list of elements. To retrieve the first element which satisfies the selector, use the querySelector
method. If you want to retrieve all matching elements, use querySelectorAll
.
function show() {
"use strict";
// '#' locates via ID, '.' locates via CSS class
let elemArray = document.querySelectorAll("h1, #p2, .head_2");
// loop over all array elements
for (let i = 0; i < elemArray.length; i++) {
alert(elemArray[i].innerHTML);
}
}
Navigating the DOM tree
You can navigate in the DOM tree in the direction from the root to the leaves. This is done by locating an element and using this node as the new root for the following navigation steps.
function show() {
"use strict";
// start at 'div_2'
const elem_1 = document.getElementById("div_2");
// use this element as the new root for further selections
const elemArray = elem_1.getElementsByTagName("h2");
for (let i = 0; i < elemArray.length; i++) {
alert(elemArray[i].innerHTML);
}
// only the child-'h2' is selected! The first 'h2' is ignored.
}
See also
Exercises
Adding elements
DOM's Document interface offers - among other things - functions that create new elements, including their attributes and content, and joins them together or into an existing DOM.
createElement()
creates an element. createAttribute()
creates an attribute that can be assigned to this new or an already existing element. setAttribute()
creates an attribute and links it to an existing element. appendChild()
integrate an element into another.
Creating elements
// an <p> element
const p = document.createElement("p");
// its content
p.innerHTML = "The new paragraph.";
Now, the element and its content are created. But until here, they are not part of a DOM. They exist only in the memory of the JavaScript engine.
To integrate them into the page, we retrieve the body or any other element of an existing page and append the new element as its last element.
const body = document.getElementById("body");
body.appendChild(p);
All in all, the HTML plus JavaScript looks like this:
<!DOCTYPE html>
<html>
<head>
<script>
function show() {
"use strict";
// an create an <p> element
const p = document.createElement("p");
// create its content
p.innerHTML = "The new paragraph.";
// integrate it into the body
const body = document.getElementById("body");
body.appendChild(p);
}
</script>
</head>
<body id="body" style="margin:2em">
<button id="buttonShow" onclick="show()">Start</button>
</body>
</html>
The original page does not contain a paragraph. But after you click on the button, the paragraph is integrated into the page and is visible. Btw: You can click more than once on the button. What will happen?
Creating attributes
Attributes are created with either the createAttribute()
or the setAttribute()
function. The first of the two acts like the above shown createElement()
function. It creates the new attribute only in memory without a connection to other elements. Because setAttribute()
integrates the new attribute directly into an element, we use this variant.
The example uses the a
element with its href
attribute.
// an <a> element
const anchor = document.createElement("a");
// its content
anchor.innerHTML = "The IANA example daomain.";
// its 'href' attribute
anchor.setAttribute("href", "https://www.example.com");
Now, the element, a single attribute, and the element's content are created. Again, we integrate them into the page as we have done above.
All in all, the HTML plus JavaScript looks like this:
<!DOCTYPE html>
<html>
<head>
<script>
function show() {
"use strict";
// an create an <a> element
const anchor = document.createElement("a");
// create its content
anchor.innerHTML = "The IANA example domain.";
// create its 'href' attribute
anchor.setAttribute("href", "https://www.example.com");
// see below: anchor.href = "https://www.example.com";
// integrate the element inclusive its attribute into the body
const body = document.getElementById("body");
body.appendChild(anchor);
/* now, the body looks like this:
<button id="buttonShow" onclick="show()">Start</button>
<a href="https://www.example.com">The IANA example domain.</a>
*/
}
</script>
</head>
<body id="body" style="margin:2em">
<button id="buttonShow" onclick="show()">Start</button>
</body>
</html>
The original page does not contain a link. But after you click on the button, the link to the IANA example page is integrated into the page and is usable.
Alternative syntax
One of the previous pages has explained how to change attributes with a different syntax.
element_name.attribute_name = "new value";
Just use the element plus its attribute name and assign the attribute value to it. If you change the previous example to this syntax, you will reach the same behavior of adding the link.
anchor.href = "https://www.example.com";
// instead of the above:
// anchor.setAttribute("href", "https://www.example.com");
Join the puzzles pieces
The shown functions create elements and attributes. Such new objects can be joined together to create huger parts - of course in a nested way. And they can be joined to an already existing HTML page, respectively, the DOM tree.
const div = document.getElementById("div_1");
const anchor = document.createElement("a");
div.appendChild(anchor);
'Misusing' innerHTML
The content of an element can be changed by assigning a new value to its property innerHTML
. If this new value contains the string representation of an HTML fragment, the assignment creates child nodes within the element. That's possible but not the intended way of using innerHTML
.
const elem = document.getElementById("p1");
elem.innerHTML = "New text in the paragraph.<p>next P</p><p>and even one more P</p>";
.. leads to ..
<p id="p1">New text in the paragraph
<p>next P</p>
<p>and even one more P</p>
</p>
The JavaScript fragment inserts two more paragraphs, but not behind the first one. They exist within the first one.
write()
The antiquated function document.write()
was able to insert new elements into an HTML page. Its usage is strongly discouraged nowadays.
See also
Exercises
Changing elements
On this page, we show how to change two different things of an HTML element, respectively, DOM node.
- Its content (there is only one - or none)
- Any of its attributes (there may be many)
Please take note of this distinction between content and attributes.
<!-- in general: -->
<element_name attribute_name="attribute_value">content of the element</element_name>
<!-- a concrete example. 'href' is an attribute. 'Visit IANA...' is the content. -->
<a href="https://www.example.com">Visit IANA's example domain.</a>
Example page
We use the following example HTML page to demonstrate the possibilities.
<!DOCTYPE html>
<html>
<head>
<script>
function show() {
"use strict";
// ...
}
</script>
</head>
<body id="body" style="margin:2em">
<p id="p1" style="background: aqua">A blue paragraph</p>
<svg id="svgSrc" width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="25" fill="green"/>
</svg>
<p />
<a id="refToSomewhere" href="https://www.example.com">Visit IANA's example domain.</a>
<p />
<button id="buttonShow" onclick="show()">Start</button>
</body>
</html>
Clicking on the button
invokes the function show
. The examples should be included there.
Change the content
We use the example of a paragraph p
. To change its content, the text, just assign the new value to its innerHTML
.
function show() {
"use strict";
const elem = document.getElementById("p1");
elem.innerHTML = "New text in the paragraph.";
}
Or, to do the same with a different HTML element, we change the SVG graphic.
function show() {
"use strict";
const elem = document.getElementById("svgSrc");
elem.innerHTML = "<rect width='80' height='40' fill='blue'/>";
}
Because the new text is HTML code, you can 'misuse' this approach to add child nodes.
function show() {
"use strict";
const elem = document.getElementById("p1");
elem.innerHTML = "New text in the paragraph.<p>next P</p><p>and even one more P</p>";
}
The script inserts two more paragraphs, but not behind the first one. They are within the first one.
<p id="p1">New text in the paragraph
<p>next P</p>
<p>and even one more P</p>
</p>
Change an attribute
In general, the syntax to change attributes is as follows:
element_name.attribute_name = "new value";
// or:
element_name.setAttribute("attribute_name", "new value");
The HTML element a
knows a href
attribute: <a id="refToSomewhere" href="https://www.example.com">...</a>
. This href
attribute can be changed:
function show() {
"use strict";
const elem = document.getElementById("refToSomewhere");
elem.href = "https://en.wikibooks.org";
elem.innerHTML = "Link changed";
}
First, the element is located. Second, the function assigns a new value to its attribute 'href' (and to the innerHTML).
The following example changes the src
attribute of img
element and the value
attribute of button
element
// The HTML
<img id="imgOne" src="myPicture.jpg">
<input id="buttonOne" value="I'm a button!">
// The JavaScript
document.getElementById("imgOne").src = "otherPicture.jpg";
const b = document.getElementById("buttonOne");
b.value = "I'm a changed button";
setAttribute()
The modification of attributes can also be done via the function setAttribute
.
function show() {
"use strict";
const elem = document.getElementById("refToSomewhere");
elem.setAttribute("href", "https://en.wikibooks.org");
elem.innerHTML = "Link changed";
}
See also
Exercises
Removing elements
HTML pages and DOM objects are hierarchically structured. Every element and attribute belongs to exactly one parent. To delete an element or attribute, first, you must locate the parent element. The remove operation can be done on this object.
Remove elements
Elements are removed with the removeChild
function. To delete the <p>
element from the <div>
element in the following example
<div id="parent">
<p id="child">I'm a child!</p>
</div>
the JavaScript code is ...
// get elements
const parent = document.getElementById("parent");
const child = document.getElementById("child");
// delete child
parent.removeChild(child);
... and the remaining HTML structure will be
<div id="parent"></div>
Children of children
If an element is removed, all of its children are removed as well. By this, you can remove huge parts of the DOM with one command if they have a common root. E.g., remove a complete list:
<div id="div_1">
<ul id="listOfNames">
<li>Albert</li>
<li>Betty</li>
<li>Charles</li>
</ul>
</div>
The JavaScript fragment removes the <ul>
element as well as all <li>
elements.
const parent = document.getElementById("div_1");
const child = document.getElementById("listOfNames");
parent.removeChild(child);
parentNode
To remove an element, you need to know its parent element. If you can locate only the child, but for some reason, not the parent, the child's property parentNode
shows you the way.
// get the child element
const child = document.getElementById("child");
// retrieve the parent
const parent = child.parentNode; // no parenthesis ()
// remove the child element from the document
parent.removeChild(child);
Remove attributes
Attributes are removed with the removeAttribute
function. To delete the href
attribute from the <a>
element in the following example
<a id="anchor" href="https://en.wikibooks.org">Wikibook</a>
the JavaScript code is:
// get element
const anchor = document.getElementById("anchor");
// remove attribute
anchor.removeAttribute("href");
The element itself, including the text of the link, keeps alive, but you cannot navigate anymore.
See also
Changing element styles
As you have seen in previous chapters, the attributes of an element can be modified by JavaScript. Two attributes, the class
and style
, influences the visual representation of an element. They contain CSS code.
<!DOCTYPE html>
<html>
<head>
<script>
function toggle() {
"use strict";
// ...
}
</script>
<style>
.divClassGreen {
background-color: green;
padding: 2em;
margin: 1em
}
.divClassRed {
background-color: red;
padding: 2em;
margin: 1em
}
</style>
</head>
<body id="body">
<div id="div_1" class="divClassGreen">A DIV element</div>
<div id="div_2" style="background-color: blue; padding: 2em; margin: 1em">Another DIV element</div>
<button id="buttonToggle" onclick="toggle()" style="margin:1em 0em 1em 0em">Start</button>
</body>
</html>
The class
attribute identifies a CSS class that is created in the style
element of HTML. The style
attribute defines CSS rules inline (locally).
To modify them, handle them like any other attribute. They do not have special rules or exceptions.
An example
We use the above HTML file; only the JavaScript function is changed. When the button is clicked, the function assigns the CSS class 'divClassRed' to 'div_1' and it changes the inline 'style' attribute of 'div_2' to a different value.
function toggle() {
"use strict";
// locate the elements to be changed
const div_1 = document.getElementById("div_1");
const div_2 = document.getElementById("div_2");
// modify its 'class' attribute
div_1.setAttribute("class", "divClassRed");
// an 'inline' modification
div_2.setAttribute("style", "background-color: silver; padding: 4em; margin: 2em");
//or: div_2.style = "background-color: silver; padding: 4em; margin: 2em";
}
The 'style' attribute stores the CSS properties like 'color' or 'padding' in its own properties. This correlates with the general JavaScript object rules. Therefore the following syntax is equivalent to the previous div_2.setAttribute
call.
div_2.style.backgroundColor = "silver"; // see below: camel-case
div_2.style.padding = "4em";
div_2.style.margin = "2em";
Properties of 'style'
In CSS, some properties are defined with a hyphen in their name, e.g., 'background-color' or 'font-size'. When you use them in JavaScript in the syntax of a property of style, the names change slightly. The character after the hyphen must be written in upper-case, and the hyphen disappears: 'style.backgroundColor' or 'style.fontSize'. This is called camel-case.
div_1.style.fontSize = "2em"; // the font's size as property of 'style'
/*
The next line would run into a syntax error because the hyphen
would be interpreted as a minus operation.
div_1.style.font-size = "2em";
*/
All other places where such names appear in CSS do not change. Especially the shown syntax with hyphens inside the HTML <style>
element, as well as the use in the form of an inline definition, keeps unchanged.
Exercises
See also
Event handling
Applications with a user interface - and other application types - are predominantly driven by events. Here we focus on DOM events. They originate (or fire) from specific actions or situations in a browser or a native app, e.g., a user clicks with the mouse, types a keyboard key, or works on a touch screen; a visual object is 'dragged & dropped', 'copied & pasted', or resized; the HTML page is loaded or shall be unloaded; the browser or a visual object gets or loses the focus; and much more. It's also possible that the application creates events programmatically (dispatch).
An event is related to its originating object and with a JavaScript statement; that is, in most cases, a call to a function that is denoted as the event handler. The JavaScript part is invoked after the event arises. Common actions are communication with a server, validation of user input, or modification of DOM or graphics.
Create and invoke events
Embedded in HTML
A short example shows how events are defined in an HTML page, fire, and execute. This syntax version is denoted as inline JavaScript.
<!DOCTYPE html>
<html>
<head>
<script>
function handleEvent(evt) {
"use strict";
alert("Perform some actions via a JavaScript function.");
}
</script>
</head>
<body id="body" style="margin:2em">
<!-- inline all statements; unusual -->
<button onclick="const msg='Direct invocation of small actions.'; alert(msg);">First button</button>
<!-- call a function (the event handler) -->
<button onclick="handleEvent(event)">Second button</button>
</body>
</html>
When a user clicks on one of the buttons, the browser reads the button's attribute onclick
, creates a JavaScript object that contains all properties of the event, and executes the JavaScript statement(s). Mostly, the JavaScript part is a call to a function. That function is denoted the event handler. It receives the JavaScript object as its first parameter. Only in very simple cases, the complete JavaScript script is inlined.
Of course, the called function must exist somewhere. Some standard functions like alert()
are predefined and provided by the browser. Your self-written functions exist within the HTML tag <script>
, or the tag refers to a file or URL where they exist.
Hint: Embedding event definitions directly into HTML is called inline JavaScript. It's the earliest method of registering event handlers, but it tends to make the source hard to read for non-trivial applications. Therefore it can be seen as being a less desirable technique than other unobtrusive techniques; see next chapter. The use of inline JavaScript can be considered to be similar in nature to that of using inline CSS, where HTML is styled by putting CSS in style
attributes.
Nevertheless, the Wikibook on hand will use inline JavaScript often in its demonstration pages because the syntax is short and the concept is easy to understand.
Programmatically in JavaScript
JavaScript knows two ways to register an event handler for an HTML element. First, the event handler function can be directly assigned to the element's properties onxxx
(onclick, onkeydown, onload, onfocus, ...). Their name starts with 'on' and ends with the value of the event type. Second, the function addEventListener
registers the event type and the event handler.
<!DOCTYPE html>
<html>
<head>
<script>
function register() {
"use strict";
const p1 = document.getElementById("p1");
// do it this way ...
p1.onclick = showMessage; // no parenthesis ()
// ... or this way (prefered)
//p1.addEventListener('click', showMessage);
alert("The event handler is assigned to the paragraph.");
}
function showMessage(evt) {
"use strict";
// the parameter 'evt' contains many information about
// the event, e.g., the position from where it originates
alert("A click-event to the paragraph. At position " +
evt.clientX + " / " + evt.clientY + ". Event type is: " + evt.type);
}
</script>
</head>
<body>
<h1>Register an event</h1>
<p id="p1" style="margin:2em; background-color:aqua">
A small paragraph. First without, then with an event handler.
</p>
<button onclick="register()" id="button_1">A button</button>
</body>
</html>
The onclick event handler 'register' of button 'button_1' is registered with the above inline JavaScript syntax. When the page loads, only this event handler is known. Clicks to the paragraph 'p1' don't trigger any action because it does not have any event handler so far. But when the button gets pressed, the handler of button 'button_1' registers the second event handler, the function 'showMessage'. Now, after a click on the paragraph, the alert "A click-event to the paragraph..." occurs.
The registration is done in line 10 p1.onclick = showMessage
. The noticeable difference to the inline JavaScript is that there are no parenthesizes. The inline JavaScript calls the function showMessage
and hence needs to use parenthesizes. The function register
does NOT call showMessage
. It uses only its name for the registration process.
The alternative to assigning the function to the paragraph's 'onclick' property is the use of the function addEventListener
. It acts on the element 'p1' and takes two parameters. The first one is the event type (click, keydown, ...). Such event types correlate with the onxxx names in that they miss the first two characters 'on'. The second parameter is the name of the function that acts as the event handler.
You can test the example by commenting out either line 10 or line 12. Line 12 with the addEventListener
function is the preferred version.
Event types
Different kinds of events exist depending on the kind of the originating element. The complete list of event types is incredibly huge (MDN) (W3schools). We show some important types and examples.
Name | Description |
---|---|
blur
|
An input element loses focus |
change
|
An element gets modified |
click
|
An element gets clicked |
dblclick
|
An element gets double-clicked |
error
|
An error occurred loading an element |
focus
|
An input element received focus |
keydown
|
A key was pressed when an element had focus |
keyup
|
A key was released when the element had focus |
load
|
An element was loaded |
mouseenter
|
The mouse pointer was moved into the element |
mousemove
|
The mouse pointer moves while inside the element |
mousedown
|
The mouse button was pressed on the element |
mouseup
|
The mouse button was released on the element |
mouseleave
|
The mouse pointer was moved out of the element |
reset
|
The form's reset button was clicked |
resize
|
The containing window or frame was resized |
select
|
Some text within the element was selected |
submit
|
A form is being submitted |
unload
|
The content is being unloaded (e.g., window being closed) |
The following example demonstrates some different event types.
<!DOCTYPE html>
<html>
<head>
<script>
function registerAllEvents() {
"use strict";
// register different event types
document.getElementById("p1").addEventListener("click", handleAllEvents);
document.getElementById("p2").addEventListener("dblclick", handleAllEvents);
document.getElementById("p3").addEventListener("mouseover", handleAllEvents);
document.getElementById("t1").addEventListener("keydown", handleAllEvents);
document.getElementById("t2").addEventListener("select", handleAllEvents);
document.getElementById("button_1").addEventListener("mouseover", handleAllEvents);
}
function handleAllEvents(evt) {
"use strict";
alert("An event occurred from element: " +
evt.target.id + ". Event type is: " + evt.type);
}
</script>
</head>
<body onload="registerAllEvents()" style="margin:1em">
<h1 id="h1" style="background-color:aqua">Check Events</h1>
<p id="p1" style="background-color:aqua">A small paragraph. (click)</p>
<p id="p2" style="background-color:aqua">A small paragraph. (double click)</p>
<p id="p3" style="background-color:aqua">A small paragraph. (mouse over)</p>
<p style="background-color:aqua">
<textarea id="t1" rows="1" cols="50">(key down)</textarea>
</p>
<p style="background-color:aqua">
<textarea id="t2" rows="1" cols="50">(select)</textarea>
</p>
<button id="button_1">A button (mouse over)</button>
</body>
</html>
When the page is loaded, the onload event of the body
is fired. Please notice that here the 'on' prefix is necessary because it's the inline JavaScript syntax (line 23). The called function registerAllEvents locates diverse HTML elements and registers event handlers of different types (lines 8 - 13). Often you will register different functions, but to keep things easy, we register in this example the same function handleAllEvents to all elements. This function reports the event type and the originating HTML element.
Event properties
The event is always passed to the event handler as its first parameter in the form of a JavaScript object. JavaScript objects consist of properties; properties are key/value pairs. In all cases, one of the keys is 'type'. It contains the event's type; some of its possible values are shown in the above table. Depending on the event type, a lot of other properties are available.
Here are some examples of more or less important properties.
Name | Description |
---|---|
button |
Returns which mouse button was clicked |
clientX |
Returns the horizontal coordinate of the mouse pointer within the local coordinates: scrolled-out parts don't count |
clientY |
Returns the vertical coordinate of the mouse pointer within the local coordinates: scrolled-out parts don't count |
code |
Returns a textual representation of the pressed key, e.g., "ShiftLeft" or "KeyE" |
key |
Returns the value of the pressed key, e.g., "E" |
offsetX |
Returns the horizontal coordinate of the mouse pointer within the target DOM element |
offsetY |
Returns the vertical coordinate of the mouse pointer within the target DOM element |
pageX |
Returns the horizontal coordinate of the mouse pointer within the page coordinates - including scrolled-out parts |
pageY |
Returns the vertical coordinate of the mouse pointer within the page coordinates - including scrolled-out parts |
screenX |
Returns the horizontal coordinate of the mouse pointer within the complete monitor coordinates |
screenY |
Returns the vertical coordinate of the mouse pointer within the complete monitor coordinates |
target |
Returns the element that triggered the event |
timeStamp |
Returns the number of milliseconds between element creation and event creation |
type |
Returns the type of the element that triggered the event |
x |
An alias for clientX |
y |
An alias for clientY |
An example of accessing a property is given in the following script.
<!DOCTYPE html>
<html>
<head>
<script>
function changeTitle(evt) {
"use strict";
const xPos = evt.x;
const yPos = evt.y;
document.title = [xPos, yPos];
}
</script>
</head>
<body onmousemove="changeTitle(event)" style="margin:1em; border-width: 1px; border-style: solid;">
<h1 id="h1" style="background-color:aqua">Check Events</h1>
<p id="p1" style="margin:2em; background-color:aqua">A small paragraph.</p>
<p id="p2" style="margin:2em; background-color:aqua">Another small paragraph.</p>
<button id="button_1">Button</button>
</body>
</html>
A mouse-move event is registered for the body
. Whenever the mouse moves across the body, the event is triggered. The event handler reads the x/y properties out of the JavaScript object and shows them in the title of the browser's active tab.
removeEventListener
Similar to addEventListener
the function removeEventListener
removes an event listener from an element.
Synthetic events
The system offers the above-shown rich set of event types. Additionally, you can create your own events and trigger them from your application.
First, you create a function with one parameter, the event handler. Next, you register this event handler for an element. So far, everything is the same as with predefined event types.
function register() {
"use strict";
// ...
// choose an arbitrary event type (first parameter of 'addEventListener')
// and register function on element
document.getElementById("p1").addEventListener("syntheticEvent", f);
}
// the event handler for the non-system event
function f(evt) {
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type);
}
Now you can trigger this event in any part of your application. To do so, you create a new event of precisely the chosen type and fire it with a call to dispatchEvent
.
function triggerEvent(evt) {
"use strict";
// create a new event with the appropriate type
const newEvent = new Event("syntheticEvent");
// trigger this event on element 'p1'
document.getElementById("p1").dispatchEvent(newEvent);
}
For test purposes, we bind this functionality to the button. The complete page now looks like this:
<!DOCTYPE html>
<html>
<head>
<script>
function register() {
"use strict";
document.getElementById("p1").addEventListener("click", showMessage);
document.getElementById("p2").addEventListener("click", showMessage);
document.getElementById("button_1").addEventListener("click", triggerEvent);
// choose an arbitrary event type (first parameter of 'addEventListener')
// and register function on element
document.getElementById("p1").addEventListener("syntheticEvent", f);
}
// the event handler for the non-system event
function f(evt) {
"use strict";
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type);
}
function showMessage(evt) {
"use strict";
// the parameter 'evt' contains many information about
// the event, e.g., the position from where it originates
alert("A click event to element: " + evt.target.id +
" The event type is: " + evt.type);
}
function triggerEvent(evt) {
"use strict";
// create a new event with the appropriate type
const newEvent = new Event("syntheticEvent");
// trigger this event on element 'p1'
document.getElementById("p1").dispatchEvent(newEvent);
}
</script>
</head>
<body onload="register()">
<h1>Create an event programmatically.</h1>
<p id="p1" style="margin:2em; background-color:aqua">A small paragraph.</p>
<p id="p2" style="margin:2em; background-color:aqua">Another small paragraph.</p>
<button id="button_1">Button</button>
</body>
</html>
At the beginning, the button listens to click events, and 'p1' listens to events of type 'click' as well as of type 'syntheticEvent'. When the button is clicked, his event handler 'triggerEvent' creates a new event of type 'syntheticEvent' and fires it on 'p1' (what is the primary purpose of this example). The event handler showMessage
shows a message without 'p1' being clicked. In other words: The event on 'p1' occurs without a click on 'p1'.
Possibly you need in the event handler some data from the calling function, e.g., the text of an error-message, the data of an HTTP response, ... . You can pass such data by using the CustonEvent
and its property 'detail':
const newEvent = new CustomEvent("syntheticEvent", {detail: "A short message."});
Access the data in the event handle:
function f(evt) {
"use strict";
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type + ". " + evt.detail);
}
(A)synchronous behaviour
Most events are handled synchronously, e.g., 'click', 'key', or 'mouse'. But there are a few exceptions that are handled asynchronously, e.g., 'load' [7] [8]. 'Synchronous' means that the sequence of invocations is the same as the sequence of their creations. Clicking on Button A, B, and then C leads to the invocation of A's, B's, and then C's event handler in exactly this sequence. In contrast, 'asynchronous' events can lead to the invocation of the correlated handlers in an arbitrary sequence.
You must distinguish this question, the invocation of event handlers, from the implementation of their bodies. Every implementation may act strictly sequential or may contain asynchronous calls - it depends on your intention. Typical asynchronous calls are HTTP requests or database queries. Of course, they can be part of the handler of a click event.
Exercises
See also
- MDN: Event handlers introduction
- MDN: Event reference and types
- W3Schools: Events
- W3Schools: EventListener
W3C event handlers
This page or section is an undeveloped draft or outline. You can help to develop the work, or you can ask for assistance in the project room. |
Attribute | Trigger |
---|---|
onabort | Loading of image was interrupted |
onblur | Element loses focus |
onchange | Element gets modified |
onclick | Element gets clicked |
ondblclick | Element gets double clicked |
onerror | An error occurred loading an element |
onfocus | An element received focus |
onkeydown | A key was pressed when an element has focus |
onkeypress | A keystroke was received by the element |
onkeyup | A key was released when the element has focus |
onload | An element was loaded |
onmousedown | The mouse button was pressed on the element |
onmousemove | The mouse pointer moves while inside the element |
onmouseout | The mouse pointer was moved outside the element |
onmouseover | The mouse pointer was moved onto the element |
onmouseup | The mouse button was released on the element. |
onreset | The form's reset button was clicked |
onresize | The containing window or frame was resized |
onselect | Text within the element was selected |
onsubmit | A form is being submitted |
onunload | The content is being unloaded (e.g. window being closed) |
Working with the mouse
JavaScript Console Tests
Test x,y
window.onmouseover = function(){document.title = [event.x,event.y];};
Test offsetX,offsetY
window.onmouseover = function(){document.title = [event.offsetX,event.offsetY];};
Test clientX,clientY
window.onmouseover = function(){document.title = [event.clientX,event.clientY];};
Test pageX,pageY
window.onmouseover = function(){document.title = [event.pageX,event.pageY];};
References
Further reading
- JavaScript Events
- offsetX Property
- CSS Object Model: Extensions to the mouseevent Interface
- Creating an HTML5 Canvas Painting Application
- DOM Events Portability: Not that Hard
Forms
Many users are familiar with filling out forms on a web page and then hitting the "submit" button. There are at least two ways JavaScript can improve this process:
- JavaScript can be used to check the data before it is sent to the server.
- JavaScript can pre-validate the data, to catch common errors and suggest improvements immediately while the user is filling out the form, before the user clicks the "submit" button.
- JavaScript can take text typed into a text area and pre-render it in a separate area of the page, so people can see how it will be rendered and formatted before clicking the "preview" button.[1][2][3]
- JavaScript can give a live wordcount or character count of text typed into a text area.[4][5][6][7]
- Sometimes a web site does a "online" calculation involving only a small amount of data and returns the result to the user. In this case, JavaScript can intercept the "submit" button, do the entire calculation locally in the browser. The user gets the results more or less immediately, rather than waiting for the data he typed in to be sent to the server, waiting for the server to get around to processing that data, and waiting for the data to come back from the server.
Many people recommend keeping all the content accessible to people with disabilities and to people who have JavaScript turned off. One way to do that is to start with standard HTML forms, and then add unobtrusive JavaScript to improve the user experience to people who have JavaScript turned on. The system should degrade gracefully, keeping all the content available (and validating user data, if necessary), whether or not any or all of the JavaScript runs successfully.
Further reading
- JavaScript/Working With Files mentions one use of HTML forms
- HyperText Markup Language/Forms explains how to write "plain" forms without JavaScript.
References
- ↑ "How can I display the HTML content of a Text Area within a div as HTML content and not text?"
- ↑ Andrey Fedoseev. "jQuery plugin to add realtime preview panel to text areas, similar to StackOverflow edit interface"
- ↑ Guillaume DE LA RUE. "A Markdown live editor in JS".
- ↑ Sacha Schmid. JavaScript word count
- ↑ Drew Schrauf. "JavaScript Wordcount That Works".
- ↑ "JavaScript word-count for any given DOM element"
- ↑ Jake Rocheleau. "Building a Live Text area Character Count Limit with CSS3 and jQuery"
Images
Introduction
Images can be available in the DOM (Document Object Model) of the browser in mainly to ways:
- in an img tag
- in a canvas tag
Image Tag
The img is used for images that you might want
- to scale in size,
- change the alignment (left, right, center) of the image on page,
- extract the image size (width and height) of loaded image and display the size in the text
- ...
with Javascript.
Canvas Tag
The canvas tag provides (as you might already assume by the name) options to
- modify loaded images on the pixel level (e.g. exchange a red pixel on the canvas by a blue pixel),
- mark specific areas on the image and annotate the image with
- text (e.g. add the name of the building on the image),
- geometric objects (e.g. mark a person with a circle or add an arrow in the image, ...
- merge two source images in one image with a canvas.
Workflow
Workflow with images has the following main steps:
- (S1) Assignment: to work in Javascript with an image/canvas it is necessary to have a variable in Javascript to work with the image/canvas.
- (S2) Processing: to select in Javascript the available methods to handle/process images in img or canvas tag
- (S3) Load/Save (locally): if images are modified on a canvas the result must be provided and converted, so that they result can be submitted to a server or stored locally on a client for further use
For the workflow mentioned above, we will use the image processing workflows as examples.
- get information about an image with Javacript.
- load one image into a canvas and add annotation of text or geometric elements like lines, circles and boxes to the image and export the modified image again,
- load two images and merge them together in a canvas.
As example images we will use the creative commons images of
- the Golden Gate Bridge in San Francisco and
- the Brandenburg Gate in Berlin.
Assignment
For an assignment of an image variable it is recommended to use a unique meaningful identfier (id) for the image or canvas, with which you can access the information about the inage or modify the image in the canvas later. This is a standard procedure using document.getElementById(pID) call with the respective identifier pID.
<!DOCTYPE html>
<html>
<body>
<img id="myImage" src="Golden_Gate_Bridge_as_seen_from_Battery_East.jpg" alt="Golden Gate Bridge">
<script>
var var4image = document.getElementById("myImage");
</script>
</body>
</html>
In the previous example we assume that you have a file index.html and the image Golden_Gate_Bridge_as_seen_from_Battery_East.jpg in one directory.
Adding an Image to HTML Page
The image is added to the HTML as usual with the img tag.
<img id="myImage" src="Golden_Gate_Bridge_as_seen_from_Battery_East.jpg" >
The important aspect to mention is, that all images have a unique ID in the DOM of your HTML page. So having more images in one page you have to assign a new identifier to each image.
<img id="myImage" src="Golden_Gate_Bridge_as_seen_from_Battery_East.jpg">
<img id="myImage2" src="Brandenburger_Tor_abends.jpg">
To make the Javascript code more readable and maintainable you might want to assign meaning identifiers to image. As an example we change the identifier of the second image to a meaningful name.
<img id="myImage" src="Golden_Gate_Bridge_as_seen_from_Battery_East.jpg">
<img id="imgBrandenburgGate" src="Brandenburger_Tor_abends.jpg">
A prefix img... might be used for the identifier imgBrandenburgGate that indicates the id refers to an image and not in a canvas. Now we add also a canvas to the HTML code. The canvas has a specific a specific width and size. Due to the fact that the canvas is empty we must specific the size of the canvas with width and height attribute that you can use with images as well.
<img id="myImage" src="Golden_Gate_Bridge_as_seen_from_Battery_East.jpg">
<img id="imgBrandenburgGate" src="Brandenburger_Tor_abends.jpg">
<canvas id="canvImageAnnotation" width="320" height="213"></canvas>
Assigning the Image Variable
The following Javascript code assigns the img object in the DOM of the HTML page to the variable var4image.
var var4image = document.getElementById("myImage");
Especially in more complex code you might want to use meaningful variable names in your Javascript code as well, similar to the identifiers in the DOM. We use meaningful names for the second image and the canvas.
var var4image = document.getElementById("myImage");
var imgGate = document.getElementById("imgBrandenburgGate");
var canvas4annotation = document.getElementById("canvImageAnnotation");
Error Handling for Image Assignment
Keep in mind that an image with identifier might not exist in the DOM tree and then the image variable is null. This might be handled with a getImage4Id(pID) function.
function getImage4Id(pID) {
var vImg;
if (pID) {
vImg = document.getElementById(pID);
if (vImg) {
return vImg;
} else {
console.error("Image with identifier '" + pID + "' does not exist in the DOM tree.");
}
} else {
console.error("No identifier 'pID' was provided for function call getImage4ID()");
}
Once the function is defined you can replace the document.getElementById(...) by an getImage4Id(...).
var var4image = getImage4Id("myImage");
You might want to use the function getImage4Id(...) in multiple HTML projects so we assume to aggregate all the function that we want to reuse in a Javascript file imagehandlers.js stored in a subdirectory "js/.
Processing
For the processing we assume you have a folder LearnJavacript/ on your computer with the subdirectories img/, js/ and css/.
- the subdirectory img/ contains all the images that we use in the examples for images processing,
- the subdirectory js/ contains Javascript code that we use e.g. in multiple examples for images processing,
- the subdirectory css/ contains style sheet file the define the layout of the canvas or the image that we alter we Javascript according to the learning task.
Images and Canvas Tags
For the reduction of the code length we rename and shorten the image names given from Wiki Commons for both example images about Golden Gate Bridge and the Brandenburg Gate. So move both examples to the img/ subdirectory of the folder LearnJavacript/ folder and name them
- ggbridge.jpg for the Golden Gate Bridge and
- bbgate.jpg for the Brandenburg Gate.
So we change the code fragments mentioned above for the image definition as follows
<img id="imgBridge" src="img/ggbridge.jpg" width="320" height="200" >
<img id="imgGate" src="img/bbgate.jpg" width="320" height="213" >
<canvas id="canvImageAnnotation" width="640" height="213"></canvas>
Javascript Function to merge Images
As you might assume from the image sizes, we want combine both images next to each other on the canvas "canvImageAnnotation" and have 13 pixel height empty space on the canvas below the Golden Gate Bridge. The width of the canvas "canvas1" is 640 pixel and the height was chosen as maximum of both heights of both source images.
function mergeImage() {
var canvas1 = document.getElementById("canvImageAnnotation");
var ctx=canvas1.getContext("2d");
var image1 = document.getElementById("imgBridge");
var image2 = document.getElementById("imgGate");
ctx.drawImage(image1, 0, 0, 320, 200);
ctx.drawImage(image2, 321, 0, 320, 213);
};
Button to Execute Image Merge
Now we assign the function mergeImage() to an onclick event of a button.
<button onclick="mergeImage();return false" >Merge 2 Images </button>
Load/Save
In the next step an image from the local filesystem should be loaded into the browser locally (not on a remote server - see AppLSAC). The following steps are included and applied to a HTML canvas because the user should be able to annotate the image e.g. with a red circle to mark or highlight a specific area on the image:
- Create a HTML page imagehandler.html with header and body
- create Javascript file js/imageloadsave.js in a js subfolder, that contains the used Javascript libraries,
- add file button to HTML page imagehandler.html, that creates a file dialog for the user to select and load an image from the local hard disk,
- assign an event handler to the file button, that checks if the loaded file is an image,
- resize a canvas to 90% of window width and calculate the height of the canvas according to aspect ratio of the image.
- modify the image and paint a red circle on the loaded image,
- save the image to PNG, JPG, SVG to the download folder (see AppLSAC/Save).
Cookies
Intoduction
Cookies are used to store small amount of date in the browser, e.g. used to identify the user or settings or preferences of the user,
LocalStorage
Introduction
Localstorage is an option to store JSON in localstorage of the browser. This could be used to store a language setting (e.g. from english language to german language in the user profile) and when the user revisits the page the setting of pages are restored according to previous selection of the user (e.g. the selection of the language),
Activities
- Compare the approach for the language selection of the user interface with JavaScript/Cookies that can be used for the same purpose.
- Analyze simple WebApp of Javascript BubbleBreaker by bobak7 and identify how the highscores are stored and loaded from the Local Storage.
Bookmarklets
Bookmarklets are one line scripts stored in the URL field of a bookmark. Bookmarklets have been around for a long time so they will work in older browsers.
JavaScript URI scheme
You should be familiar with URL that start with schemes like http and ftp, e.g. http://en.wikibooks.org/. There is also the JavaScript scheme, which is used to start every bookmarklet.
JavaScript:alert('Hello, World!');
Example uses
Media controls
The values in these examples can be adapted as desired.
One may replace video
with audio
where applicable, meaning where an <audio>
tag is embedded.
- Loop the video
javascript:document.getElementsByTagName("video")[0].loop=1;
javascript:document.getElementsByTagName("video")[0].loop=true; // also works
Can be switched off using 0
or false
.
- Jump to ten minutes (using multiplication)
javascript:document.getElementsByTagName("video")[0].currentTime=60*10;
- Jump forward by one minute (sixty seconds)
javascript:document.getElementsByTagName("video")[0].currentTime+=60;
- Jump back by half a minute (using division)
javascript:document.getElementsByTagName("video")[0].currentTime-=60/2;
- Get duration of a video on the page in console
javascript:document.getElementsByTagName("video")[0].duration
- Alert the duration
javascript:alert('This video is '+document.getElementsByTagName("video")[0].duration+' seconds long.')
- Alert the playback time
javascript:alert('The current position of the video is at '+document.getElementsByTagName("video")[0].currentTime+' seconds.')
- Set audio volume to 50%
javascript:document.getElementsByTagName("video")[0].volume=50/100
- Mute audio
javascript:document.getElementsByTagName("video")[0].muted=1 // "true" works as well
Unmute using 0
or false
.
- Double the playback speed
javascript:document.getElementsByTagName("video")[0].playbackRate=2
- Ask for playback speed
javascript:document.getElementsByTagName("video")[0].playbackRate= parseFloat( prompt("How fast should it play?") );
parseFloat
is necessary to prevent setting the value to zero if the dialogue window is closed without user input.
- Ask for playback position in seconds
javascript:document.getElementsByTagName("video")[0].currentTime=parseFloat( prompt("Jump to playback position in seconds:") );
- Ask for playback position in minutes
javascript:document.getElementsByTagName("video")[0].currentTime=60*parseFloat( prompt("Jump to playback position in minutes:") );
- Ask for playback position in percentage (0 to 100)
javascript:document.getElementsByTagName("video")[0].currentTime=document.getElementsByTagName("video")[0].duration/100*parseFloat( prompt("Jump to playback position in percents:") );
Using multiple lines of code
Since you cannot have line breaks in bookmarklets you must use a semicolon at the end of each code statement instead.
JavaScript:name=prompt('What is your name?'); alert('Hello, ' + name);
The JavaScript Protocol in Links
The JavaScript protocol can be used in links. This may be considered bad practice, as it prevents access for or confuses users who have disabled JavaScript. See Best Practices.
<a href="JavaScript:document.bgColor='#0000FF'">blue background</a>
Examples
A large quantity of links may be found on bookmarklets.com, which show a variety of features that can be performed within JavaScript.
XMLHttpRequest
This is a guide and reference to the XMLHttpRequest object, a key component to know in Ajax programming.
Example
Here is a brief example of a Web page that uses XMLHttpRequest. We'll get into the details and ways to work-around various things that can go wrong later.
<html><!-- example.html : public domain -->
<script language="JavaScript" type="text/JavaScript" >
function alertContents(httpRequest) {
if (httpRequest.readyState == 4) {
// Everything is good, the response is received
if ((httpRequest.status == 200) || (httpRequest.status == 0)) {
// FIXME: perhaps a better example is to *replace* some text in the page.
var htmlDoc = document.createElement('div'); // Create a new, empty DIV node.
htmlDoc.innerHTML = httpRequest.responseText; // Place the returned HTML page inside the new node.
alert("The response was: " + httpRequest.status + httpRequest.responseText);
} else {
alert('There was a problem with the request. ' + httpRequest.status + httpRequest.responseText);
}
}
}
function send_with_ajax(the_url) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
httpRequest.open("GET", the_url, true);
httpRequest.send(null);
}
</script>
<p onClick="send_with_ajax('example.html');">
Click me!
</p>
</html>
Object reference
Methods
- abort()
- Cancels the current request.
- getAllResponseHeaders()
- Returns the complete set of HTTP headers as a string.
- getResponseHeader(headerName)
- Returns the value of the specified HTTP header.
- open(method, URL)
- open(method, URL, async)
- open(method, URL, async, userName)
- open(method, URL, async, userName, password)
- Specifies the method, URL, and other optional attributes of a request.
- The method parameter can have a value of GET, POST, HEAD, PUT, DELETE, or a variety of other HTTP methods listed in the W3C specification.
- The URL parameter may be either a relative or complete URL.
- The async parameter specifies whether the request should be handled asynchronously or not – true means that script processing carries on after the send() method, without waiting for a response, and false means that the script waits for a response before continuing script processing.
- send(content)
- Sends the request. content can be a string or reference to a document.
- setRequestHeader(label, value)
- Adds a label/value pair to the HTTP header to be sent.
Properties
- onreadystatechange
- Specifies a reference to an event handler for an event that fires at every state change
- readyState
- Returns the state of the object as follows:
- 0 = uninitialized – open() has not yet been called.
- 1 = open – send() has not yet been called.
- 2 = sent – send() has been called, headers and status are available.
- 3 = receiving – Downloading, responseText holds partial data
- 4 = loaded – Finished.
- responseText
- Returns the response as a string.
- responseXML
- Returns the response as XML. This property returns an XML document object, which can be examined and parsed using W3C DOM node tree methods and properties.
- responseBody
- Returns the response as a binary encoded string. This property is not part of the native XMLHttpRequest wrapper. For this property to be available, the XHR object must be created with an ActiveX component. A JScript example:
if (typeof ActiveXObject !== "undefined") { xmlhttp = new ActiveXObject("MSXML2.XMLHTTP"); xmlhttp.open("GET", "#", false); xmlhttp.send(null); alert(xmlhttp.responseBody); } else { alert("This browser does not support Microsoft ActiveXObjects.") }
- status
- Returns the HTTP status code as a number (e.g. 404 for "Not Found" and 200 for "OK"). Some network-related status codes (e.g. 408 for "Request Timeout") cause errors to be thrown in Firefox if the status fields are accessed. If the server does not respond (properly), IE returns a WinInet Error Code (e.g 12029 for "cannot connect").
- statusText
- Returns the status as a string (e.g. "Not Found" or "OK").
Bugs and inconsistencies
Dealing with bugs and inconsistencies in XMLHttpRequest implementations:
Caching
Most of the implementations also realize HTTP caching. Internet Explorer and Firefox do, but there is a difference in how and when the cached data is revalidated. Firefox revalidates the cached response every time the page is refreshed, issuing an "If-Modified-Since" header with value set to the value of the "Last-Modified" header of the cached response.
Internet Explorer does so only if the cached response is expired (i.e., after the date of received "Expires" header). This raises some issues, since a bug exists in Internet Explorer, where the cached response is never refreshed.
It is possible to unify the caching behavior on the client. The following script illustrates an example approach:
var request = new XMLHttpRequest();
request.open("GET", url, false);
request.send(null);
if (!request.getResponseHeader("Date")) {
var cached = request;
request = new XMLHttpRequest();
var ifModifiedSince =
cached.getResponseHeader("Last-Modified") ||
new Date(0); // January 1, 1970
request.open("GET", url, false);
request.setRequestHeader("If-Modified-Since", ifModifiedSince);
request.send("");
if (request.status == 304) {
request = cached;
}
}
In Internet Explorer, if the response is returned from the cache without revalidation, the "Date" header is an empty string. The workaround is achieved by checking the "Date" response header and issuing another request if needed. In case a second request is needed, the actual HTTP request is not made twice, as the first call would not produce an actual HTTP request.
The reference to the cached request is preserved, because if the response code/status of the second call is "304 Not Modified", the response body becomes an empty string ("") and then it is needed to go back to the cached object. A way to save memory and expenses of second object creation is to preserve just the needed response data and reuse the XMLHttpRequest object.
The above script relies on the assumption that the "Date" header is always issued by the server, which should be true for most server configurations. Also, it illustrates a synchronous communication between the server and the client. In case of asynchronous communication, the check should be made during the callback.
This problem is often overcome by employing techniques preventing the caching at all. Using these techniques indiscriminately can result in poor performance and waste of network bandwidth.
If script executes operation that has side effects (e.g. adding a comment, marking message as read) which requires that request always reaches the end server, it should use POST method instead.
Workaround
Internet Explorer will also cache dynamic pages and this is a problem because the URL of the page may not change but the content will (for example a news feed). A workaround for this situation can be achieved by adding a unique time stamp or random number, or possibly both, typically using the Date object and/or Math.random().
For simple document request the query string delimiter '?' can be used, or for existing queries a final sub-query can be added after a final '&' – to append the unique query term to the existing query. The downside is that each such request will fill up the cache with useless (never reused) content that could otherwise be used for other cached content (more useful data will be purged from cache to make room for these one-time responses).
A better workaround can be achieved by adding meta tags to dynamic pages in order to make them no-cachable:
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
Reusing XMLHttpRequest Object in IE
In IE, if the open method is called after setting the onreadystatechange callback, there will be a problem when trying to reuse the XHR object. To be able to reuse the XHR object properly, use the open method first and set onreadystatechange later. This happens because IE resets the object implicitly in the open method if the status is 'completed'. For more explanation of reuse: Reusing XMLHttpRequest Object in IE. The downside to calling the open method after setting the callback is a loss of cross-browser support for readystates. See the quirksmode article.
Links
- Ajax
- Programming Ajax
- Starting A Website/Server-Side Services#How to do ajax with jQuery
- XMLHttpRequest
Further reading
Handling HTML
Handling HTML returned from AJAX is surprisingly difficult. Since XMLHttpRequest was designed to work with well-formed XML, attempting to parse an HTML page with the responseXML property almost invariably produces an XML parse error. The responseText property contains the HTML source, of course, and one can attempt to parse this with regular string functions or regular expressions. However, the XMLHttpRequest object does not provide an easy way to parse returned HTML with DOM operations.
Using the browser's HTML parser
One way to parsing HTML returned from AJAX is to add the entire page as a child of the current document in the browser. Simply create a new node and place the returned HTML text in the new node's innerHTML property, as shown below:
var htmlDoc = document.createElement('div'); // Creates a new, empty DIV node.
htmlDoc.innerHTML = ajaxResult.responseText; // Places the returned HTML page inside the new node.
The HTML document is parsed automatically, and can then be searched, navigated, and manipulated with DOM operations.
Handling JSON
Native JSON
Modern JSON Handling
Handling JSON may require adding a supporting library, which creates the global JSON object. This object is present natively only in new browsers (e.g. FF 3.5, IE8). Such a library can be found here:
//Parsing JSON:
var myObject = JSON.parse(myJSONtext)
//Creating JSON:
var myJSONText = JSON.stringify(myObject);
Old way
In old browsers you could use the following syntax, but this raises issues of security, such as XSS.
var myObject = eval("(" + myJSONtext + ")")
JSONP
Given browser restrictions on cross-domain Ajax (allowed only by configuration in some earlier browsers, by non-standard means in IE8, and with server headers in HTML5), one way to circumvent such restrictions (while still requiring some server-side script coordination) is for sites to insert an HTML script tag dynamically into their code, whereby the cross-domain script they target (typically) supplies JSON, but wrapped inside a function call (the function name being supplied according to the value of a "callback" parameter supplied by the requestor) or some other executable code.
In PHP, one might serve such JSONP in as simple a fashion as this:
<?php
if (isset($_GET['callback'])) {
header('Content-Type: application/javascript');
$our_site_data = ... // Set to an array or object containing data to be supplied for use at other sites
print $_GET['callback'] . '(' . json_encode($our_site_data) . ')';
}
?>
jQuery and other frameworks have their own means of generating JSONP requests, but we'll use the following custom code.
Note: It is important to bear in mind that the following code should not be used, if the targeted site or the data supplied by the target site, may come from a non-trustworthy source, since it is possible for such scripts to run with the privileges of the using site (e.g., to read user cookies and pass them on to another site) and thereby execute a Cross-site scripting attack.
<script>
var JSONP = function(global) { // Introduces only one global
// MIT Style license, adapted from http://devpro.it/code/209.html
function JSONP(uri, callback) {
function JSONPResponse() {
// Reduce memory by deleting callbacks when finished
try { delete JSONP[src] } catch(e) { JSONP[src] = null; }
documentElement.removeChild(script);
// Execute the user's callback with the arguments supplied by the server's JSONP call
if (typeof callback === 'string') { // Assumes only one return argument and that it is an HTML string
document.getElementById(callback).innerHTML = arguments[0];
} else {
callback.apply(this, arguments);
}
}
// Ensure a unique callback
var src = '_' + id++,
script = document.createElement("script");
// Add our callback as a property of this JSONP
// function to avoid introducing more globals
JSONP[src] = JSONPResponse;
// We include "callback", as it is typically used in
// JSONP (e.g., on Wikibooks' API) to specify the callback
documentElement.insertBefore(
script,
documentElement.lastChild
).src = uri + (uri.indexOf('?') === -1 ? '?' : '&') + "callback=JSONP." + src;
}
var id = 0, documentElement = document.documentElement;
return JSONP;
}(this);
// Get the parsed HTML of this page you are reading now
// using the Mediawiki API (See http://en.wikibooks.org/w/api.php
// for Wikibooks, but it also applies at other Mediawiki wikis) that
// allows for such cross-domain calls
JSONP('http://en.wikibooks.org/w/api.php?action=parse&format=json&page=JavaScript/Handling_JSON',
function (data) {
alert(data.parse.text['*']);
}
);
</script>
More information
- Using native JSON in Firefox
- Using native JSON in IE8
- Web Application Security Guide/XML, JSON and general API security
Handling XML
Simple function to open an XML file
This function first tries for Microsoft Internet Explorer, then for Firefox and others:
function loadXMLDoc(xmlfilename) {
var event = new Error();
// Internet Explorer
try {
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
} catch(event) {
// Firefox, Mozilla, Opera, others
try {
xmlDoc = document.implementation.createDocument("","",null);
} catch(event) {
throw(event.message);
}
}
try {
xmlDoc.async = false;
xmlDoc.load(xmlfilename);
return(xmlDoc);
} catch(event) {
throw(event.message);
}
return(null);
}
Usage
var objXML = loadXMLDoc("filename.xml");
var oNodes = objXML.getElementsByTagName("AnyTagYouWish");
Now you can do any DOM operations on oNodes.
XML modifications can't be saved in JavaScript, as this is clientside…
Working with files
With pure HTML4 and pure JavaScript, there's really only one thing you can do with the users files:
The server sends a Web page that includes a form something like this:[1]
<form action="/upload_handler" method="post">
<input type="file" />
</form>
and then, the browser allows the user to select one file, and the browser uploads it -- without any JavaScript on the client being able to see any of that data or cancel the transmission or even show a progress bar.
If you want JavaScript to know anything about the file before it is transmitted (for example, to immediately cancel the transmission of a large file rather than wait an hour for the file to be transmitted, then tell the user "File too large"; or to show a progress bar), you'll have to use something other than pure JavaScript on pure HTML4.
Some popular options are:[2][3][4][5]
- use a modern Web browser that supports the HTML5 File API.
- use Flash (perhaps a tiny flash utility like Gmail uses to draw a little progress bar)
- use a Java applet
- use an ActiveX control
References
JScript in Microsoft WSH
A Wikibookian has nominated this page for cleanup because: This page needs to be expanded You can help make it better. Please review any relevant discussion. |
A Wikibookian has nominated this page for cleanup because: This page needs to be expanded You can help make it better. Please review any relevant discussion. |
Using just a file with the ".js" extension in Windows, it's possible to run JScript code. Of course within this environment many of the commonly known HTML, DOM and DHTML elements are not available.
Instead, you have access to a different set of default tools, for system administrative tasks.
This topic is covered better at Windows Programming/Windows Script Host, from a non-JScript-specific perspective.
Index
A
B
C
D
E
F
H
I
L
M
N
O
R
S
T
U
V
Links
Featured weblinks:
- JavaScript portal at developer.mozilla.org
- JavaScript Reference at developer.mozilla.org
- JavaScript Guide at developer.mozilla.org
- Gecko DOM Reference at developer.mozilla.org
- JavaScript Reference at msdn.microsoft.com
- JavaScript Tutorial at w3schools.com
- JavaScript Reference at w3schools.com
- About: Focus on JavaScript from Stephen Chapman at javascript.about.com
- ecmascript.org
- ecma-international.org
- http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
Discussion forums, bulletin boards:
- HTML, CSS and JavaScript at coderanch.com
- JavaScript Workshop forums at jsworkshop.com
- Forum: Client-side technologies at webxpertz.net
More Web sites:
- JavaScript tutorials at webreference.com
- Videos from w:Douglas Crockford on JavaScript
- JavaScript at epanorama.net
- JavaScript Tutorials at pickatutorial.com
- JavaScript Essentials at techotopia.com - An online JavaScript book designed to provide Web developers with everything they need to know to create rich, interactive and dynamic Web pages using JavaScript.
- JavaScript Tutorials at yourhtmlsource.com
- www.quirksmode.org - over 150 useful pages for CSS and Javascript tips & cross-browser compatibility matrices.
- Wiki: I wanna Learn JavaScript at c2.com - A list of links to Web resources on JavaScript
- Unobtrusive JavaScript at onlinetools.org - a guide on how to write JavaScript so that your site degrades gracefully (i.e., if the browser does not support or has turned off JavaScript, your site is still usable).
Bookmarklets
Bookmarklets are one line scripts stored in the URL field of a bookmark. Bookmarklets have been around for a long time so they will work in older browsers.
JavaScript URI scheme
You should be familiar with URL that start with schemes like http and ftp, e.g. http://en.wikibooks.org/. There is also the JavaScript scheme, which is used to start every bookmarklet.
JavaScript:alert('Hello, World!');
Example uses
Media controls
The values in these examples can be adapted as desired.
One may replace video
with audio
where applicable, meaning where an <audio>
tag is embedded.
- Loop the video
javascript:document.getElementsByTagName("video")[0].loop=1;
javascript:document.getElementsByTagName("video")[0].loop=true; // also works
Can be switched off using 0
or false
.
- Jump to ten minutes (using multiplication)
javascript:document.getElementsByTagName("video")[0].currentTime=60*10;
- Jump forward by one minute (sixty seconds)
javascript:document.getElementsByTagName("video")[0].currentTime+=60;
- Jump back by half a minute (using division)
javascript:document.getElementsByTagName("video")[0].currentTime-=60/2;
- Get duration of a video on the page in console
javascript:document.getElementsByTagName("video")[0].duration
- Alert the duration
javascript:alert('This video is '+document.getElementsByTagName("video")[0].duration+' seconds long.')
- Alert the playback time
javascript:alert('The current position of the video is at '+document.getElementsByTagName("video")[0].currentTime+' seconds.')
- Set audio volume to 50%
javascript:document.getElementsByTagName("video")[0].volume=50/100
- Mute audio
javascript:document.getElementsByTagName("video")[0].muted=1 // "true" works as well
Unmute using 0
or false
.
- Double the playback speed
javascript:document.getElementsByTagName("video")[0].playbackRate=2
- Ask for playback speed
javascript:document.getElementsByTagName("video")[0].playbackRate= parseFloat( prompt("How fast should it play?") );
parseFloat
is necessary to prevent setting the value to zero if the dialogue window is closed without user input.
- Ask for playback position in seconds
javascript:document.getElementsByTagName("video")[0].currentTime=parseFloat( prompt("Jump to playback position in seconds:") );
- Ask for playback position in minutes
javascript:document.getElementsByTagName("video")[0].currentTime=60*parseFloat( prompt("Jump to playback position in minutes:") );
- Ask for playback position in percentage (0 to 100)
javascript:document.getElementsByTagName("video")[0].currentTime=document.getElementsByTagName("video")[0].duration/100*parseFloat( prompt("Jump to playback position in percents:") );
Using multiple lines of code
Since you cannot have line breaks in bookmarklets you must use a semicolon at the end of each code statement instead.
JavaScript:name=prompt('What is your name?'); alert('Hello, ' + name);
The JavaScript Protocol in Links
The JavaScript protocol can be used in links. This may be considered bad practice, as it prevents access for or confuses users who have disabled JavaScript. See Best Practices.
<a href="JavaScript:document.bgColor='#0000FF'">blue background</a>
Examples
A large quantity of links may be found on bookmarklets.com, which show a variety of features that can be performed within JavaScript.
Useful software tools
A list of useful tools for JS programmers.
Editors / IDEs
- Adobe Brackets: Another browser-based editor by Adobe
- Codelobster: A handy code editor with special support for all popular frameworks.
- Eclipse: The Eclipse IDE includes an editor and debugger for JS
- Notepad++: A great tool for editing any kind of code. It includes syntax highlighting for many programming languages.
- Programmers' Notepad: A general tool for programming many languages.
- Scripted: An open-source browser-based editor by Spring Source
- Sublime Text: One of the most used editors for HTML/CSS/JS editing
- Web Storm or IntelliJ IDEA: both IDEs include an editor and debugger for JS, IDEA also includes a Java development platform
Engines and other tools
- JSLint: static code analysis for JS
- jq - " 'jq' is like sed for JSON data "
- List of ECMAScript engines
History of JavaScript
JavaScript was originally developed by Brendan Eich of Netscape Communications Corporation under the name Mocha, then LiveScript, and finally renamed to JavaScript. The change of name from LiveScript to JavaScript roughly coincided with Netscape adding support for Java technology in its Netscape Navigator web browser. JavaScript was first introduced and deployed in the Netscape browser version 2.0B3 in December of 1995. When JavaScript was added to Internet Explorer, it had to be officially called "JScript", because Netscape owns the name "JavaScript". The choice of name proved to be a source of much confusion.
As of 2006, the latest version of the language is JavaScript 1.7, which corresponds to ECMA-262 Edition 3 like JavaScript 1.5, except for Array extras, Array and String generics, and pythonic generators and array comprehensions. ECMAScript, in simple terms, is a standardized version of JavaScript. The ECMA-357 standard specifies E4X (ECMAscript For XML), a language extension dealing with XML.
Versions of JavaScript
JavaScript | ||
---|---|---|
Version | Description | Support |
1.0 | Netscape 2.0 | |
1.1 | Netscape 3.0 | |
1.2 | Netscape 4.0-4.05 | |
1.3 | Netscape 4.06-4.7x | |
1.4 | Netscape server products | |
1.5 | Netscape 6.0 |
JScript | ||
---|---|---|
Version | Description | Support |
1.0 | Internet Explorer 3.0 | |
2.0 | ||
3.0 | Internet Explorer 4.0 | |
4.0 | ||
5.0 | Internet Explorer 5.0 | |
5.1 | Internet Explorer 5.01 | |
5.5 | Internet Explorer 5.5, 6.0 |
ECMAScript | ||
---|---|---|
Version | Description | Support |
v1 | ||
v2 | ||
v3 |
ECMAScript
ECMAScript, also known as ISO standard 16262, is a standardization based on JavaScript by ECMA International, first written in 1996. It defines a dynamically-typed language (derived from C) very loosely based on Java and other C-like languages. It supports some object-oriented features through prototype-based objects and pseudo-classes.
All ECMAScript code should work in all major browsers that support JavaScript, but not necessarily vice versa, as there are many proprietary extensions by Netscape, Microsoft (IE), and Opera.
The latest version is ECMA-262 Edition 3, based on version 1.5 of JavaScript, and the specification is publicly available.
Best practices
This chapter will describe current JavaScript community standards and best practices that every programmer should know.
document.write
This is deprecated. Use innerHTML
or DOM manipulation methods instead.
In XHTML, document.write
does not work, but you can achieve the same effects with DOM manipulation methods[9].
JavaScript protocol
Try to avoid links that exist solely for executing JavaScript code.
<a href="javascript:resumeFancyVersion()">See my résumé!</a>
Instead consider:
<a href="resumePlainVersion.html" onclick="return !resumeFancyVersion()">See my résumé!</a>
Users with JavaScript enabled will be provided with the JavaScript-embellished version of content (perhaps using DHTML), whereas those without will be redirected to a XHTML page that provides it statically. This is more accessible than having an <a> element lacking a normal href attribute value. First, it uses proper language semantics. Second, it provides access to your content for users without JavaScript. Third, it detects whether the function execution succeeded and redirects JS-enabled readers too in case of a failure.
The onclick expression evaluates to a Boolean value. If the function succeeds to perform the desired effect and returns true, the onclick will return a failure and hyperlink not execute. When the function fails for whatever reason, the false, null or undefined value will evaluate to true and not prevent the link from being executed. Alternatively, if you do not wish to provide a static equivalent, you may embed the onclick attribute within an element with less demanding semantics:
<strong onclick="resumeFancyVersion()">See my résumé!</strong>
Thus no user agent will be confused upon reading a reduced <a> element.
Email validation
Many people use JavaScript functions to immediately catch the most common sorts of errors in form entry. For example, some web forms ask people to enter the same thing twice. Other web forms ask people to enter an email address. Then they do a quick check to see if what was entered looks at least vaguely like an email address:
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
Unfortunately, some other "email validation" JavaScript functions reject perfectly valid email addresses. For example, some incorrectly reject valid addresses that include "+" signs.
The original email address syntax (RFC 821) did allow "+" signs. RFC 822, published in the same month (August 1982), also allowed them. The current version of the syntax is given in RFC2821/RFC2822. Section 3 of RFC3696 gives useful examples of unusual valid email addresses.
After validation, many JavaScript programmers encode the email address
using encodeURIComponent()
to work-around certain client-side languages that can't seem to handle plus signs properly.[1][2]
The complexity of the quoting rules used for email addresses makes it impractical to test the local-part of an address or a domain literal completely. Given that there isn't necessarily a real mailbox corresponding to a valid local-part how much extra download time is worth spending on a complex validation script?
Examples valid according to RFC2822
me@example.com
a.nonymous@example.com
name+tag@example.com
a.name+tag@example.com
me.example@com
"spaces must be quoted"@example.com
!#$%&'*+-/=.?^_`{|}~@[1.0.0.127]
!#$%&'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]
me(this is a comment)@example.com
– comments are discouraged but not prohibited by RFC2822.
Examples invalid according to RFC2822s
me@
@example.com
me.@example.com
.me@example.com
me@example..com
me\@example.com
spaces\ must\ be\ within\ quotes\ even\ when\ escaped@example.com
a\@mustbeinquotes@example.com
Test page
COMMENT: This code has been incorrectly designed to reject certain emails that are actually valid. If changes to valid and invalid emails are accepted, the following code should also be reviewed.
The following test page can be used to test an email validation function. Save the three files in the same directory and then open validEmail.htm in a web browser.
validEmail.js
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
function testIsValidEmail(string) {
alert("'" + string + "' is " + (isValidEmail(string) ? "" : "NOT ") + " a valid email address.");
}
function checkSamples() {
var validAddress = [
'me@example.com',
'a.nonymous@example.com',
'name+tag@example.com',
'name\\@tag@example.com',
'spaces\\ are\\ allowed@example.com',
'"spaces may be quoted"@example.com',
'!#$%&\'*+-/=.?^_`{|}~@[1.0.0.127]',
'!#$%&\'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]',
'me(this is a comment)@example.com'
];
var invalidAddress = [
'me@',
'@example.com',
'me.@example.com',
'.me@example.com',
'me@example..com',
'me.example@com',
'me\\@example.com'
];
var results = new StringBuffer();
var handlesValidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function accept all the valid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < validAddress.length; i++) {
results.append('<tr><td>');
results.append(validAddress[i]);
results.append('</td><td>');
if (isValidEmail(validAddress[i])) {
results.append('<span class="good">true</span>');
} else {
handlesValidAddressesCorrectly = false;
results.append('<strong class="fail">false</strong>');
}
results.append('</td></tr>');
}
results.append('</table>');
var handlesInvalidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function reject all the invalid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < invalidAddress.length; i++) {
results.append('<tr><td>');
results.append(invalidAddress[i]);
results.append('</td><td>');
if (!isValidEmail(invalidAddress[i])) {
results.append('<span class="good">false</span>');
} else {
handlesInvalidAddressesCorrectly = false;
results.append('<em class="warning">true</em>');
}
results.append('</td></tr>');
}
results.append('</table>');
var conclusion;
if (handlesValidAddressesCorrectly) {
if (handlesInvalidAddressesCorrectly) {
conclusion = '<p><strong class="good">The function works correctly with all the sample addresses.</strong></p>';
} else {
conclusion = '<p><em class="warning">The function incorrectly accepts some invalid addresses.</em></p>';
}
} else {
conclusion = '<p><strong class="fail">The function incorrectly rejects some valid addresses.</strong></p>';
}
document.getElementById('testResults').innerHTML = conclusion + results.toString();
}
function StringBuffer() {
this.buffer = "";
}
StringBuffer.prototype.append = function(string) {
this.buffer += string;
return this;
};
StringBuffer.prototype.toString = function() {
return this.buffer;
};
validEmail.css
body {
background-color: #fff;
color: #000
}
table {
margin-bottom: 2em
}
caption {
margin-top: 2em
}
.good {
background-color: #0f0;
color: #000
}
.warning {
background-color: #fc9;
color: #000
}
.fail {
background-color: #f00;
color: #fff
}
validEmail.htm
<!DOCTYPE html>
<html lang="en">
<head>
<title>Valid email test</title>
<link rel="stylesheet" href="validEmail.css">
<script src="validEmail.js"></script>
</head>
<body onload="checkSamples()">
<h1>Unit test for email address validation functions</h1>
<h2>Interactive test</h2>
<form action="#">
<fieldset>
<legend>Email address</legend>
<label for="emailAddress">Email</label>
<input type="text" size="40" value="" name="email" id="emailAddress">
<input type="button" name="validate" value="Check address"
onclick="testIsValidEmail(this.form.email.value)">
</fieldset>
</form>
<h2>Selected samples</h2>
<p>This section shows the results of testing the function with sample strings.
The sample includes both valid strings and invalid strings
according to <a href="http://www.faqs.org/rfcs/rfc2822.html">RFC2822</a>.
</p>
<div id="testResults">You need to enable JavaScript for this unit test to work.</div>
</body>
</html>
use strict
Many JavaScript programmers recommend enabling ECMAScript 5's strict mode by putting the exact statement "use strict";
before any other statements:[3][4][5]
"use strict";
function …
For further reading
JavaScript best practices:
- Coding Cookbook/Validate Email Address
- "Six JavaScript features we do not need any longer" by Christian Heilmann.
- source code for Apple's recommended JavaScript validation functions: checkEmail(), etc.
- JavaScript form validation - doing it right
- "FORM submission and the ENTER key?" discusses forms that submit when you press Enter; forms that don't submit when you press Enter; and how to make a form work the other way.
- "JavaScript Best Practices" by Matt Kruse
- "Comparing E-mail Address Validating Regular Expressions" has a list of valid and invalid email addresses and a variety of regular expressions and how well each one works on that list.
Best practices in other languages
References
- ↑ Jan Wolter. "JavaScript Madness: Query String Parsing" 2011.
- ↑ PHP bug #39078: Plus sign in URL arg received as space.
- ↑ Nicholas C. Zakas. "It’s time to start using JavaScript strict mode". 2012.
- ↑ "What does “use strict” do in JavaScript, and what is the reasoning behind it?"
- ↑ Mozilla Developer Network: "Strict mode".
Code structuring
Generators
The term generator denotes a technique to generate a sequence of 'anything': numbers, strings, rows of database queries, elements of an array, nodes of a DOM tree, ...
It's used to avoid any memory overloading, by splitting the data in small chunks.
To use them, you must define a generator function[1] first. Such functions are notated with an asterisk *
directly behind the keyword function
, e.g., function* myGenerator(){...}
.
When called, the function will not run instantly. It only runs just before the first occurrence of a yield
statement and returns a 'Generator object'. This 'Generator object' offers a next
method. Calling next
again and again returns the sequence elements one after the next. The elements arise by each yield
that is reached within the generator function.
Examples
- The script generates a sequence of 4 integers.
function* fourInts() {
let int = 0;
while (int < 4) {
yield int; // each .next() receives the current value of 'int'
int++;
}
}
const gen = fourInts(); // creation
alert(gen.next().value); // 0
alert(gen.next().value); // 1
alert(gen.next().value); // 2
alert(gen.next().value); // 3
alert(gen.next().value); // undefined
- Every
next()
call returns not only avalue
; there is also a booleandone
. Hence you can call the generator in loops.
function* fourInts() {
let int = 0;
while (int < 4) {
yield int;
int++;
}
}
const gen = fourInts(); // creation
do {
const tmp = gen.next();
if (tmp.done) {
break;
} else {
alert(tmp.value); // 0, 1, 2, 3
}
} while (true)
- Generation out of an array, a database request, or a tree traversal.
function* arrayElements() {
// for simplicity, we use an array; database queries or tree traversals
// are more realistic.
const myArray = ["yellow", "green", "blue"];
for (const elem of myArray) {
yield elem;
}
}
const sequence = arrayElements(); // creation
do {
const tmp = sequence.next();
if (tmp.done) {
break;
} else {
alert(tmp.value); // "yellow", "green", "blue"
}
} while (true)
- Creation of the infinite sequence of all even numbers.
function* evenNumbers() {
for (let i = 0; true; i = i + 2) {
yield i;
}
}
const sequence = evenNumbers(); // creation
let i = 0;
while (i < 20) {
i = sequence.next().value;
alert(i); // 0, 2, 4, ...
}
Parameters
The generator function may receive parameters. In this example, the 'pageSize' defines the number of array elements to be returned.
function* pagination(arr, pageSize) {
for (let i = 0; i < arr.length; i = i + pageSize) {
yield arr.slice(i, i + pageSize);
}
}
const arr = [1, 2, 3, 4, 5, 6, 7]
const page = pagination(arr, 3);
alert (page.next().value); // { value: [1, 2, 3], done: false }
alert (page.next().value); // { value: [4, 5, 6], done: false }
alert (page.next().value); // { value: [7], done: false }
alert (page.next().value); // { value: undefined, done: true }
References
Contributors
Those who are going to contribute please sign your name by adding four tildas (~~~~).
- cuckooman4 04:26, 20 December 2007 (UTC) -- Might make some contributions
- Cparker (talk) 18:26, 17 March 2008 (UTC) --
- Extremecircuitz 20:55, 29 September 2007 (UTC) -- Adding a page here and there
- Jaymac407 (talk) 20:35, 21 July 2008 (UTC) -- Hopefully going to do extensive changes.
- Jesdisciple (talk) 08:37, 11 May 2010 (UTC) -- Looking into a restructuring of the chapter order to be more newbie-friendly.
- Pmw57 (talk) 10:28, 21 February 2009 (UTC) -- Am performing extensive updates
- Pranakhan 16:39, 6 November 2007 (UTC) -- Slowly reworking and adding content to this book
- Sae1962 (discuss • contribs) 06:06, 10 March 2015 (UTC) -- Reworking & extending content of this book