Java Script
Java Script
1. An Introduction to JavaScript
Origin: Created to make web pages interactive, originally named "LiveScript," later renamed
JavaScript.
Execution: Runs scripts directly within HTML, no special preparation or compilation needed.
Independence: Evolved into an independent language with its own ECMAScript specification.
Engines: Runs in browsers through engines like V8 (Chrome, Opera, Edge) and SpiderMonkey
(Firefox).
Capabilities:
Manipulates web content (HTML/CSS).
Handles user interactions (clicks, key presses).
Communicates with servers via AJAX and COMET.
Stores data locally (local storage).
Limitations:
Restricted file system access for security.
Limited cross-origin communication (Same Origin Policy).
Uniqueness:
Full integration with HTML/CSS.
Simple syntax for basic tasks.
Supported by all major browsers by default.
Trans-piled Languages: Languages like TypeScript, Dart, and Coffee-Script extend JavaScript’s
functionality by adding features like strict data typing or syntactic sugar.
2. JavaScript Fundamentals
We can use a <script> tag to add JavaScript code to a page.
The type and language attributes are not required.
A script in an external file can be inserted with <script src="path/to/script.js"></script>.
We must choose either an external <script src="…"> or a regular <script> with code.
JavaScript programs can be inserted almost anywhere into an HTML document using
the <script> tag.
To attach several scripts, use multiple tags:
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
JavaScript interprets the line break as an “implicit” semicolon. This is called an automatic
semicolon insertion.
In most cases, a newline implies a semicolon. But “in most cases” does not mean “always”!
There are cases when a newline does not mean a semicolon. For example:
alert (3 +
1
+ 2);
The directive looks like a string: "use strict" or 'use strict'. When it is located at the top of a
script, the whole script works the “modern” way, please make sure that "use strict" is at the
top of your scripts, otherwise strict mode may not be enabled.
A Variable is a “named storage” for data
Capital-named constants are only used as aliases for “hard-coded” values.
Data types- There are eight basic data types in JavaScript, we can put any type in a variable.
For example, a variable can at one moment be a string and then store a number:
Programming languages that allow such things, such as JavaScript, are called “dynamically
typed” meaning that there exist data types, but variables are not bound to any of them.
if there’s a NaN somewhere in a mathematical expression, it propagates to the whole result
(there’s only one exception to that: NaN ** 0 is 1).
The meaning of undefined is “value is not assigned”.
There are 8 basic data types in JavaScript.
Seven primitive data types:
number for numbers of any kind: integer or floating-point, integers are limited by ±(253-1).
bigint for integer numbers of arbitrary length.
string for strings. A string may have zero or more characters, there’s no separate single-
character type.
boolean for true/false.
null for unknown values – a standalone type that has a single value null.
undefined for unassigned values – a standalone type that has a single value undefined.
symbol for unique identifiers.
And one non-primitive data type:
object for more complex data structures.
The typeof operator allows us to see which type is stored in a variable.
Usually used as typeof x, but typeof(x) is also possible, typeof is an operator, not a function.
The parentheses here aren’t a part of typeof. It’s the kind of parentheses used for
mathematical grouping.
alert
shows a message.
prompt
shows a message asking the user to input text. It returns the text or, if Cancel button or Esc is
clicked, null.
confirm
shows a message and waits for the user to press “OK” or “Cancel”. It returns true for OK and
false for Cancel/Esc.
All these methods are modal: they pause script execution and don’t allow the visitor to
interact with the rest of the page until the window has been dismissed.
Exponentiation **
The exponentiation operator a ** b raises a to the power of b.
alert( 2 ** 2 ); // 2² = 4
alert( 2 ** 3 ); // 2³ = 8
alert( 2 ** 4 ); // 2⁴ = 16
Just like in maths, the exponentiation operator is defined for non-integer numbers as well.
For example, a square root is an exponentiation by ½:
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root)
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
if the binary + is applied to strings, it merges (concatenates) them:
let s = "my" + "string";
alert(s); // mystring
Note that if any of the operands is a string, then the other one is converted to a string too.
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
See, it doesn’t matter whether the first operand is a string or the second one.
Here’s a more complex example:
alert(2 + 2 + '1' ); // "41" and not "221"
Here, operators work one after another. The first + sums two numbers, so it returns 4, then
the next + adds the string 1 to it, so it’s like 4 + '1' = '41'.
alert('1' + 2 + 2); // "122" and not "14"
Here, the first operand is a string, the compiler treats the other two operands as strings too.
The 2 gets concatenated to '1', so it’s like '1' + 2 = "12" and "12" + 2 = "122".
The binary + is the only operator that supports strings in such a way. Other arithmetic
operators work only with numbers and always convert their operands to numbers.
Here’s the demo for subtraction and division:
alert( 6 - '2' ); // 4, converts '2' to a number
alert( '6' / '2' ); // 3, converts both operands to numbers
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
The addition with a string "" + 1 converts 1 to a string: "" + 1 = "1", and then we have "1" + 0,
the same rule is applied.
The subtraction - (like most math operations) only works with numbers, it converts an empty
string "" to 0.
The addition with a string appends the number 5 to the string.
The subtraction always converts to numbers, so it makes " -9 " a number -9 (ignoring spaces
around it).
null becomes 0 after the numeric conversion.
undefined becomes NaN after the numeric conversion.
Space characters are trimmed off string start and end when a string is converted to a
number. Here the whole string consists of space characters, such as \t, \n and a “regular”
space between them. So, similarly to an empty string, it becomes 0.
Several variants of case which share the same code can be grouped.
For example, if we want the same code to run for case 3 and case 5:
let a = 3;
switch (a) {
case 4:
alert('Right!');
break;
case 3: // (*) grouped two cases
case 5:
alert('Wrong!');
alert("Why don't you take a math class?");
break;
default:
alert('The result is strange. Really.');
}
Now both 3 and 5 show the same message.
The ability to “group” cases is a side effect of how switch/case works without break. Here the
execution of case 3 starts from the line (*) and goes through case 5, because there’s no
break.
Type matters
Let’s emphasize that the equality check is always strict. The values must be of the same type
to match.
For example, let’s consider the code:
let arg = prompt("Enter a value?");
switch (arg) {
case '0':
case '1':
alert( 'One or zero' );
break;
case '2':
alert( 'Two' );
break;
case 3:
alert( 'Never executes!' );
break;
default:
alert( 'An unknown value' );
}
For 0, 1, the first alert runs.
For 2 the second alert runs.
But for 3, the result of the prompt is a string "3", which is not strictly equal === to the
number 3. So we’ve got a dead code in case 3! The default variant will execute.
if (i > 5) {
alert(i);
} else {
continue;
}
…and rewrite it using a question mark
(i > 5) ? alert(i) : continue; // continue isn't allowed
here
Functions
The function has full access to the outer variable. It can modify it as well.
An argument is the value that is passed to the function when it is called (it’s a call time term).
The default value also jumps in if the parameter exists, but strictly equals undefined, like this:
showMessage("Ann", undefined); // Ann: no text given
Function expressions
Functions are values. They can be assigned, copied or declared in any place of the code.
If the function is declared as a separate statement in the main code flow, that’s called a
“Function Declaration”.
If the function is created as a part of an expression, it’s called a “Function Expression”.
As the function creation happens in the context of the assignment expression, this is a
Function Expression.
Function Declarations are processed before the code block is executed. They are visible
everywhere in the block.
Function Expressions are created when the execution flow reaches them.
In most cases when we need to declare a function, a Function Declaration is preferable,
because it is visible prior to the declaration itself. That gives us more flexibility in code
organization, and is usually more readable.
So we should use a Function Expression only when a Function Declaration is not fit for the
task.
Why is there a semicolon at the end?
You might wonder, why do Function Expressions have a semicolon ; at the end, but Function
Declarations do not:
function sayHi() {
// ...
}
let sayHi = function() {
// ...
};
The answer is simple: a Function Expression is created here as function(…) {…} inside the
assignment statement: let sayHi = …;. The semicolon ; is recommended at the end of the
statement, it’s not a part of the function syntax.
The semicolon would be there for a simpler assignment, such as let sayHi = 5;, and it’s also
there for a function assignment.
Code Quality
We can also pause the code by using the debugger command in it, like this:
function hello(name) {
let phrase = `Hello, ${name}!`;
debugger; // <-- the debugger stops here
say(phrase);
}
Such command works only when the development tools are open, otherwise the browser
ignores it.
A transpiler is a special piece of software that translates source code to another source code.
It can parse (“read and understand”) modern code and rewrite it using older syntax
constructs, so that it’ll also work in outdated engines.
A script that updates/adds new functions is called “polyfill”. It “fills in” the gap and adds
missing implementations.
A breakpoint is a point of code where the debugger will automatically pause the JavaScript
execution.
Watch – shows current values for any expressions.
Call Stack – shows the nested calls chain.
Scope – current variables.
“Resume”: continue the execution, hotkey F8.
“Step”: run the next command, hotkey F9.
“Step over”: run the next command, but don’t go into a function, hotkey F10.
“Step into”, hotkey F11.
“Step out”: continue the execution till the end of the current function, hotkey Shift+F11.
// get
alert(user["likes birds"]); // true
// delete
delete user["likes birds"];
Square brackets also provide a way to obtain the property name as the result of any
expression – as opposed to a literal string – like from a variable as follows:
let key = "likes birds";
// same as user["likes birds"] = true;
user[key] = true;
Here, the variable key may be calculated at run-time or depend on the user input. And then
we use it to access the property. That gives us a great deal of flexibility.
For instance:
let user = {
name: "John",
age: 30
};
let key = prompt("What do you want to know about the user?", "name");
// access by variable
alert( user[key] ); // John (if enter "name")
The dot notation cannot be used in a similar way:
let user = {
name: "John",
age: 30
};
let key = "name";
alert( user.key ) // undefined
We can use more complex expressions inside square brackets:
let fruit = 'apple';
let bag = {
[fruit + 'Computers']: 5 // bag.appleComputers = 5
};
We can use both normal properties and shorthands in the same object:
let user = {
name, // same as name:name
age: 30
};
a variable cannot have a name equal to one of the language-reserved words like “for”, “let”,
“return” etc.
But for an object property, there’s no such restriction:
operator "in"
for (key in object) { // executes the body for each key among object properties}
let user = {
name: "John",
age: 30,
isAdmin: true
};
One of the fundamental differences of objects versus primitives is that objects are stored and
copied “by reference”, whereas primitive values: strings, numbers, booleans, etc – are always
copied “as a whole value”.
A variable assigned to an object stores not the object itself, but its “address in memory” –
in other words “a reference” to it
When an object variable is copied, the reference is copied, but the object itself is not
duplicated.
Two objects are equal only if they are the same object.
An important side effect of storing objects as references is that an object declared
as const can be modified.
The value of user is constant, it must always reference the same object, but properties of
that object are free to change.
In other words, the const user gives an error only if we try to set user=... as a whole.
So, copying an object variable creates one more reference to the same object.
structuredClone
The call structuredClone(object) clones the object with all nested properties.
The structuredClone method can clone most data types, such as objects, arrays, primitive
values.
It also supports circular references, when an object property references the object itself
(directly or via a chain or references).
let user = {};
// let's create a circular reference:
// user.me references the user itself
user.me = user;
let clone = structuredClone(user);
alert(clone.me === clone); // true
Although, there are cases when structuredClone fails.
For instance, when an object has a function property:
structuredClone({ // error
f: function() {}
});
Function properties aren’t supported.
To handle such complex cases we may need to use a combination of cloning methods, write
custom code or, to not reinvent the wheel, take an existing implementation, for instance
_.cloneDeep(obj) from the JavaScript library lodash.
To make a “real copy” (a clone) we can use Object.assign for the so-called “shallow copy” (nested
objects are copied by reference) or a “deep cloning” function structuredClone or use a custom
cloning implementation, such as _.cloneDeep(obj).
Reachability
Internal algorithms
Optional chaining
The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and
returns undefined.
The optional chaining ?. syntax has three forms:
obj?.prop – returns obj.prop if obj exists, otherwise undefined.
obj?.[prop] – returns obj[prop] if obj exists, otherwise undefined.
obj.method?.() – calls obj.method() if obj.method exists, otherwise returns undefined.
We can use ?. for safe reading and deleting, but not writing
The optional chaining ?. has no use on the left side of an assignment.
let user = null;
user?.name = "John"; // Error, doesn't work // because it evaluates to: undefined = "John"
“this” in methods
It’s common that an object method needs to access the information stored in the object to
do its job. For instance, the code inside user.sayHi() may need the name of the user.
To access the object, a method can use the this keyword.
The value of this is the object “before dot”, the one used to call the method.
Here during the execution of user.sayHi(), the value of this will be user.
It’s also possible to access the object without this, by referencing it via the outer variable:
…But such code is unreliable. If we decide to copy user to another variable, e.g. admin =
user and overwrite user with something else, then it will access the wrong object.
If we used this.name instead of user.name inside the alert, then the code would work.
“this” is not bound - In JavaScript, keyword this behaves unlike most other programming
languages. It can be used in any function, even if it’s not a method of an object.
Functions that are stored in object properties are called “methods”.
Methods allow objects to “act” like object.doSomething().
Methods can reference the object as this.
The value of this is defined at run-time.
When a function is declared, it may use this, but that this has no value until the function is
called. A function can be copied between objects.
The rule is simple: if obj.f() is called, then this is obj during the call of f.
In JavaScript this is “free”, its value is evaluated at call-time and does not depend on where
the method was declared, but rather on what object is “before the dot”.
We can even call the function without an object at all:
function sayHi() {
alert(this);
}
sayHi(); // undefined
In this case this is undefined in strict mode. If we try to access this.name, there will be an
error. In non-strict mode the value of this in such case will be the global object (window in a
browser)
When a function is called in the “method” syntax: object.method(), the value of this during
the call is object.
Please note that arrow functions are special: they have no this. When this is accessed inside
an arrow function, it is taken from outside. That’s a special feature of arrow functions, it’s
useful when we actually do not want to have a separate this, but rather to take it from the
outer context.
When a function is called in the “method” syntax: object.method(), the value of this during
the call is objects
this is context-dependent: The value of this depends on how a function is called.
Object literals do not change this: Defining an object inside a function does not change the
value of this.
Method vs. Function Call:
When a function is called as a method (object.method()), this is set to the object.
When a function is called as a regular function (functionName()), this is either undefined (in
strict mode) or the global object.
For making the code chainable, we should return object. return this
Constructor Operator
Constructor functions technically are regular functions. There are two conventions though:
They are named with capital letter first.
They should be executed only with "new" operator.
When a function is executed with new, it does the following steps:
A new empty object is created and assigned to this.
The function body executes. Usually it modifies this, adds new properties to it.
The value of this is returned.
Main purpose of constructors – to implement reusable object creation code.
Let’s note once again – technically, any function (except arrow functions, as they don’t have
this) can be used as a constructor. It can be run with new, and it will execute the algorithm
above. The “capital letter first” is a common agreement, to make it clear that a function is to
be run with new.
new function() { … }
Constructor functions or, briefly, constructors, are regular functions, but there’s a common
agreement to name them with capital letter first.
Constructor functions should only be called using new. Such a call implies a creation of
empty this at the start and returning the populated one at the end.
we can omit parentheses after new: new User() is same as new User
// create a function and immediately call it with new
let user = new function() {
this.name = "John";
this.isAdmin = false;
// ...other code for user creation
// maybe complex logic and statements
// local variables etc
};
Symbol Type
Only two primitive types may serve as object property keys: string type, or symbol type.
Otherwise, if one uses another type, such as number, it’s auto converted to string.
A “symbol” represents a unique identifier.
A value of this type can be created using Symbol(): let id = Symbol();
Upon creation, we can give symbols a description (also called a symbol name), mostly useful
for debugging purposes:
// id is a symbol with the description "id"
let id = Symbol("id");
Symbols are guaranteed to be unique. Even if we create many symbols with exactly the same
description, they are different values. The description is just a label that doesn’t affect
anything. A symbol is a “primitive unique value” with an optional description.
Most values in JavaScript support implicit conversion to a string. For instance, we can alert
almost any value, and it will work. Symbols are special. They don’t auto-convert.
If we really want to show a symbol, we need to explicitly call .toString() on it, like here:
let id = Symbol("id"); alert(id.toString());
Symbols allow us to create “hidden” properties of an object, that no other part of code can
accidentally access or overwrite. For instance, if we’re working with user objects, that belong
to a third-party code. We’d like to add identifiers to them.
let user = { // belongs to another code
name: "John"
};
let id = Symbol("id");
user[id] = 1;
alert( user[id] );
What’s the benefit of using Symbol("id") over a string "id"?
As user objects belong to another codebase, it’s unsafe to add fields to them, since we might
affect pre-defined behavior in that other codebase. However, symbols cannot be accessed
accidentally. The third-party code won’t be aware of newly defined symbols, so it’s safe to
add symbols to the user objects.
Also, imagine that another script wants to have its own identifier inside user, for its own
purposes.
Then that script can create its own Symbol("id"), like this:
// ...
let id = Symbol("id");
user[id] = "Their id value";
There will be no conflict between our and their identifiers, because symbols are always
different, even if they have the same name.
…But if we used a string "id" instead of a symbol for the same purpose, then there would be
a conflict:
If we want to use a symbol in an object literal {...}, we need square brackets around it.
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // not "id": 123
}; // That’s because we need the value from the variable id as the key, not the string “id”.
Symbolic properties do not participate in for..in loop.
Object.keys(user) also ignores them. That’s a part of the general “hiding symbolic
properties” principle. If another script or a library loops over our object, it won’t
unexpectedly access a symbolic property. In contrast, Object.assign copies both string and
symbol properties:
Data Types
Method of Primitives
Objects are “heavier” than primitives. They require additional resources to support the
internal machinery.
There are many things one would want to do with a primitive, like a string or a number. It
would be great to access them using methods.
Primitives must be as fast and lightweight as possible.
The solution looks a little bit awkward, but here it is:
Primitives are still primitive. A single value, as desired.
The language allows access to methods and properties of strings, numbers, booleans and
symbols.
In order for that to work, a special “object wrapper” that provides the extra functionality is
created, and then is destroyed.
The “object wrappers” are different for each primitive type and are called: String, Number,
Boolean, Symbol and BigInt. Thus, they provide different sets of methods.
Here’s what actually happens in str.toUpperCase():
The string str is a primitive. So in the moment of accessing its property, a special object is
created that knows the value of the string, and has useful methods, like toUpperCase().
That method runs and returns a new string (shown by alert).
The special object is destroyed, leaving the primitive str alone.
alert( typeof 0 ); // "number"
alert( typeof new Number(0) ); // "object"!
Objects are always truthy in if, so here the alert will show up:
let zero = new Number(0);
if (zero) { // zero is true, because it's an object
alert( "zero is truthy!?!" );
}
On the other hand, using the same functions String/Number/Boolean without new is totally
fine and useful thing. They convert a value to the corresponding type: to a string, a number,
or a boolean (primitive).
For example, this is entirely valid:
let num = Number("123"); // convert a string to number
null/undefined have no methods
The special primitives null and undefined are exceptions. They have no corresponding
“wrapper objects” and provide no methods. In a sense, they are “the most primitive”.
An attempt to access a property of such value would give the error:
alert(null.test); // error
Try running it:
let str = "Hello";
str.test = 5; // (*)
alert(str.test);
Depending on whether you have use strict or not, the result may be:
undefined (no strict mode)
An error (strict mode).
Why? Let’s replay what’s happening at line (*):
When a property of str is accessed, a “wrapper object” is created.
In strict mode, writing into it is an error.
Otherwise, the operation with the property is carried on, the object gets the test property,
but after that the “wrapper object” disappears, so in the last line str has no trace of the
property.
This example clearly shows that primitives are not objects.
They can’t store additional data.
Number
Imagine we need to write 1 billion. The obvious way is: let billion = 1000000000;
We also can use underscore _ as the separator: let billion = 1_000_000_000;
let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes
alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
let mcs = 1e-6; // five zeroes to the left from 1
The method num.toString(base) returns a string representation of num in the numeral
system with the given base.
let num = 255
alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
The base can vary from 2 to 36. By default, it’s 10.
alert( 123456..toString(36) ); // 2n9c
Two dots to call a method
Please note that two dots in 123456..toString(36) is not a typo. If we want to call a method
directly on a number, like toString in the example above, then we need to place two dots ..
after it.
If we placed a single dot: 123456.toString(36), then there would be an error, because
JavaScript syntax implies the decimal part after the first dot. And if we place one more dot,
then JavaScript knows that the decimal part is empty and now goes the method.
Also could write (123456).toString(36).
The method toFixed(n) rounds the number to n digits after the point and returns a string
representation of the result.
Please note that the result of toFixed is a string. If the decimal part is shorter than required,
zeroes are appended to the end:
The numeric format IEEE-754 solves this by rounding to the nearest possible number. These
rounding rules normally don’t allow us to see that “tiny precision loss”, but it exists.
We can see this in action:
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
And when we sum two numbers, their “precision losses” add up.
That’s why 0.1 + 0.2 is not exactly 0.3.
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // "0.30"
Please note that toFixed always returns a string. It ensures that it has 2 digits after the
decimal point. That’s actually convenient if we have an e-shopping and need to show $0.30.
For other cases, we can use the unary plus to coerce it into a number:
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
isNaN(value) converts its argument to a number and then tests it for being NaN:
alert( isNaN(NaN) ); // true
alert( isNaN("str") ); // true
But do we need this function? Can’t we just use the comparison === NaN? Unfortunately
not. The value NaN is unique in that it does not equal anything, including itself:
alert( NaN === NaN ); // false
isFinite(value) converts its argument to a number and returns true if it’s a regular number,
not NaN/Infinity/-Infinity:
Please note that an empty or a space-only string is treated as 0 in all numeric functions
including isFinite.
Number.isNaN and Number.isFinite methods are the more “strict” versions of isNaN and
isFinite functions. They do not autoconvert their argument into a number, but check if it
belongs to the number type instead.
Number.isNaN(value) returns true if the argument belongs to the number type and it is NaN.
In any other case, it returns false.
alert( Number.isNaN(NaN) ); // true
alert( Number.isNaN("str" / 2) ); // true
// Note the difference:
alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the
number type
alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN
as a result of this conversion
Number.isFinite(value) returns true if the argument belongs to the number type and it is not
NaN/Infinity/-Infinity. In any other case, it returns false.
alert( Number.isFinite(123) ); // true
alert( Number.isFinite(Infinity) ); // false
alert( Number.isFinite(2 / 0) ); // false
// Note the difference:
alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the
number type
alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123
There is a special built-in method Object.is that compares values like ===, but is more reliable
for two edge cases:
It works with NaN: Object.is(NaN, NaN) === true, that’s a good thing.
Values 0 and -0 are different: Object.is(0, -0) === false, technically that’s correct because
internally the number has a sign bit that may be different even if all other bits are zeroes.
In all other cases, Object.is(a, b) is the same as a === b.
parseInt and parseFloat are for.
They “read” a number from a string until they can’t. In case of an error, the gathered number
is returned. The function parseInt returns an integer, whilst parseFloat will return a floating-
point number:
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12, only the integer part is returned
alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading
There are situations when parseInt/parseFloat will return NaN. It happens when no digits
could be read:
alert( parseInt('a123') ); // NaN, the first symbol stops the process
Can write numbers directly in hex (0x), octal (0o) and binary (0b) systems.
parseInt(str, base) parses the string str into an integer in numeral system with given base, 2 ≤
base ≤ 36.
num.toString(base) converts a number to a string in the numeral system with the given base.
Round using Math.floor, Math.ceil, Math.trunc, Math.round or num.toFixed(precision).