Understanding JavaScript Symbols

Understanding JavaScript Symbols

Symbols, the newest JavaScript primitive, bring a few benefits to the language and are particularly useful when used as object properties. But, what can they do for us that strings cannot?

Symbols, the newest JavaScript primitive, bring a few benefits to the language and are particularly useful when used as object properties. But, what can they do for us that strings cannot?

Before we explore symbols too much let’s first look at some JavaScript features which many developers might not be aware of.

Background

There are essentially two types of values in JavaScript. The first type is primitives, and the second type is objects (which also includes functions). Primitive values include simple value types such as numbers (which includes everything from integers to floats to Infinity to NaN), booleans, strings, undefined, and null (note: even though typeof null === 'object', null is a still primitive value).

Primitive values are also immutable. They can’t be changed. Of course, a variable with a primitive assigned can be reassigned. For example, when you write the code let x = 1; x++;, you've reassigned the variable x. But, you haven't mutated the primitive numeric value of 1.

Some languages, such as C, have the concept of pass-by-reference and pass-by-value. JavaScript sort of has this concept too, though, it’s inferred based on the type of data being passed around. If you ever pass a value into a function, reassigning that value will not modify the value in the calling location. However, if you modify a non-primitive value, the modified value will also be modified where it has been called from.

Consider the following example:

function primitiveMutator(val) {
  val = val + 1;
}let x = 1;
primitiveMutator(x);
console.log(x); // 1function objectMutator(val) {
  val.prop = val.prop + 1;
}let obj = { prop: 1 };
objectMutator(obj);
console.log(obj.prop); // 2

Primitive values (except for the mystical NaN value) will always be exactly equal to another primitive with an equivalent value. Check it out here:

const first = "abc" + "def";
const second = "ab" + "cd" + "ef";console.log(first === second); // true

However, constructing equivalent non-primitive values will not result in values which are exactly equal. We can see this happening here:

const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };console.log(obj1 === obj2); // false// Though, their .name properties ARE primitives:
console.log(obj1.name === obj2.name); // true

Objects play an elemental role in the JavaScript language. They’re used everywhere. They’re often used as collections of key/value pairs. However, this is a big limitation of using them in this manner: Until symbols existed, object keys could only be strings. If we ever attempt to use a non-string value as a key for an object, the value will be coerced to a string. We can see this feature here:

const obj = {};
obj.foo = 'foo';
obj['bar'] = 'bar';
obj[2] = 2;
obj[{}] = 'someobj';console.log(obj);
// { '2': 2, foo: 'foo', bar: 'bar',
     '[object Object]': 'someobj' }

Note: It’s slightly off topic, but the Map data structure was created in part to allow for key/value storage in situations where a key is not a string.

What is a Symbol?

Now that we know what a primitive value is, we’re finally ready to define what a symbol is. A symbol is a primitive which cannot be recreated. In this case a symbols is similar to an object as creating multiple instances will result in values which are not exactly equal. But, a symbol is also a primitive in that it cannot be mutated. Here is an example of symbol usage:

const s1 = Symbol();
const s2 = Symbol();console.log(s1 === s2); // false

When instantiating a symbol there is an optional first argument where you can choose to provide it with a string. This value is intended to be used for debugging code, it otherwise doesn’t really affect the symbol itself.

const s1 = Symbol('debug');
const str = 'debug';
const s2 = Symbol('xxyy');console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)

Symbols as Object Properties

Symbols have another important use. They can be used as keys in objects! Here is an example of using a symbol as a key within an object:

const obj = {};
const sym = Symbol();
obj[sym] = 'foo';
obj.bar = 'bar';console.log(obj); // { bar: 'bar' }
console.log(sym in obj); // true
console.log(obj[sym]); // foo
console.log(Object.keys(obj)); // ['bar']

Notice how they are not returned in the result of Object.keys(). This is, again, for the purpose of backwards compatibility. Old code isn't aware of symbols and so this result shouldn't be returned from the ancient Object.keys() method.

At first glance, this almost looks like symbols can be used to create private properties on an object! Many other programming languages have hidden properties in their classes and this omission has long been seen as a shortcoming of JavaScript.

Unfortunately, it is still possible for code which interacts with this object to access properties whose keys are symbols. This is even possible in situations where the calling code does not already have access to the symbol itself. As an example, the Reflect.ownKeys() method is able to get a list of all keys on an object, both strings and symbols alike:

