demo2s-1
Must Watch!
MustWatch
Implementations
JavaScript is a scripting language designed to interact with web pages and is made up of the following
three distinct parts:
The Core (ECMAScript)
The Document Object Model (DOM)
The Browser Object Model (BOM)
JavaScript and ECMAScript are often used synonymously, but JavaScript is much more than just
what is defined in ECMA-262.
ECMAScript
ECMAScript, the language defined in ECMA-262, isn't tied to web browsers.
ECMA-262 defines this language as a base upon which
more-robust scripting languages may be built.
Web browsers are just one host environment in which
an ECMAScript implementation may exist.
Extensions, such as the Document Object Model (DOM), use ECMAScript's core types and syntax
to provide additional functionality that's more specific to the environment.
Other host environments
include NodeJS, a server-side JavaScript platform, and the increasingly obsolete Adobe Flash.
ECMA-262 describes the following parts of the language:
Syntax
Types
Statements
Keywords
Reserved words
Operators
Global objects
ECMAScript is a description of a language implementing all of the facets described in the
specification.
JavaScript implements ECMAScript, but so does Adobe ActionScript.
The different versions of ECMAScript are defined as editions.
For example, the ECMA-262 edition 7 is released in 2016.
The Document Object Model
The Document Object Model (DOM) is an application programming interface (API) for XML that
was extended for use in HTML.
The DOM maps out an entire page as a hierarchy of nodes.
Each part of an HTML or XML page is a type of node.
By creating a tree to represent a document, the DOM can control its content and structure.
Nodes can be removed, added, replaced, and modified by using the DOM API.
The Browser Object Model
The BOM deals with the browser window and frames.
For example, with BOM we can pop up new browser windows,
move, resize, and close browser windows
<script> Element, attribute and its meaning
The primary method of inserting JavaScript into an HTML page is via the <script> element.
There are six attributes for the <script> element:
Attribute | Optional | Description |
async | Optional | The script should begin downloading immediately. Should not prevent other actions on the page such as downloading resources or waiting for other scripts to load. Valid only for external script files. |
charset | Optional | The character set of the code specified using the src attribute. This attribute is rarely used. |
crossorigin | Optional | Configures the settings for the associated request. crossorigin="anonymous" will configure the request for the file to not have the credentials flag set. crossorigin="use-credentials" will set the credentials flag, meaning the outgoing request will include credentials. |
defer | Optional | Indicates that the execution of the script can safely be deferred until after the document's content has been completely parsed and displayed. Valid only for external scripts. |
integrity | Optional | Allows for verification of Sub resource Integrity (SRI) by checking the retrieved resource against a provided cryptographic signature. If the signature of the retrieved resource does not match that specified by this attribute, the page will error and the script will not execute. |
language | Deprecated | Originally indicated the scripting language being used by the code block |
src | Optional | Indicates an external file that contains code to be executed. |
type | Optional | Replaces language attribute. indicates the content type, also called MIME type of the scripting language. If the value is module, the code is treated as an ES6 module and only then is eligible to use the import and export keywords. |
<script> Usage
There are two ways to use the <script> element:
embed JavaScript code directly into the page or
include JavaScript from an external file.
To include inline JavaScript code, place JavaScript code inside the <script> element directly,
as follows:
function test() {
console.log("Hi!");
}
</script>
The JavaScript code contained inside a <script> element is interpreted from top to bottom.
In the case of this example, a function definition is interpreted and stored inside the interpreter environment.
The rest of the page content is not loaded and/or displayed until after all of the code inside the <script> element has been evaluated.
When using inline JavaScript code, you cannot have the string "</script>"
anywhere in your code.
For example, the following code causes an error when loaded into a browser:
<script>
function test() {
console.log("</script>");
}
</script>
You can escape the "/" character, as follows:
<script>
function test() {
console.log("<\/script>");
}
</script>
To include JavaScript from an external file, the src attribute is required.
The value of src is a URL linked to a file containing JavaScript code, like this:
<script src="example.js"></script>
In this example, an external file named example.js is loaded into the page.
The file need only contain the JavaScript code that would occur between the opening <script> and closing </script>
tags.
Processing of the page is halted while the external file is interpreted.
In XHTML documents, you can omit the closing tag, as in this example:
<script src="example.js"/>
By convention, external JavaScript files have a .js extension.
If you don't use a .js extension, double-check that your server is returning the correct MIME type.
The <script> elements are interpreted in the order in which
they appear in the page so long as the defer and async attributes are not present.
<script> Tag Placement
Traditionally, all <script> elements were placed within the <head> element on a page, as in
this example:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script src="example1.js"></script>
<script src="example2.js"></script>
</head>
<body>
<!-- content here -->
</body>
</html>
Including all JavaScript files in the <head> of a document means that
all of the JavaScript code must be downloaded, parsed, and interpreted before the page begins
rendering.
The rendering begins when the browser receives the opening <body> tag.
This may cause a noticeable delay in page rendering.
For this reason, modern web applications typically include all JavaScript
references in the <body> element, after the page content, as shown in this example:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
</head>
<body>
<!-- content here -->
<!-- content here -->
<!-- content here -->
<script src="example1.js"></script>
<script src="example2.js"></script>
</body>
</html>
Using this approach, the page is completely rendered in the browser before the JavaScript code is
processed.
<script> deferred Scripts
HTML 4.01 defines an attribute named defer for the <script> element.
defer indicates that a script won't be changing the structure of the page as it executes.
The script can be run safely after the entire page has been parsed.
Setting the defer attribute on a <script> element
signals to the browser that download should begin immediately but execution should be deferred:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script defer src="example1.js"></script>
<script defer src="example2.js"></script>
</head>
<body>
<!-- content here -->
</body>
</html>
Note
Even though the two <script> elements are included in the document <head>,
they will not be executed until after the browser has received the closing </html> tag.
The defer attribute is supported only for external script files.
Some browsers may ignore this attribute.
We should put deferred scripts at the bottom of the page.
For XHTML documents, specify the defer attribute as defer="defer".
Asynchronous Scripts
HTML5 adds the async attribute for <script> elements.
The async attribute changes the way the script is processed.
async applies only to external scripts and signals the browser to begin downloading the file immediately.
Unlike defer, scripts marked as async are not guaranteed to execute in the order in which they are specified.
For example:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script async src="example1.js"></script>
<script async src="example2.js"></script>
</head>
<body>
<!-- content here -->
</body>
</html>
In this code, the second script file might execute before the first.
There should be no dependencies between the two script files.
For XHTML documents, specify the async attribute as async="async".
<noscript> element
The <noscript> element was created to provide alternate content for browsers without JavaScript.
This element is useful for browsers that explicitly disable JavaScript.
The <noscript> element can contain any HTML elements, aside from <script>, that can be
included in the document <body>.
Any content contained in a <noscript> element will be displayed
under only the following two circumstances:
The browser doesn't support scripting.
The browser's scripting support is turned off.
If either of these conditions is met, then the content inside the <noscript> element is rendered. In all
other cases, the browser does not render the content of <noscript>.
Here is a simple example:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script src="example1.js"></script>
<script src="example2.js"></script>
</head>
<body>
<noscript>
<p>This page requires a JavaScript-enabled browser.</p>
</noscript>
</body>
</html>
In this example, a message is displayed to the user when the scripting is not available.
For scripting-enabled browsers, this message will never be seen even though it is still a part of the page.
Case-Sensitivity
ECMAScript's syntax borrows heavily from C and other C-like languages such as Java and Perl.
Javascript is case-sensitive.
The variables, function names, and operators are all case-sensitive.
JavaScript language keywords, variables,
function names, and other identifiers must always be typed with a consistent
capitalization of letters.
The while keyword, for example, must be typed "while," not "While"
or "WHILE."
A variable named test is different from a variable named Test.
let test = 'a';
let Test = 'v';
console.log(test);
console.log(Test);
Output:
Identifiers
An identifier is the name of a variable, function, property, or function argument.
Identifiers may be one or more characters in the following format:
The first character must be a letter, an underscore _, or a dollar sign $.
All other characters may be letters, underscores, dollar signs, or numbers.
Letters in an identifier may include extended ASCII or Unicode letter characters.
JavaScript identifiers are used to name constants,
variables, properties, functions, and classes and to provide labels for certain loops in
JavaScript code.
A JavaScript identifier must begin with a letter, an underscore (_), or
a dollar sign ($).
Subsequent characters can be letters, digits, underscores, or dollar
signs.
Digits are not allowed as the first character.
By convention, ECMAScript identifiers use camel case, meaning that the first letter is lowercase and
each additional word is offset by a capital letter, like this:
firstSecondThird
myCarIsGood
doSomethingImportantAnd
These are all legal identifiers:
x
my_variable_name
v13
_dummy
$string
$$abc
Keywords, reserved words, true, false, and null cannot be used as
identifiers.
let firstSecondThird = 'a';
console.log(firstSecondThird);
Output:
Comments
ECMAScript uses C-style comments for both single-line and block comments.
A single-line comment begins with two forward-slash characters, such as this:
// single line comment
A block comment begins with a forward slash and asterisk (/*) and ends with the opposite (*/), as in
this example:
/* This is a multi-line
comment */
Example
The following code adds some comments to a simple script.
//This is a test file
let a= 'a';
/*
The following code output message to console
*/
console.log(a);
Output:
Strict Mode
ECMAScript strict mode is a different parsing and execution model for JavaScript.
To enable strict mode for an entire script, include the following at the top:
"use strict";
This is a pragma that tells supporting JavaScript engines to change into strict mode.
You may also specify just a function to execute in strict mode by including the pragma at the top of
the function body:
function doSomething() {
"use strict";
// function body
}
Strict mode changes how JavaScript is executed.
All modern browsers support strict mode.
"use strict" does not include any language keywords:
the directive is just an expression
statement that consists of a special string literal (in single or double quotes).
"use strict" can appear only at the start of a script or at the start of a function body, before
any real statements have appeared.
Statements
Statements in ECMAScript are terminated by a semicolon.
Omitting the semicolon makes the parser determine where the end of a statement occurs.
let sum = a + b // valid even without a semicolon - not recommended
let diff = a - b; // valid - preferred
Even though a semicolon is not required at the end of statements,
you should always include one.
Including semicolons helps prevent errors of omission.
Including semicolons also improves performance in certain situations.
Multiple statements can be combined into a code block by using C-style syntax, beginning with a left
curly brace { and ending with a right curly brace }:
let test = true;
if (test) {
test = false;
console.log(test);
}
Control statements, such as if, require code blocks only when executing multiple statements.
It is a best practice to always use code blocks with control statements, even if there's
only one statement to be executed:
let test = true;
// valid, but error-prone and should be avoided
if (test)
console.log(test);
// preferred
if (test) {
console.log(test);
}
Using code blocks for control statements makes the intent clearer, and there's less chance for errors
when changes need to be made.
Keywords And Reserved Words
ECMA-262 describes a set of reserved keywords that have specific uses.
The keywords are reserved and cannot be used as identifiers or property names.
The complete list of keywords for ECMA-262, sixth edition is as follows:
break do in typeof
case else instanceof var
catch export new void
class extends return while
const finally super with
continue for switch yield
debugger function this
default if throw
delete import try
The specification also describes a set of future reserved words that cannot be used as identifiers
or property names.
Though reserved words don't have any specific usage in the language, they are
reserved for future use as keywords.
The following is the complete list of future reserved words defined in ECMA-262, sixth edition:
Always reserved:
enum
Reserved in strict mode:
implements package public
interface protected static
let private
Reserved in module code:
await
These words may still not be used as identifiers but now can be used as property names in objects.
We should avoid using both keywords and reserved words as both identifiers and
property names to ensure compatibility with past and future ECMAScript editions.
Try Out
With Browser Developer Tools
We try out short JavaScript code in a browser with the following steps:
Open up the web developer tools in your web browser (with F12, Ctrl-Shift-I, or Command-Option-I) and
Select the Console tab.
Type code at the prompt
See the results as you type.
Browser developer tools often appear as panes at the bottom or right of the browser
window, but you can usually detach them as separate windows,
which is often quite convenient.
Node.js
Another way to try out JavaScript code is to download and install Node from
https://nodejs.org.
Once Node is installed on your system, you can simply open a Terminal
window and type node to begin an interactive JavaScript session like this one:
$ node/*w w w .d e mo 2 s . co m */
Welcome to Node.js v15.2.1.
Type ".help" for more information.
> .help
.break Sometimes you get stuck, this gets you out
.clear Alias for .break
.editor Enter editor mode
.exit Exit the REPL
.help Print this help message
.load Load JS from a file into the REPL session
.save Save all evaluated commands in this REPL session to a file
Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL
>
> let x = 2, y = 3;
undefined
> x + y
5
> (x === 2) && (y === 3)
true
> (x > 3) || (y < 3)
false
Hello World
You can write your code in a text editor.
From there, you can copy and paste to the JavaScript console or into a Node session.
Or you can save your code to a file with file name extension .js and then run
that file of JavaScript code with Node:
$ node yourFileName.js
If you use Node in a non interactive manner like this, it won't automatically print out
the value of all the code you run.
You can use the function console.log() to display text and other JavaScript
values in your terminal
window or in a browser's developer tools console.
So, for example, if you create a hello.js file containing this line of code:
console.log("Hello World!");
and execute the file with node hello.js, you'll see the message "Hello World!"
printed out.
If you want to see that same message printed out in the JavaScript console of a web
browser, create a new file named hello.html, and put this text in it:
<script src="hello.js"></script>
Then load hello.html into your web browser using a file:// URL like this one:
file:///Users/username/javascript/hello.html
Open the developer tools window to see the greeting in the console.
You can drag and drop your html file to browser.
Quick Syntax Reference
This section presents a quick introduction, through code examples, to the JavaScript
language.
Anything following double slashes is an English-language comment.
They explain the JavaScript code.
Variable
A variable is a symbolic name for a value.
Variables are declared with the let keyword:
let x; // Declare a variable named x.
Values can be assigned to variables with an = sign
x = 0; // Now the variable x has the value 0
x // => 0: A variable evaluates to its value.
Data Types
JavaScript supports several types of values
x = 1; // Numbers.
x = 0.01; // Numbers can be integers or reals.
x = "hello world"; // Strings of text in quotation marks.
x = 'JavaScript'; // Single quote marks also delimit strings.
x = true; // A Boolean value.
x = false; // The other Boolean value.
x = null; // Null is a special value that means "no value."
x = undefined; // Undefined is another special value like null.
Object
JavaScript also have objects and arrays.
JavaScript's most important datatype is the object.
An object is a collection of name/value pairs, or a string to value map.
let book = { // Objects are enclosed in curly braces.
topic: "JavaScript", // The property "topic" has value "JavaScript."
edition: 7 // The property "edition" has value 7
}; // The curly brace marks the end of the object.
Access the properties of an object with . or []:
book.topic // => "JavaScript"
book["edition"] // => 7: another way to access property values.
book.author = "CSS"; // Create new properties by assignment.
book.contents = {}; // {} is an empty object with no properties.
book.contents?.c?.s // => undefined: book.contents has no c property.
JavaScript also supports arrays of values:
let myArray = [2, 3, 5, 7]; // An array of 4 values, delimited with [ and ].
myArray[0] // => 2: the first element (index 0) of the array.
myArray.length // => 4: how many elements in the array.
myArray[myArray.length-1] // => 7: the last element of the array.
myArray[4] = 9; // Add a new element by assignment.
myArray[4] = 11; // Or alter an existing element by assignment.
let empty = []; // [] is an empty array with no elements.
empty.length // => 0
Arrays and objects can hold other arrays and objects:
let points = [ // An array with 2 elements.
{x: 0, y: 0}, // Each element is an object.
{x: 1, y: 1}
];
let data = { // An object with 2 properties
trial1: [[1,2], [3,4]], // The value of each property is an array.
trial2: [[2,3], [4,5]] // The elements of the arrays are arrays.
};
Operators
To form expressions in JavaScript, use operators:
Operators act on values to produce a new value.
Arithmetic operators are simple:
3 + 2 // => 5: addition
3 - 2 // => 1: subtraction
3 * 2 // => 6: multiplication
3 / 2 // => 1.5: division
"3" + "2" // => "32": + adds numbers and concatenates strings
JavaScript defines some shorthand arithmetic operators
let count = 0; // Define a variable
count++; // Increment the variable
count--; // Decrement the variable
count += 2; // Add 2: same as count = count + 2;
count *= 3; // Multiply by 3: same as count = count * 3;
count // => 6: variable names are expressions, too.
Equality and relational operators test whether two values are equal,
unequal, less than, greater than, and so on.
They evaluate to true or false.
let x = 2, y = 3; // These = signs are assignment, not equality tests
x === y // => false: equality
x !== y // => true: inequality
x < y // => true: less-than
x <= y // => true: less-than or equal
x > y // => false: greater-than
x >= y // => false: greater-than or equal
"two" === "three" // => false: the two strings are different
"two" > "three" // => true: "tw" is alphabetically greater than "th"
false === (x > y) // => true: false is equal to false
Logical operators combine or invert boolean values
(x === 2) && (y === 3) // => true: both comparisons are true. && is AND
(x > 3) || (y < 3) // => false: neither comparison is true. || is OR
!(x === y) // => true: ! inverts a boolean value
Function
A function is a named and parameterized block of JavaScript code.
You can define them once, and then invoke them many times.
Functions are parameterized blocks of JavaScript code that we can invoke.
function myFunc(x) {// Define a function named "myFunc" with parameter "x"
return x + 1; // Return a value one larger than the value passed in
} // Functions are enclosed in curly braces
myFunc(y) // => 4: y is 3, so this invocation returns 3+1
let square = function(x) { // Functions are values and can be assigned to vars
return x * x; // Compute the function's value
}; // Semicolon marks the end of the assignment.
square(myFunc(y)) // => 16: invoke two functions in one expression
We can use => to separate the argument list from the function body,
and functions defined this way are known as arrow functions.
Arrow functions are most commonly used
when you want to pass an unnamed function as an argument to another function.
The preceding code looks like this when rewritten to use arrow functions:
const myFunc = x => x + 1; // The input x maps to the output x + 1
const square = x => x * x; // The input x maps to the output x * x
myFunc(y) // => 4: function invocation is the same
square(myFunc(y)) // => 16
When we use functions with objects, we get methods:
When functions are assigned to the properties of an object, we call them "methods."
All JavaScript objects including arrays have methods:
let a = []; // Create an empty array
a.push(1,2,3); // The push() method adds elements to an array
a.reverse(); // reverse the order of elements
We can define our own methods.
The "this" keyword refers to the object on which the method is defined.
The points array from earlier.
points.dist = function() { // Define a method to compute distance between points
let p1 = this[0]; // First element of array we're invoked on
let p2 = this[1]; // Second element of the "this" object
let a = p2.x-p1.x; // Difference in x coordinates
let b = p2.y-p1.y; // Difference in y coordinates
return Math.sqrt(a*a + // The Pythagorean theorem
b*b); // Math.sqrt() computes the square root
};
points.dist() // => Math.sqrt(2): distance between our 2 points
Statement
JavaScript statements include conditionals and loops using the syntax
of C, C++, Java, and other languages.
if statement
function abs(x) { // compute the absolute value.
if (x >= 0) { // if statement
return x; // executes this code if the comparison is true.
} // This is the end of the if clause.
else { // The optional else clause executes its code if
return -x; // the comparison is false.
} // Curly braces optional when 1 statement per clause.
} // Note return statements nested inside if/else.
abs(-10) === abs(10) // => true
for of loop
function sum(array) { // sum elements of an array
let sum = 0; // Start with an initial sum of 0.
for(let x of array) { // Loop over array, assigning each element to x.
sum += x; // Add the element value to the sum.
} // This is the end of the loop.
return sum; // Return the sum.
}
while loop
function factorial(n) { // A function to compute factorials
let product = 1; // Start with a product of 1
while(n > 1) { // Repeat statements in {} while expr in () is true
product *= n; // Shortcut for product = product * n;
n--; // Shortcut for n = n - 1
} // End of loop
return product; // Return the product
}
for loop
function factorial2(n) { // Another version using a different loop
let i, product = 1; // Start with 1
for(i=2; i <= n; i++) // Automatically increment i from 2 up to n
product *= i; // Do this each time. {} not needed for 1-line loops
return product; // Return the factorial
}
Class
JavaScript supports an object-oriented programming style.
The following code shows how to define a JavaScript class to represent
2D geometric points.
Objects that are instances of this class have a single method,
named distance(), that computes the distance of the point from the origin:
class Point { // class names are capitalized.
constructor(x, y) { // Constructor function to initialize new instances.
this.x = x; // this refers to the new object being initialized.
this.y = y; // store function arguments as object properties.
} // no return for constructor functions.
distance() { // Method to compute distance from origin to point.
return Math.sqrt( // Return the square root of x square + y square.
this.x * this.x + // this refers to the Point object on which
this.y * this.y // the distance method is invoked.
);
}
}
// Use the Point() constructor function with "new" to create Point objects
let p = new Point(1, 1); // The geometric point (1,1).
// Now use a method of the Point object p
console.log(p.distance());
Variables
ECMAScript variables are loosely typed.
A variable can hold any type of data.
Every variable is a named placeholder for a value.
There are three keywords that can be used to declare a variable:
var
const
let
var is supported for all versions and let and const were introduced in ECMAScript 6.
var a = 'a';
console.log(a);
let b = 'b';
console.log(b);
const c = 'c'
console.log(c);
Output:
Note
The introduction of let and const in ECMAScript 6 bring better tooling to the Javascript language.
It increases precision of declaration scope and semantics.
Don't Use var
With let and const, we no longer need to use var in our code base.
Prefer const Over let
Using const declarations allows the browser runtime to enforce constant variables.
We should declare variables as const unless they know they will need to
reassign its value at some point.
Use 'var' Keyword to define variables
To define a variable, use the var operator followed by the variable name, like this:
var myValue;
This code defines a variable named myValue that can be used to hold any value.
ECMAScript supports variable initialization,
so it's possible to define the variable and set its value at the same time, as in this example:
var myValue = "hi";
Here, myValue is defined to hold a string value of "hi".
Doing this initialization doesn't mark the
variable as being a string type.
It is the assignment of a value to the variable.
It is still possible to not only change the value stored in the variable but also change the type of value, such as this:
var myValue = "hi";
myValue = 100; // legal, but not recommended
In this example, the variable myValue is first defined as having the string value "hi" and then
overwritten with the numeric value 100.
Although it's not recommended to switch the data type that a
variable contains, it is completely valid in ECMAScript.
More than one
If you need to define more than one variable, you can do it using a single statement, separating each
variable and optional initialization with a comma like this:
var myValue = "hi",
found = false,
age = 12;
Here, three variables are defined and initialized.
Because ECMAScript is loosely typed,
variable initializations using different data types may be combined into a single statement.
Inserting line breaks and indenting the variables helps to improve readability.
When you are running in strict mode, you cannot define variables named eval or arguments.
Doing so results in a syntax error.
var Declaration Scope
Using the var operator to define a variable makes it local to the function
scope where it was defined.
For example, defining a variable inside of a function using var means
that the variable is destroyed as soon as the function exits, as shown here:
function test() {
var myValue = "hi"; // local variable
}
test();
console.log(myValue); // error!
Here, the myValue variable is defined within a function using var.
The function test() creates the variable and assigns its value.
Immediately after that, the variable is destroyed so
the last line in this example causes an error.
Global
We can define a variable globally by simply omitting the var operator as follows:
function test() {
myValue = "hi"; // global variable
}
test();
console.log(myValue); // "hi"
By removing the var operator from the example, the myValue variable becomes global.
When the function test() is called, the variable is defined and becomes accessible outside of the function once
it has been executed.
Although it's possible to define global variables by omitting the var operator,
this approach is not recommended.
Global variables defined locally are hard to maintain and cause confusion.
Strict mode throws a ReferenceError when an undeclared variable is assigned a value.
var Declaration Hoisting
When using var, the following is possible because variables declared using that keyword are hoisted
to the top of the function scope:
function foo() {
console.log(age);
var age = 26;
}
foo(); // undefined
Output:
This does not throw an error because the ECMAScript runtime technically treats it like this:
function foo() {
var age;
console.log(age);
age = 26;
}
foo(); // undefined
Output:
This is "hoisting" where the interpreter pulls all variable declarations to the top of its scope.
It also allows you to use redundant var declarations without penalty:
function foo() {
var age = 16;
var age = 26;
var age = 36;
console.log(age);
}
foo(); // 36
let Declarations
let keyword works like var with some important differences.
let is block scoped, but var is function scoped.
if (true) {
var name = 'Javascript';
console.log(name); // Javascript
}
console.log(name); // Javascript
if (true) {
let age = 11;
console.log(age); // 11
}
console.log(age); // ReferenceError: age is not defined
Here, the age variable cannot be referenced outside the if block because its scope does not extend
outside the block.
Block scope is a subset of function scope, so any scope limitations that
apply to var declarations will also apply to let declarations.
A let declaration does not allow for any redundant declarations within a block scope.
Doing so will result in an error:
var name;
var name; //no error
let age;
let age; // SyntaxError; identifier 'age' has already been declared
The JavaScript engine keeps track of identifiers used for variable declarations.
Nesting identifiers behaves as you would
expect with no errors because no redeclaration is occurring:
var name = 'CSS';
console.log(name); // 'CSS'
if (true) {
var name = 'Javascript';
console.log(name); // 'Javascript'
}
let age = 12;
console.log(age); // 12
if (true) {
let age = 11;
console.log(age); // 11
}
The declaration redundancy errors are not a function of order, and are not
affected if let is mixed with var.
The two keywords specify how
the variables exist inside the relevant scope.
var name;
let name; // SyntaxError
let age;
var age; // SyntaxError
let declarations does not support hoisting:
let declarations cannot be used in a way that assumes hoisting:
// name is hoisted, since it is defined by var
console.log(name); // undefined
var name = 'Javascript';
// age is not hoisted, since it is defined by let
console.log(age); // ReferenceError: age is not defined
let age = 26;
JavaScript engines knows that the let declarations that appear later in a block.
These variables cannot be referenced before the actual declaration
occurs.
Any attempted references to these variables
from the segment of execution that occurs before the declaration
will throw a ReferenceError.
let Global Declarations
When declaring variables using let in the global context, variables will not
attach to the window object as they do with var.
var name = 'Javascript';
console.log(window.name); // 'Javascript'
let age = 26;
console.log(window.age); // undefined
However, let declarations will still occur inside the global block scope, which will persist for the
lifetime of the page.
let age = 26;
function test(){
console.log(age);
}
test();
Output:
You must ensure your page does not attempt duplicate declarations in
order to avoid throwing a SyntaxError.
let Declaration in for Loops
Prior to let, for loop definition involved using an iterator variable whose definition
would go outside the loop body:
for (var i = 0; i < 5; ++i) {
// do loop things
}
console.log(i); // 5
This is no longer a problem when switching to let declarations, as the iterator variable will be
scoped only to the for loop block:
for (let i = 0; i < 5; ++i) {
// do loop things
}
console.log(i); // ReferenceError: i is not defined
When using var, there is a problem in the singular declaration and modification of
the iterator variable:
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// You might expect this to console.log 0, 1, 2, 3, 4
// It will actually console.log 5, 5, 5, 5, 5
This happens because the loop exits with its iterator variable still set to the value that caused the
loop to exit: 5.
When the timeouts later execute, they reference this same variable, and consequently
console.log its final value.
When using let to declare the loop iterator, behind the scenes the JavaScript engine will
actually declare a new iterator variable each loop iteration.
Each setTimeout references that separate
instance, and therefore it will console.log the expected value: the value of the iterator variable
when that loop iteration was executed.
for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// console.logs 0, 1, 2, 3, 4
This per-iteration declarative behavior is applicable for all styles of for loops, including for-in and
for-of loops.
const Declarations
const must be initialized with a value, and that value cannot be redefined after declaration.
Attempting to modify a const variable will result in a runtime error.
const age = 26;
age = 36; // TypeError: assignment to a constant
const still disallows redundant declaration
const name = 'Javascript';
const name = 'CSS'; // SyntaxError
const is still scoped to blocks
const name = 'Javascript';
if (true) {
const name = 'CSS';
}
console.log(name); // Javascript
The const declaration is only enforced with respect to the reference to the variable that it points to.
If a const variable references an object, it does not violate the const constraints to modify properties
inside that object.
const person = {};
person.name = 'Javascript'; // ok
You cannot use const to declare for loop iterators:
for (const i = 0; i < 10; ++i) {}
// TypeError: assignment to constant variable
You can use const to declare a for loop variable that is not modified.
This is especially relevant in the case of for-of and for-in loops:
Define j as constant inside for loop
let i = 0;
for (const j = 7; i < 5; ++i) {
console.log(j);
}
Output:
Use const in for-in loop
for (const key in {a: 1, b: 2}) {
console.log(key);
}
Output:
Use const in for-of loop
for (const value of [1,2,3,4,5]) {
console.log(value);
}
Output:
Data Types
There are six simple data types or primitive types in ECMAScript:
undefined
null,
Boolean,
Number,
String,
Symbol.
There is also one complex data type called Object.
Object is an unordered list of name-value pairs.
let a = undefined;
console.log(a);
a = null;
console.log(a);
a = true;
console.log(a);
a = 123.123;
console.log(a);
a = 'string';
console.log(a);
a = Symbol();
console.log(a);
Output:
Data Type Overview and Definitions
JavaScript types can be divided into two categories:
primitive types and
object types.
Primitive types
JavaScript's primitive types include numbers, strings, and booleans.
The special JavaScript values null and undefined are primitive values,
but they are not numbers, strings, or booleans.
ES6 adds a new special-purpose type, known as Symbol, that enables the
definition of language extensions without harming backward compatibility.
Object
Any JavaScript value that is not a number, a string, a boolean, a symbol, null, or unde
fined is an object.
An object is a collection of
properties where each property has a name and a value.
An ordinary JavaScript object is an unordered collection of named values.
Array
The language also defines a special kind of object, known as an array, that represents an
ordered collection of numbered values.
The JavaScript language includes special syntax for working with arrays, and arrays have some special behavior that distinguishes
them from ordinary objects.
Other
JavaScript defines a number of other useful object types.
A Set object represents a set of values.
A Map object represents a mapping from keys to values.
Various "typed array" types facilitate operations on arrays of
bytes and other binary data.
The RegExp type represents String patterns and enables
matching, searching, and replacing operations on strings.
The Date type
represents dates and times.
Error and its subtypes represent errors that can arise when executing JavaScript code.
JavaScript functions and classes are not just
part of the language syntax, they are values that can be manipulated by
JavaScript programs.
Note
JavaScript's object types are mutable and its primitive types are immutable.
JavaScript liberally converts values from one type to another.
If a program expects a
string, for example, and you give it a number, it will automatically convert the num
ber to a string for you.
And if you use a non-boolean value where a boolean is
expected, JavaScript will convert accordingly.
typeof Operator to check data type
The typeof operator determine the data type in Javascript.
Using the typeof operator on a value returns one of the following strings:
"undefined" if the value is undefined
"boolean" if the value is a Boolean
"string" if the value is a string
"number" if the value is a number
"object" if the value is an object (other than a function) or null
"function" if the value is a function
"symbol" if the value is a Symbol
Example
The typeof operator is called like this:
let message = "some string";
console.log(typeof message); // "string"
console.log(typeof(message)); // "string"
console.log(typeof 5); // "number"
In this example, both a variable message and a numeric literal are passed into the typeof operator.
typeof is an operator and not a function, no parentheses are required although
they can be used.
Note
typeof might return a confusing but technically correct value.
Calling typeof null returns a value of "object", as the special value null is considered to be
an empty object reference.
Technically, functions are considered objects in ECMAScript and don't
represent another data type.
undefined Type
The undefined type has only one value, which is the special value undefined.
When a variable is
declared using var or let but not initialized, it is assigned the value of undefined as follows:
let message;
console.log(message == undefined); // true
In this example, the variable message is declared without initializing it.
When compared with the literal value of undefined, the two are equal.
This example is identical to the following:
let message = undefined;
console.log(message == undefined); // true
Here the variable message is explicitly initialized to be undefined.
This is unnecessary because, by default, any uninitialized variable gets the value of undefined.
You should never explicitly set a variable to be undefined.
The literal undefined value is provided for comparison.
A variable containing the value of undefined is different from a variable that hasn't been
defined at all.
Consider the following:
let message; // this variable is declared
// but has a value of undefined
console.log(message); // "undefined"
console.log(age); // causes an error
In this example, the first console.log displays the variable message,
which is "undefined".
In the second console.log, an undeclared variable called age is passed into the console.log() function,
which causes an error because the variable hasn't been declared.
The typeof operator returns "undefined" when called on an uninitialized variable, but it also
returns "undefined" when called on an undeclared variable, which can be a bit confusing.
Consider this example:
let message; // this variable is declared but has a value of undefined
console.log(typeof message); // "undefined"
console.log(typeof age); // "undefined"
In both cases, calling typeof on the variable returns the string "undefined".
Logically, this makes sense because no real operations can be performed with either variable even though they are
technically different.
We should always initialize variables.
That way, when typeof returns "undefined", you'll know that it's because a given variable hasn't been
declared rather than was simply not initialized.
falsy
The value undefined is falsy and you can use if to check it.
There are many other possible values as falsy,
so be careful when using if statement to test undefined:
let message; // this variable is declared but has a value of undefined
// 'age' is not declared
if (message) {
// This block will not execute
}
if (!message) {
// This block will execute
}
if (age) {
// This will throw an error
}
null Type
The Null type has only one value: the special value null.
Logically, a null value is an empty object pointer, which is why typeof returns "object" when it's passed a
null value in the following example:
let myValue = null;
console.log(typeof myValue); // "object"
When defining a variable that is meant to later hold an object, we should initialize the variable
to null as opposed to anything else.
You can explicitly check for the value null to determine if the variable has been filled with an object reference at a later time, such as in this example:
let myValue = null;
if (myValue != null) {
// do something with myValue
}
The value undefined is a derivative of null, so ECMA-262 defines them to be superficially equal
as follows:
console.log(null == undefined); // true
Using the equality operator (==) between null and undefined always returns true.
falsy
The null type is falsy; therefore, you can use if statement to check it.
let myValue = null;
let age;
if (myValue) {
// This block will not execute
}
if (!myValue) {
// This block will execute
}
if (age) {
// This block will not execute
}
if (!age) {
// This block will execute
}
Boolean Type
The Boolean type has only two literal values: true and false.
These values are distinct from numeric values, so true is not equal to 1, and
false is not equal to 0.
Assignment of Boolean values to variables is as follows:
let found = true;
let lost = false;
The Boolean literals true and false are case-sensitive, so True and False
are valid as identifiers but not as Boolean values.
let found = true;
let lost = false;
console.log(found === 0); //false
console.log(found === 1); //false
console.log(lost === 0); //false
console.log(lost === 1); //false
console.log(found == 0); //false
console.log(found == 1); //true
console.log(lost == 0); //true
console.log(lost == 1); //false
Convert values to Boolean using Boolean() convert function
All types of values have Boolean equivalents in ECMAScript.
To convert a value into its Boolean equivalent, the special Boolean() casting function
is called, like this:
let message = "hi!";
let messageAsBoolean = Boolean(message);
In this example, the string message is converted into a Boolean value and stored in
messageAsBoolean.
The Boolean() casting function can be called on any type of data and will always return
a Boolean value.
The rules for when a value is converted to true or false depend on the data type
as much as the actual value.
The following table outlines the various data types and their specific conversions.
Data Type | Values Converted To True | Values Converted To False |
Boolean | true | false |
String | Any nonempty string | "" (empty string) |
Number | Any nonzero number (including infinity) | 0, NaN |
Object | Any object | null |
Undefined | n/a | undefined |
The flow-control statements, such as the if statement,
automatically perform this Boolean conversion, as shown here:
let message = "hi!";
if (message) {
console.log("Value is true");
}
In this example, the console.log will be displayed because the string message is automatically
converted into its Boolean equivalent (true).
It's important to understand what variable you're using in a
flow-control statement because of this automatic conversion.
Integer Literal Octal and hexadecimal value
JavaScript's primary numeric type, Number, is used to represent integers and to
approximate real numbers.
ECMAScript Number uses the IEEE-754 format to represent both integers and floating-point values.
The JavaScript number format allows you to exactly represent all integers between
-9,007,199,254,740,992 and 9,007,199,254,740,992, inclusive.
To support the various types of numbers, there are several different number literal formats.
Literal
The most basic number literal format is that of a decimal integer, which can be entered directly as
shown here:
let intNum = 55; // integer
In a JavaScript program, a base-10 integer is written as a sequence of digits.
For example:
0
3
10000000
Octal
Integers can also be represented as either octal (base 8) or hexadecimal (base 16) literals.
For an octal literal, the first digit must be a zero (0) followed by a sequence of octal digits (numbers 0 through 7).
If a number out of this range is detected in the literal, then the leading zero is ignored and the number
is treated as a decimal, as in the following examples:
let octalNum1 = 070; // octal for 56
let octalNum2 = 079; // invalid octal - interpreted as 79
let octalNum3 = 08; // invalid octal - interpreted as 8
Octal literals are invalid when running in strict mode and will cause the JavaScript engine to throw a
syntax error.
hexadecimal
To create a hexadecimal literal, you must make the first two characters 0x (case insensitive), followed
by any number of hexadecimal digits (0 through 9, and A through F).
Letters may be in uppercase or lowercase.
Here's an example:
let hexNum1 = 0xA; // hexadecimal for 10
let hexNum2 = 0x1f; // hexadecimal for 31
Numbers created using octal or hexadecimal format are treated as decimal numbers in all arithmetic
operations.
Binary
In ES6 and later, you can also express integers in binary (base 2)
or octal (base 8)
using the prefixes 0b and 0o (or 0B and 0O):
0b10101 // => 21: (1*16 + 0*8 + 1*4 + 0*2 + 1*1)
0o377 // => 255: (3*64 + 7*8 + 7*1)
Floating-Point Values
To define a floating-point value, you must include a decimal point and at least one number after the
decimal point.
Although an integer is not necessary before a decimal point, it is recommended.
Here are some examples:
let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = .1; // valid, but not recommended
You can use underscores within numeric literals to break long literals up into chunks
that are easier to read:
let billion = 1_000_000_000; // Underscore as a thousands separator.
let bytes = 0x89_AB_CD_EF; // As a bytes separator.
let bits = 0b0001_1101_0111; // As a nibble separator.
let fraction = 0.123_456_789; // Works in the fractional part, too.
When there is no digit after the
decimal point, the number becomes an integer.
If the number being represented is a whole number (such as 1.0), it will be converted into an integer, as in this example:
let floatNum1 = 1.; // missing digit after decimal - interpreted as integer 1
let floatNum2 = 10.0; // whole number - interpreted as integer 10
E-notation
Javascript floating-point values can be represented using e-notation.
E-notation is used to indicate a number that should be multiplied by 10 raised to a given power.
The format of e-notation in ECMAScript is to have a number, integer or floating-point,
followed by an uppercase or lowercase letter E, followed by the power of 10 to multiply by.
Consider the following:
let floatNum = 3.125e7; // equal to 31250000
In this example, floatNum is equal to 31,250,000 and it is represented in a more compact
form using e-notation.
The notation says, "Take 3.125 and multiply it by 10000000."
E-notation can also be used to represent very small numbers, such as 0.00000000000000003, which
can be written more succinctly as 3e-17.
By default, ECMAScript converts any floating-point value
with at least six zeros after the decimal point into e-notation (for example, 0.0000003 becomes 3e-7).
Accuracy
Floating-point values are accurate up to 17 decimal places but are far less accurate in arithmetic
computations than whole numbers.
For instance, adding 0.1 and 0.2 yields 0.30000000000000004
instead of 0.3.
These small rounding errors make it difficult to test for specific floating-point values.
Consider this example:
const a = 0.1;
const b = 0.2;
if (a + b == 0.3) { // avoid!
console.log("You got 0.3.");
}else{
console.log("not 0.3.");
}
Output:
Here, the sum of two numbers is tested to see if it's equal to 0.3.
You should never test for specific floating-point values.
Note
The rounding errors are caused by how the floating-point arithmetic is done in IEEE-754-based numbers and is not
unique to ECMAScript.
Other languages that use the same format have the same issues.
Number Range of Values, Infinity, and negative infinity
The smallest number that can be represented in ECMAScript is stored in Number.MIN_VALUE
and is 5e-324 on most browsers; the largest number is stored in Number.MAX_VALUE and is
1.7976931348623157e+308 on most browsers.
If a calculation results in a number that cannot be
represented by JavaScript's numeric range, the number automatically gets the special value of
Infinity.
Any negative number that can't be represented is -Infinity (negative infinity), and any
positive number that can't be represented is Infinity (positive infinity).
If a calculation returns either positive or negative Infinity, that value cannot be used in any
further calculations.
To determine if a value is finite, there is the isFinite() function.
This function returns true only if the argument is between the minimum and
the maximum values, as in this example:
let result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false
You can get the values of positive and negative Infinity by accessing
*Number.NEGATIVE _INFINITY* and *Number.POSITIVE _INFINITY*.
These properties contain the values -Infinity and Infinity, respectively.
Float value NaN, Not a Number
There is a special numeric value called NaN, short for Not a Number, which indicates the returned value is Not a Number.
For example, in ECMAScript, dividing a number by 0 returns NaN, which allows other processing to continue.
The value NaN has a couple of unique properties.
First, any operation involving NaN always returns NaN, For example NaN /20.
Second, NaN is not equal to any value, including NaN.
For example, the following returns false:
console.log(NaN == NaN); // false
isNaN()
ECMAScript provides the isNaN() function.
isNaN() function accepts a single argument to determine if the value is "not a number".
When a value is passed into
isNaN(), an attempt is made to convert it into a number.
Some nonnumerical values convert into numbers directly, such as the string "10" or a Boolean value.
Any value that cannot be converted into a number causes the function to return true.
Consider the following:
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false - 10 is a number
console.log(isNaN("10")); // false - can be converted to number 10
console.log(isNaN("demo2s.com")); // true - cannot be converted to a number
console.log(isNaN(true)); // false - can be converted to number 1
This example tests five different values.
The first test is on the value NaN itself, which returns true.
The next two tests use numeric 10 and the string "10", which both return false
because the numeric value for each is 10.
The string "demo2s.com" cannot be converted into a
number, so the function returns true.
The Boolean value of true can be converted into the number 1,
so the function returns false.
isNaN() can be applied to objects.
In that case, the object's valueOf() method is first called to determine if the returned value can be converted into a number.
If not, the toString() method is called and its returned value is tested as well.
Number Conversions with Number() function, convert values into numbers
There are three functions to convert nonnumeric values into numbers: the Number() casting function, the parseInt() function, and the parseFloat() function.
The first function, Number(), can be
used on any data type; the other two functions are used specifically for converting strings to numbers.
The Number() function performs conversions based on these rules:
1. When applied to Boolean values, true and false get converted into 1 and 0, respectively.
2. When applied to numbers, the value is returned.
3. When applied to null, Number() returns 0.
4. When applied to undefined, Number() returns NaN.
5. When applied to strings, the following rules are applied:
If the string contains only numeric characters, optionally preceded by a plus or minus sign, it is always converted to a decimal number, so Number("1") becomes 1, Number("123") becomes 123, and Number("011") becomes 11. Leading zeros are ignored.
If the string contains a valid floating-point format, such as "1.1", it is converted into the appropriate floating-point numeric value. Leading zeros are ignored.
If the string contains a valid hexadecimal format, such as "0xf", it is converted into an integer that matches the hexadecimal value.
If the string is empty, it is converted to 0.
If the string contains anything other than these previous formats, it is converted into NaN.
6. When applied to objects, the valueOf() method is called and the returned value is converted based on the previously described rules. If that conversion results in NaN, the toString() method is called and the rules for converting strings are applied.
. Here are some concrete examples:
let num1 = Number("hi!"); // NaN
let num2 = Number(""); // 0
let num3 = Number("00002");// 2
let num4 = Number(true); // 1
In these examples, the string "hi!" is converted into NaN because it has no corresponding numeric value, and the empty string is converted into 0.
The string "00002" is converted to the number 2 because the initial zeros are ignored.
Last, the value true is converted to 1.
Convert String to Number with parseInt()
parseInt() function is usually used when you are dealing with integers.
The parseInt() function examines the string to see if it matches a number pattern.
Leading white space in the string is ignored until the first non-white space character is found.
If this first character isn't a number, the minus sign, or the plus sign, parseInt() always returns NaN, which means the empty string returns NaN.
If the first character is a number,
plus, or minus, then the conversion goes on to the second character and continues on until either the end of the string is reached or a nonnumeric character is found.
For instance, "1234demo" is converted to 1234 because "demo" is completely ignored.
"22.5" will be converted to 22 because the decimal is not a valid integer character.
If the first character in the string is a number, the parseInt() function also recognizes the various integer formats: decimal, octal, and hexadecimal.
The string begins with "0x" is interpreted as a hexadecimal integer; if it begins with "0" followed by a number, it is interpreted as an octal value.
Here are some conversion examples to illustrate what happens:
let num1 = parseInt("1234demo"); // 1234
let num2 = parseInt(""); // NaN
let num3 = parseInt("0xA"); // 10 - hexadecimal
let num4 = parseInt(2.5); // 2
let num5 = parseInt("70"); // 70 - decimal
let num6 = parseInt("0xf"); // 15 - hexadecimal
parseInt("3 asdf ") // => 3
parseFloat(" 3.14 meters") // => 3.14
parseInt("-12.34") // => -12
parseInt("0xFF") // => 255
parseInt("0xff") // => 255
parseInt("-0XFF") // => -255
parseFloat(".1") // => 0.1
parseInt("0.1") // => 0
parseInt(".1") // => NaN: integers can't start with "."
parseFloat("$72.47") // => NaN: numbers can't start with "$"
radix
parseInt() provides a second argument: the radix.
If you know that the value you're parsing is in hexadecimal format,
you can pass in the radix 16 as a second argument and ensure that the correct parsing
will occur, as shown here:
let num = parseInt("0xAF", 16); // 175
In fact, by providing the hexadecimal radix, you can leave off the leading "0x" and the conversion will work as follows:
let num1 = parseInt("AF", 16); // 175
let num2 = parseInt("AF"); // NaN
In this example, the first conversion occurs correctly, but the second conversion fails.
The radix is passed in on the first line, telling parseInt() that it will be passed a hexadecimal string; the second line sees that the first character is not a number and stops automatically.
Passing in a radix can change the outcome of the conversion. Consider the following:
let num1 = parseInt("10", 2); // 2 - parsed as binary
let num2 = parseInt("10", 8); // 8 - parsed as octal
let num3 = parseInt("10", 10); // 10 - parsed as decimal
let num4 = parseInt("10", 16); // 16 - parsed as hexadecimal
parseInt("11", 2) // => 3: (1*2 + 1)
parseInt("ff", 16) // => 255: (15*16 + 15)
parseInt("zz", 36) // => 1295: (35*36 + 35)
parseInt("077", 8) // => 63: (7*8 + 7)
parseInt("077", 10) // => 77: (7*10 + 7)
We should always include a radix to avoid errors.
Most of the time you'll be parsing decimal numbers, so it's good to
always include 10 as the second argument.
Convert String to Number with parseFloat()
The parseFloat() function looks at each character starting in position 0.
It continues to parse the string until it reaches either the end of the string or a
character that is invalid in a floating-point number.
This means that a decimal point (.) is valid the first
time it appears, but a second decimal point is invalid and the rest of the string is ignored, resulting in "2.34.5" being converted to 2.34.
parseFloat() always ignores the initial zeros.
parseFloat() recognizes any of the floating-point formats.
Hexadecimal numbers always become 0 in parseFloat().
Because parseFloat() parses only decimal values, there is no radix mode.
If the string represents a whole number without decimal point or only a zero after the decimal point, parseFloat() returns an integer.
Here are some examples:
let num1 = parseFloat("1234demo"); // 1234 - integer
let num2 = parseFloat("0xA"); // 0
let num3 = parseFloat("2.5"); // 2.5
let num4 = parseFloat("2.34.5"); // 2.34
let num5 = parseFloat("0907.6"); // 907.6
let num6 = parseFloat("3.125e7"); // 31250000
Number Format
Formatting and parsing numbers are common tasks in computer programs.
JavaScript has specialized functions and methods that provide more precise control over
number-to-string and string-to-number conversions.
The toString() method defined by the Number class accepts an optional argument
that specifies a radix, or base, for the conversion.
If you do not specify the argument,
the conversion is done in base 10.
However, you can also convert numbers in other
bases (between 2 and 36).
For example:
let n = 17;
let binary = "0b" + n.toString(2); // binary == "0b10001"
let octal = "0o" + n.toString(8); // octal == "0o21"
let hex = "0x" + n.toString(16); // hex == "0x11"
You may want to convert numbers to
strings in ways that give you control over the number of decimal places or the
number of significant digits in the output, or you may want to control whether
exponential notation is used.
The Number class defines three methods for these kinds of
number-to-string conversions.
toFixed() converts a number to a string with a specified number of digits after the decimal point.
It never uses exponential notation.
toExponential() converts a number to a string using exponential notation, with one
digit before the decimal point and a specified number of digits after the decimal point.
toPrecision() converts a number to a string with the number of significant
digits you specify.
It uses exponential notation if the number of significant digits is
not large enough to display the entire integer portion of the number.
Note that all
three methods round the trailing digits or pad with zeros as appropriate.
Consider the following examples:
let n = 123456.789;
n.toFixed(0) // => "123457"
n.toFixed(2) // => "123456.79"
n.toFixed(5) // => "123456.78900"
n.toExponential(1) // => "1.2e+5"
n.toExponential(3) // => "1.235e+5"
n.toPrecision(4) // => "1.235e+5"
n.toPrecision(7) // => "123456.8"
n.toPrecision(10) // => "123456.7890"
Object Type
Objects in ECMAScript are created
by using the new operator followed by the name of the object type to create.
Developers create their
own objects by creating instances of the Object type and adding properties and/or methods to it, as
shown here:
let o = new Object();
ECMAScript requires parentheses to be used only when providing arguments to the constructor.
If there are no arguments, as in the following example, then the
parentheses can be omitted safely. It is not recommended to do this.
let o = new Object; // legal, but not recommended
The Object type in ECMAScript is the base from which all other objects are derived.
All of the properties and methods of the Object type are also
present on other, more specific objects.
Each Object instance has the following properties and methods:
Method | Description |
constructor | The function that was used to create the object. In the previous example, the constructor is the Object() function. |
hasOwnProperty(propertyName ) | Indicates if the given property exists on the object instance (not on the prototype). The property name must be specified as a string (for example, o.hasOwnProperty("name")). |
isPrototypeof(object) | Determines if the object is a prototype of another object. |
propertyIsEnumerable(propertyName ) | Indicates if the given property can be enumerated using the for-in statement (discussed later in this chapter). As with hasOwnProperty(), the property name must be a string. |
toLocaleString() | Returns a string representation of the object that is appropriate for the locale of execution environment. |
toString() | Returns a string representation of the object. |
valueOf() | Returns a string, number, or Boolean equivalent of the object. It often returns the same value as toString(). |
Because Object is the base for all objects in ECMAScript, every object has these base properties and methods.
Arbitrary Precision Integers with BigInt
BigInt is a numeric type whose values are integers.
The type was added to JavaScript
mainly to allow the representation of 64-bit integers.
BigInt values can have thousands or even millions of digits.
Literal
BigInt literals are written as a string of digits followed by a
lowercase letter n.
By default, the are in base 10, but you can use the 0b, 0o, and 0x prefixes for binary, octal,
and hexadecimal BigInts:
1234n // A not-so-big BigInt literal
0b111111n // A binary BigInt
0o7777n // An octal BigInt
0x8000000000000000n // => 2n**63n: A 64-bit integer
You can use BigInt() as a function for converting regular JavaScript numbers or
strings to BigInt values:
BigInt(Number.MAX_SAFE_INTEGER) // => 9007199254740991n
//from string
let string = "1" + "0".repeat(100); // 1 followed by 100 zeros.
BigInt(string) // => 10n**100n: one googol
Arithmetic with BigInt values works like arithmetic with regular JavaScript numbers,
except that division drops any remainder and rounds down (toward zero):
1000n + 2000n // => 3000n
3000n - 2000n // => 1000n
2000n * 3000n // => 6000000n
3000n / 997n // => 3n: the quotient is 3
3000n % 997n // => 9n: and the remainder is 9
(2n ** 131071n) - 1n // A Mersenne prime with 39457 decimal digits
Although the standard +, -, *, /, %, and ** operators work with BigInt,
you may not mix operands of type BigInt with regular number
operands.
Comparison operators, by contrast, do work with mixed numeric types:
1 < 2n // => true
2 > 1n // => true
0 == 0n // => true
0 === 0n // => false: the === checks for type equality as well
The bitwise operators work with BigInt operands.
None of the functions of the Math object accept BigInt operands, however.
Primitive And Reference Values
ECMAScript variables has two different types of data: primitive values and reference values.
Primitive values
Primitive values are simple atomic pieces of data, while reference values are objects that may be made up of multiple values.
When a value is assigned to a variable, the JavaScript engine must determine if it's a primitive or a reference value.
The six primitive types are:
undefined,
null,
Boolean,
Number,
String, and
Symbol.
These variables are accessed by value, because you
are manipulating the actual value stored in the variable.
Reference values
Reference values are objects stored in memory.
JavaScript does not permit direct access of memory locations, so direct manipulation of the object's memory space is not allowed.
When you manipulate an object, you're really working on a reference to that object rather than the actual object itself.
For this reason, such values are said to be accessed by reference.
Dynamic Properties on Primitive And Reference Values
When you work with reference values, you can add, change, or delete properties and methods at any time.
Consider this example:
let person = new Object();
person.name = "CSS";
console.log(person.name); // "CSS"
Here, an object is created and stored in the variable person.
Next, a property called name is added
and assigned the string value of "CSS".
The new property is accessible until the object is destroyed or the property is explicitly removed.
Primitive values can't have properties added to them even though attempting to do so won't cause an error.
Here's an example:
let name = "CSS";
name.age = 12;
console.log(name.age); // undefined
Here, a property called age is defined on the string name and assigned a value of 12.
On the next line, however, the property is gone.
Only reference values can have properties defined dynamically for later use.
Note that the instantiation of a primitive type can be accomplished using only the primitive literal form.
If you were to use the new keyword, JavaScript will create an Object type, but one that behaves like a primitive.
Here's an example to distinguish between the two:
let name1 = "CSS";
let name2 = new String("Javascript");
name1.age = 27;
name2.age = 26;
console.log(name1.age); // undefined
console.log(name2.age); // 26
console.log(typeof name1); // string
console.log(typeof name2); // object
Copying Values on Primitive And Reference Values
Javascript primitive and reference values act differently when
copied from one variable to another.
Primitive value
When a primitive value is assigned from one variable to another,
the value stored on the variable object is created and copied into the location for the new variable.
Consider the following example:
let num1 = 5;
let num2 = num1;
Here, num1 contains the value of 5.
When num2 is initialized to num1, it also gets the value of 5.
This value is completely separate from the one
that is stored in num1 because it's a copy of that value.
Each of these variables can now be used separately with no side effects.
Reference value
When a reference value is assigned from one variable to another, the value stored on the variable object is copied into the location for the new
variable.
This value is a pointer to an object variable object after copy stored on the heap.
Once the operation is complete, two variables point to
exactly the same object, so changes to one are reflected on the other, as in
the following example:
let obj1 = new Object();
let obj2 = obj1;
obj1.name = "CSS";
console.log(obj2.name); // "CSS"
In this example, the variable obj1 is filled with a new instance of an
object.
This value is then copied into obj2, meaning that both variables
are now pointing to the same object.
When the property name is set on obj1, it can later be accessed
from obj2 because they both point to the same object.
Argument Passing on Primitive And Reference Values
All function arguments in ECMAScript are passed by value.
The value outside of the
function is copied into an argument on the inside of the function the same way a value is copied from one variable to another.
If the value is primitive, then it acts just like a primitive variable copy, and if
the value is a reference, it acts just like a reference variable copy.
Pass by value vs Pass by reference
When an argument is passed by value, the value is copied into a local variable.
When an argument is passed by reference, the
location of the value in memory is stored into a local variable, which means that changes to the local variable are reflected outside of the function.
Pass by reference is not supported by Javascript.
Example
Consider the following example:
function addTen(num) {
num += 10;
return num;
}
let count = 20;
let result = addTen(count);
console.log(count); // 20 - no change
console.log(result); // 30
Here, the function addTen() has an argument, num, which is essentially a local variable.
When called, the variable count is passed in as an argument.
This variable has a value of 20, which is copied into the argument num for use inside of addTen().
Within the function, the argument num has its value changed by adding 10, but this doesn't change the original variable count that exists outside of
the function.
The argument num and the variable count are not related and they just have the same value.
If num had been passed by reference (which cannot be done by Javascript), then the value of count would have changed to 30 to reflect the change made inside the function.
Pass reference type
For reference type value such as Javascript objects, for example:
function setName(obj) {
obj.name = "CSS";
}
let person = new Object();
setName(person);
console.log(person.name); // "CSS"
In this code, an object is created and stored in the variable person.
This object is then passed into the setName() method, where it is copied into obj.
Inside the function, obj and person both point to the same object.
The result is that obj is accessing an object by reference, even though it was passed
into the function by value.
When the name property is set on obj inside the function, this change is
reflected outside the function, because the object that it points to exists globally on the heap.
NOT passed by reference
Javascript object is NOT passed by reference.
To prove that objects are passed by value, consider the following modified code:
function setName(obj) {
obj.name = "CSS";
obj = new Object();
obj.name = "HTML";
}
let person = new Object();
setName(person);
console.log(person.name); // "CSS"
This example added two lines to redefine obj as a new object with a different name.
When person is passed into setName(), its name property is set to "CSS".
Then the variable obj is set to be a new object and its name property is set to "HTML".
If person were passed by reference, then person would automatically be changed to point to the object whose name is "HTML".
However, when person.name is accessed again, its value is "CSS", indicating that the original reference remained intact
even though the argument's value changed inside the function.
When obj is overwritten inside the
function, it becomes a pointer to a local object.
That local object is destroyed as soon as the function
finishes executing.
You can think function arguments in ECMAScript as local variables.
Determining Type using instanceof operator
The typeof operator can determine if a variable is a primitive type.
We can use typeof operator to determine if a variable is a string,
number, Boolean, or undefined.
If the value is an object or null, then typeof returns "object", as in
this example:
let s = "CSS";
let b = true;
let i = 2;
let u;
let n = null;
let o = new Object();
console.log(typeof s); // string
console.log(typeof i); // number
console.log(typeof b); // boolean
console.log(typeof u); // undefined
console.log(typeof n); // object
console.log(typeof o); // object
typeof operator has little use for reference values.
instanceof
instanceof operator can check object type.
ECMAScript instanceof operator is used with the following syntax:
result = variable instanceof constructor
The instanceof operator returns true if the variable is an instance of the given reference type.
Consider this example:
console.log(person instanceof Object); // is the variable person an Object?
console.log(colors instanceof Array); // is the variable colors an Array?
console.log(pattern instanceof RegExp); // is the variable pattern a RegExp?
All reference values, by definition, are instances of Object, so the instanceof operator always
returns true when used with a reference value and the Object constructor.
Similarly, if instanceof
is used with a primitive value, it will always return false, because primitives aren't objects.
Execution Context And Scope
The JavaScript execution context of a variable or function defines what other data it has access to, as well as how it should behave.
Each execution context has an associated variable object upon which
all of its defined variables and functions exist.
This object is not accessible by code but is used behind
the scenes to handle data.
Global
The global execution context is the outermost one.
Depending on the host environment for an
ECMAScript implementation, the object representing this context may differ.
In web browsers, the global context is said to be that of the window object, so all global variables and functions defined with var are created as properties and methods on the window object.
Declarations using let and const at the top level are not defined in the global
context, but they are resolved identically on the scope chain.
When an execution context has executed all of its code, it is destroyed.
The global context isn't destroyed until the application exits, such as when a web page is closed or a web browser is shut down.
Function
Each function call has its own execution context.
Whenever code execution flows into a function, the
function's context is pushed onto a context stack.
After the function has finished executing, the stack
is popped, returning control to the previously executing context.
Scope
When code is executed in a context, a scope chain of variable objects is created.
The scope chain provides ordered access to all variables and functions that an execution context has access to.
The top of the scope chain is the variable object of the context whose code is
executing.
If the context is a function, then the activation object is used as the variable object.
This pattern continues until the global context is reached; the
global context's variable object is always the last of the scope chain.
Identifiers are resolved by navigating the scope chain in search of the identifier name.
The search
always begins at the front of the chain and proceeds to the back until the identifier is found.
If the identifier isn't found, typically an error occurs.
Consider the following code:
var color = "blue";
function changeValue() {
if (color === "blue") {
color = "red";
} else {
color = "blue";
}
}
changeValue();
In this simple example, the function changeValue() has a scope chain with two objects in it: its
own variable object, where the arguments object is defined, and the global context's variable object.
The variable color is therefore accessible inside the function, because it can be found in the scope chain.
Local
The locally defined variables can be used interchangeably with global variables in a local context.
Here's an example:
var color = "blue";
function changeValue() {
let anotherValue = "red";
function swapValues() {
let tempValue = anotherValue;
anotherValue = color;
color = tempValue;
// color, anotherValue, and tempValue are all accessible here
}
// color and anotherValue are accessible here, but not tempValue
swapValues();
}
// only color is accessible here
changeValue();
There are three execution contexts in this code:
global context,
the local context of changeValue(), and
the local context of swapValues().
The global context has one variable, color, and one function, changeValue().
The local context of changeValue() has one variable named anotherValue
and one function named swapValues(), but it can also access the variable color from the global context.
The local context of swapValues() has one variable, named tempValue, that is accessible only within that context.
Neither the global context nor the local context of swapValues() has
access to tempValue.
Within swapValues(), though, the variables of the other two contexts are fully
accessible because they are parent execution contexts.
The connection between the contexts is linear and ordered.
Each context can search up the scope chain for variables and
functions, but no context can search down the scope chain into another execution context.
There are three objects in the scope chain for the
local context of swapValues(): the swapValues() variable object, the
variable object from changeValue(), and the global variable object.
The local context of swapValues() begins its search for variable and
function names in its own variable object before moving along the
chain.
The scope chain for the changeValue() context has only two
objects: its own variable object and the global variable object.
This means that it cannot access the context of swapValues().
NOTE Function arguments are considered to be variables and follow the same
access rules as any other variable in the execution context.
Execution Context And Scope Chain Augmentation
Even though there are only two primary types of execution contexts, global and function,
there are other ways to augment the scope chain.
Certain statements
cause a temporary addition to the front of the scope chain that is later removed after code execution.
There are two times when this occurs, specifically when execution enters either of the following:
The catch block in a try-catch statement
A with statement
Both of these statements add a variable object to the front of the scope chain.
For the with statement,
the specified object is added to the scope chain; for the catch statement, a new variable object is created and contains a declaration for the thrown error object.
Consider the following:
function buildUrl() {
let qs = "?debug=true";
with(location){
let url = href + qs;
}
return url;
}
In this example, the with statement is acting on the location object, so location itself is added to the front of the scope chain.
There is one variable, qs, defined in the buildUrl() function.
When the variable href is referenced, it's actually referring to location.href, which is in its own variable object.
When the variable qs is referenced, it's referring to the variable defined in buildUrl(), which
is in the function context's variable object.
Inside the with statement is a variable declaration for url,
which becomes part of the function's context and can, therefore, be returned as the function value.
Function Scope Declaration Using var
When a variable is declared using var, it is automatically added to the most immediate context available.
In a function, the most immediate one is the function's local context; in a with statement, the
most immediate is the function context.
If a variable is initialized without first being declared, it gets
added to the global context automatically, as in this example:
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
let result = add(10, 20); // 30
console.log(sum); // causes an error: sum is not a valid variable
Here, the function add() defines a local variable named sum that contains the result of an addition operation.
This value is returned as the function value, but the variable sum isn't accessible outside the function.
If the var keyword is omitted from this example, sum becomes accessible after add() has been called, as shown here:
function add(num1, num2) {
sum = num1 + num2;
return sum;
}
let result = add(10, 20); // 30
console.log(sum); // 30
Here, the variable sum is initialized to a value without ever having been declared using var.
When add() is called, sum is created in the global context and continues to exist even after the function has completed, allowing you to access it later.
It's advisable to always declare variables before initializing them.
In strict mode, initializing variables without declaration causes an error.
A var declaration will be brought to the top of the function or global scope and before any existing code inside it.
This is referred to as "hoisting".
This allows you to safely use a hoisted variable anywhere in the same scope without consideration for whether or not it was declared yet.
Here is an example of two equivalent code in the global scope:
var name = "css";
// This is equivalent to:
name = 'css';
var name;
Here is an example of two equivalent functions:
function fn1() {
var name = 'css';
}
// This is equivalent to:
function fn2() {
var name;
name = 'css';
}
You can prove to yourself that a variable is hoisted by inspecting it before its declaration.
The hoisting of the declaration means you will see undefined instead of ReferenceError:
console.log(name); // undefined
var name = 'css';
function() {
console.log(name); // undefined
var name = 'css';
}
Block Scope Declaration Using let
ES6 let operates much in the same way as var, but it is scoped at the block level in JavaScript.
Block scope is defined as the nearest set of enclosing curly braces {}.
This means if blocks, while blocks, function blocks, and even standalone blocks will be the extent of the scope of any variable declared with let.
if (true) {/*w w w . d e m o2 s . c o m*/
let a;
}
console.log(a); // ReferenceError: a is not defined
while (true) {
let b;
}
console.log(b); // ReferenceError: b is not defined
function foo() {
let c;
}
console.log(c); // ReferenceError: c is not defined
// This is not an object literal, this is a standalone block.
{
let d;
}
console.log(d); // ReferenceError: d is not defined
let cannot be declared twice inside the same
block scope.
Duplicate var declarations are simply ignored; duplicate let declarations throw a
SyntaxError.
var a;
var a;
// No errors thrown
{
let b;
let b;
}
// SyntaxError: Identifier 'b' has already been declared
The behavior of let is especially useful when using iterators inside loops.
Iterator declarations using
var will bleed outside the loop after it completes, which is frequently a very undesirable behavior.
Consider these two examples:
for (var i = 0; i < 10; ++i) {}
console.log(i); // 10
for (let j = 0; j < 10; ++j) {}
console.log(j); // ReferenceError: j is not defined
Constant Declaration Using const
ES6 introduces const as companion to let.
A variable declared using const must be initialized to some value.
Once declared, it cannot be reassigned to a new value at any point in its lifetime.
const a; // SyntaxError: Missing initializer in const declaration
const b = 3;
console.log(b); // 3
b = 4; // TypeError: Assignment to a constant variable
Apart from its const rule enforcement, const variables behave identically to let variables:
if (true) {
const a = 0;
}
console.log(a); // ReferenceError: a is not defined
while (true) {
const b = 1;
}
console.log(b); // ReferenceError: b is not defined
function foo() {
const c = 2;
}
console.log(c); // ReferenceError: c is not defined
{
const d = 3;
}
console.log(d); // ReferenceError: d is not defined
The const declaration only applies to the top-level primitive or object.
A const variable assigned to an object cannot be reassigned to another reference value, but the keys inside that
object are not protected.
const o1 = {};
o1 = {}; // TypeError: Assignment to a constant variable;
const o2 = {};
o2.name = 'css';
console.log(o2.name); // 'css'
If you wish to make the entire object immutable, you can use Object.freeze(), although attempted property assignment will not raise errors; it will just silently fail:
const o3 = Object.freeze({});
o3.name = 'css';
console.log(o3.name); // undefined
Identifier Lookup
When an identifier is referenced for reading or writing, Javascript
need to determine what identifier it represents.
The search starts at the front of the scope
chain, looking for an identifier with the given name.
If it finds that identifier name in the local
context, then the search stops and the variable is set; if the search doesn't find the variable name, it
continues along the scope chain.
This process continues until the search reaches
the global context's variable object.
If the identifier isn't found there, it hasn't been declared.
Consider the following example:
var color = 'blue';
function getColor() {
return color;
}
console.log(getColor()); // 'blue'
When the function getColor() is called in this example, the variable color is referenced.
At that point, a two-step search begins.
First getColor()'s variable object is searched for an identifier named color.
When it isn't found, the search goes to the next variable object, the global context, and then searches for an identifier named color.
Because color is defined in that variable object, the search ends.
Referencing local variables automatically stops the search from going into
another variable object.
The identifiers in a parent context cannot be referenced if an
identifier in the local context has the same name, as in this example:
var color = 'blue';
function getColor() {
let color = 'red';
return color;
}
console.log(getColor()); // 'red'
Using block scoped declarations does not change the search process, but it can add extra levels to the lexical hierarchy:
var color = 'blue';
function getColor() {
let color = 'red';
{
let color = 'green';
return color;
}
}
console.log(getColor()); // 'green'
In this modified code, a local variable named color is declared inside the getColor() function.
When the function is called, the variable is declared.
When the second line of the function is executed,
it knows that a variable named color must be used.
The search begins in the local context, where it
finds a variable named color with a value of 'green'.
Because the variable was found, the search
stops and the local variable is used, meaning that the function returns 'green'.
String Type
The String data type represents a sequence of zero or more 16-bit Unicode characters.
Strings can be delineated by either double quotes ", single quotes ', or backticks `, so all of the following are legal:
let firstName = "css";
let lastName = 'html';
let lastName = `this is a test from demo2s.com`;
A string beginning with a certain character must end with the same character.
For example, the following will cause a syntax error:
let firstName = 'CSS"; // syntax error - quotes must match
Here are some examples
const a = 'a';
console.log(a);
const b = "b";
console.log(b);
const c = `c`;
console.log(c);
Output:
Operation
To determine the length of a string-the number of 16-bit values it contains-use the
length property of the string:
s.length
JavaScript provides a rich API for working with strings:
let s = "Hello, world"; // Start with some text.
Obtaining portions of a string
s.substring(1,4) // => "ell": the 2nd, 3rd, and 4th characters.
s.slice(1,4) // => "ell": same thing
s.slice(-3) // => "rld": last 3 characters
s.split(", ") // => ["Hello", "world"]: split at delimiter string
Searching a string
s.indexOf("l") // => 2: position of first letter l
s.indexOf("l", 3) // => 3: position of first "l" at or after 3
s.indexOf("zz") // => -1: s does not include the substring "zz"
s.lastIndexOf("l") // => 10: position of last letter l
Boolean searching functions
s.startsWith("Hell") // => true: the string starts with these
s.endsWith("!") // => false: s does not end with that
s.includes("or") // => true: s includes substring "or"
Creating modified versions of a string
s.replace("llo", "ya") // => "Heya, world"
s.toLowerCase() // => "hello, world"
s.toUpperCase() // => "HELLO, WORLD"
s.normalize() // Unicode NFC normalization: ES6
s.normalize("NFD") // NFD normalization.
Inspecting individual (16-bit) characters of a string
s.charAt(0) // => "H": the first character
s.charAt(s.length-1) // => "d": the last character
s.charCodeAt(0) // => 72: 16-bit number at the specified position
s.codePointAt(0) // => 72: ES6, works for codepoints > 16 bits
String padding functions
"x".padStart(3) // => " x": add spaces on the left to a length of 3
"x".padEnd(3) // => "x ": add spaces on the right to a length of 3
"x".padStart(3, "*") // => "**x": add stars on the left to a length of 3
"x".padEnd(3, "-") // => "x--": add dashes on the right to a length of 3
Space trimming functions. trim() is ES5; others ES2019
" test ".trim() // => "test": remove spaces at start and end
" test ".trimStart() // => "test ": remove spaces on left. Also trimLeft
" test ".trimEnd() // => " test": remove spaces at right. Also trimRight
Other string methods
s.concat("!") // => "Hello, world!": just use + operator instead
"<>".repeat(5) // => "<><><><><>": concatenate n copies. ES6
Javascript strings are immutable.
Methods like replace() and
toUpperCase() return new strings: they do not modify the string on which they are
invoked.
Strings can also be treated like read-only arrays, and you can access individual
characters (16-bit values) from a string using square brackets instead of the charAt()
method:
let s = "hello, world";
s[0] // => "h"
s[s.length-1] // => "d"
String Literals
To include a string in a JavaScript program, simply enclose the
characters of the
string within a matched pair of single or double quotes or
backticks ( ' ' or " " or ` `).
Double-quote characters and backticks may be contained within strings delimited by
single-quote characters, and similarly for strings delimited by double quotes and
backticks.
Here are examples of string literals:
"" // The empty string: it has zero characters
'testing'
"3.14"
'name="myform"' //mix
"Wouldn't you prefer paper book?"
`"She said 'hi'", he said.`
Strings delimited with backticks are a feature of ES6, and allow JavaScript expressions
to be embedded within the string literal.
Multiline
You can break a string literal across multiple lines by ending each line but the
last with a backslash (\).
Neither the backslash nor the line terminator that follow it are part of the
string literal.
If you need to include a newline character in a single-quoted or double-quoted string
literal, use the character sequence \n.
The ES6 backtick syntax allows strings to be broken across multiple lines, and in this case, the
line terminators are part of the string literal:
A string representing 2 lines written on one line:
A one-line string written on 3 lines:
"one\
long\
line"
A two-line string written on two lines:
`the newline character at the end of this line
is included literally in this string`
Note that when you use single quotes to delimit your strings, you must be careful
with English contractions and possessives, such as can't.
Since the apostrophe is the same as the single-quote character, you must use the backslash character
(\) to "escape" any apostrophes that appear in single-quoted strings.
In client-side JavaScript programming, JavaScript code may contain strings of HTML
code, and HTML code may contain strings of JavaScript code.
Like JavaScript, HTML
uses either single or double quotes to delimit its strings.
Thus, when combining Java
Script and HTML, it is a good idea to use one style of quotes for JavaScript and the
other style for HTML.
In the following example, the string "Thank you" is single-
quoted within a JavaScript expression, which is then double-quoted within an HTML
event-handler attribute:
<button onclick="alert('Thank you')">Click Me</button>
String Character Escape
The String data type includes several character literals to represent nonprintable
useful characters, as listed in the following table:
LITERAL | MEANING |
\n | New line |
\t | Tab |
\b | Backspace |
\r | Carriage return |
\f | Form feed |
\\ | Backslash (\) |
\' | Single quote '-used when the string is delineated by single quotes. Example: 'He said, \'hi.\''. |
\" | Double quote (")-used when the string is delineated by double quotes. Example: "He said, \"hi.\"". |
\` | Backtick (`)-used when the string is delineated by backticks. Example: `He said, \`hi.\``. |
\xnn | A character represented by hexadecimal code nn (where n is a hexadecimal digit 0-F). Example: \x41 is equivalent to "A". |
\unnnn | A Unicode character represented by the hexadecimal code nnnn (where n is a hexadecimal digit 0-F). Example: \u03a3 is equivalent to the Greek character S. |
These character literals can be included anywhere with a string and will be interpreted as if they were
a single character, as shown here:
let text = "This is the letter sigma: \u03a3.";
The entire escape sequence represents a single character, so it is counted as such.
The length of any string can be returned by using the length property, as follows:
console.log("asdf".length); //4
This property returns the number of 16-bit characters in the string.
If a string contains double-byte characters, the length property may not
accurately return the number of characters in the string.
immutable String
Strings are immutable in ECMAScript, and once they are created, their values cannot
change.
To change the string held by a variable, the original string must be destroyed and the variable
filled with another string containing a new value, like this:
let lang = "Java";
lang = lang + "Script";
Here, the variable lang is defined to contain the string "Java".
On the next line, lang is redefined to combine "Java" with "Script", making its value "JavaScript".
The last step in the process is to destroy the original string "Java" and the string "Script", because
neither is necessary anymore.
All of this happens behind the scenes.
Converting value to a String using toString() method
There are two ways to convert a value into a string.
The first is to use the toString() method that almost every value has.
This method's job is to return the string equivalent of the value.
Consider this example:
let age = 11;
let ageAsString = age.toString(); // the string "11"
let found = true;
let foundAsString = found.toString(); // the string "true"
The toString() method is available on values that are numbers, Booleans, objects, and strings.
If a value is null or undefined, this method is not available.
In most cases, toString() doesn't have any arguments.
However, when used on a number value,
toString() actually accepts a single argument: the radix in which to output the number.
By default, toString() always returns a string that represents the number as a decimal, but by passing in a
radix, toString() can output the value in binary, octal, hexadecimal, or any other valid base, as in
this example:
let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
This example shows how the output of toString() can change for numbers when providing a radix.
The value 10 can be output into any number of numeric formats.
The default is the same as providing a radix of 10.
Converting value to a String using String() method
If you're not sure that a value isn't null or undefined, you can use the String() casting function,
which always returns a string regardless of the value type.
The String() function follows these rules:
If the value has a toString() method, it is called and the result is returned.
If the value is null, "null" is returned.
If the value is undefined, "undefined" is returned.
Consider the following:
let value1 = 10;
let value2 = true;
let value3 = null;
let value4;
console.log(String(value1)); // "10"
console.log(String(value2)); // "true"
console.log(String(value3)); // "null"
console.log(String(value4)); // "undefined"
Here, four values are converted into strings: a number, a Boolean, null, and undefined.
The result for the number and the Boolean are the same as if toString() were called.
Because toString()
isn't available on "null" and "undefined", the String() method simply returns literal text for
those values.
You can also convert a value to a string by adding an empty string ("") to
that value using the plus operator.
String Template Literals
ECMAScript can define strings using template literals.
Unlike single and double quoted, template literals respect new line characters, and can be defined
spanning multiple lines:
let myMultiLineString = 'first line\nsecond line';
console.log(myMultiLineString);
// first line
// second line
let myMultiLineTemplateLiteral = `first line
second line`;
console.log(myMultiLineTemplateLiteral);
// first line
// second line
console.log(myMultiLineString === myMultiLineTemplateLiteral); // true
Output:
The template literals are useful when defining templates, such as HTML:
let pageHTML = `
<div>
<a href="#">
<span>Javascript</span>
</a>
</div>`;
Because template literals will exactly match the whitespace inside the backticks, special care will
need to be applied when defining them.
A correctly formatted template string may appear to have improper indentation:
This template literal has 25 spaces following the line return character
let myTemplateLiteral = `A
B`;
console.log(myTemplateLiteral.length);
Output:
This template literal begins with a line return character
let secondTemplateLiteral = `
A
B`;
console.log(secondTemplateLiteral[0] === '\n'); // true
This template literal has no unexpected whitespace characters
let thirdTemplateLiteral = `A
B`;
console.log(thirdTemplateLiteral[0]);
Output:
String Template Interpolation
Javascript template literals supports for interpolation.
We can insert values at one or more places inside a String template.
Javascript String Templates are special JavaScript syntactical expressions that evaluate into strings.
Template literals are evaluated immediately when they are defined and converted into a string
instance, and any interpolated variables will be drawn from its immediate scope.
This can be accomplished using a JavaScript expression inside ${}:
let name = "Bill";
let greeting = `Hello ${ name }.`; // greeting == "Hello Bill."
let value = 5;
let exponent = '2';
let interpolatedTemplateLiteral =
`${ value } to the ${ exponent } power is ${ value * value }`;
console.log(interpolatedTemplateLiteral); // 5 to the 2 power is 25
The value being interpolated will eventually be coerced into a string using toString(), but any
JavaScript expression can safely be interpolated.
Nesting template strings is safe with no escaping required:
console.log(`Hello, ${ `World` }!`); // Hello, World!
toString() is invoked to coerce expression result into string:
let foo = { toString: () => 'World' };
console.log(`Hello, ${ foo }!`); // Hello, World!
Invoking functions and methods inside interpolated expressions is allowed:
function capitalize(word) {
return `${ word[0].toUpperCase() }${ word.slice(1) }`;
}
console.log(`${ capitalize('hello') }, ${ capitalize('world') }!`);
// Hello, World!
Additionally, templates can safely interpolate their previous value:
let value = '';
function append() {
value = `${value}a`
console.log(value);
}
append(); // a
append(); // aa
append(); // aaa
String Template Literal Tag Functions
Template literals can define tag functions to define custom interpolation behavior.
A tag function is defined as a regular function and is applied to a template literal by being prepended
to it.
The tag function will be passed the template literal split
into its pieces: the first argument is an array of the raw strings, and the remaining arguments are the
results of the evaluated expressions.
The return value of this function will be the string evaluated from the template literal.
let a = 6;/*w w w. d e m o 2 s . c o m */
let b = 9;
function simpleTag(strings, aVal, bVal, sumVal) {
console.log(strings);
console.log(aVal);
console.log(bVal);
console.log(sumVal);
return 'test';
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(untaggedResult); // "6 + 9 = 15"
console.log(taggedResult); // "test"
Because there are a variable number of expression arguments, using the spread operator to combine
them into a single collection is usually prudent:
let a = 6;
let b = 9;
function simpleTag(strings, ...expressions) {
console.log(strings);
for(const expression of expressions) {
console.log(expression);
}
return 'test';
}
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(taggedResult); // "test"
For a template literal with n interpolated values, the number of expression arguments to the tag
function will always be n, and the number of string pieces in the first argument will always be exactly n + 1.
If you wished to zip the strings and the evaluated expressions together into the default
returned string, you could do so as follows:
let a = 6;
let b = 9;
function zipTag(strings, ...expressions) {
return strings[0] +
expressions.map((e, i) => `${e}${strings[i + 1]}`)
.join('');
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = zipTag`${ a } + ${ b } = ${ a + b }`;
console.log(untaggedResult); // "6 + 9 = 15"
console.log(taggedResult); // "6 + 9 = 15"
Get Raw Strings from String template literals
We can get the raw template literal contents without being converted into actual character representations, such as a new line or Unicode character.
This can be done by using the String.raw tag function.
Unicode demo
// \u00A9 is the copyright symbol
console.log(`\u00A9`);
console.log(String.raw`\u00A9`); // \u00A9
Newline demo
console.log(`first line\nsecond line`);
// first line
// second line
console.log(String.raw`first line\nsecond line`); // "first line\nsecond line"
This does not work for actual newline characters: they do not undergo conversion from their plaintext escaped equivalents
console.log(`first line
second line`);
// first line
// second line
console.log(String.raw`first line
second line`);
// first line
// second line
The raw values are available as a property on each element in the string piece collection inside
the tag function:
function printRaw(strings) {
console.log('Actual characters:');
for (const string of strings) {
console.log(string);
}
console.log('Escaped characters;');
for (const rawString of strings.raw) {
console.log(rawString);
}
}
printRaw`a\nb${ 'and' }\n`;
Output:
String Reference Type
The String type is the object representation for strings and is created using the String constructor
as follows:
let stringObject = new String("hi");
The methods of a String object are available on all string primitives.
All three of the inherited methods-valueOf(), toLocaleString(), and toString()-return the object's primitive string value.
Each instance of String contains a single property, length, which indicates the number of characters
in the string.
Consider the following example:
let stringValue = "hello world";
console.log(stringValue.length); // "11"
This example outputs "11", the number of characters in "hello world".
Note that even if the string contains a double-byte character, each
character is still counted as one.
The String type has a large number of methods to aid in the dissection and manipulation of strings
in ECMAScript.
String Character
JavaScript strings consist of 16 bit code units.
For most characters, each 16 bit code unit will correspond to a single character.
Length
The length property indicates how many 16 bit code units occur inside
the string:
let message = "abcde";
console.log(message.length); // 5
charAt()
The charAt() returns the character at a given index, specified by an integer argument to
the method.
This method finds the 16 bit code unit at the specified index and returns the
character that corresponds to that code unit:
let message = "abcde";
console.log(message.charAt(2)); // "c"
charCodeAt()
You can inspect the character encoding of a given code unit with the charCodeAt() method.
This method returns the code unit value at a given index, specified by an integer argument to the method.
This method is demonstrated here:
let message = "abcde";
// Unicode "Latin small letter C" is U+0063
console.log(message.charCodeAt(2)); // 99
// Decimal 99 === Hexadecimal 63
console.log(99 === 0x63); // true
fromCharCode()
The fromCharCode() method is used for creating characters in a string from their UTF-16 code unit
representation.
This method accepts any number of numbers and returns their character equivalents
concatenated into a string:
// Unicode "Latin small letter A" is U+0061 and === 97
// Unicode "Latin small letter B" is U+0062 and === 98
// Unicode "Latin small letter C" is U+0063 and === 99
// Unicode "Latin small letter D" is U+0064 and === 100
// Unicode "Latin small letter E" is U+0065 and === 101
console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
console.log(String.fromCharCode(97, 98, 99, 100, 101)); // "abcde"
For characters in the range of U+0000 to U+FFFF, length, charAt(), charCodeAt(), and
fromCharCode() all behave exactly as you would expect them to.
This is because every character is
represented by exactly 16 bits, and each of these methods are all operating on 16 bit code units.
String normalize Unicode character
Some Unicode characters can be encoded in more than one way.
Sometimes, a character can be
represented by either a single BMP character or a surrogate pair.
For example, consider the following:
// U+00C5: Latin capital letter A with ring above
console.log(String.fromCharCode(0x00C5));
// U+212B: Angstrom sign
console.log(String.fromCharCode(0x212B));
Comparison operators do not care about the visual appearance of characters, and so these three will
be considered distinct:
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
console.log(a1, a2);
console.log(a1 === a2); // false
Normalization
We can normalize Unicode by four normalization forms.
The unicode character can be normalized into a consistent format irrespective of their character code derivation.
These four normalization forms can be applied to a string using the normalize() method.
Normalization Form D (NFD),
Normalization Form C (NFC),
Normalization Form KD (NFKD), and
Normalization Form KC (NFKC)
The normalize() method should be provided with a string identifier to specify which
normalization form to apply: NFD, NFC, NFKD, or NFKC.
It is possible to determine if a string is already normalized by checking it against the return value of
normalize():
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
// U+00C5 is the NFC/NFKC normalized form of 0+212B
console.log(a1 === a1.normalize("NFD")); // false
console.log(a1 === a1.normalize("NFC")); // true
console.log(a1 === a1.normalize("NFKD")); // false
console.log(a1 === a1.normalize("NFKC")); // true
// U+212B is non-normalized
console.log(a2 === a2.normalize("NFD")); // false
console.log(a2 === a2.normalize("NFC")); // false
console.log(a2 === a2.normalize("NFKD")); // false
console.log(a2 === a2.normalize("NFKC")); // false
Selecting a normal form will allow for the comparison operator to behave as expected between
identical characters:
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
console.log(a1.normalize("NFD") === a2.normalize("NFD")); // true
String concatenation
Several methods manipulate the values of strings.
concat()
The first of these methods is concat(), which is
used to concatenate one or more strings to another, returning the concatenated string as the result.
Consider the following example:
let stringValue = "hello ";
let result = stringValue.concat("world");
console.log(result); // "hello world"
console.log(stringValue); // "hello"
The result of calling the concat() method on stringValue in this example is "hello world"-
the value of stringValue remains unchanged.
The concat() method accepts any number of arguments,
so it can create a string from any number of other strings, as shown here:
let stringValue = "hello ";
let result = stringValue.concat("world", "!");
console.log(result); // "hello world!"
console.log(stringValue); // "hello"
This modified example concatenates "world" and "!" to the end of "hello".
The addition operator (+) is used more often than concat() method.
Get String sub string using slice(), substr(), and substring()
ECMAScript provides three methods for creating string values from a substring: slice(), substr(),
and substring().
All three methods return a substring of the string they act on, and all accept either
one or two arguments.
The first argument is the position where capture of the substring begins; the
second argument, if used, indicates where the operation should stop.
For slice() and substring(),
this second argument is the position before which capture is stopped. All characters up to this point
are included except the character at that point.
For substr(), the second argument is the number of characters to return.
If the second argument is omitted in any case, it is assumed that the ending
position is the length of the string.
slice(), substr(), and substring() do not alter the value of the string itself-they simply return a primitive string value as
the result, leaving the original unchanged.
Consider this example:
let stringValue = "hello world";
console.log(stringValue.slice(3)); // "lo world"
console.log(stringValue.substring(3)); // "lo world"
console.log(stringValue.substr(3)); // "lo world"
console.log(stringValue.slice(3, 7)); // "lo w"
console.log(stringValue.substring(3,7)); // "lo w"
console.log(stringValue.substr(3, 7)); // "lo worl"
In this example, slice(), substr(), and substring() are used in the same manner and, in most
cases, return the same value.
When given just one argument, 3, all three methods return "lo world"
because the second "l" in "hello" is in position 3.
When given two arguments, 3 and 7, slice()
and substring() return "lo w", while
substr() returns "lo worl" because the second argument specifies the number of characters
to return.
Negative index
There are different behaviors for these methods when an argument is a negative number.
For the slice() method, a negative argument is treated as the length of the string plus the
negative argument.
For the substr() method, a negative first argument is treated as the length of the string plus the
number, whereas a negative second number is converted to 0.
For the substring() method, all negative numbers are converted to 0.
Consider this example:
let stringValue = "hello world";
console.log(stringValue.slice(-3)); // "rld"
console.log(stringValue.substring(-3)); // "hello world"
console.log(stringValue.substr(-3)); // "rld"
console.log(stringValue.slice(3, -4)); // "lo w"
console.log(stringValue.substring(3, -4)); // "hel"
console.log(stringValue.substr(3, -4)); // "" (empty string)
When slice() and substr() are called with a single negative argument, they act the same.
This occurs because -3 is translated into
7, which is the length plus the argument, effectively making the calls slice(7) and substr(7).
The substring() method returns the entire string because -3 is translated to 0.
When the second argument is negative, the three methods act differently from one another.
The slice() method translates the second argument to 7, making the call equivalent to slice(3, 7)
and so returning "lo w".
For the substring() method, the second argument gets translated to 0,
making the call equivalent to substring(3, 0), which is actually equivalent to substring(0,3)
because this method expects that the smaller number is the starting position and the larger one is the
ending position.
For the substr() method, the second argument is also converted to 0, which means
there should be zero characters in the returned string, leading to the return value of an empty string.
String Search Location Methods
There are two methods for locating substrings within another string: indexOf() and
lastIndexOf().
Both methods search a string for a given substring and return the position or -1
if the substring isn't found.
The indexOf() method begins looking for the substring at the beginning of the string, whereas the lastIndexOf() method begins
looking from the end of the string.
Consider this example:
let stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7
Here, the first occurrence of the string "o" is at position 4, which is the "o" in "hello".
The last
occurrence of the string "o" is in the word "world", at position 7.
If there is only one occurrence of "o" in the string, then indexOf() and lastIndexOf() return the same position.
Each method accepts an optional second argument that indicates the position to start searching
from within the string.
The indexOf() method will start searching from that position and go toward the end of the string, ignoring everything before the start position.
The last IndexOf() starts searching from the given position and continues searching toward the
beginning of the string, ignoring everything between the given position and the end of the string.
Here's an example:
let stringValue = "hello world";
console.log(stringValue.indexOf("o", 6)); // 7
console.log(stringValue.lastIndexOf("o", 6)); // 4
When the second argument of 6 is passed into each method, the results are the opposite from the
previous example.
indexOf() returns 7 because it starts searching the string from position 6
(the letter "w") and continues to position 7, where "o" is found.
The lastIndexOf() method returns
4 because the search starts from position 6 and continues back toward the beginning of the string,
where it encounters the "o" in "hello".
Using this second argument allows you to locate all instances
of a substring by looping callings to indexOf() or lastIndexOf(), as in the following example:
let stringValue = "Cate Kate Jane Jason Joe";
let positions = new Array();
let pos = stringValue.indexOf("e");
while(pos > -1) {
positions.push(pos);
pos = stringValue.indexOf("e", pos + 1);
}
console.log(positions);
Output:
This example works through a string by constantly increasing the position at which indexOf()
should begin.
It begins by getting the initial position of "e" in the string and then enters a loop that
continually passes in the last position plus one to indexOf(), ensuring that the search continues
after the last substring instance.
Each position is stored in the positions array so the data can be used later.
String Inclusion Contains Methods
ECMAScript 6 added three additional methods for determining if a string is included inside another
string: startsWith(), endsWith(), and includes().
All methods search a string for a given substring and return a boolean indicating whether it is not included.
startsWith() checks for a match beginning at index 0.
endsWith() checks for a match beginning at index of (string.length - substring.length).
includes() checks the entire string.
let message = "foobarbaz";
console.log(message.startsWith("foo")); // true
console.log(message.startsWith("bar")); // false
console.log(message.endsWith("baz")); // true
console.log(message.endsWith("bar")); // false
console.log(message.includes("bar")); // true
console.log(message.includes("qux")); // false
The startsWith() and includes() methods accept an optional second argument that indicates the
position to start searching from within the string.
The methods will start searching
from that position and go toward the end of the string, ignoring everything before the start position.
Here's an example:
let message = "foobarbaz";
console.log(message.startsWith("foo")); // true
console.log(message.startsWith("foo", 1)); // false
console.log(message.includes("bar")); // true
console.log(message.includes("bar", 4)); // false
The endsWith() method accepts an optional second argument that indicates the position that should
be treated as the end of the string.
If this value is not provided, the length of the string is used by
default.
When a second argument is provided, the method will treat the string as if it only has that
many characters:
let message = "foobarbaz";
console.log(message.endsWith("bar")); // false
console.log(message.endsWith("bar", 6)); // true
Use String trim() Method to remove leading and trailing white space
ECMAScript features a trim() method on all strings.
The trim() method creates a copy of the
string, removes all leading and trailing white space, and then returns the result.
For example:
let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();
console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // "hello world"
Since trim() returns a copy of a string, the original string remains intact with leading and
trailing white space in place.
The trimLeft() and trimRight() methods remove white space only from the
beginning or end of the string, respectively.
Create a String via repeat() Method
ECMAScript features a repeat() method on all strings.
The repeat() method accepts a single
integer argument count, copies the string count times, and concatenates all the copies.
let stringValue = "hi ";
console.log(stringValue.repeat(6) + "hello");
Output:
Pad a String via padStart() and padEnd() Methods
The padStart() and padEnd() methods will copy a string and, if the length of the string is less than
the specified length, add padding to either side of a string to extend it to a certain length.
The first argument is the desired length, and the second is the optional string to add as a pad.
If not provided, the U+0020 'space' character will be used.
let stringValue = "foo";
console.log(stringValue.padStart(6)); // " foo"
console.log(stringValue.padStart(9, ".")); // "......foo"
console.log(stringValue.padEnd(6)); // "foo "
console.log(stringValue.padEnd(9, ".")); // "foo......"
The optional argument is not limited to a single character.
If provided a multiple-character string,
the method will use the concatenated padding and truncate it to the exact length.
Additionally, if the
length is less than or equal to the string length, the operation is effectively a no-op.
let stringValue = "foo";
console.log(stringValue.padStart(8, "bar")); // "barbafoo"
console.log(stringValue.padStart(2)); // "foo"
console.log(stringValue.padEnd(8, "bar")); // "foobarba"
console.log(stringValue.padEnd(2)); // "foo"
String Iterators
The string prototype exposes an @@iterator method on each string, which allows for iteration
through individual characters.
Manual use of the iterator works as follows:
let message = "abc";
let stringIterator = message[Symbol.iterator]();
console.log(stringIterator.next()); // {value: "a", done: false}
console.log(stringIterator.next()); // {value: "b", done: false}
console.log(stringIterator.next()); // {value: "c", done: false}
console.log(stringIterator.next()); // {value: undefined, done: true}
When used in a for of loop, the loop will use this iterator to visit each character in order:
for (const c of "abcde") {
console.log(c);
}
Output:
String Destructuring
The string iterator allows for interoperability with the destructuring
operator.
This allows you to easily split a string by its characters:
let message = "abcde";
console.log([...message]);
Output:
Use String Case Methods to change string case
Four methods perform case conversion: toLowerCase(), toLocaleLowerCase(), toUpperCase(), and toLocaleUpperCase().
The toLowerCase() and toUpperCase() methods are the original methods.
The toLocaleLowerCase() and toLocaleUpperCase() methods are intended to be
implemented based on a particular locale.
In many locales, the locale-specific methods are identical
to the generic ones; however, a few languages (such as Turkish) apply special rules to Unicode case
conversion, and this necessitates using the locale-specific methods for proper conversion.
Here are some examples:
let stringValue = "hello world";
console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD"
console.log(stringValue.toUpperCase()); // "HELLO WORLD"
console.log(stringValue.toLocaleLowerCase()); // "hello world"
console.log(stringValue.toLowerCase()); // "hello world"
This code outputs "HELLO WORLD" for both toLocaleUpperCase() and toUpperCase(), just as
"hello world" is output for both toLocaleLowerCase() and toLowerCase().
String Pattern-Matching Methods
The String type has several methods designed to pattern-match within the string.
match()
String match() is the same as calling a RegExp object's exec() method.
The match() method accepts a single argument, which is either a regular-expression string or a RegExp
object.
Consider this example:
let text = "cat, bat, sat, fat";
let pattern = /.at/;
// same as pattern.exec(text)
let matches = text.match(pattern);
console.log(matches.index); // 0
console.log(matches[0]); // "cat"
console.log(pattern.lastIndex); // 0
The array returned from match() is the same array that is returned when the RegExp object's exec()
method is called with the string as an argument:
the first item is the string that matches the entire pattern, and
each other item (if applicable) represents capturing groups in the expression.
search()
String search() finds patterns.
String search() method accepts a regular expression specified by either a string or a RegExp object as its parameter.
The search() method returns the index of the first pattern occurrence in the string or -1 if it's not found.
search() always begins looking for the pattern at the beginning of the string.
Consider this example:
let text = "cat, bat, sat, fat";
let pos = text.search(/at/);
console.log(pos); // 1
Here, search(/at/) returns 1, which is the first position of "at" in the string.
Use replace() method to replace sub string
To replace substrings, ECMAScript provides the replace() method.
This method accepts two arguments.
The first argument can be a RegExp object or a string which is not converted to
a regular expression, and the second argument can be a string or a function.
If the first argument is a string, then only the first occurrence of the substring will be replaced.
The only way to replace all instances of a substring is to provide a regular expression with the global flag specified, as in
this example:
let text = "cat, bat, sat, fat";
let result = text.replace("at", "ond");
console.log(result); // "cond, bat, sat, fat"
result = text.replace(/at/g, "ond");
console.log(result); // "cond, bond, sond, fond"
In this example, the string "at" is first passed into replace() with a replacement text of "ond".
The result of the operation is that "cat" is changed to "cond", but the rest of the string remains intact.
By changing the first argument to a regular expression with the global flag set, each instance of "at" is
replaced with "ond".
When the second argument is a string, there are several special character sequences that can be used
to insert values from the regular-expression operations.
ECMA-262 specifies the following table of values.
Sequence | Replacement Text |
$$ | $ |
$& | The substring matching the entire pattern. Same as RegExp.lastMatch. |
$' | The part of the string occurring before the matched substring. Same as RegExp.rightContext. |
$` | The part of the string occurring after the matched substring. Same as RegExp.leftContext. |
$n | The nth capture, where n is a value 0-9. For instance, $1 is the first capture, $2 is the second, and so on. If there is no capture then the empty string is used. |
$nn | The nnth capture, where nn is a value 01-99. For instance, $01 is the first capture, $02 is the second, and so on. If there is no capture then the empty string is used. |
Using these special sequences allows replacement using information about the last match, such as in
this example:
let text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
console.log(result); // word (cat), word (bat), word (sat), word (fat)
Here, each word ending with "at" is replaced with "word" followed in parentheses by what it
replaces by using the $1 sequence.
The second argument of replace() may also be a function.
When there is a single match, the function gets passed three arguments: the string match, the position of the match within the string, and
the whole string.
When there are multiple capturing groups, each matched string is passed in as an
argument, with the last two arguments being the position of the pattern match in the string and the
original string.
The function should return a string indicating what the match should be replaced
with.
Using a function as the second argument allows more granular control over replacement text, as
in this example:
function htmlEscape(text) {
return text.replace(/[<>"&]/g, function(match, pos, originalText) {
switch(match) {
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\"":
return """;
}
});
}
console.log(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
Output:
Here, the function htmlEscape() is defined to escape four characters for insertion into HTML: the
less-than, greater-than, ampersand, and double-quote characters all must be escaped.
We use a regular expression to look for those characters and then define a
function that returns the specific HTML entities for each matched character.
Use split() method to split string
String split() separates the string into an array of substrings based on a separator.
The separator may be a string or a RegExp object.
The string is not converted to a regular expression for this method.
An optional second argument, the array limit,
ensures that the returned array will be no larger than a certain size.
Consider this example:
let sText = "A,B,C,D,E";
let s1 = sText.split(",");
let s2 = sText.split(",", 2);
let s3 = sText.split(/[^\,]+/);
console.log(s1);
console.log(s2);
console.log(s3);
Output:
In this example, the string sText is a comma-separated string of letters.
The call to split(",")
retrieves an array of those letters, splitting the string on the comma character.
To truncate the results
to only two items, a second argument of 2 is specified.
Last, using a regular expression, it's possible
to get an array of the comma characters.
Compare String with localeCompare() Method
String localeCompare() compares one string to another and returns one of three
values as follows:
If the string should come alphabetically before the string argument, a negative number is returned.
If the string is equal to the string argument, 0 is returned.
If the string should come alphabetically after the string argument, a positive number is returned.
Here's an example:
let stringValue = "y";
console.log(stringValue.localeCompare("b")); // 1
console.log(stringValue.localeCompare("y")); // 0
console.log(stringValue.localeCompare("z")); // -1
In this code, the string "y" is compared to three different values: "b", "y", and
"z".
Because "b" comes alphabetically before "y", localeCompare() returns 1;
"y" is equal to "y", so localeCompare() returns 0 for that line; and "z" comes after
"y", so localeCompare() returns -1 for that line.
The returned values are implementation-specific, it is best to use localeCompare() as shown in this example:
function determineOrder(value) {
let stringValue = "y";
let result = stringValue.localeCompare(value);
if (result < 0) {
console.log(`The string 'y' comes before the string '${value}'.`);
} else if (result > 0) {
console.log(`The string 'y' comes after the string '${value}'.`);
} else {
console.log(`The string 'y' is equal to the string '${value}'.`);
}
}
determineOrder("b");
determineOrder("y");
determineOrder("z");
Output:
localeCompare() is depended on an implementation's locale (country and language).
In the United States, where English is the standard language
for ECMAScript implementations, localeCompare() is case-sensitive, determining that uppercase
letters come alphabetically after lowercase letters.
This may not be the case in other locales.
String HTML Methods
The web browser vendors recognized a need early on to format HTML dynamically using JavaScript.
As a result, they extended the specification to include several methods specifically designed to aid in
common HTML formatting tasks.
The following table enumerates the HTML methods.
Typically these methods aren't used, because they tend to create non-semantic markup.
Method | Output |
anchor(name) | <a name="name ">string</a> |
big() | <big>string</big> |
bold() | <b>string</b> |
fixed() | <tt>string</tt> |
fontcolor(color) | <font color="color">string</font> |
fontsize(size) | <font size="size">string</font> |
italics() | <i>string</i> |
link(url) | <a href="url">string</a> |
small() | <small>string</small> |
strike() | <strike>string</strike> |
sub() | <sub>string</sub> |
sup() | <sup>string</sup> |
String search()
The search() method executes a search for a regular expression.
Syntax
str.search(regexp)
Parameters
Item | Description |
regexp | A regular expression object. |
If a non-RegExp object regexp is passed, it is implicitly converted to a RegExp with new RegExp(regexp).
Return value
The index of the first match between the regular expression and the given string, or -1 if no match was found.
The following example searches a string with two different regex objects to show a successful search (positive value) vs. an unsuccessful search (-1)
let str = "hey JudE";
let re = /[A-Z]/g;
let reDot = /[.]/g;
console.log(str.search(re));
// returns 4, which is the index of the first capital letter "J"
console.log(str.search(reDot));
// returns -1 cannot find '.' dot punctuation
Result
String slice()
The slice() method extracts a sub string and returns it as a new string, without modifying the original string.
Syntax
str.slice(begin_Index[, end_Index])
Parameters
Item | Description |
begin_Index | The zero-based index at which to begin extraction. |
end_Index | Optional The zero-based index before which to end extraction. The character at this index will not be included. |
begin_Index possible values:
If negative, it is treated as str.length + begin_Index. For example, if begin_Index is -3, it is treated as str.length - 3.)
If begin_Index is not a number after Number(begin_Index), it is treated as 0.
If begin_Index is greater than or equal to str.length, an empty string is returned.
end_Index possible values:
If end_Index is omitted or undefined, or greater than str.length, slice() extracts to the end of the string.
If negative, it is treated as str.length + end_Index. For example, if end_Index is -3, it is treated as str.length - 3.
If it is not undefined and not a number after Number(end_Index), an empty string is returned.
If end_Index is specified and start_Index is negative, end_Index should be negative, otherwise an empty string is returned. For example, slice(-3, 0) returns "".
If end_Index is specified, and start_Index and end_Index are both positive or negative, end_Index should be greater than start_Index, otherwise an empty string is returned. For example, slice(-1, -3) or slice(3, 1) returns "".
Return value
A new string containing the extracted section of the string.
The following example uses slice() to create a new string.
let str1 = 'This is a test from us.',
str2 = str1.slice(1, 8),
str3 = str1.slice(4, -2),
str4 = str1.slice(12),
str5 = str1.slice(30);
console.log(str2);
console.log(str3);
console.log(str4);
console.log(str5);
Result
Using slice() with negative indexes
The following example uses slice() with negative indexes.
let str = 'This is a test from us.';
a = str.slice(-3);
console.log(a);
a = str.slice(-3, -1);
console.log(a);
a = str.slice(0, -1);
console.log(a);
Result
This example counts backwards from the end of the string by 11 to find the start index and forwards from the start of the string by 16 to find the end index.
let str = 'This is a test from us.';
console.log(str.slice(-11, 16));
Result
Here it counts forwards from the start by 11 to find the start index and backwards from the end by 7 to find the end index.
let str = 'This is a test from us.';
console.log(str.slice(11, -7));
Result
These arguments count backwards from the end by 5 to find the start index and backwards from the end by 1 to find the end index.
let str = 'This is a test from us.';
console.log(str.slice(-5, -1));
Result
String small()
Deprecated
The small() method creates a <small> HTML element to display a string in a small font.
The small() method embeds a string in a <small> tag: "<small>str</small>".
Syntax
str.small()
Return value
A string containing a <small> HTML element.
The following example uses string methods to change the size of a string:
var worldString = 'Hello, world';
console.log(worldString.small()); // <small>Hello, world</small>
console.log(worldString.big()); // <big>Hello, world</big>
console.log(worldString.fontsize(7)); // <font size="7">Hello, world</fontsize>
Result
With the element.style object you can get the element's style attribute and set it:
document.getElementById('yourElemId').style.fontSize = '0.7em';
String split()
The split() method divides a String into an array of substrings by a separator.
Syntax
str.split([separator[, limit]])
Item | Description |
separator | Optional The separator can be a simple string or it can be a regular expression. |
limit | Optional A non-negative integer specifying a limit on the number of substrings to be included in the array. If limit is 0, [] is returned. |
Return value
An Array of strings, split at each point where the separator occurs in the given string.
If the string and separator are both empty strings, an empty array is returned.
const myString = ''
const splits = myString.split()
console.log(splits)
Result
The following example defines a function that splits a string into an array of strings using separator.
After splitting the string, the function logs messages indicating the original string, the separator used, the number of elements in the array, and the individual array elements.
function splitString(stringToSplit, separator) {
const arrayOfStrings = stringToSplit.split(separator)
console.log('The original string is: ', stringToSplit)
console.log('The separator is: ', separator)
console.log('The array has ', arrayOfStrings.length,
' elements: ', arrayOfStrings.join(' / '))
}
const tempestString = 'This is a test from demo2s.com.'
const monthString = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'
const space = ' '
const comma = ','
splitString(tempestString, space)
splitString(tempestString)
splitString(monthString, comma)
Result
In the following example, split() looks for zero or more spaces, followed by a semicolon, followed by zero or more spaces and, when found, removes the spaces and the semicolon from the string.
const names =
'CSS HTML;HTML Javascript; Javascript SQL; SQL Database ;Database ABC '
console.log(names)
const re = /\s*(?:;|$)\s*/
const nameList = names.split(re)
console.log(nameList)
Result
In the following example, split() looks for spaces in a string and returns the first 3 splits that it finds.
const myString = 'Hello World. How are you doing?'
const splits = myString.split(' ', 3)
console.log(splits)
Result
Splitting with a RegExp to include parts of the separator in the result
If separator is a regular expression that contains capturing parentheses (), matched results are included in the array.
const myString = 'Hello 1 word. Sentence number 2.'
const splits = myString.split(/(\d)/)
console.log(splits)
Result
Note: \d matches the character class for digits between 0 and 9.
Reversing a String using split()
const str = 'demo2s.com'
const strReverse = str.split('').reverse().join('')
console.log(strReverse);
Result
String startsWith()
The startsWith() method determines whether a string begins with a specified string, returning true or false.
This method is case-sensitive.
Syntax
str.startsWith(search_String[, position])
Parameters
Item | Description |
search_String | The characters to be searched for at the start of this string. |
position | Optional The position in this string at which to begin searching for search_String. Defaults to 0. |
Return value
true if the given characters are found at the beginning of the string; otherwise, false.
Using startsWith()
//startswith
let str = 'To be, or not to be, that is the question.'
console.log(str.startsWith('To be')) // true
console.log(str.startsWith('not to be')) // false
console.log(str.startsWith('not to be', 10)) // true
Result
String strike()
Deprecated
The strike() method creates a <strike> HTML element that causes a string to be displayed as struck-out text.
The strike() method embeds a string in a <strike> tag: "<strike>str</strike>".
Syntax
str.strike()
Return value
A string containing a <strike> HTML element.
Using strike()
The following example uses string methods to change the formatting of a string:
var worldString = 'Hello, world';
console.log(worldString.blink()); // <blink>Hello, world</blink>
console.log(worldString.bold()); // <b>Hello, world</b>
console.log(worldString.italics()); // <i>Hello, world</i>
console.log(worldString.strike()); // <strike>Hello, world</strike>
Result
String constructor
The String constructor can create a new String object.
When called instead as a function, it performs type conversion to a primitive string.
Syntax
new String(thing) //constructor syntax
String(thing) //call as a function
Item | Description |
thing | Anything to be converted to a string. |
String constructor and String function
String function and String constructor produce different results:
let a = typeof String('Hello world'); // string
console.log(a);
a = typeof new String('Hello world'); // object
console.log(a);
Result
Here, the function produces a string (the primitive type) as promised.
However, the constructor produces an instance of the type String, which is an object wrapper.
You should not use the String constructor at all.
String sub()
Deprecated
The sub() method creates a <sub> HTML element that causes a string to be displayed as subscript.
The sub() method embeds a string in a <sub> tag: "<sub>str</sub>".
Syntax
str.sub()
Return value
A string containing a <sub> HTML element.
Using sub() and sup() methods
The following example uses the sub() and sup() methods to format a string:
var superText = 'superscript';
var subText = 'subscript';
console.log('This is what a ' + superText.sup() + ' looks like.');
console.log('This is what a ' + subText.sub() + ' looks like.');
Result
String substr()
The substr() method returns a portion of the string, starting at the specified index and extending for a given number of characters afterwards.
Syntax
str.substr(start[, length])
Item | Description |
start | The index of the first character to include in the returned substring. |
length | Optional. The number of characters to extract. |
If start is a positive number, the index starts counting at the start of the string. Its value is capped at str.length.
If start is a negative number, the index starts counting from the end of the string. Its value is capped at -str.length.
If length is omitted, substr() extracts characters to the end of the string.
If length is undefined, substr() extracts characters to the end of the string.
If length is a negative number, it is treated as 0.
For both start and length, NaN is treated as 0.
Return value
A new string containing the specified part of the given string.
substr() extracts length characters from a str, counting from the start index.
var aString = 'demo2s.com';
console.log(aString.substr(0, 1));
console.log(aString.substr(1, 0));
console.log(aString.substr(-1, 1));
console.log(aString.substr(1, -1));
console.log(aString.substr(-3));
console.log(aString.substr(1));
console.log(aString.substr(-20, 2));
console.log(aString.substr(20, 2));
Result
String substring()
The substring() method returns a string part between the start and end indexes, or to the end of the string.
Syntax
str.substring(index_Start[, index_End])
Parameters
Item | Description |
index_Start | The index of the first character to include in the returned substring. |
index_End | Optional The index of the first character to exclude from the returned substring. |
substring() extracts characters from index_Start up to but not including index_End. In particular:
If index_End is omitted, substring() extracts characters to the end of the string.
If index_Start is equal to index_End, substring() returns an empty string.
If index_Start is greater than index_End, then the effect of substring() is as if the two arguments were swapped
Any argument value that is less than 0 or greater than stringName.length is treated as if it were 0
and stringName.length, respectively.
Any argument value that is NaN is treated as if it were 0.
Return value
A new string containing the specified part of the given string.
The following example uses substring() to display characters from the string 'demo2s.com':
let anyString = 'demo2s.com'
console.log(anyString.substring(0, 1));
console.log(anyString.substring(1, 0));
console.log(anyString.substring(0, 6));
console.log(anyString.substring(4));
console.log(anyString.substring(4, 7));
console.log(anyString.substring(7, 4));
console.log(anyString.substring(0, 7));
console.log(anyString.substring(0, 10));
Result
Using substring() with length property
The following example uses the substring() method and length property to extract the last characters of a particular string.
Get the last four characters:
let anyString = 'demo2s.com';
let anyString4 = anyString.substring(anyString.length - 4);
console.log(anyString4);
Result
Get the last 5 characters
let anyString = 'demo2s.com';
let anyString5 = anyString.substring(anyString.length - 5);
console.log(anyString5);
Result
The difference between substring() and substr()
The arguments of substring() represent the starting and ending indexes, while the arguments of substr() represent the starting index and the number of characters to include in the returned string.
substr() is considered a legacy feature in ECMAScript.
let text = 'demo2s.com';
console.log(text.substring(2,5));
console.log(text.substr(2,3))
Result
Differences between substring() and slice()
The substring() method swaps its two arguments if index_Start is greater than index_End, meaning that a string is still returned.
The slice() method returns an empty string if this is the case.
let text = 'demo2s.com';
console.log(text.substring(5, 2));
console.log(text.slice(5, 2));
Result
If either or both of the arguments are negative or NaN, the substring() method treats them as if they were 0.
let text = 'demo2s.com';
console.log(text.substring(-5, 2));
console.log(text.substring(-5, -2));
Result
slice() also treats NaN arguments as 0, but when it is given negative values it counts backwards from the end of the string to find the indexes.
let text = 'demo2s.com';
console.log(text.slice(-5, 2));
console.log(text.slice(-5, -2));
Result
String sup()
Deprecated
The sup() method creates a <sup> HTML element that causes a string to be displayed as superscript.
The sup() method embeds a string in a <sup> tag: "<sup>str</sup>".
Syntax
str.sup()
Return value
A string containing a <sup> HTML element.
Using sub() and sup() methods
The following example uses the sub() and sup() methods to format a string:
var superText = 'superscript';
var subText = 'subscript';
console.log('This is what a ' + superText.sup() + ' looks like.');
console.log('This is what a ' + subText.sub() + ' looks like.');
Result
String toLocaleLowerCase()
The toLocaleLowerCase() method returns the calling string value converted to lower case, according to any locale-specific case mappings.
Syntax
str.toLocaleLowerCase()
str.toLocaleLowerCase(locale)
str.toLocaleLowerCase([locale, locale, ...])
Parameters
Item | Description |
locale | Optional |
The locale parameter indicates the locale to be used to convert to lower case according to any locale-specific case mappings.
If multiple locales are given in an Array, the best available locale is used.
The default locale is the host environment's current locale.
Return value
A new string representing the calling string converted to lower case, according to any locale-specific case mappings.
Using toLocaleLowerCase()
let a = 'ALPHABET'.toLocaleLowerCase(); // 'alphabet'
console.log(a);
a = '\u0130'.toLocaleLowerCase('tr') === 'i'; // true
console.log(a);
a = '\u0130'.toLocaleLowerCase('en-US') === 'i'; // false
console.log(a);
let locales = ['tr', 'TR', 'tr-TR', 'tr-u-co-search', 'tr-x-turkish'];
let b = '\u0130'.toLocaleLowerCase(locales) === 'i'; // true
console.log(b);
Result
String toLocaleUpperCase()
The toLocaleUpperCase() method returns the calling string value converted to upper case, according to any locale-specific case mappings.
Syntax
str.toLocaleUpperCase()
str.toLocaleUpperCase(locale)
str.toLocaleUpperCase([locale, locale, ...])
Parameters
Item | Description |
locale | Optional indicates the locale to be used to convert to upper case. |
If multiple locales are given in an Array, the best available locale is used.
The default locale is the host environment's current locale.
Return value
A new string representing the calling string converted to upper case, according to any locale-specific case mappings.
Using toLocaleUpperCase()
let a = 'alphabet'.toLocaleUpperCase(); // 'ALPHABET'
console.log(a);
a = 'Ges'.toLocaleUpperCase(); // 'GES
console.log(a);
a = 'i\u0307'.toLocaleUpperCase('lt-LT'); // 'I'
console.log(a);
let locales = ['lt', 'LT', 'lt-LT', 'lt-u-co-phonebk', 'lt-x-lietuva'];
a = 'i\u0307'.toLocaleUpperCase(locales); // 'I'
console.log(a);
Result
String toLowerCase()
The toLowerCase() method returns the calling string value converted to lower case.
Syntax
str.toLowerCase()
Return value
A new string representing the calling string converted to lower case.
toLowerCase() does not affect the value of the string str itself.
Using toLowerCase()
console.log('ALPHABET'.toLowerCase()); // 'alphabet'
Result
String toString()
The toString() method returns a string representing the specified object.
Syntax
str.toString()
Return value
A string representing the calling object.
Using toString()
The following example displays the string value of a String object:
var x = new String('Hello world');
console.log(x.toString()); // logs 'Hello world'
Result
String toUpperCase()
The toUpperCase() method returns the calling string value converted to uppercase.
Syntax
str.toUpperCase()
Return value
A new string representing the calling string converted to upper case.
Basic usage
console.log('alphabet'.toUpperCase()); // 'ALPHABET'
String trim()
The trim() method removes whitespace from both ends of a string.
Whitespace includes all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
Syntax
str.trim()
Return value
A new string representing the str stripped of whitespace from both ends.
Using trim()
The following example displays the lowercase string 'foo':
var orig = ' foo ';
console.log(orig.trim()); // 'foo'
Result
Another example of .trim() removing whitespace from just one side.
var orig = 'foo ';
console.log(orig.trim()); // 'foo'
Result
String trimEnd()
The trimEnd() method removes whitespace from the end of a string.
trimRight() is an alias of this method.
Syntax
str.trimEnd();
str.trimRight();
Return value
A new string representing the calling string stripped of whitespace from its (right) end.
Description
The trimEnd() / trimRight() methods return the string stripped of whitespace from its right end.
trimEnd() or trimRight() do not affect the value of the string itself.
The following example displays the lowercase string ' foo':
var str = ' foo ';
console.log(str.length); // 8
str = str.trimEnd();
console.log(str.length); // 6
console.log(str); // ' foo'
Result
String trimStart()
The trimStart() method removes whitespace from the beginning of a string.
trimLeft() is an alias of this method.
Syntax
str.trimStart();
str.trimLeft();
Return value
A new string representing the calling string stripped of whitespace from its beginning (left end).
The following example displays the lowercase string 'foo ':
var str = ' foo ';
console.log(str.length); // 8
str = str.trimStart();
console.log(str.length); // 5
console.log(str); // 'foo '
Result
String valueOf()
The valueOf() method returns the primitive value of a String object.
Syntax
str.valueOf()
Return value
A string representing the primitive value of a given String object.
This method is usually called internally by JavaScript.
Examples
Using valueOf()
var x = new String('Hello world');
console.log(x.valueOf()); // Displays 'Hello world'
Result
Symbol Type Introduction
ECMAScript Symbols are primitive values, and symbol instances
are unique and immutable.
A symbol is a unique identifier for object properties.
The symbols should be used as unique tokens to identify special properties.
Basic Symbol Use
Symbols are instantiated using the Symbol function.
Because it is its own primitive type, the typeof operator will identify a symbol as symbol.
let sym = Symbol();
console.log(typeof sym); // symbol
When invoking the function, you can provide an optional string that can be used for identifying the
symbol instance when debugging.
let genericSymbol = Symbol();
console.log(genericSymbol); // Symbol()
let fooSymbol = Symbol('foo');
console.log(fooSymbol); // Symbol(foo);
Output:
The string you provide is totally separate from the symbol's definition or identity:
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let mySymbol = Symbol('foo');
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == mySymbol); // false
We can create a new Symbol instance and use it to key a
new property on an object.
In this way we can guarantee that we will not overwrite an existing object property.
The Symbol function cannot be used with the new keyword.
This avoids symbol object wrappers, as is possible with Boolean, String, and Number, which support
constructor behavior and instantiate a primitive wrapper object:
let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"
let myString = new String();
console.log(typeof myString); // "object"
let myNumber = new Number();
console.log(typeof myNumber); // "object"
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
To utilize an object wrapper for symbol, you can make use of the Object() function:
let mySymbol = Symbol();
let myWrappedSymbol = Object(mySymbol);
console.log(typeof myWrappedSymbol); // "object"
Using the Global Symbol Registry
We can create and reuse symbols in a string-keyed global symbol registry.
let fooGlobalSymbol = Symbol.for('foo');
console.log(typeof fooGlobalSymbol); // "object"
Symbol.for() is an idempotent operation for each string key.
The first time it is called with a given
string, it will check the global runtime registry, find that no symbol exists, generate a new
symbol instance, and add it to the registry.
Additional invocations with the same string key will check
the global runtime registry, find that a symbol does exist for that string, and return that symbol
instance instead.
let fooGlobalSymbol = Symbol.for('foo'); // creates new symbol
let mySymbol = Symbol.for('foo'); // reuses existing symbol
console.log(fooGlobalSymbol === mySymbol); // true
Symbols defined in the global registry are totally distinct from symbols created using Symbol(), even
if they share a description:
let localSymbol = Symbol('foo');
let globalSymbol = Symbol.for('foo');
console.log(localSymbol === globalSymbol); // false
The global registry requires string keys, so anything you provide as an argument to Symbol.for()
will be converted to a string.
Additionally, the key used for the registry will also be used as the symbol description.
let emptyGlobalSymbol = Symbol.for();
console.log(emptyGlobalSymbol); // Symbol(undefined)
let globalSymbol = Symbol.for('foo');
console.log(globalSymbol); // Symbol(foo)
We can check the global registry using Symbol.keyFor(), which accepts a symbol
and will return the global string key for that global symbol, or undefined if the symbol is not a
global symbol.
// Create global symbol
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// Create regular symbol
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
Using Symbol.keyFor() with a non-symbol will throw a TypeError:
Symbol.keyFor(123); // TypeError: 123 is not a symbol
Using Symbols as Properties
Anywhere you can normally use a string or number property, you can also use a symbol.
This includes object literal properties and Object.defineProperty()/Object.defineProperties().
An object literal can only use a symbol as a property inside the computed property syntax.
let s1 = Symbol('test'),
s2 = Symbol('demo'),
s3 = Symbol('baz'),
s4 = Symbol('qux');
let o = {// w w w . d e m o 2 s . c o m
[s1]: 'test val'
};
// Also valid: o[s1] = 'test val';
console.log(o);
// {Symbol{test}: test val}
Object.defineProperty(o, s2, {value: 'demo val'});
console.log(o);
// {Symbol{test}: test val, Symbol(demo): demo val}
Object.defineProperties(o, {
[s3]: {value: 'baz val'},
[s4]: {value: 'qux val'}
});
console.log(o);
// {Symbol{test}: test val, Symbol(demo): demo val,
// Symbol{baz}: baz val, Symbol(qux): qux val}
Object.getOwnPropertyNames() returns an array of regular properties on an object instance.
Object.getOwnPropertySymbols() returns an array of symbol properties on an object instance.
The return values of these two methods are mutually exclusive.
Object.getOwnPropertyDescriptors() will return an object containing both regular and symbol
property descriptors.
Reflect.ownKeys() will return both types of keys:
let s1 = Symbol('test'),
s2 = Symbol('demo');
let o = {// w w w .d e m o 2 s. c o m
[s1]: 'test val',
[s2]: 'demo val',
baz: 'baz val',
qux: 'qux val'
};
console.log(Object.getOwnPropertySymbols(o));
// [Symbol(test), Symbol(demo)]
console.log(Object.getOwnPropertyNames(o));
// ["baz", "qux"]
console.log(Object.getOwnPropertyDescriptors(o));
// {baz: {...}, qux: {...}, Symbol(test): {...}, Symbol(demo): {...}}
console.log(Reflect.ownKeys(o));
// ["baz", "qux", Symbol(test), Symbol(demo)]
Symbols can be directly created and used as properties.
We can traverse all the object's symbol properties to get the property key:
let o = {
[Symbol('test')]: 'test val',
[Symbol('demo')]: 'demo val'
};
console.log(o);
// {Symbol(test): "test val", Symbol(demo): "demo val"}
let demoSymbol = Object.getOwnPropertySymbols(o)
.find((symbol) => symbol.toString().match(/demo/));
console.log(demoSymbol);
// Symbol(demo)
Well-Known Symbols
ECMAScript 6 introduced a collection of well-known
symbols that would be used throughout the language to expose internal language behaviors for direct
access, overriding, or emulating.
These well-known symbols exist as string properties on the Symbol factory function.
We can redefine well-known symbols to alter the behavior of the native language constructs.
For example, the for-of loop will use the
Symbol.iterator property, we can provide a custom
definition of Symbol.iterator's value in a custom object in order to control how for-of behaves for that
object.
These well-known symbols are regular string properties on the Symbol global that identify an instance of a symbol.
Each well-defined symbol property is non-writeable, non-enumerable, and non-configurable.
The well-known symbols are referred by their specification names prefixed with @@.
For example, @@iterator refers to Symbol.iterator.
Well Known Symbol Symbol.asyncIterator
Well Known Symbol Symbol.asyncIterator is used as a property for "A method that returns the
default AsyncIterator for an object. Called by the semantics of the for-await-of statement".
It is used to identify the function that implements the asynchronous iterator API.
The for-await-of loop uses this function to perform asynchronous iteration.
They will invoke the function keyed by Symbol.asyncIterator and expect it
to return an object which implements the Iterator API.
In many cases, this will take the form of an
AsyncGenerator, an object which implements this API:
class Foo {
async *[Symbol.asyncIterator]() {}
}
let f = new Foo();
console.log(f[Symbol.asyncIterator]());
// AsyncGenerator {<suspended>}
The object produced by the Symbol.asyncIterator function should sequentially
produce Promise instances via its next() method.
This can be through explicit next() method definition or implicitly through an async generator function:
class Emitter {/* w w w . d em o 2 s .c o m */
constructor(max) {
this.max = max;
this.asyncIdx = 0;
}
async *[Symbol.asyncIterator]() {
while(this.asyncIdx < this.max) {
yield new Promise((resolve) => resolve(this.asyncIdx++));
}
}
}
async function asyncCount() {
let emitter = new Emitter(5);
for await(const x of emitter) {
console.log(x);
}
}
asyncCount();
Output:
Symbol.asyncIterator is part of the ES2018 specification.
Well Known Symbol Symbol.hasInstance
ECMAScript Well Known Symbol Symbol.hasInstance is used as a property for "A method that determines if
a constructor object recognizes an object as one of the constructor's instances.
Called by the semantics of the instanceof operator".
The instanceof operator provides a way of determining if an
object instance has a prototype in its prototype chain.
Typical use of the instanceof is as follows:
function Demo() {}
let f = new Demo();
console.log(f instanceof Demo); // true
class Test {}
let b = new Test();
console.log(b instanceof Test); // true
In ES6, the instanceof operator is using a Symbol.hasInstance function to evaluate this
relationship.
The Symbol.hasInstance identifies a function which performs the same behavior but with the
operands reversed:
function Demo() {}
let f = new Demo();
console.log(Demo[Symbol.hasInstance](f)); // true
class Test {}
let b = new Test();
console.log(Test[Symbol.hasInstance](b)); // true
This property is defined on the Function prototype, and therefore it is automatically available by
default to all function and class definitions.
Because the instanceof operator will seek the property
definition on the prototype chain like any other property, it is possible to redefine the function on an
inherited class as a static method:
class Test {}
class Baz extends Test {
static [Symbol.hasInstance]() {
return false;
}
}
let b = new Baz();
console.log(Test[Symbol.hasInstance](b)); // true
console.log(b instanceof Test); // true
console.log(Baz[Symbol.hasInstance](b)); // false
console.log(b instanceof Baz); // false
Well Known Symbol Symbol.isConcatSpreadable
ECMAScript Well Known Symbol Symbol.isConcatSpreadable is used as a property for "A Boolean valued property
that if true indicates that an object should be flattened to its array elements by Array.prototype.concat()".
The Array.prototype.concat method in ES6 will select how to join an array-like
object to the array instance based on the type of object it is passed.
The value of Symbol.isConcatSpreadable allows you to override this behavior.
Array objects by default will be flattened into the existing array; a value of false or falsy value will
append the entire object to the array.
Array-like objects by default will be appended to the array; a
value of true or truthy value will flatten the array-like object into the array instance.
Other objects which are not array-like will be ignored when Symbol.isConcatSpreadable is set to true.
let initial = ['test'];
let array = ['demo'];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // ['test', 'demo']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // ['test', Array(1)]
let test = { length: 1, 0: 'baz' };
console.log(test[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(test)); // ['test', {...}]
test[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(test)); // ['test', 'baz']
let otherObject = new Set().add('qux');
console.log(otherObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(otherObject)); // ['test', Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject)); // ['test']
Well Known Symbol Symbol.iterator
ECMAScript Well Known Symbol Symbol.iterator is used as a property for "A method that returns the
default Iterator for an object. Called by the semantics of the for-of statement".
It is used to identify the function that implements the iterator API.
Javascript for-of loop make use of this function to perform iteration.
They will invoke the function identified by Symbol.iterator and expect it to return an object which
implements the Iterator API.
In many cases, this will take the form of a Generator, an object which implements this API:
class Foo {
*[Symbol.iterator]() {}
}
let f = new Foo();
console.log(f[Symbol.iterator]());
// Generator {<suspended>}
Specifically, the object produced by the Symbol.iterator function should sequentially produce
values via its next() method.
This can be through explicit next() method definition or implicitly through a generator function:
class Emitter {/*w w w. d em o2 s . c o m */
constructor(max) {
this.max = max;
this.idx = 0;
}
*[Symbol.iterator]() {
while(this.idx < this.max) {
yield this.idx++;
}
}
}
function count() {
let emitter = new Emitter(5);
for (const x of emitter) {
console.log(x);
}
}
count();
Output:
Well Known Symbol Symbol.match
ECMAScript Well Known Symbol Symbol.match is used as a property for "A regular expression method
that matches the regular expression against a string. Called by the String.prototype.match()
method".
The String.prototype.match() method will use the function identified by Symbol.match
to evaluate the expression.
The regular expression prototype has this function defined by default, and
therefore all regular expression instances are valid parameters to the String method by default:
console.log(RegExp.prototype[Symbol.match]);
//f [Symbol.match]() { [native code] }
console.log('foobar'.match(/bar/));
// ["bar", index: 3, input: "foobar", groups: undefined]
Providing something other than a regular expression to this method will cause it to be converted to
a RegExp object.
This function has a single parameter which is the string instance
upon which match() is invoked. The return value is unrestricted:
class FooMatcher {// w w w . de m o 2 s . c o m
static [Symbol.match](target) {
return target.includes('foo');
}
}
console.log('foobar'.match(FooMatcher)); // true
console.log('barbaz'.match(FooMatcher)); // false
class StringMatcher {
constructor(str) {
this.str = str;
}
[Symbol.match](target) {
return target.includes(this.str);
}
}
console.log('foobar'.match(new StringMatcher('foo'))); // true
console.log('barbaz'.match(new StringMatcher('qux'))); // false
Well Known Symbol Symbol.replace
ECMAScript Well Known Symbol Symbol.replace is used as a property for "A regular expression method
that replaces matched substrings of a string. Called by the String.prototype.replace() method".
The String.prototype.replace() method will use the function identified by Symbol.replace to
evaluate the expression.
The regular expression prototype has this function defined by default, and
therefore all regular expression instances are valid parameters to the String method by default:
console.log(RegExp.prototype[Symbol.replace]);
// f[Symbol.replace]() { [native code] }
console.log('foobarbaz'.replace(/bar/, 'qux'));
// 'fooquxbaz'
Providing something other than a regular expression to this method will cause it to be converted to
a RegExp object.
This function has two parameters, the string instance upon which
replace() is invoked and the replacement string.
The return value is unrestricted:
class FooReplacer {// w w w .d e m o 2 s . c o m
static[Symbol.replace](target, replacement) {
return target.split('foo').join(replacement);
}
}
console.log('barfoobaz'.replace(FooReplacer, 'qux'));
// "barquxbaz"
class StringReplacer {
constructor(str) {
this.str = str;
}
[Symbol.replace](target, replacement) {
return target.split(this.str).join(replacement);
}
}
console.log('barfoobaz'.replace(new StringReplacer('foo'), 'qux'));
// "barquxbaz"
Well Known Symbol Symbol.search
ECMAScript Well Known Symbol Symbol.search is used as a property for "A regular expression method
that returns the index within a string that matches the regular expression. Called by the String
.prototype.search() method".
The String.prototype.search() method will use the function identified by Symbol.search to evaluate the expression.
The regular expression prototype has this function defined by default, and therefore all regular expression instances are valid parameters to the
String method by default:
console.log(RegExp.prototype[Symbol.search]);
// f [Symbol.search]() { [native code] }
console.log('foobar'.search(/bar/));
// 3
Providing something other than a regular expression to this method will cause it to be converted to
a RegExp object.
This function has a single parameter which is the string instance
upon which search() is invoked.
The return value is unrestricted:
class FooSearcher {// w ww . d e m o 2 s . c om
static[Symbol.search](target) {
return target.indexOf('foo');
}
}
console.log('foobar'.search(FooSearcher)); // 0
console.log('barfoo'.search(FooSearcher)); // 3
console.log('barbaz'.search(FooSearcher)); // -1
class StringSearcher {
constructor(str) {
this.str = str;
}
[Symbol.search](target) {
return target.indexOf(this.str);
}
}
console.log('foobar'.search(new StringSearcher('foo'))); // 0
console.log('barfoo'.search(new StringSearcher('foo'))); // 3
console.log('barbaz'.search(new StringSearcher('qux'))); // -1
Well Known Symbol Symbol.species
ECMAScript Well Known Symbol Symbol.species is used as a property for "A function valued property
that is the constructor function that is used to create derived objects".
This is most commonly used
for build-in types which expose methods that instantiate derived objects for the return value of an
instance method.
Defining a static getter method with Symbol.species allows you to override the
prototype definition for the newly created instance:
class Bar extends Array {}
class Test extends Array {
static get [Symbol.species]() {// w ww . d e m o 2 s . c o m
return Array;
}
}
let bar = new Bar();
console.log(bar instanceof Array); // true
console.log(bar instanceof Bar); // true
bar = bar.concat('bar');
console.log(bar instanceof Array); // true
console.log(bar instanceof Bar); // true
let baz = new Test();
console.log(baz instanceof Array); // true
console.log(baz instanceof Test); // true
baz = baz.concat('baz');
console.log(baz instanceof Array); // true
console.log(baz instanceof Test); // false
Well Known Symbol Symbol.split
ECMAScript Well Known Symbol Symbol.split is used as a property for "A regular expression
method that splits a string at the indices that match the regular expression. Called by the String
.prototype.split() method".
The String.prototype.split() method will use the function
identified by Symbol.split to evaluate the expression.
The regular expression prototype has this function defined by default, and therefore all regular expression instances are valid parameters to the
String method by default:
console.log(RegExp.prototype[Symbol.split]);
// f [Symbol.split]() { [native code] }
console.log('footestbaz'.split(/test/));
// ['foo', 'baz']
Providing something other than a regular expression to this method will cause it to be converted to
a RegExp object.
This function has a single parameter which is the string instance
upon which split() is invoked. The return value is unrestricted:
class FooSplitter {/*w w w .d e m o2 s. c om */
static [Symbol.split](target) {
return target.split('foo');
}
}
console.log('testfoobaz'.split(FooSplitter));
// ["test", "baz"]
class StringSplitter {
constructor(str) {
this.str = str;
}
[Symbol.split](target) {
return target.split(this.str);
}
}
console.log('testfoobaz'.split(new StringSplitter('foo')));
// ["test", "baz"]
Well Known Symbol Symbol.toPrimitive
ECMAScript Well Known Symbol Symbol.toPrimitive is used as a property for "A method that converts an
object to a corresponding primitive value. Called by the ToPrimitive abstract operation".
There are a number of built-in operations which will attempt to coerce an object into a primitive value: a
string, a number, or an unspecified primitive type.
For a custom object instance, it is possible to divert
this behavior by defining a function on the instance's Symbol.toPrimitive property.
Based on a string parameter provided to the function, you are
able to control the returned primitive:
class Foo {}/*w w w . d e m o 2 s . co m */
let foo = new Foo();
console.log(3 + foo); // "3[object Object]"
console.log(3 - foo); // NaN
console.log(String(foo)); // "[object Object]"
class Bar {
constructor() {
this[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case 'number':
return 3;
case 'string':
return 'string test';
case 'default':
default:
return 'default test';
}
}
}
}
let test = new Bar();
console.log(3 + test); // "3default test"
console.log(3 - test); // 0
console.log(String(test)); // "string test"
Well Known Symbol Symbol.toStringTag
ECMAScript Well Known Symbol Symbol.toStringTag is used as a property for "A String valued property that
is used in the creation of the default string description of an object. Accessed by the built-in method
Object.prototype.toString()".
Object identification via the toString() method will retrieve the instance identifier specified by
Symbol.toStringTag, defaulting to Object.
Built-in types have this value already specified, but
custom class instances require explicit definition:
let s = new Set();
console.log(s); // Set(0) {}
console.log(s.toString()); // [object Set]
console.log(s[Symbol.toStringTag]); // Set
class Foo {}// w w w . de m o 2 s . c o m
let foo = new Foo();
console.log(foo); // Foo {}
console.log(foo.toString()); // [object Object]
console.log(foo[Symbol.toStringTag]); // undefined
class Bar {
constructor() {
this[Symbol.toStringTag] = 'Bar';
}
}
let bar = new Bar();
console.log(bar); // Bar {}
console.log(bar.toString()); // [object Bar]
console.log(bar[Symbol.toStringTag]); // Bar
Well Known Symbol Symbol.unscopables
ECMAScript Well Known Symbol Symbol.unscopables is used as a property for "An object valued
property whose own and inherited property names are property names that are excluded from the with
environment bindings of the associated object".
Setting this symbol so it keys an object mapping a
corresponding property to true will prevent a with environment binding, as shown here:
let o = { foo: 'test' };
with (o) {
console.log(foo); // test
}
o[Symbol.unscopables] = {
foo: true
};
with (o) {
console.log(foo); // ReferenceError
}
It's not recommended to use with statement, so using Symbol.unscopables is also
not recommended.
Operators
ECMA-262 describes a set of operators that can be used to manipulate data values.
The operators
range from mathematical operations, such as addition and subtraction, and bitwise operators to
relational operators and equality operators.
Operators in ECMAScript can be
used on a wide range of values, including strings, numbers, Booleans, and even objects.
When used on
objects, operators typically call the valueOf() and/or toString() method to retrieve a value they
can work with.
Unary Operators
Operators that work on only one value are called unary operators. They are the simplest operators in
ECMAScript.
Increment Decrement Operators
The increment and decrement operators are taken directly from C and come in two versions: prefix
and postfix.
The prefix versions of the operators are placed before the variable they work on; the
postfix ones are placed after the variable.
To use a prefix increment, which adds 1 to a numeric value,
you place two plus signs (++) in front of a variable like this:
let myValue = 29;
++myValue;
In this example, the prefix increment changes the value of myValue to 30 by adding 1 to its previous value
of 29.
This is effectively equal to the following:
let myValue = 29;
myValue = myValue + 1;
The prefix decrement acts in a similar manner, subtracting 1 from a numeric value.
To use a prefix decrement, place two minus signs (--) before a variable, as shown here:
let myValue = 29;
--myValue;
Here the myValue variable is decremented to 28 by subtracting 1 from 29.
When using either a prefix increment or a prefix decrement, the variable's value is converted before
the statement is evaluated.
Consider the following:
let myValue = 29;
let otherValue = --myValue + 2;
console.log(myValue); // 28
console.log(otherValue); // 30
In this example, the variable otherValue is initialized with the decremented value of myValue plus 2.
Because the decrement happens first, myValue is set to 28, and then 2 is added, resulting in 30.
The prefix increment and decrement are equal in terms of order of precedence in a statement and are
therefore evaluated left to right.
Consider this example:
let num1 = 2;
let num2 = 20;
let num3 = --num1 + num2;
let num4 = num1 + num2;
console.log(num3); // 21
console.log(num4); // 21
Here, num3 is equal to 21 because num1 is decremented to 1 before the addition occurs.
The variable num4 also contains 21, because the addition is also done using the changed values.
The postfix versions of increment and decrement use the same syntax (++ and --, respectively) but
are placed after the variable instead of before it.
Postfix increment and decrement differ from the
prefix versions in one important way: the increment or decrement doesn't occur until after the containing statement has been evaluated.
In certain circumstances, this difference doesn't matter, as in
this example:
let myValue = 29;
myValue++;
Moving the increment operator after the variable doesn't change what these statements do, because
the increment is the only operation occurring.
However, when mixed together with other operations,
the difference becomes apparent, as in the following example:
let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2;
let num4 = num1 + num2;
console.log(num3); // 22
console.log(num4); // 21
With just one simple change in this example, using postfix decrement instead of prefix, you can see
the difference.
In the prefix example, num3 and num4 both ended up equal to 21, whereas this example ends with num3 equal to 22 and num4 equal to 21.
The difference is that the calculation for num3
uses the original value of num1 (2) to complete the addition, whereas num4 is using the decremented
value (1).
Converting Rules
All four of these operators work on any values, meaning not just integers but strings, Booleans,
floating-point values, and objects.
The increment and decrement operators follow these rules regarding values:
When used on a string that is a valid representation of a number, convert to a number and apply the change. The variable is converted from a string to a number.
When used on a string that is not a valid number, the variable's value is set to NaN. The variable is converted from a string to a number.
When used on a Boolean value that is false, convert to 0 and apply the change. The variable is converted from a Boolean to a number.
When used on a Boolean value that is true, convert to 1 and apply the change. The variable is converted from a Boolean to a number.
When used on a floating-point value, apply the change by adding or subtracting 1.
When used on an object, call its valueOf() method to get a value to work with. Apply the other rules. If the result is NaN, then call toString() and apply the other rules again. The variable is converted from an object to a number.
The following example demonstrates some of these rules:
let s1 = "2";
let s2 = "z";
let b = false;
let f = 1.1;
let o = {
valueOf() {
return -2;
}
};
s1++; // value becomes numeric 3
s2++; // value becomes NaN
b++; // value becomes numeric 1
f--; // value becomes 0.10000000000000009 (due to floating-point inaccuracies)
o--; // value becomes numeric -3
Unary Plus and Minus Operator
The unary plus is represented by a single plus
sign (+) placed before a variable and does nothing to a numeric value, as shown in this example:
let num = 25;
num = +num;
console.log(num); // 25
When the unary plus is applied to a nonnumeric value, it performs the same conversion as the Number() casting function:
the Boolean values of false and true are converted to 0 and 1,
string values are parsed according to a set of specific rules, and
objects have their valueOf() and/or toString() method called to get a value to convert.
The following example demonstrates the behavior of the unary plus when acting on different
data types:
let s1 = "01";
let s2 = "1.1";
let s3 = "z";
let b = false;
let f = 1.1;
let o = {
valueOf() {
return -1;
}
};
s1 = +s1; // value becomes numeric 1
s2 = +s2; // value becomes numeric 1.1
s3 = +s3; // value becomes NaN
b = +b; // value becomes numeric 0
f = +f; // no change, still 1.1
o = +o; // value becomes numeric -1
unary minus
The unary minus operator's primary use is to negate a numeric value, such as converting 1 into -1.
The simple case is illustrated here:
let num = 25;
num = -num;
console.log(num); // -25
When used on a numeric value, the unary minus simply negates the value.
When used on nonnumeric values, unary minus applies all of the same rules as unary plus and then negates
the result, as shown here:
let s1 = "01";
let s2 = "1.1";
let s3 = "z";
let b = false;
let f = 1.1;
let o = {
valueOf() {
return -1;
}
};
s1 = -s1; // value becomes numeric -1
s2 = -s2; // value becomes numeric -1.1
s3 = -s3; // value becomes NaN
b = -b; // value becomes numeric 0
f = -f; // change to -1.1
o = -o; // value becomes numeric 1
The unary plus and minus operators are used primarily for basic arithmetic but can also be useful for conversion purposes.
Number bit format for Bitwise Operators
Bits
All numbers in ECMAScript are stored in IEEE-754 64-bit format, but the bitwise
operations do not work directly on the 64-bit representation.
Instead, the value is converted into a
32-bit integer, the operation takes place, and the result is converted back into 64 bits.
To the developer, it appears that only the 32-bit integer exists because the 64-bit storage format is transparent.
Signed integers use the first 31 of the 32 bits to represent the numeric value of the integer.
The 32nd bit represents the sign of the number: 0 for positive or 1
for negative. Depending on the value of that bit, called
the sign bit, the format of the rest of the number is determined.
Positive numbers are stored in true binary format, with each of the 31 bits representing a power of 2, starting with the first bit (called bit 0) representing 20, the second bit represents 21.
If any bits are unused, they are filled with 0 and essentially
ignored.
For example, the number 18 is represented as 00000000000000000000000000010010, or more succinctly as 10010.
These are the five most
significant bits and can be used, by themselves, to determine the actual value.
Negative numbers
Negative numbers are also stored in binary code but in a format called two's complement.
The two's complement of a number is calculated in three steps:
Determine the binary representation of the absolute value (for example, to find -18, first determine the binary representation of 18).
Find the one's complement of the number, which essentially means that every 0 must be
replaced with a 1, and vice versa.
Add 1 to the result.
Using this process to determine the binary representation -18, start with the binary representation of 18, which is the following:
0000 0000 0000 0000 0000 0000 0001 0010
Next, take the one's complement, which is the inverse of this number:
1111 1111 1111 1111 1111 1111 1110 1101
Finally, add 1 to the one's complement as follows:
1111 1111 1111 1111 1111 1111 1110 1101
1
---------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
So the binary equivalent of -18 is 11111111111111111111111111101110.
Keep in mind that you have no access to bit 31 when dealing with signed integers.
When outputting a negative number as a binary string, you get the binary code of the absolute value preceded by a minus sign, as in
this example:
let num = -18;
console.log(num.toString(2)); // "-10010"
When you convert the number -18 to a binary string, the result is -10010.
The conversion process
interprets the two's complement and represents it in an arguably more logical form.
By default, all integers are represented as signed in ECMAScript.
In an unsigned integer, the 32nd
bit doesn't represent the sign because there are only positive numbers.
Unsigned integers can be larger, because the extra bit becomes part of the number
instead of an indicator of the sign.
Apply bitwise operators
When you apply bitwise operators to numbers in ECMAScript, a conversion takes place behind the
scenes: the 64-bit number is converted into a 32-bit number, the operation is performed, and then
the 32-bit result is stored back into a 64-bit number.
This gives the illusion that you're dealing with
true 32-bit numbers, which makes the binary operations work in a way similar to the operations of
other languages.
NaN and Infinity both are treated as equivalent to 0 when used in bitwise operations.
If a bitwise operator is applied to a nonnumeric value, the value is first converted into a number using the Number() function and then the bitwise operation is applied.
The resulting value is a number.