
According to the 2024 Stack Overflow Annual Report, JavaScript has been the most commonly used programming language for six years in a row. Let’s face it, JavaScript is the core of your Full Stack Development skills and can’t be avoided at any Development Interview. Follow through and read the Indiahires.in the collection of the most popular and tricky JavaScript Interview Questions and Answers to get the next dream job.
While typeof foo === “object” is a reliable way to test whether foo is an object, the surprising thing in JavaScript is that null is also considered an object!
Therefore, to the surprise of most developers, the following code must log true (not false) to the console:
var foo = null;
console.log(typeof foo === "object"); // logs true!
As long as one is aware of this, the issue can easily be avoided by checking that the foo is null:
console.log((foo !== null) && (typeof foo === "object")); // logs false
To be absolutely comprehensive in our response, there are two other things worth noting:
First, the above solution returns false if the foo is a function. In most cases, this is the optimal action, but in circumstances where you want to return true for functions as well, the following solution may be modified to:
console.log((foo !== null) && ((typeof foo === "object") || (typeof foo === "function")));
Second, the above solution returns true if the foo is an array (e.g. if var foo =[];). In most cases, this is the optimal action, because arrays are actually objects, but in circumstances where you want to false for arrays as well, the above solution may be modified to:
console.log((foo !== null) && (typeof foo === "object") && (toString.call(foo) !== "[object Array]"));
// However, there is another option that returns false for nulls, arrays, and functions, but valid for objects:
console.log((foo !== null) && (foo.constructor === Object));
Or, if you’re using jQuery:
console.log((foo !== null) && (typeof foo === "object") && (! $.isArray(foo)));
ES5 makes the case of the array quite easy, including its own null check:
console.log(Array.isArray(foo));
An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.
(function () {
statements
})();
It is a design pattern which is also known as the Self-Executing Anonymous Function and contains two key parts:
- The first is an anonymous lexical function contained within the grouping operator (). This restricts access to variables within the IIFE syntax and pollutes the global context.
- The second component generates the instantly invoked function expression () from which the JavaScript engine explicitly interprets the function.
Examples
The function becomes an expression of the function that is performed immediately. The element inside the expression can not be reached from outside the expression.
(function () {
var aName = "Barry";
})();
// Variable aName is not accessible from the outside scope
aName // throws "Uncaught ReferenceError: aName is not defined"
Assigning the IIFE to a variable stores the return value of the function, not the function description itself.
var result = (function () {
var name = "Barry";
return name;
})();
// Immediately creates the output:
result; // "Barry"
ES6 Interview Questions and Answers
I’m now using the following rule of thumb for functions in ES6 and beyond:
- Use
function
in the global framework and for Object.prototype properties. - Using the
class
Object Constructor Method. - Usage
=>
somewhere else.
Why do you use arrow functions almost everywhere?
- Scope Security: Since arrow functions are used continuously, it is assumed to use the same object as the root. If even a single regular function callback is mixed in with a lot of arrow functions, there’s a risk that the target will get messed up.
- Compactness: The arrow functions are easier to read and write to.
- Clarity: When nearly all is an arrow function, every normal function automatically sticks out for defining the scope. The developer will still look up the next-higher function statement and see what the object is.
The code shown above will output the following to the console:
outer func: this.foo = bar
outer func: self.foo = bar
inner func: this.foo = undefined
inner func: self.foo = bar
In the outer function, both this
and self
refer to myObject
and then both may properly refer to and access foo
.
In the inner function, though, this
no longer refers to myObject
. As a consequence, this.foo
is undefined in the inner function, whereas the reference to the local variable self
remains in scope and is accessible there.
The shortest and most important answer here is that use strict
is a way to voluntarily implement stricter parsing and error handling of your JavaScript code during runtime. Code errors that may either have been overlooked or failed quietly would now result in errors or exceptions. It’s a safe practice in general.
key benefits of strict mode:
- Makes debugging easier: Code bugs that might otherwise have been overlooked or failed quietly would now create bugs or throw exceptions, warn you faster to problems with your code, and send you more quickly to their source.
- Prevents accidental globals:Without a strict mode, assigning a value to an undeclared variable immediately generates a global variable with that name. This is one of the most popular JavaScript errors. In strict mode, trying to do so will create an mistake.
- Eliminates this coercion: Without a strict mode, the reference to this value of null or undefined is automatically linked to the global. This can trigger a lot of head-fakes and pull-out bugs. In strict mode, referencing a null or undefined value may cause an error.
- Disallows duplicate parameter values: Strict mode throws an error when it detects a duplicate named argument for a function ( e.g.,
function foo(val1, val2, val1)}
), thereby finding what is almost certainly an error in your code that you would otherwise have spent a lot of time monitoring.Note: It used to be (in ECMAScript 5) that strict mode would disallow duplicate property names (e.g.
var object = {foo: "bar", foo: "baz"};
) but as of ECMAScript 2015 this is no longer the case. - Makes eval() safer: There are some differences in the behavior of eval() in strict mode and in non-strict mode. Most importantly, in strict mode, variables and functions declared within the eval() statement are not created within the scope of the statement (they are created within the scope of the statement in non-strict mode, which can also be a common source of problems).
- Throws error on invalid usage of
delete
: Thedelete
operator (used to remove properties from objects) can not be used for non-configurable properties of the object. Non-strict code will fail silently when an attempt is made to remove a non-configurable property, whereas strict mode will cause an error in such a case.
The
bind()
method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
It took me a while to understand how 'this'
keyword works, but after reading about the bind method, it’s starting to make sense when it comes to the context of an object set explicitly and being able to use 'this'
keyword.
A common problem that occurs when we use functions is the scope of the function and the context of the object.
Every function has its own scope with visible and accessible variables, but when we define closures, these internal functions often have their own scope. If we were to access the context of the external function from the inner functions (closures), we would have to define a variable specifying the context of the outer function.
var self = this;
To avoid polluting the scope of an external function, we may use the bind approach instead. If we invoke a function with bind method, our closure or inner function is bound to the scope of the outer function.
var foo = function() {
// some content
console.log(this);
}.bind(this);
Code sample
var user = {
// this just holds our data in json object format
data:[
{name:"T. Woods", age:37},
{name:"P. Mickelson", age:43}
],
// this is an event handler we will use with our 'Get Random Person' button
clickHandler: function(event) {
// generates random number between 0 and 1
var randomNum = ((Math.random () * 2 | 0) + 1) - 1;
/*
This line is adding a random person from the data array to the text field
Note, that we are using 'this' keyword
As of now, 'this' keyword is bound to the user object since we have not executed this function
*/
$("input").val(this.data[randomNum].name + " " + this.data[randomNum].age);
}
}
/*
Now, we are assigning an event handler to the button's click event.
We are invoking the clickHandler method from the user object.
Once the button is clicked, the user.clickHandler method will be executed.
Since we are invoking clickHandler method inside of click method.
Our 'this' keyword inside of user.clickHandler is actually bound
to our button HTML element and NOT user object.
Executing code below will output an error because
it cannot find the data property item from our user object.
*/
//$("button").click(user.clickHandler); // Uncaught TypeError: Cannot read property '--' of undefined
/*
But when we use bind() on our method invocation.
bind() sets the scope of our clickHandler method to the user object.
Assigning the 'this' keyword to point to the user object rather
then the button HTML element.
*/
$("button").click(user.clickHandler.bind(user));
The NaN
property is a value that is “not a number.” This special value is the result of an operation that could not be done either because one of the operands are non-numeric (e.g. “abc”/4) or because the outcome of the operation is non-numeric.
Although this seems clear enough, there are a few very surprising features of NaN
that can lead to hair-pulling bugs if one is not aware of them.
For one thing, even though NaN
means “not a number,” its form is, believe it or not, Number:
console.log(typeof NaN === "number"); // logs "true"
/*Additionally, NaN compared to anything – even itself! – is false:*/
console.log(NaN === NaN); // logs "false"
The semi-reliable way to verify if a number is equal to NaN
is with built-in function isNaN()
, but even using isNaN()
is an incomplete solution.
Then a better way would be to use value !== value
, which will only be valid if the value is equal to NaN
. ES6 also provides a new Number isNaN()
function, which is different and more accurate than the old global isNaN()
function.
Since Anonymous Functions are function expressions, rather than regular function declarations, which are statements. Function expressions are often more versatile. We can assign functions to variables, object properties, move them as arguments to other functions, and even write a single line code enclosed in anonymous functions.
Example
var squaredArray = inputArray.map(function(x) { return x * x; });
This becomes much more concise with the ES6 syntax.
var squaredArray = inputArray.map(x => x * x);
Another common example will be the anonymous function used by the famous frameworks such as the IIFE (immediate Invoked Feature Expression) feature.
(function() { })();
const
and Object.freeze()
serve totally different purposes.
-
const
const
is there for declaring a variable which has to assinged right away and can’t be reassigned. variables declared byconst
are block scoped and not function scoped like variables declared withvar
Example 1: Can’t reassign const
const foo = 5; foo = 6;
The following code throws an error because we are trying to reassign the variable foo who was declared with the const keyword, we can’t reassign it.
Example 2: Data structures which are assigned to const can be mutated
const object = { prop1: 1, prop2: 2 } object.prop1 = 5; // object is still mutable! object.prop3 = 3; // object is still mutable! console.log(object); // object is mutated
In this example we declare a variable using the
const
keyword and assign an object to it. Although we can’t reassign to this variable called object, we can mutate the object itself. If we change existing properties or add new properties this will this have effect. To disable any changes to the object we needObject.freeze()
. -
Object.freeze()
Object.freeze()
is a method which accepts an object and returns the same object. Now the object cannot have any of its properties removed or any new properties added.Example 1: Can’t mutate a frozen object
object1 = { prop1: 1, prop2: 2 } object2 = Object.freeze(object1); console.log(object1 === object2); // both objects are refer to the same instance object2.prop3 = 3; // no new property can be added, won't work delete object2.prop1; // no property can be deleted, won't work console.log(object2); // object unchanged
In this example when we call
Object.freeze()
and give object1 as an argument the function returns the object which is now ‘frozen’. If we compare the reference of the new object to the old object using the===
operator we can observe that they refer to the same object. Also when we try to add or remove any properties we can see that this does not have any effect (will throw error in strict mode).Example 2: Objects with references aren’t fully frozen
const object = { prop1: 1, nestedObj: { nestedProp1: 1, nestedProp2: 2, } } const frozen = Object.freeze(object); frozen.prop1 = 5; // won't have any effect frozen.nestedObj.nestedProp1 = 5; //will update because the nestedObject isn't frozen console.log(frozen);
This example shows that the properties of nested objects (and other by reference data structures) are still mutable. So
Object.freeze()
doesn’t fully ‘freeze’ the object when it has properties which are references (to e.g. Arrays, Objects).
This which sound trivial and, in fact, it is trivial with ECMAscript 6, which introduces a new Number.isInteger() function for this very reason. However, this is a little more complicated before ECMAScript 6, as no equivalent Number.isInteger() method is given.
The problem is that integers only exist conceptually in the ECMAScript specification; i.e. numeric values are only represented as floating point values.
With that in mind, the easiest and cleanest pre-ECMAScript-6 solution (which is also sufficiently robust to return false
even if a non-numeric value such as string
or null
is passed to the function) will be the following use by the Bitwise XOR operator:
function isInteger(x) { return (x ^ 0) === x; }
The following solution will also work, but not as elegant as the one mentioned above:
function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0); }
The following function (or with Math.ceil()
or Math.floor()
instead of Math.round()
)might also seem useful, but the results are not exactly the same as the following two functions:
function isInteger(x) { return Math.round(x) === x; }
The difference is that these Math-based solutions return true to Infinity and-Infinity, while the others (and in particular ES6’s Number.isInteger()
) return false
.
Another reasonably common incorrect solution is as follows:
function isInteger(x) { return parseInt(x, 10) === x; }
Although this parseInt-based approach will work well for many values of x, once x becomes very high, it will not work properly.The problem is that parseInt()
pushes its first parameter to a string before parsing the digits. Therefore, until the number becomes sufficiently large, the representation of the string will be shown in exponential form (e.g., 1e+21). parseInt()
will then attempt to parse 1e+21, but will avoid parsing when the e character is reached and will then return a value of 1. Remarks:
> String(1000000000000000000000)
'1e+21'
> parseInt(1000000000000000000000, 10)
1
> parseInt(1000000000000000000000, 10) === 1000000000000000000000
false
Callbacks are a smart way to do something when something important has been done. We mean the execution of a function by something here. If we want to perform a function right after some other function is returned, then callbacks can be used.
The JavaScript functions have an object type. So, just like any other object (string, arrays, etc.), it can be passed as an argument to any other function when calling.
// add() function is called with arguments a, b
// and callback, callback will be executed just
// after ending of add() function
function add(a, b , callback){
document.write(`The sum of ${a} and ${b} is ${a+b}.` +"
");
callback();
}
// add() function is called with arguments given below
// Calling add() function
add(5,6,function disp(){
document.write('This must be printed after addition');
});
Callbacks are mainly used when performing asynchronous operations, such as making an API request to Google Maps, retrieving / writing data from / to a file, recording event listeners and related stuff. The callbacks are used for all the operations listed. This way, once the data / error from the asynchronous operation is returned, the callbacks are used to do something within our code.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
Lexical scoping
function init() {
var name = 'Mozilla'; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert(name); // use variable declared in the parent function
}
displayName();
}
init();
init() creates a local variable called name and a function called displayName(). The displayName() function is an inner function that is defined inside init() and is available only within the body of the init() function. Note that the displayName() function has no local variables of its own. However, since inner functions have access to the variables of outer functions, displayName() can access the variable name declared in the parent function, init().
Closure
var globalVar = "xyz";
(function outerFunc(outerArg) {
var outerVar = 'a';
(function innerFunc(innerArg) {
var innerVar = 'b';
console.log(
"outerArg = " + outerArg + "\n" +
"innerArg = " + innerArg + "\n" +
"outerVar = " + outerVar + "\n" +
"innerVar = " + innerVar + "\n" +
"globalVar = " + globalVar);
})(456);
})(123);
Output
outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz
The reason is that functions in JavaScript form closures. A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.
In this case, myFunc is a reference to the instance of the function displayName that is created when makeFunc is run. The instance of displayName maintains a reference to its lexical environment, within which the variable name exists. For this reason, when myFunc is invoked, the variable name remains available for use, and “Mozilla” is passed to alert.
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
n this example, we have defined a function makeAdder(x), that takes a single argument x, and returns a new function. The function it returns takes a single argument y, and returns the sum of x and y.
a) No matter what button the user presses, the number 5 will still be logged in to the console. This is because, at the point where the onclick method is invoked (for each of the buttons), the for loop has already been completed and the variable I already has a value of 5.
b) The key to this work is to catch the value of I at each pass through the for loop by passing it to the newly generated function object. Here are four potential ways to do this:
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
(function (i) {
btn.addEventListener('click', function() { console.log(i); });
})(i);
document.body.appendChild(btn);
}
The logged output will be:
"array 1: length=5 last=j,o,n,e,s"
"array 2: length=5 last=j,o,n,e,s"
arr1
and arr2
are the same (i.e. ['n','h','o','j', ['j','o','n','e',"']) after the above code has been executed for the following reasons:
- Calling the
reverse()
method of an array object not only returns the array in reverse order, it also reverses the order of the array itself (i.e., in this case,arr1
). - The
reverse()
method returns a reference to the array itself (i.e., in this case,arr1
). As a response,arr2
is simply a reference to (rather than a copy of)arr1
. Therefore, when something is done witharr2
(i.e. when we invokearr2.push(arr3)
,arr1
will be affected as well, becausearr1
andarr2
are simply references to the same object.
The above code will output the following to the console:
"122"
"32"
"02"
"112"
"NaN2"
"NaN"
The main issue here is that the JavaScript (ECMAScript) is a loosely typed language and performs automatic type conversion on values to accommodate the operation being performed. Let's see how this is achieved for each of the examples above.
-
Example 1:
1 + "2" + "2"
Outputs:
"122"
Explanation:
The first operation to be done in
1 + "2"
. Since one of the operands ("2"
) is a string, JavaScript assumes that it needs to perform string concatenation and thus converts the type1
to"1"
,1 + "2"
returns"12"
Then,"12" + "2"
returns code>"122" -
Example 2:
1 + +"2" + "2"
Outputs:
"32"
Explanation:
Based on the order of operation, the first operation to be performed is
+ "2"
(extra+
before the first"2"
is regarded as a single operator). As a result, JavaScript converts the form of"2"
to numeric and then applies the unary+
sign to it (i.e. treats it as a positive number). As a consequence of this, the next operation is now1 + 2
which, of course, yields3
. But then, we have an operation between a number and a string (i.e.,3
and"2"
), so again, JavaScript converts the type of the numeric value to a string and performs a string concatenation, yielding"32."
-
Example 3:
1 + -"1" + "2"
Outputs:
"02"
Explanation:
The explanation here is the same as the previous example, except that the unary operator is
-
rather than+
. Thus,"1"
becomes1
, which then becomes-1
when applied, which is then added to1
yielding0
, which is then converted to a string and concatenated to the final"2"
operand, yielding"02"
-
Example 4:
+"1" + "1" + "2"
Outputs:
"112"
Explanation:
While the first
"1"
operand is typed to a numeric value based on the unary+
operator that precedes it, it is automatically transformed back to a string when it is concatenated to the second"1"
operand, which is then concatenated to the final"2"
operand, yielding the string"112."
-
Example 5:
"A" - "B" + "2"
Outputs:
"NaN2"
Explanation:
Since the-operator can not be applied to strings, and because neither
"A"
nor"B"
can be converted to numeric values,"A"-"B"
produce NaN which is then concatenated to string"2"
to produce"NaN2."
-
Example 6:
"A" - "B" + 2
Outputs:
"NaN"
Explanation:
Since the-operator can not be applied to strings, and because neither
"A"
nor"B"
can be converted to numeric values,"A"-"B"
produce NaN. But any operator applied to NaN with any other numeric operand will still produce"NaN"
The code sample shown will not display the values 0, 1, 2, 3, and 4 as might be expected; rather, it will display 5, 5, 5, 5, and 5.
The explanation for this is that each function executed inside the loop will be executed after the entire loop has been completed and all will then apply to the last value stored in I which was 5.
Closures can be used to avoid this issue by creating a specific scope for each iteration, and by storing each specific value of the variable within its scope as follows:
for (var i = 0; i < 5; i++) {
(function(x) {
setTimeout(function() { console.log(x); }, x * 1000 );
})(i);
}
This will produce the presumably desired result of logging 0, 1, 2, 3, and 4 to the console.
The output of this code will be 456 (not 123).
The explanation for this is as follows: when setting an object property, JavaScript will implicitly string the value of the parameter. In this case, since b and c are both objects, both objects will be translated to "[object object]
." As a result, a[b]
and a[c]
are both similar to a ['[object object]']
and can be used interchangeably. Setting or referencing a[c]
is also exactly the same as setting or referencing a[b]
.
The code will output the value of 10 factorial (i.e., 10!, or 3,628,800).
The named function f()
calls itself recursively, until it gets down to calling f(1)
which simply returns 1
. Here, therefore, is what this does:
f(1): returns n, which is 1
f(2): returns 2 * f(1), which is 2
f(3): returns 3 * f(2), which is 6
f(4): returns 4 * f(3), which is 24
f(5): returns 5 * f(4), which is 120
f(6): returns 6 * f(5), which is 720
f(7): returns 7 * f(6), which is 5040
f(8): returns 8 * f(7), which is 40320
f(9): returns 9 * f(8), which is 362880
f(10): returns 10 * f(9), which is 3628800
The output will be 1
, even though the value of x
is never set in the inner function. Here’s why:.
Closure is a function, along with all variables or functions that were in-scope at the time the closure was made. In JavaScript, a closure is performed as a "inner function;"
i.e. a function specified within the body of another function. An significant feature of closures is that the inner function always has access to the variables of the outer function.
Therefore, in this case, since x
is not specified in the inner function, the scope of the outer function is searched for the given variable x
, which is found to have a value of 1
.
Output:
10
2
First, as fn
is passed as a parameter to the function method
, the scope (this
) of the fn function is a window. var length = 10;
is stated at the window level. You may also view it as window.length
or length or this.length
(when this === window
).
Method
is bound to Object obj
, and obj.method
is named for parameters fn
and 1
. Although the method accepts just one parameter, it has passed two parameters when invoking; the first is a callback function and the second is just an amount.
When fn()
is called inside method
, which passed the function as a global parameter, this.length
will have access to var length = 10
(declared globally) not length = 5
as described in Object obj
.
Now, we know that we can use the arguments[]
array to access any number of arguments in the JavaScript function. Hence arguments[0]()
is nothing but a call to fn()
. Inside fn now, the scope of this function becomes the arguments list, and logging the duration of the arguments[]
returns 2
.
Output:
1
undefined
2
var
statements are hoisted (without their initialization value) to the top of the global or function range to which they belong, even if they are within a with or catch row
. However, the error identifier is only visible inside the catch
block. It is equivalent to the following:
(function () {
var x, y; // outer and hoisted
try {
throw new Error();
} catch (x /* inner */) {
x = 1; // inner x, not the outer one
y = 2; // there is only one y, which is in the outer scope
console.log(x /* inner */);
}
console.log(x);
console.log(y);
})();
var obj = {a: 1 ,b: 2}
var objclone = Object.assign({},obj);
Now the value of objclone is {a: 1 ,b: 2}
but points to a different object than obj
.
Note the possible pitfall, though: Object.clone()
can only create a shallow copy, not a deep copy. This ensures that the nested objects are not copied. They still refer to the same nested objects as the original ones:
let obj = {
a: 1,
b: 2,
c: {
age: 30
}
};
var objclone = Object.assign({},obj);
console.log('objclone: ', objclone);
obj.c.age = 45;
console.log('After Change - obj: ', obj); // 45 - This also changes
console.log('After Change - objclone: ', objclone); // 45
The first statement returns true
which is as expected.
The second returns false
because of how the JavaScript engine works regarding operator associativity for <
and >
. It compares left to right, so 3 > 2 > 1
JavaScript translates to true > 1
. true has value 1
, so it then compares 1 > 1
, which is false
.
ES5:
var myArray = ['a', 'b', 'c', 'd'];
myArray.push('end');
myArray.unshift('start');
console.log(myArray); // ["start", "a", "b", "c", "d", "end"]
With ES6:
myArray = ['start', ...myArray];
myArray = [...myArray, 'end'];
myArray = ['start', ...myArray, 'end'];
A Set
is a collection of items that are unique, i.e. no element can be replicated. Set
in ES6 are ordered: the Set
elements can be iterated in the insertion order. Set
can store any kind of value, whether primitive or entity.
Syntax:
new Set([it]);
Parameter:
it - It is an iterable object whose all elements are
added to the new set created,
If the parameter is not specified or null is passed
then a new set created is empty.
Returns:
A new set object
Example:
// it contains
// ["sumit","amit","anil","anish"]
var set1 = new Set(["sumit","sumit","amit","anil","anish"]);
// it contains 'f', 'o', 'd'
var set2 = new Set("fooooooood");
// it contains [10, 20, 30, 40]
var set3 = new Set([10, 20, 30, 30, 40, 40]);
// it is an empty set
var set4 = new Set();
Properties:
Set.prototype.size – It returns the number of elements in the Set
.
Methods:
-
Set.prototype.add() – It adds the new element with a specified value at the end of the Set object.
Syntax:
set1.add(val); Parameter: val - It is a value to be added to the set. Returns: The set object
-
Set.prototype.delete() – It deletes an element with the specified value from the Set object.
Syntax:
set1.delete(val); Parameter: val - It is a value to be deleted from the set. Returns: true if the value is successfully deleted from the set else returns false.
-
Set.prototype.clear() – It removes all the element from the set.
Syntax:
set1.clear(); Parameter: No parameters Returns: undefined
-
Set.prototype.entries() – It returns an iterator object which contains an array having the entries of the set, in the insertion order.
Syntax:
set1.entries(); Parameter: No parameters Returns: It returns an iterator object that contains an array of [value, value] for every element of the set, in the insertion order.
-
Set.prototype.has() – It returns true if the specified value is present in the Set object.
Syntax:
set1.has(val); Parameter: val - The value to be searched in the Set Returns: True if the value is present else it returns false.
-
Set.prototype.values() – It returns all the values from the Set in the same insertion order.
Syntax:
set1.values(); Parameter: No parameters Returns: An iterator object that contains all the values of the set in the same order as they are inserted.
-
Set.prototype.keys() – It also returns all the values from the Set in the insertion order.
Syntax:
set1.keys(); Parameter: No parameters Returns: An iterator object that contains all the values of the set in the same order as they are inserted.
-
Set.prototype.forEach() – It executes the given function once for every element in the Set, in the insertion order.
Syntax:
set1.forEach(callback[,thisargument]); Parameter: callback - It is a function which is to be executed for each element of the Set. thisargument - Value to be used as this when executing the callback. Returns: Undefined
The callback function is provided with three parameters as follows:
- the element key
- the element value
- the Set object to be traversed
Example:
// Using Set.prototype.forEach(callback[, thisarg]) // creating set var set1 = new Set(); // adding element to the set set1.add({Firstname: "India", Lastname: "Hires"}); set1.add(50); set1.add(30); set1.add(40); set1.add("Geeks"); set1.add("GFG"); // Declaring a call back function // we are using only one parameter value // so it will ignore other two . function printOne(values) { console.log(values); } // It prints value of all the element // of the set set1.forEach(printOne); // Declaring a call back function // we are using two parameter value // so it will ignore last one function printTwo(key, values) { console.log(key+" "+values); } // As key and values are same in set // so it will print values twice set1.forEach(printTwo); // Declaring a call back function // we are using all three parameter value function printThree(key, values, set) { // it will print key and value // and the set object console.log(key+" "+values); console.log(set); } // It prints key and value of each // element and the entire set object set1.forEach(printThree);
-
Set.protoype[@@iterator]() – It returns a Set iterator function which is values() function by default.
Syntax:
set1[Symbol.iterator](); Parameter: No parameters Returns: A Set iterator function and it is values() by default.
Example:
// using Set.protoype[@@Iterator]() var set1 = new Set(["india","india","hires","jobs"]); var getit = set1[Symbol.iterator](); // Printing the values in the // iterator "getit" // prints {value: "india", done: false} console.log(getit.next()); // prints {value: "hires", done: false} console.log(getit.next());
Set Operations:
-
subSet() – It returns true if Set A is a subset of Set B
A Set A is said to be a subset of Set B, if all the elements of Set A is also present in Set B. Now lets implement and use the subset function.
Example:
// check whether the set on which the // method is invoked is the subset of // otherset or not Set.prototype.subSet = function(otherSet) { // if size of this set is greater // than otherSet then it can'nt be // a subset if(this.size > otherSet.size) return false; else { for(var elem of this) { // if any of the element of // this is not present in the // otherset then return false if(!otherSet.has(elem)) return false; } return true; } } // using the subSet function // Declaring different sets var setA = new Set([10, 20, 30]); var setB = new Set([50, 60, 10, 20, 30, 40]); var setC = new Set([10, 30, 40, 50]); // prints true console.log(setA.subSet(setB)); // prints false console.log(setA.subSet(setC)); // prints true console.log(setC.subSet(setB));
-
union() – It returns a Set which consists of union of Set A and Set B
A Set is said to be a union of two set, if it contains all element of Set A as well as all elements of Set B, but it does’nt contains duplicate elements
Example:
// Perform union operation between // called set and otherSet Set.prototype.union = function(otherSet) { // creating new set to store union var unionSet = new Set(); // iterate over the values and add // it to unionSet for (var elem of this) { unionSet.add(elem); } // iterate over the values and add it to // the unionSet for(var elem of otherSet) unionSet.add(elem); // return the values of unionSet return unionSet; } // using the union function // Declaring values for set1 and set2 var set1 = new Set([10, 20, 30, 40, 50]); var set2 = new Set([40, 50, 60, 70, 80]); // performing union operation // and storing the resultant set in // unionSet var unionSet = set1.union(set2); // prints [10, 20, 30, 40, 50, 60, 70, 80] console.log(unionSet.values());
-
intersection() – It returns the intersection of Set A and Set B.
A Set is said to be the intersection of Set A and B if contains element which is present both in Set A and Set B.
Example:
// Performs intersection operation between // called set and otherSet Set.prototype.intersection = function(otherSet) { // creating new set to store intersection var intersectionSet = new Set(); // Iterate over the values for(var elem of otherSet) { // if the other set contains a // similar value as of value[i] // then add it to intersectionSet if(this.has(elem)) intersectionSet.add(elem); } // return values of intersectionSet return intersectionSet; } // using intersection function // Declaring values for set1 and set2 var set1 = new Set([10, 20, 30, 40, 50]); var set2 = new Set([40, 50, 60, 70, 80]); // performing union operation // and storing the resultant set in // intersectionset var intersectionSet = set1.intersection(set2); // prints {40, 50} console.log(intersectionSet.values());
-
difference() – It returns the Set which contains difference of Set A and Set B.
A Set is said to be a difference of Set A and B if it contains set of element e which are present in Set A but not in Set B.
Example:
// Performs difference operation between // called set and otherSet Set.prototype.difference = function(otherSet) { // creating new set to store difference var differenceSet = new Set(); // iterate over the values for(var elem of this) { // if the value[i] is not present // in otherSet add to the differenceSet if(!otherSet.has(elem)) differenceSet.add(elem); } // returns values of differenceSet return differenceSet; } // using difference function // Declaring values for set1 and set2 var set1 = new Set([10, 20, 30, 40, 50]); var set2 = new Set([40, 50, 60, 70, 80]); // performing union operation // and storing the resultant set in // intersectionset var differenceSet = set1.difference(set2); // prints {10, 20, 30} console.log(differenceSet);
Prototypal inheritance
Programming, we always want to do something and expand it.
For example, we have a user object with its properties and methods, and we want to make admin and guest as slightly changed variants. We'd like to reuse what we have in the user, not copy / re-implement the methods, just create a new object on top of it.
Prototypal inheritance is a language feature that benefits.
[[Prototype]]
In JavaScript, objects have a special hidden property [[Prototype]] (as defined in the specification) that is either null or a reference to another object. The object is called the "prototype":
If we try to read an object property and it's missing, JavaScript will automatically take it from the prototype. In programming, this is called the "prototypal inheritance." A lot of cool language features and programming techniques are based on that.
The [[Prototype]] property is internal and secret, but there are several ways to set it up. One of them is to use the special name proto , as follows:
let animal = {
eats: true,
walk() {
alert("Animal walk");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
};
// walk is taken from the prototype chain
longEar.walk(); // Animal walk
alert(longEar.jumps); // true (from rabbit)
__proto__ is a historical getter/setter for [[Prototype]]
Please note that __proto__ is not the same as [[Prototype]]. It’s a getter/setter for it.
It exists for historical reasons. In modern language it is replaced with functions Object.getPrototypeOf/Object.setPrototypeOf
that also get/set the prototype.
rabbit.__proto__ = animal; // (*)
// we can find both properties in rabbit now:
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true
Here the line (*) sets animal to be a prototype of rabbit
Then, when alert
tries to read property rabbit.eats (**)
, it’s not in rabbit, so JavaScript follows the [[Prototype]]
reference and finds it in animal
(look from the bottom up):
The method is automatically taken from the prototype, like this:
There are only two drawbacks to this:
- The references can’t go in circles. JavaScript will throw an error if we try to assign
__proto__
in a circle. - The value of
__proto__
can be either an object ornull
. Other types are ignored.
It can also be obvious, but still: there can only be one [[Prototype]]
. An object is not allowed to inherit from two others
Writing doesn’t use prototype
The prototype is only used for reading properties. Write/delete operations work directly with the object.
rabbit.walk = function() {
alert("Rabbit! Bounce-bounce!");
};
rabbit.walk(); // Rabbit! Bounce-bounce!
From now on, rabbit.walk()
call will automatically find the method in the object and execute it without using the prototype:
The properties of the accessor are an exception, as the assignment is done by a setter function. So writing to a property like that is actually the same as calling a function.
For this purpose, admin.fullName
is functioning correctly in the code below:
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = {
__proto__: user,
isAdmin: true
};
alert(admin.fullName); // John Smith (*)
// setter triggers!
admin.fullName = "Alice Cooper"; // (**)
alert(admin.fullName); // Alice Cooper , state of admin modified
alert(user.fullName); // John Smith , state of user protected
The value of "this"
No matter where the method is found: in an object or its prototype. In a method call, this is always the object before the dot.
So, the setter call admin.fullName=
uses admin
as this
, not user.
That's actually a really interesting thing, because we could have a large object with a lot of methods, and we could have objects that inherit it. And as inherited objects function inherited methods, they can only change their own states, not the state of the big object.
For instance, here animal
represents a "method storage", and rabbit
makes use of it.
The call rabbit.sleep()
sets this.isSleeping
on the rabbit
object:
// animal has methods
let animal = {
walk() {
if (!this.isSleeping) {
alert(`I walk`);
}
},
sleep() {
this.isSleeping = true;
}
};
let rabbit = {
name: "White Rabbit",
__proto__: animal
};
// modifies rabbit.isSleeping
rabbit.sleep();
alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (no such property in the prototype)
As a result, methods are shared, but the object state is not.
for…in loop
The for..in
loop iterates over inherited properties too.
If that’s not what we want, and we’d like to exclude inherited properties, there’s a built-in method obj.hasOwnProperty(key): it returns true if obj has its own (not inherited) property named key.
So we can filter out inherited properties (or do something else with them):
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
for(let prop in rabbit) {
let isOwn = rabbit.hasOwnProperty(prop);
if (isOwn) {
alert(`Our: ${prop}`); // Our: jumps
} else {
alert(`Inherited: ${prop}`); // Inherited: eats
}
}
Here we have the following inheritance chain: rabbit
inherits from animal
, that inherits from Object.prototype
(because animal
is a literal object {...}
, so it’s by default), and then null
above it:
As functions are also Objects in JavaScript, these 3 methods are used to control the invocation of the function. call()
and apply()
were introduced in ECMAScript 3 while bind()
was added as part of ECMAScript 5.
Uses
We can use call()
and apply()
to invoke the function immediately. bind()
returns a bound function that, when executed later, will have the correct context ("this"
) for calling the original function. So bind()
can be used when the function needs to be called later in certain events when it's useful.
call() or Function.prototype.call()
var person = {
name: "India",
hello: function(thing) {
console.log(this.name + " says hello to" + thing);
}
}
person.hello("world"); // output: "India says hello to world"
person.hello.call({ name: "India hires" }, "Javascipt"); // output: "India hires says Javascipt"
The first parameter in call()
method sets the "this" value, which is the object, on which the function is invoked upon. In this case, it's the "obj" object above. The rest of the parameters are the arguments to the actual function.
apply() or Function.prototype.apply()
function personContainer() {
var person = {
name: "James Smith",
hello: function() {
console.log(this.name + " says hello " + arguments[1]);
}
}
person.hello.apply(person, arguments);
}
personContainer("world", "mars"); // output: "James Smith says hello mars", note: arguments[0] = "world" , arguments[1] = "mars"
Similarly to call()
method the first parameter in apply()
method sets the "this" value which is the object upon which the function is invoked. In this case it's the "obj" object above. The only difference of apply()
with the call()
method is that the second parameter of the apply()
method accepts the arguments to the actual function as an array.
bind() or Function.prototype.bind()
var person = {
name: "James Smith",
hello: function(thing) {
console.log(this.name + " says hello " + thing);
}
}
person.hello("world"); // output: "James Smith says hello world"
var helloFunc = person.hello.bind({ name: "Jim Smith" });
helloFunc("world"); // output: Jim Smith says hello world"
In the above code sample for bind()
we are returning a bound function with the context which will be invoked later.
The first parameter to the bind()
method sets the value of "this" in the target function when the bound function is called.
Event Bubbling and Capturing are two ways of event propagation in the HTML DOM API, when an event occurs in an element within a different one, and all elements have registered a handle for that event. The event propagation mode specifies the order in which the event is received by the elements.
With bubbling, the event is first captured and handled by the innermost element and then propagated to the outer element. With the capture, the event is first captured by the outermost element and propagated to the inner elements.
We can use the addEventListener(type, listener, useCapture)
to register event handlers for in either bubbling (default) or capturing mode. To use the capturing model pass the third argument as true
.
Example
<div>
<ul>
<li></li>
</ul>
</div>
In the structure above, assume that a click event occurred in the li
element. In capturing model, the event will be handled by the div
first (click event handlers in the div
will fire first), then in the ul, then at the last in the target element, li
.
In the bubbling model, the opposite will happen: the event will be first handled by the li
, then by the ul
, and at last by the div
element.
In the example below, if you click on any of the highlighted elements, you can see that the event propagation flow capture phase occurs first, followed by the bubbling phase.
var logElement = document.getElementById('log');
function log(msg) {
logElement.innerHTML += ('' + msg + '
');
}
function capture() {
log('capture: ' + this.firstChild.nodeValue.trim());
}
function bubble() {
log('bubble: ' + this.firstChild.nodeValue.trim());
}
function clearOutput() {
logElement.innerHTML = "";
}
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>