function tryToAddPrivate(o) {
  o[Symbol('Pseudo Private')] = 42;
}const obj = { prop: 'hello' };
tryToAddPrivate(obj);console.log(Reflect.ownKeys(obj));
        // [ 'prop', Symbol(Pseudo Private) ]
console.log(obj[Reflect.ownKeys(obj)[1]]); // 42

Note: There is currently work being done to tackle the issue of adding private properties to classes in JavaScript. The name of this feature is called Private Fields, and although this won’t benefit all objects, it will benefit objects which are class instances. Private Fields are available as of Chrome 74.

Preventing Property Name Collisions

Symbols may not directly benefit JavaScript for providing private properties to objects. However, they are beneficial for another reason. They are useful in situations where disparate libraries want to add properties to objects without the risk of having name collisions.

Consider the situation where two different libraries want to attach some sort of metadata to an object. Perhaps they both want to set some sort of identifier on the object. By simply using the two character string id as a key, there is a huge risk that multiple libraries will use the same key.

function lib1tag(obj) {
  obj.id = 42;
}function lib2tag(obj) {
  obj.id = 369;
}

By making use of symbols, each library can generate their required symbols upon instantiation. Then the symbols can be checked on objects, and set to objects, whenever an object is encountered.

const library1property = Symbol('lib1');
function lib1tag(obj) {
  obj[library1property] = 42;
}const library2property = Symbol('lib2');
function lib2tag(obj) {
  obj[library2property] = 369;
}

For this reason it would seem that symbols do benefit JavaScript.

However, you may be wondering, why can’t each library simply generate a random string, or use a specially namespaced string, upon instantiation?

const library1property = uuid(); // random approach
function lib1tag(obj) {
  obj[library1property] = 42;
}const library2property = 'LIB2-NAMESPACE-id'; // namespaced approach
function lib2tag(obj) {
  obj[library2property] = 369;
}

Well, you’d be right. This approach is actually pretty similar to the approach with symbols. Unless two libraries would choose to use the same property name, then there wouldn’t be a risk of overlap.

At this point the astute reader would point out that the two approaches haven’t been entirely equal. Our property names with unique names still have a shortcoming: their keys are very easy to find, especially when code runs to either iterate the keys or to otherwise serialize the objects. Consider the following example:

const library2property = 'LIB2-NAMESPACE-id'; // namespaced
function lib2tag(obj) {
  obj[library2property] = 369;
}const user = {
  name: 'Thomas Hunter II',
  age: 32
};lib2tag(user);JSON.stringify(user);
// '{"name":"Thomas Hunter II","age":32,"LIB2-NAMESPACE-id":369}'

If we had used a symbol for a property name of the object then the JSON output would not contain its value. Why is that? Well, just because JavaScript gained support for symbols doesn’t mean that the JSON spec has changed! JSON only allows strings as keys and JavaScript won’t make any attempt to represent symbol properties in the final JSON payload.

We can easily rectify the issue where our library object strings are polluting the JSON output by making use of Object.defineProperty():

const library2property = uuid(); // namespaced approach
function lib2tag(obj) {
  Object.defineProperty(obj, library2property, {
    enumerable: false,
    value: 369
  });
}const user = {
  name: 'Thomas Hunter II',
  age: 32
};lib2tag(user);// '{"name":"Thomas Hunter II",
   "age":32,"f468c902-26ed-4b2e-81d6-5775ae7eec5d":369}'
console.log(JSON.stringify(user));
console.log(user[library2property]); // 369

String keys which have been “hidden” by setting their enumerable descriptor to false behave very similarly to symbol keys. Both are hidden by Object.keys(), and both are revealed with Reflect.ownKeys(), as seen in the following example:

const obj = {};
obj[Symbol()] = 1;
Object.defineProperty(obj, 'foo', {
  enumberable: false,
  value: 2
});console.log(Object.keys(obj)); // []
console.log(Reflect.ownKeys(obj)); // [ 'foo', Symbol() ]
console.log(JSON.stringify(obj)); // {}

At this point we’ve nearly recreated symbols. Both our hidden string properties and symbols are hidden from serializers. Both properties can be extracted using the Reflect.ownKeys() method and are therefor not actually private. Assuming we use some sort of namespace / random value for the string version of the property name then we've removed the risk of multiple libraries accidentally having a name collision.

But, there’s still just one tiny difference. Since strings are immutable, and symbols are always guaranteed to be unique, there is still the potential for someone to generate every single possible string combination and come up with a collision. Mathematically this means symbols do provide a benefit that we just can’t get from strings.

In Node.js, when inspecting an object (such as using console.log()), if a method on the object named inspect is encountered, that function is invoked and the output is used as the logged representation of the object. As you can imagine, this behavior isn't expected by everyone and the generically-named inspect method often collides with objects created by users. There is now a symbol available for implementing this functionality and is available at require('util').inspect.custom. The inspect method is deprecated in Node.js v10 and entirely ignored in v11. Now no one will ever change the behavior of inspect by accident!

Simulating Private Properties

Here’s an interesting approach that we can use to simulate private properties on an object. This approach will make use of another JavaScript feature available to us today: proxies. A proxy essentially wraps an object and allows us to interpose on various interactions with that object.

A proxy offers many ways to intercept actions performed on an object. The one we’re interested in affects when an attempt at reading the keys of an object occurs.

We can use a proxy to then lie about which properties are available on our object. In this case we’re going to craft a proxy which hides our two known hidden properties, one being the string _favColor, and the other being the symbol assigned to favBook:

let proxy;

{
  const favBook = Symbol('fav book');

  const obj = {
    name: 'Thomas Hunter II',
    age: 32,
    _favColor: 'blue',
    [favBook]: 'Metro 2033',
    [Symbol('visible')]: 'foo'
  };

  const handler = {
    ownKeys: (target) => {
      const reportedKeys = [];
      const actualKeys = Reflect.ownKeys(target);

      for (const key of actualKeys) {
        if (key === favBook || key === '_favColor') {
          continue;
        }
        reportedKeys.push(key);
      }

      return reportedKeys;
    }
  };

  proxy = new Proxy(obj, handler);
}

console.log(Object.keys(proxy)); // [ 'name', 'age' ]
console.log(Reflect.ownKeys(proxy)); // [ 'name', 'age', Symbol(visible) ]
console.log(Object.getOwnPropertyNames(proxy)); // [ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(proxy)); // [Symbol(visible)]
console.log(proxy._favColor); // 'blue'

It’s easy to come up with the _favColor string: just read the source code of the library. Additionally, dynamic keys (e.g., the uuid example from before) can be found via brute force. But without a direct reference to the symbol, no one can access the 'Metro 2033' value from the proxy object.

Node.js Caveat: There is a feature in Node.js which breaks the privacy of proxies. This feature doesn’t exist in the JavaScript language itself and doesn’t apply in other situations, such as a web browser. It allows one to gain access to the underlying object when given a proxy. Here is an example of using this functionality to break the above private property example:

const [originalObject] = process
  .binding('util')
  .getProxyDetails(proxy);const allKeys = Reflect.ownKeys(originalObject);
console.log(allKeys[3]); // Symbol(fav book)

We would now need to either modify the global Reflect object, or modify the util process binding, to prevent them from being used in a particular Node.js instance. But that's one heck of a rabbit hole.

JavaScript ES6 Classes

JavaScript ES6 Classes

An exciting new construct that was introduced in the ES6 specification is the ES6 classes. If you're a Javascript developer, you will be aware that Javascript follows prototypal inheritance and sometimes it can get a little messy. However, with ES6 classes the syntax is simpler and much more intuitive.

An exciting new construct that was introduced in the ES6 specification is the ES6 classes. If you're a Javascript developer, you will be aware that Javascript follows prototypal inheritance and sometimes it can get a little messy. However, with ES6 classes the syntax is simpler and much more intuitive.

Classes are a fundamental part of object oriented programming (OOP). They define “blueprints” for real-world object modeling and organize code into logical, reusable parts. Classes share many similarities with regular objects. They have their own constructor functions, properties, and methods. In this tutorial, we’ll demonstrate how to create and work with ES6 classes.

Creating a class

You can create a class using the class keyword:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }

}



let person = new Person("Sam", 30)



person.name

//returns 'Sam'



person.age

//returns 30

Notice how we define our Person class with a constructor() function taking two arguments name and age. Using the this keyword, we set the name and age properties based on the provided arguments. Remember that the constructor function is called whenever we create a new instance of the Person class.

Similar to objects, we can read class properties using dot notation so that person.name returns Sam.

Defining properties and methods

You can define properties and methods for a class the same way you do for regular objects:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}



let person = new Person('Tim', 40)



person.sayName()

//logs 'My name is Tim'



person.location = 'London'



person.location

//returns 'London'

Notice how we define a sayName() function within our Person class definition. Once we create a new instance of Person, we can call the method via person.sayName().

You can also add properties and methods on the fly. You’ll notice that while the location property isn’t defined in our constructor function, we can still dynamically add it later on for the person instance. Remember that if we created a new instance of Person, it would not have a location property because that property is not defined in the class definition. Only properties and methods that we explicitly define will be shared by all instances of the class.

Static functions

You can use the static keyword to make class methods static. A static method acts on the class itself, not on instances of the class:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  static describe(){

    console.log("This is a person.")

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}



Person.describe()

//logs 'This is a person.'

Notice how static methods operate on the class itself and not an instance of the class. We didn’t have to create a new Person to call the static method.

Static methods are useful for common or shared class functionality. In this case, the describe() method is used to describe what the Person class is. It will apply to every instance of Person. This is why we make it a static method.

Class Inheritance

Inheritance allows you to create new classes based off existing ones. These new classes “inherit” the methods and properties of their parent. They can also override or extend the parent:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }


  static describe(){

    console.log("This is a person.")

  }


  sayName() {

    console.log("My name is " + this.name)

  }

}



class Programmer extends Person {

  sayName(){

    console.log("My name is " + this.name + " and I am a programmer!")

  }

}



let averageJoe = new Person('Todd', 40)

let programmer = new Programmer('Sam', 33)



averageJoe.sayName()


//logs 'My name is Todd'


programmer.sayName()


//logs 'My name is Sam and I am a programmer!'

Using the extends keyword, we can create a new class sharing the same characteristics as Person. Notice how we override the sayName() method with a new definition for the Programmer class. Apart from overriding this method, everything else remains the same for both Person and Programmer.

Using super

The super keyword allows a child class to invoke parent class properties and methods.

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  static describe(){

    console.log("This is a person.")

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}





class Programmer extends Person {

  sayName(){

    super.sayName()

    console.log("My name is " + this.name + " and I am a programmer!")

  }

}



let averageJoe = new Person('Todd', 40)

let programmer = new Programmer('Sam', 33)



programmer.sayName()



//logs 'My name is Sam'

//logs 'My name is Sam and I am a programmer!'

Notice how we call super.sayName() in the Programmer implementation of sayName(). While this invokes the parent implementation of super.sayName(), the name property still references the Programmer class.

Conclusion

Classes facilitate object oriented programming in JavaScript. While regular objects provide similar functionality, classes provide the extra advantage of inheritance and static methods.

Javascript ES6: Map-Reduce-Filter-Find

Javascript ES6: Map-Reduce-Filter-Find

If you are a fan of javascript and use it daily then you will love this. Way you write your JavaScript by using .map(), .reduce() and .filter() ... concise with arrow functions

If you are a fan of javascript and use it daily then you will love this

Javascript is a language that give freedom of writing code in any style, from imperative to declarative styles. Most programmer use imperative because either they are coming from OOPs background, may be they love it or they are not familiar with other style. Before we dive into the declarative style which is FP, let’s understand the differences in two by looking at an example(If you already know the difference then you may skip few paragraphs).

Imperative

// to calculate the sum of array elements
const sum = (arr) => {
  let result = 0; 
  for (let i = 0; i < arr.length; i++) {
    result += arr[i];
  }  
  return result;
};

Imperative style is cool but imagine what if there is a complex mathematics logic here then size of code and the readability will suck. It increases the cognitive load when reading, and over time makes it easier to faulter in reasoning and logic. Also, the main complexity of this code snippet derives from the fact that instead of telling the computer what we want it to do, we are instructing it on how to do it.

Declarative

// calculate the sum of array elements
const sum = (arr) => arr.reduce((total, item) => total += item, 0);

Now, this looks pretty clean, shorter, expressive, concise code, less error prone, easier to maintain and easier to debug. We are telling computer what we want it to do rather how to do it.

Declarative approach are easily optimisable at complier end and also have less side effects.

Note: if you are concerned about the performance of above two and other javascript function (map, reduce, filter, find) then you should for small data set and can view here for large data set(100–1000000)

Without more delay, let’s start the real action with most used Javascript function for functional programming.

Map

// definition 
collection.map((currentValue, index) => {
    // Return element for newArray
});
// example
const arr = [1,2,3,4,5];
const newArray = arr.map(i => i*10);
// return a new array with all value as multiple of 10;

Map works on an array and return an array that’s it. Above code snippet works on an collection i.e an array and takes a callback with current iteration value, index as arguments and return a new array.

Note: Maps are well suited for change/transforming whole array rather than breaking the flow for some conditions, Map suck’s performance wise, check out "underlined" here but are easy to be used for small data sets.

Reduce

// definition 
collection.reduce((accumulator, item, index) => {
    // logic to perform to get accumulator as a return value
}, initialValue for accumulator);
// example
const arr = [1,2,3,4,5];
const total = arr.reduce((acc, item) => acc+= item, 0);
// return a total as 15

Reduce works on an array but can return anything you want it to return. As the name speaks for itself it can be reduce to anything and can behave like map, find, filter or any other javascript function. The above code snippet works on an array and reduce to compute the total value of item of array.

Explanation of example above : On reduce first run, acc is assigned a 0 value and then acc+= item i.e acc = acc+item which will compute to0+1 i.e 1. This 1 will be acc value for next iteration and this continues until we are done with all array items.

Find

// definition 
collection.find((item) => {
    // return first element that satisfy the condition
});
// example
const arr = [1,2,8,4,5];
const value = arr.find(i => i%4 == 0);
// return the first value i.e 8 

Find works on an array and return the first element that satisfy the condition in function.

Note: Easy, simple but not efficient on large data set, why ? look here

Filter

// definition 
collection.filter((currentValue, index) => {
    // logic to filter array on
});
// example
const arr = [1,2,3,4,5];
const newArray = arr.filter(i => i%2 == 0);
// return a new array with value [2, 4]

Filter works on array return an array for filtered items.


Lets use them for some real world scenarios + some ES6. (lets try some ARMD on below object keys)

Wondering what is ARMD its Add, Read, Modify, Delete, its cool to coin your own jargon

const users = [
  {
    id: 1,
    name: "Jonathon Haley",
    username: "Monte.Weber2",
    email: "[email protected]",
    phone: "1-563-675-1857 x11708",
    website: "carmela.net",
    password: "hashed_password"
  },
  {
    id: 2,
    name: "Dean John",
    username: "dd.1",
    email: "[email protected]",
    phone: "1-123-543-1857 123212",
    website: "dd.net",
    password: "Dean_hashed_password"
  }

We will use users as array for further examples

1. ARMD — Adding a new element to users

const newUser = {
    id: 4,
    name: "Denomer Crazy",
    username: "crazy.1",
    email: "[email protected]",
    phone: "",
    website: "crazy.app",
    password: "crazed_checker"
};
const newData = [...users, newUser]; // add element at last
or 
const newData = [newUser, ...users]; // add element at first
or 
const newData = users.concat(newUser) // the old way

The use of es6 spread operator make super easy to add elements to array. We can use spread operator to concat two different array, modify shape of objects or add dynamic key value pairs etc.

const hobbies = ['chess', 'pool'];
const newUsers = users.map(u => ({...u, hobbies}))
// this will add hobbies to users array and return newUsers array

2. ARMD — Get email address, phone number and website of users into new array

const contactInfo = users.map(({email, website, phone}) => ({email, website, phone}));

The use es6 of destructuring of object keys and map to get the contact info array for user.

3. ARMD — Find and replace value for key of objects

const newUsers = users.map(u => u.id == 2? ({...u, name: 'te'}): u);
// this will return newUsers with all user having name 'te'

4. ARMD —Delete some key’s from object

Note: We will actually not delete the key but return a new object, if you want to delete the key use delete operator, here we are considering object immutability.

To delete keys there are lot of ways but we will look at the most easy, single lined. Lets try to delete website from users.

const newUsers = users.map({id, email, name, username, phone, password} => ({id, email, username, email, phone, password}));
// will return an array with all keys other than website

Above code seems to be practically difficult to code for big objects.

const newUsers = users.map(u => Object.keys(u).reduce((newObj, key) => key != 'website' ? { ...newObj, [key]: u[key]} : newObj, {}));

We map through the users and then on each user we do a reduce and form a new object (newObj) and then check for website key, if its a website we return the previously formed newObj, if not then we do a spread operator and add require key to obj and finally return newObj.

If anything is not clear or you want to point out something, please comment down below.

Thank you

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20