JavaScript Course 6: Functions: Difference between revisions

From WikiMLT
m (Стадий: 6 [Фаза:Утвърждаване, Статус:Утвърден]; Категория:JavaScript)
 
m (Text replacement - "mlw-continue" to "code-continue")
 
Line 19: Line 19:
== Basics ==
== Basics ==
JavaScript Functions are ''sets of statements'' ''(like a block of code)'' that ''perform a task'' or ''calculates a value''. The Functions could have Inputs, they could use (multiple) parameters, but it is not mandatory.
JavaScript Functions are ''sets of statements'' ''(like a block of code)'' that ''perform a task'' or ''calculates a value''. The Functions could have Inputs, they could use (multiple) parameters, but it is not mandatory.
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
function greet(name) {  // 'name' is an input parameter of the function
function greet(name) {  // 'name' is an input parameter of the function
     console.log('Hello ' + name);
     console.log('Hello ' + name);
}  
}  
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
greet('Spas');        // 'Spas' is an argument which become a value of the param. 'name'
greet('Spas');        // 'Spas' is an argument which become a value of the param. 'name'
</syntaxhighlight>
</syntaxhighlight>
Line 37: Line 37:


=== Function declaration ===
=== Function declaration ===
The first thing you should know about <u>function declarations</u> is that <u>they are '''hoisted'''</u>. The function name cannot be changed once declared since it is loaded into the memory. In ES2015 and later, <u>functions inside blocks are scoped to that block</u>. <syntaxhighlight lang="javascript" class="mlw-continue">
The first thing you should know about <u>function declarations</u> is that <u>they are '''hoisted'''</u>. The function name cannot be changed once declared since it is loaded into the memory. In ES2015 and later, <u>functions inside blocks are scoped to that block</u>. <syntaxhighlight lang="javascript" class="code-continue">
function walk() {
function walk() {
     console.log('Walk');
     console.log('Walk');
Line 57: Line 57:


==== Named Function expression ====
==== Named Function expression ====
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
let run = function walk() {
let run = function walk() {
     console.log('Run');
     console.log('Run');
Line 64: Line 64:


==== Anonymous Function expression ====
==== Anonymous Function expression ====
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
let run = function() {
let run = function() {
     console.log('Run');
     console.log('Run');
};
};
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
run(); // just how we call a function in JavaScript
run(); // just how we call a function in JavaScript
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Run
Run
</syntaxhighlight>We can declare another variable (called <code>move</code> in the example below) which refers to the same function (object in the memory).<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>We can declare another variable (called <code>move</code> in the example below) which refers to the same function (object in the memory).<syntaxhighlight lang="javascript" class="code-continue">
let move = run;  
let move = run;  
move();
move();
Line 88: Line 88:
   console.log('lumos'); // lumos
   console.log('lumos'); // lumos
})();
})();
</syntaxhighlight>IIFEs can also have parameters.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>IIFEs can also have parameters.<syntaxhighlight lang="javascript" class="code-continue">
// Declaring the parameter required.
// Declaring the parameter required.
(function(dt) {
(function(dt) {
Line 100: Line 100:


=== Nested Functions and Closures ===
=== Nested Functions and Closures ===
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const add = (function() {
const add = (function() {
   let counter = 0;
   let counter = 0;
   return function() {counter += 1; return counter};
   return function() {counter += 1; return counter};
})();
})();
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
add(); add(); add(); // the counter is now 3
add(); add(); add(); // the counter is now 3
</syntaxhighlight>Example Explained:
</syntaxhighlight>Example Explained:
Line 116: Line 116:


== Arguments and Parameters ==
== Arguments and Parameters ==
Within the function's declaration or expression <code>a</code> and <code>b</code> (of the following example) are called '''Parameters'''.<syntaxhighlight lang="javascript" class="mlw-continue">
Within the function's declaration or expression <code>a</code> and <code>b</code> (of the following example) are called '''Parameters'''.<syntaxhighlight lang="javascript" class="code-continue">
function sum(a, b) {
function sum(a, b) {
     return a + b;
     return a + b;
}
}
</syntaxhighlight>When we call the function and pass values to its parameters, these values are called '''Arguments'''.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>When we call the function and pass values to its parameters, these values are called '''Arguments'''.<syntaxhighlight lang="javascript" class="code-continue">
console.log(sum(1, 2));   
console.log(sum(1, 2));   
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
3
3
</syntaxhighlight>In JavaScript have a '''special object, called <code>arguments</code>''', that holds all arguments pass to the function.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>In JavaScript have a '''special object, called <code>arguments</code>''', that holds all arguments pass to the function.<syntaxhighlight lang="javascript" class="code-continue">
function sum(a, b) {
function sum(a, b) {
     console.log(arguments);
     console.log(arguments);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
sum(1, 2);   
sum(1, 2);   
</syntaxhighlight>
</syntaxhighlight>
Line 140: Line 140:
     Symbol(Symbol.iterator): ƒ values()
     Symbol(Symbol.iterator): ƒ values()
     [[Prototype]]: Object
     [[Prototype]]: Object
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
function sumArguments() {
function sumArguments() {
     let total = 0;
     let total = 0;
Line 147: Line 147:
     return total;
     return total;
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(sumArguments(1, 2, 3, 4, 5, 10));   
console.log(sumArguments(1, 2, 3, 4, 5, 10));   
</syntaxhighlight>
</syntaxhighlight>
Line 157: Line 157:
In modern JS, if you want to have a function with varying number of parameters, you can use the '''Rest operator: <code class="noTypo">...args</code>'''. It looks like exactly to the Spread operator used with the Arrays and Objects, but don't confuse them.
In modern JS, if you want to have a function with varying number of parameters, you can use the '''Rest operator: <code class="noTypo">...args</code>'''. It looks like exactly to the Spread operator used with the Arrays and Objects, but don't confuse them.


The ''Rest operator'' creates an array of the Arguments that are not associated to a function Parameters.<syntaxhighlight lang="javascript" class="mlw-continue">
The ''Rest operator'' creates an array of the Arguments that are not associated to a function Parameters.<syntaxhighlight lang="javascript" class="code-continue">
function sum(...args) {
function sum(...args) {
     console.log(args);
     console.log(args);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
sum(1, 2, 3, 4, 5, 10);  // will return a real array.
sum(1, 2, 3, 4, 5, 10);  // will return a real array.
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
(6) [1, 2, 3, 4, 5, 10]
(6) [1, 2, 3, 4, 5, 10]
</syntaxhighlight>In contrast if we do not use the Rest operator the above function will return only the first argument.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>In contrast if we do not use the Rest operator the above function will return only the first argument.<syntaxhighlight lang="javascript" class="code-continue">
function sum(args) {
function sum(args) {
     console.log(args);
     console.log(args);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
sum(1, 2, 3, 4, 5, 10);   
sum(1, 2, 3, 4, 5, 10);   
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
1
1
</syntaxhighlight>Another example where we use Named Parameters and the Rest operator. Note, it is not possible to have more parameters after the Rest operator.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>Another example where we use Named Parameters and the Rest operator. Note, it is not possible to have more parameters after the Rest operator.<syntaxhighlight lang="javascript" class="code-continue">
function sum(first, second, ...rest) {
function sum(first, second, ...rest) {
     console.log(first, second, rest);
     console.log(first, second, rest);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
sum(1, 2, 3, 4, 5, 10);   
sum(1, 2, 3, 4, 5, 10);   
</syntaxhighlight>
</syntaxhighlight>
Line 188: Line 188:
So, when we apply the Rest operator to a parameter of a function, we can pass very number of arguments and the Rest operator will take all of them and put them in an array.
So, when we apply the Rest operator to a parameter of a function, we can pass very number of arguments and the Rest operator will take all of them and put them in an array.


Now, if you want to get the sum of all the numbers in an array, we can use the <code>.reduce()</code> method and the Rest operator, instead of looping over the <code>arguments</code> object as it was done in the above section.<syntaxhighlight lang="javascript" class="mlw-continue">
Now, if you want to get the sum of all the numbers in an array, we can use the <code>.reduce()</code> method and the Rest operator, instead of looping over the <code>arguments</code> object as it was done in the above section.<syntaxhighlight lang="javascript" class="code-continue">
function sumRest(...args) {
function sumRest(...args) {
     return args.reduce((a, b) => a + b );
     return args.reduce((a, b) => a + b );
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(sumRest(1, 2, 3, 4, 5, 10));   
console.log(sumRest(1, 2, 3, 4, 5, 10));   
</syntaxhighlight>
</syntaxhighlight>
Line 202: Line 202:


== Default Parameters ==
== Default Parameters ==
In order to define default values for a function's Parameters we can use the ''logical or operator'' in the following way.<syntaxhighlight lang="javascript" class="mlw-continue">
In order to define default values for a function's Parameters we can use the ''logical or operator'' in the following way.<syntaxhighlight lang="javascript" class="code-continue">
function interest(principal, rate, years) {
function interest(principal, rate, years) {
     rate = rate || 3.5; // if the parameter 'rate' is 'undifined' which is falsy value the value 3.5 will be used
     rate = rate || 3.5; // if the parameter 'rate' is 'undifined' which is falsy value the value 3.5 will be used
Line 214: Line 214:
     return principal * rate / 100 * years;
     return principal * rate / 100 * years;
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(interest(1000));
console.log(interest(1000));
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
175
175
</syntaxhighlight>It is more correct to place the parameters with default values at the end of the parameter's list. But there is one ''confusing'' way gow to use the default value to one parameter and pass a custom value to the next.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>It is more correct to place the parameters with default values at the end of the parameter's list. But there is one ''confusing'' way gow to use the default value to one parameter and pass a custom value to the next.<syntaxhighlight lang="javascript" class="code-continue">
console.log(interest(1000, undefined, 10)); // the 'rate' will be used with its default value '3.5'
console.log(interest(1000, undefined, 10)); // the 'rate' will be used with its default value '3.5'
</syntaxhighlight>
</syntaxhighlight>
Line 228: Line 228:
     return principal * rate / 100 * years;
     return principal * rate / 100 * years;
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(interest(1000, undefined, 5)); // the 'rate' will be used with its default value '3.5'
console.log(interest(1000, undefined, 5)); // the 'rate' will be used with its default value '3.5'
</syntaxhighlight>
</syntaxhighlight>
Line 250: Line 250:
};
};


</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
person.fullName = 'John Smith';    // Use the Setter function
person.fullName = 'John Smith';    // Use the Setter function
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(person.fullName);    // Use the Getter function
console.log(person.fullName);    // Use the Getter function
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
John Smith
John Smith
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(person);
console.log(person);
</syntaxhighlight>
</syntaxhighlight>
Line 291: Line 291:
     }
     }
};
};
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
try {
try {
     person.fullName = null;
     person.fullName = null;
Line 304: Line 304:
     at <anonymous>:2:21
     at <anonymous>:2:21
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
try {
try {
     person.fullName = '';
     person.fullName = '';
Line 322: Line 322:


Scope of a variable or constant determinates where that variable or constant is accessible. When we declare variables or constants with <code>let</code> or <code>const</code> their scope is limited to the block in which they are defined.
Scope of a variable or constant determinates where that variable or constant is accessible. When we declare variables or constants with <code>let</code> or <code>const</code> their scope is limited to the block in which they are defined.
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
{
{
     const message = 'hi';
     const message = 'hi';
Line 331: Line 331:
hi
hi
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
{
{
     const message = 'hi';
     const message = 'hi';
Line 341: Line 341:
     at <anonymous>:4:13
     at <anonymous>:4:13
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     const message = 'hi';
     const message = 'hi';
Line 358: Line 358:
     at <anonymous>:11:1
     at <anonymous>:11:1
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     for (let i = 0; i < 2; i++)
     for (let i = 0; i < 2; i++)
Line 367: Line 367:
start();
start();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
0
0
1
1
Line 378: Line 378:


We can have different variables with the same name within different scope - different function as it is in the following example.
We can have different variables with the same name within different scope - different function as it is in the following example.
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     const message = 'hi';
     const message = 'hi';
Line 390: Line 390:


When we define a variable or constant outside any code block that variable or constant has global scope and can be accessed by all code blocks as functions, loops, etc. Global means the variable is accessible everywhere - globally.
When we define a variable or constant outside any code block that variable or constant has global scope and can be accessed by all code blocks as functions, loops, etc. Global means the variable is accessible everywhere - globally.
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const color = 'red';
const color = 'red';


Line 400: Line 400:


</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
red
red
</syntaxhighlight>
</syntaxhighlight>


When we have a constant or variable with exact same name at the global and a local scope - the local scope takes precedence over the global level.
When we have a constant or variable with exact same name at the global and a local scope - the local scope takes precedence over the global level.
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const color = 'red';
const color = 'red';


Line 416: Line 416:


</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
blue
blue
</syntaxhighlight>
</syntaxhighlight>
Line 428: Line 428:


===Issues with Var #1: Var Creates Function Scope===
===Issues with Var #1: Var Creates Function Scope===
The first issue with <code class="noTypo">var</code> is that, it creates function scope, while <code>let</code> (and <code>const</code>) creates block scope. So the variables created by <code>var</code> can be accessed outside of the scope where they are defined.<syntaxhighlight lang="javascript" class="mlw-continue">
The first issue with <code class="noTypo">var</code> is that, it creates function scope, while <code>let</code> (and <code>const</code>) creates block scope. So the variables created by <code>var</code> can be accessed outside of the scope where they are defined.<syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     for (let i = 0; i < 2; i++)
     for (let i = 0; i < 2; i++)
Line 435: Line 435:
     console.log(i);
     console.log(i);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
start();
start();
</syntaxhighlight><syntaxhighlight lang="shell-session" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="shell-session" class="code-continue">
0
0
1
1
Line 445: Line 445:
     at start (<anonymous>:5:17)
     at start (<anonymous>:5:17)
     at <anonymous>:1:1
     at <anonymous>:1:1
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     for (var i = 0; i < 2; i++)
     for (var i = 0; i < 2; i++)
Line 452: Line 452:
     console.log(i);
     console.log(i);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
start();  // In the output below you can see the 'var i' is not terminated when the 'for(...){...}'' block is finished
start();  // In the output below you can see the 'var i' is not terminated when the 'for(...){...}'' block is finished
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 458: Line 458:
1
1
2
2
</syntaxhighlight>Another example how <code>var</code> is accessible from outside the block where it is defined.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>Another example how <code>var</code> is accessible from outside the block where it is defined.<syntaxhighlight lang="javascript" class="code-continue">
function start() {
function start() {
     for (let i = 0; i < 5; i++) {
     for (let i = 0; i < 5; i++) {
Line 468: Line 468:
     console.log(color);
     console.log(color);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
start();
start();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 474: Line 474:
</syntaxhighlight>If we have used <code class="noTypo">'''let''' color = 'red'</code> the above code will throw an error, but when we use <code class="noTypo">'''var''' color = 'red'</code> we can access the variable inside the whole function not only within the <code>if(...){...}</code> scope.
</syntaxhighlight>If we have used <code class="noTypo">'''let''' color = 'red'</code> the above code will throw an error, but when we use <code class="noTypo">'''var''' color = 'red'</code> we can access the variable inside the whole function not only within the <code>if(...){...}</code> scope.
===Issues with Var #2: Variables are Attached to the Window===
===Issues with Var #2: Variables are Attached to the Window===
The second issue with <code>var</code> is related to the Global and the Local Scope - the variables, defined by <code>var</code> are attached to the <code>window</code> object (used by the browsers at the frontend).<syntaxhighlight lang="javascript" class="mlw-continue">
The second issue with <code>var</code> is related to the Global and the Local Scope - the variables, defined by <code>var</code> are attached to the <code>window</code> object (used by the browsers at the frontend).<syntaxhighlight lang="javascript" class="code-continue">
var firstName = 'John';
var firstName = 'John';
let lastName = 'Smith';
let lastName = 'Smith';
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(window.firstName, window.lastName);
console.log(window.firstName, window.lastName);
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 484: Line 484:


== Local vs Global Scope of Functions ==
== Local vs Global Scope of Functions ==
The functions defined by the <code>function</code> keyword are technically global functions, attached to the Window Object. This is actually bad practice - to prevent this behavior we need to use modules in order to encapsulate the functions in these modules and won't be attached to the Window Object.<syntaxhighlight lang="javascript" class="mlw-continue">
The functions defined by the <code>function</code> keyword are technically global functions, attached to the Window Object. This is actually bad practice - to prevent this behavior we need to use modules in order to encapsulate the functions in these modules and won't be attached to the Window Object.<syntaxhighlight lang="javascript" class="code-continue">
function sayHi() {
function sayHi() {
     console.log('hi');
     console.log('hi');
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
window.sayHi();
window.sayHi();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 497: Line 497:
'''The <code>this</code> keyword references the Object that is executing the current Function.''' For example if a function is method of an Object <code>this</code> references to that Object itself. Otherwise if that function is a regular <code>function</code> (which means it is not part of certain Object) it references to the global object which is the Window Object in the bowsers and Global Object in Nod.js.
'''The <code>this</code> keyword references the Object that is executing the current Function.''' For example if a function is method of an Object <code>this</code> references to that Object itself. Otherwise if that function is a regular <code>function</code> (which means it is not part of certain Object) it references to the global object which is the Window Object in the bowsers and Global Object in Nod.js.


'''Examples for Methods.''' Which references to that Object itself.<syntaxhighlight lang="javascript" class="mlw-continue">
'''Examples for Methods.''' Which references to that Object itself.<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'a',
   title: 'a',
Line 504: Line 504:
   }
   }
};
};
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.play();
video.play();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
{title: 'a', play: ƒ}
{title: 'a', play: ƒ}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.stop = function() {
video.stop = function() {
     console.log(this);
     console.log(this);
Line 517: Line 517:
</syntaxhighlight>
</syntaxhighlight>


'''Examples for Regular Functions.''' Which references to the global object which is the Window Object in the bowsers and Global Object in Nod.js.<syntaxhighlight lang="javascript" class="mlw-continue">
'''Examples for Regular Functions.''' Which references to the global object which is the Window Object in the bowsers and Global Object in Nod.js.<syntaxhighlight lang="javascript" class="code-continue">
function playVideo() {
function playVideo() {
     console.log(this);
     console.log(this);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
playVideo();
playVideo();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
</syntaxhighlight>'''Example for Constructor Functions.''' Note the Constructor Functions are used with the <code>new</code> operator, that creates a new empty object and set <code>this</code> from the constructor function to point to this new empty object.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Example for Constructor Functions.''' Note the Constructor Functions are used with the <code>new</code> operator, that creates a new empty object and set <code>this</code> from the constructor function to point to this new empty object.<syntaxhighlight lang="javascript" class="code-continue">
function Video(title) {
function Video(title) {
     this.title = title;
     this.title = title;
     console.log(this);
     console.log(this);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
const video = new Video('b');
const video = new Video('b');
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Video {title: 'b'}
Video {title: 'b'}
</syntaxhighlight>'''Example for Callback Function.''' The Callback Functions are regular functions despite they can be called inside of a method of an object. Exception of this rule are the Arrow Function and they will be described within the next section.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Example for Callback Function.''' The Callback Functions are regular functions despite they can be called inside of a method of an object. Exception of this rule are the Arrow Function and they will be described within the next section.<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 544: Line 544:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 551: Line 551:
undefined 'c'
undefined 'c'
</syntaxhighlight>
</syntaxhighlight>
We can see <code>this.title</code> returns <code>undefined</code> because the callback function refers to the global Window Object.<syntaxhighlight lang="javascript" class="mlw-continue">
We can see <code>this.title</code> returns <code>undefined</code> because the callback function refers to the global Window Object.<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 561: Line 561:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 568: Line 568:
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
</syntaxhighlight>
</syntaxhighlight>
A solution for this particular case is to pass this as argument to the second parameter to the <code>.'''forEach'''(calback_fn(){...}, '''thisArg''')</code> method. <syntaxhighlight lang="javascript" class="mlw-continue">
A solution for this particular case is to pass this as argument to the second parameter to the <code>.'''forEach'''(calback_fn(){...}, '''thisArg''')</code> method. <syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 578: Line 578:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 586: Line 586:
</syntaxhighlight>
</syntaxhighlight>


Within the <code>.forEach()</code> method <code>thisArg</code> could be <code>this</code> that refers to the parent object which runs the <code>.showTags()</code> method, but it could be any other object.<syntaxhighlight lang="javascript" class="mlw-continue">
Within the <code>.forEach()</code> method <code>thisArg</code> could be <code>this</code> that refers to the parent object which runs the <code>.showTags()</code> method, but it could be any other object.<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 596: Line 596:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 609: Line 609:
'''Get reference of <code class="noTypo">this</code> by a variable and use it later.''' It is a valid but not recommended approach.
'''Get reference of <code class="noTypo">this</code> by a variable and use it later.''' It is a valid but not recommended approach.


<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 620: Line 620:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 626: Line 626:
Title b
Title b
Title c
Title c
</syntaxhighlight>'''Use the built-in methods <code class="noTypo">.apply()</code>, <code class="noTypo">.call()</code> or <code class="noTypo">.bund()</code>''' - remember in JavaScript the functions are objects and there are few built-in methods for them. By default the functions are attached to the Window (or Global) Object - as we saw above.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Use the built-in methods <code class="noTypo">.apply()</code>, <code class="noTypo">.call()</code> or <code class="noTypo">.bund()</code>''' - remember in JavaScript the functions are objects and there are few built-in methods for them. By default the functions are attached to the Window (or Global) Object - as we saw above.<syntaxhighlight lang="javascript" class="code-continue">
function playVideo() {
function playVideo() {
     console.log(this);
     console.log(this);
}
}
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
playVideo();
playVideo();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
</syntaxhighlight>'''Use the <code class="noTypo">.call(thisArg</code>'''<code class="noTypo">, 'a', 'b'<nowiki/>''')'''</code> '''method''' - it is the simplest methods.<code class="noTypo"><nowiki/></code><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Use the <code class="noTypo">.call(thisArg</code>'''<code class="noTypo">, 'a', 'b'<nowiki/>''')'''</code> '''method''' - it is the simplest methods.<code class="noTypo"><nowiki/></code><syntaxhighlight lang="javascript" class="code-continue">
playVideo.call({name: 'Spas'});
playVideo.call({name: 'Spas'});
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
{name: 'Spas'}
{name: 'Spas'}
</syntaxhighlight>'''Use the <code class="noTypo">.apply(thisArg</code>'''<code class="noTypo">, ['a', 'b']''')'''</code> '''method''' - its usage is similar to the above, the difference is how the next arguments are passed.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Use the <code class="noTypo">.apply(thisArg</code>'''<code class="noTypo">, ['a', 'b']''')'''</code> '''method''' - its usage is similar to the above, the difference is how the next arguments are passed.<syntaxhighlight lang="javascript" class="code-continue">
playVideo.apply({name: 'Spas'});
playVideo.apply({name: 'Spas'});
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 644: Line 644:
</syntaxhighlight>'''The difference between <code class="noTypo">.call()</code> and <code class="noTypo">.apply()</code>''' is only about the way passing arguments. Let's assume the function has multiple parameters like a and b, we can supply them in the following way.
</syntaxhighlight>'''The difference between <code class="noTypo">.call()</code> and <code class="noTypo">.apply()</code>''' is only about the way passing arguments. Let's assume the function has multiple parameters like a and b, we can supply them in the following way.


<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
function playVideo(a, b) {
function playVideo(a, b) {
     console.log(this, a, b);
     console.log(this, a, b);
Line 651: Line 651:
{name: 'Spas'} 1 2
{name: 'Spas'} 1 2
{name: 'Spas'} 1 2
{name: 'Spas'} 1 2
</syntaxhighlight>'''Use the <code class="noTypo">.bind()</code> method''' - it doesn't call the function, instead '''it creates (returns) a new function''' and sets its <code class="noTypo">this</code> to point the new (passed) Object permanently. We can store the result in a constant and that constant can be used just like a regular function. <syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Use the <code class="noTypo">.bind()</code> method''' - it doesn't call the function, instead '''it creates (returns) a new function''' and sets its <code class="noTypo">this</code> to point the new (passed) Object permanently. We can store the result in a constant and that constant can be used just like a regular function. <syntaxhighlight lang="javascript" class="code-continue">
const fn = playVideo.bind({name: 'Spas'});
const fn = playVideo.bind({name: 'Spas'});
fn(1, 2);
fn(1, 2);
Line 657: Line 657:
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
{name: 'Spas'} 1 2
{name: 'Spas'} 1 2
</syntaxhighlight>We can '''immediately call the function''' that is returned from the <code class="noTypo">.bind()</code> method.<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>We can '''immediately call the function''' that is returned from the <code class="noTypo">.bind()</code> method.<syntaxhighlight lang="javascript" class="code-continue">
playVideo.bind({name: 'Spas'})(1, 2);
playVideo.bind({name: 'Spas'})(1, 2);


Line 664: Line 664:
</syntaxhighlight>'''Use the <code class="noTypo">.bind()</code> method within callback function''' - in order to solve the problem from the beginning of the section.
</syntaxhighlight>'''Use the <code class="noTypo">.bind()</code> method within callback function''' - in order to solve the problem from the beginning of the section.


<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 674: Line 674:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">
Line 680: Line 680:
Title b
Title b
Title c
Title c
</syntaxhighlight>'''Using the Arrow Functions''' in order to solve such problems (with the callback functions) is a newer and better solution. '''The Arrow Functions inherit the <code class="noTypo">this</code> value from the containing function.'''<syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight>'''Using the Arrow Functions''' in order to solve such problems (with the callback functions) is a newer and better solution. '''The Arrow Functions inherit the <code class="noTypo">this</code> value from the containing function.'''<syntaxhighlight lang="javascript" class="code-continue">
const video = {
const video = {
   title: 'Title',
   title: 'Title',
Line 688: Line 688:
   }
   }
};  
};  
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
video.showTags();
video.showTags();
</syntaxhighlight><syntaxhighlight lang="shell-session">
</syntaxhighlight><syntaxhighlight lang="shell-session">

Latest revision as of 08:28, 26 September 2022

Ref­er­ences

Read al­so:

Ba­sics

JavaScript Func­tions are sets of state­ments (like a block of code) that per­form a task or cal­cu­lates a val­ue. The Func­tions could have In­puts, they could use (mul­ti­ple) pa­ra­me­ters, but it is not manda­to­ry.

function greet(name) {   // 'name' is an input parameter of the function
    console.log('Hello ' + name);
}
greet('Spas');         // 'Spas' is an argument which become a value of the param. 'name'
Hello Spas

Func­tion De­c­la­ra­tions vs Ex­pres­sions

Func­tions are one of the build­ing blocks of any pro­gram­ming lan­guage and JavaScript has tak­en the Func­tions to a whole new lev­el. Func­tions are said to be a col­lec­tion of state­ments to be ex­e­cut­ed in a prop­er se­quence in a par­tic­u­lar con­text.

JavaScript pro­vides a va­ri­ety of meth­ods to de­fine and ex­e­cute Func­tions, there are Named Func­tions, Anony­mous Func­tions and Func­tions that are ex­e­cut­ed as soon as they are mount­ed, these func­tions are known as Im­me­di­ate­ly In­voked Func­tion Ex­pres­sions or IIFEs.

Func­tion de­c­la­ra­tion

The first thing you should know about func­tion de­c­la­ra­tions is that they are hoist­ed. The func­tion name can­not be changed once de­clared since it is loaded in­to the mem­o­ry. In ES2015 and lat­er, func­tions in­side blocks are scoped to that block.

function walk() {
    console.log('Walk');
}

Func­tion ex­pres­sion

JavaScript's the func­tions are ob­jects. So set­ting a vari­able to a func­tion is sim­i­lar to set it to an Ob­ject. Note, with func­tion ex­pres­sion we need to put semi­colon ; at the end of the state­ment, in con­trast with func­tion de­c­la­ra­tion, by a con­ven­tion, we do need do that.

Func­tion ex­pres­sions deal with the act of defin­ing a func­tion in­side an ex­pres­sion and they are not hoist­ed. It is es­sen­tial­ly while cre­at­ing a func­tion di­rect­ly in func­tion ar­gu­ments like a call­back or as­sign­ing it to a vari­able.

The func­tion ex­pres­sions can be named or anony­mous. Named func­tions are use­ful for a good de­bug­ging ex­pe­ri­ence, while anony­mous func­tions pro­vides con­text scop­ing for eas­i­er de­vel­op­ment.

? Ar­row func­tions should on­ly be used when func­tions act as da­ta.

Func­tion Ex­pres­sions al­so do not have ac­cess to their constructor's name since it is anony­mous, it will re­turn the string ‘anony­mous’ in­stead.

The ar­row func­tions – () => {} – are an ES2015 on­ly syn­tax that lex­i­cal­ly binds its this val­ue.

Named Func­tion ex­pres­sion

let run = function walk() {
    console.log('Run');
};

Anony­mous Func­tion ex­pres­sion

let run = function() {
    console.log('Run');
};
run(); // just how we call a function in JavaScript
Run

We can de­clare an­oth­er vari­able (called move in the ex­am­ple be­low) which refers to the same func­tion (ob­ject in the mem­o­ry).

let move = run; 
move();
Run

Im­me­di­ate­ly In­vok­able Func­tion Ex­pres­sion (IIFEs)

Al­so called Self-in­vok­ing Func­tions or Func­tion Clo­sures: JavaScript vari­ables can be­long to the lo­cal or glob­al scope. Glob­al vari­ables can be made lo­cal (pri­vate) with clo­sures.

The Im­me­di­ate­ly In­vok­able Func­tions can be named and anony­mous, but even if an IIFE does have a name it is im­pos­si­ble to refer/​​​invoke it, so this is use­ful on­ly for de­bug­ging. They could be writ­ten as de­c­la­ra­tion or as ex­pres­sion.

// IIFE (Immediately Invokable Function Expression)
(function() {
  console.log('lumos'); // lumos
})();

IIFEs can al­so have pa­ra­me­ters.

// Declaring the parameter required.
(function(dt) {
    console.log(dt.toLocaleTimeString());
    // Passing the Parameter.
})(new Date());
4:30:12 PM

Nest­ed Func­tions and Clo­sures

const add = (function() {
  let counter = 0;
  return function() {counter += 1; return counter};
})();
add(); add(); add(); // the counter is now 3

Ex­am­ple Ex­plained:

  • The vari­able add is as­signed to the re­turn val­ue of a self-in­vok­ing func­tion.
  • The self-in­vok­ing func­tion on­ly runs once. It sets the counter to ze­ro (0), and re­turns a func­tion ex­pres­sion.
  • This way add be­comes a func­tion. The "won­der­ful" part is that it can ac­cess the counter in the par­ent scope.
  • This is called a JavaScript clo­sure. It makes it pos­si­ble for a func­tion to have "pri­vate" vari­ables.
  • The counter is pro­tect­ed by the scope of the anony­mous func­tion, and can on­ly be changed us­ing the add func­tion.

Ar­gu­ments and Pa­ra­me­ters

With­in the function's de­c­la­ra­tion or ex­pres­sion a and b (of the fol­low­ing ex­am­ple) are called Pa­ra­me­ters.

function sum(a, b) {
    return a + b;
}

When we call the func­tion and pass val­ues to its pa­ra­me­ters, these val­ues are called Ar­gu­ments.

console.log(sum(1, 2));
3

In JavaScript have a spe­cial ob­ject, called ar­gu­ments, that holds all ar­gu­ments pass to the func­tion.

function sum(a, b) {
    console.log(arguments);
}
sum(1, 2);
Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    0: 1
    1: 2
    callee: ƒ sum(a, b)
    length: 2
    Symbol(Symbol.iterator): ƒ values()
    [[Prototype]]: Object
function sumArguments() {
    let total = 0;
    for (let argument of arguments)
        total += argument;
    return total;
}
console.log(sumArguments(1, 2, 3, 4, 5, 10));
25

Rest Op­er­a­tor

In mod­ern JS, if you want to have a func­tion with vary­ing num­ber of pa­ra­me­ters, you can use the Rest op­er­a­tor: ...args. It looks like ex­act­ly to the Spread op­er­a­tor used with the Ar­rays and Ob­jects, but don't con­fuse them.

The Rest op­er­a­tor cre­ates an ar­ray of the Ar­gu­ments that are not as­so­ci­at­ed to a func­tion Pa­ra­me­ters.

function sum(...args) {
    console.log(args);
}
sum(1, 2, 3, 4, 5, 10);  // will return a real array.
(6) [1, 2, 3, 4, 5, 10]

In con­trast if we do not use the Rest op­er­a­tor the above func­tion will re­turn on­ly the first ar­gu­ment.

function sum(args) {
    console.log(args);
}
sum(1, 2, 3, 4, 5, 10);
1

An­oth­er ex­am­ple where we use Named Pa­ra­me­ters and the Rest op­er­a­tor. Note, it is not pos­si­ble to have more pa­ra­me­ters af­ter the Rest op­er­a­tor.

function sum(first, second, ...rest) {
    console.log(first, second, rest);
}
sum(1, 2, 3, 4, 5, 10);
1 2 (4) [3, 4, 5, 10]

So, when we ap­ply the Rest op­er­a­tor to a pa­ra­me­ter of a func­tion, we can pass very num­ber of ar­gu­ments and the Rest op­er­a­tor will take all of them and put them in an ar­ray.

Now, if you want to get the sum of all the num­bers in an ar­ray, we can use the .re­duce() method and the Rest op­er­a­tor, in­stead of loop­ing over the ar­gu­ments ob­ject as it was done in the above sec­tion.

function sumRest(...args) {
    return args.reduce((a, b) => a + b );
}
console.log(sumRest(1, 2, 3, 4, 5, 10));
25

So you see, in mod­ern JavaScript we can achieve the same func­tion­al­i­ty with less code. In­stead of defin­ing a to­tal vari­able, set­ting it to ze­ro and then loop­ing over the ar­gu­ments ob­ject… we can have one line of code that gives us the same re­sult, and this is more el­e­gant and more pro­fes­sion­al.

De­fault Pa­ra­me­ters

In or­der to de­fine de­fault val­ues for a function's Pa­ra­me­ters we can use the log­i­cal or op­er­a­tor in the fol­low­ing way.

function interest(principal, rate, years) {
    rate = rate || 3.5; // if the parameter 'rate' is 'undifined' which is falsy value the value 3.5 will be used
    years = years || 5; // if the parameter 'years' is 'undifined' which is falsy value the value 5 will be used
    return principal * rate / 100 * years;
}

In ES6, there is more el­e­gant and clear way to de­fine de­fault val­ues.

function interest(principal, rate = 3.5, years = 5) {
    return principal * rate / 100 * years;
}
console.log(interest(1000));
175

It is more cor­rect to place the pa­ra­me­ters with de­fault val­ues at the end of the parameter's list. But there is one con­fus­ing way gow to use the de­fault val­ue to one pa­ra­me­ter and pass a cus­tom val­ue to the next.

console.log(interest(1000, undefined, 10)); // the 'rate' will be used with its default value '3.5'
350

An­oth­er ex­am­ple for the same trick., where the years pa­ra­me­ter even doesn't have a de­fault val­ue. Note, in such cas­es case is more cor­rect to move the pa­ra­me­ters with de­fault val­ues (in this case rate pa­ra­me­ter) at the end of the list in or­der to avoid such con­fus­ing so­lu­tion.

function interest(principal, rate = 3.5, years) {
    return principal * rate / 100 * years;
}
console.log(interest(1000, undefined, 5)); // the 'rate' will be used with its default value '3.5'
175

Get­ter and Set­ters

Get­ters and set­ters al­low you to de­fine Ob­ject Ac­ces­sors (Com­put­ed Prop­er­ties). These are spe­cial meth­ods of JavaScript Ob­jects, which al­lows to threat meth­ods as prop­er­tioes. We use get­ters to ac­cess the prop­er­ties of an an Ob­ject, and use set­ters to change or mu­tate them.

const person = {
    firstName: "Spas",
    lastName: "Spasov",
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    },
    set fullName(value) {               // 'value' is the value that will be assigned by this method 
        const parts = value.split(' '); // the split() method will return an array...
        this.firstName = parts[0];
        this.lastName = parts[1];
    }
};
person.fullName = 'John Smith';     // Use the Setter function
console.log(person.fullName);     // Use the Getter function
John Smith
console.log(person);
{firstName: 'John', lastName: 'Smith'}
firstName: "John"
lastName: "Smith"
fullName: (...)
get fullName: ƒ fullName()
set fullName: ƒ fullName(value)
[[Prototype]]: Object

Er­ror han­dling – Try and Catch

In the above ex­am­le we as­sume the val­ue that will be passed is a valid string of two parts, sep­a­rat­ed by a whites­pase, but what will hap­pen if we pass a boolean, or some­thin oth­er?

const person = {
    firstName: "Spas",
    lastName: "Spasov",
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    },
    set fullName(value) {
        if (typeof value !== 'string')
            throw new Error('Value is not a string.');              // Error1
        
        const parts = value.split(' ');
        
        if (parts.length !== 2)
            throw new Error('Enter First Name and Last name.');     // Error2
        
        this.firstName = parts[0];
        this.lastName = parts[1];
    }
};
try {
    person.fullName = null;
}
catch (e) {         // here 'e' is the JS error object thrown by the setter function - Error 1
    console.log(e); // or provide it as a message within the user interface
}
Error: Value is not a string.
    at Object.set fullName [as fullName] (<anonymous>:9:19)
    at <anonymous>:2:21
try {
    person.fullName = '';
}
catch (e) {         // here 'e' is the JS error object thrown by the setter function - Error 2
    console.log(e); // or provide it as a message within the user interface
}
Error: Enter First Name and Last name.
    at Object.set fullName [as fullName] (<anonymous>:14:19)
    at <anonymous>:2:21

Lo­cal vs Glob­al Scope

Read al­so:

Scope of a vari­able or con­stant de­ter­mi­nates where that vari­able or con­stant is ac­ces­si­ble. When we de­clare vari­ables or con­stants with let or con­st their scope is lim­it­ed to the block in which they are de­fined.

{
    const message = 'hi';
    console.log(message);
}
hi
{
    const message = 'hi';
}
console.log(message);
Uncaught ReferenceError: message is not defined 
    at <anonymous>:4:13
function start() {
    const message = 'hi';
    
    if (true) {
        const another = 'bye';
    }
    
    console.log(another);   
}
start();
Uncaught ReferenceError: another is not defined
    at start (<anonymous>:8:17)
    at <anonymous>:11:1
function start() {
    for (let i = 0; i < 2; i++)
        console.log(i);
    
    console.log(i);
}
start();
0
1
Uncaught ReferenceError: i is not defined
    at start (<anonymous>:5:17)
    at <anonymous>:7:1

We can have dif­fer­ent vari­ables with the same name with­in dif­fer­ent scope – dif­fer­ent func­tion as it is in the fol­low­ing ex­am­ple.

function start() {
    const message = 'hi';
}

function stop() {
    const message = 'bye';
}

When we de­fine a vari­able or con­stant out­side any code block that vari­able or con­stant has glob­al scope and can be ac­cessed by all code blocks as func­tions, loops, etc. Glob­al means the vari­able is ac­ces­si­ble every­where – glob­al­ly.

const color = 'red';

function paint() { 
    console.log(color);
}

paint();
red

When we have a con­stant or vari­able with ex­act same name at the glob­al and a lo­cal scope – the lo­cal scope takes prece­dence over the glob­al lev­el.

const color = 'red';

function paint() {
    const color = 'blue';
    console.log(color);
}

paint();
blue

Defin­ing glob­al vari­ables or con­stants is con­sid­ered bad prac­tice! We should avoid that when it is pos­si­ble.

Let vs Var

Read al­so:

Is­sues with Var #1: Var Cre­ates Func­tion Scope

The first is­sue with var is that, it cre­ates func­tion scope, while let (and con­st) cre­ates block scope. So the vari­ables cre­at­ed by var can be ac­cessed out­side of the scope where they are de­fined.

function start() {
    for (let i = 0; i < 2; i++)
        console.log(i);
    
    console.log(i);
}
start();
0
1
Uncaught ReferenceError: i is not defined 
    at start (<anonymous>:5:17)
    at <anonymous>:1:1
function start() {
    for (var i = 0; i < 2; i++)
        console.log(i);
    
    console.log(i);
}
start();  // In the output below you can see the 'var i' is not terminated when the 'for(...){...}'' block is finished
0
1
2

An­oth­er ex­am­ple how var is ac­ces­si­ble from out­side the block where it is de­fined.

function start() {
    for (let i = 0; i < 5; i++) {
        if (true) {
            var color = 'red';
        }
    }

    console.log(color);
}
start();
red

If we have used let color = 'red' the above code will throw an er­ror, but when we use var color = 'red' we can ac­cess the vari­able in­side the whole func­tion not on­ly with­in the if(…){…} scope.

Is­sues with Var #2: Vari­ables are At­tached to the Win­dow

The sec­ond is­sue with var is re­lat­ed to the Glob­al and the Lo­cal Scope – the vari­ables, de­fined by var are at­tached to the win­dow ob­ject (used by the browsers at the fron­tend).

var firstName = 'John';
let lastName = 'Smith';
console.log(window.firstName, window.lastName);
John undefined

Lo­cal vs Glob­al Scope of Func­tions

The func­tions de­fined by the func­tion key­word are tech­ni­cal­ly glob­al func­tions, at­tached to the Win­dow Ob­ject. This is ac­tu­al­ly bad prac­tice – to pre­vent this be­hav­ior we need to use mod­ules in or­der to en­cap­su­late the func­tions in these mod­ules and won't be at­tached to the Win­dow Ob­ject.

function sayHi() {
    console.log('hi');
}
window.sayHi();
hi

This Key­word in JavaScript

The this key­word ref­er­ences the Ob­ject that is ex­e­cut­ing the cur­rent Func­tion. For ex­am­ple if a func­tion is method of an Ob­ject this ref­er­ences to that Ob­ject it­self. Oth­er­wise if that func­tion is a reg­u­lar func­tion (which means it is not part of cer­tain Ob­ject) it ref­er­ences to the glob­al ob­ject which is the Win­dow Ob­ject in the bowsers and Glob­al Ob­ject in Nod.js.

Ex­am­ples for Meth­ods. Which ref­er­ences to that Ob­ject it­self.

const video = {
  title: 'a',
  play() {
      console.log(this);
  }
};
video.play();
{title: 'a', play: ƒ}
video.stop = function() {
    console.log(this);
};
video.stop();
{title: 'a', play: ƒ, stop: ƒ}

Ex­am­ples for Reg­u­lar Func­tions. Which ref­er­ences to the glob­al ob­ject which is the Win­dow Ob­ject in the bowsers and Glob­al Ob­ject in Nod.js.

function playVideo() {
    console.log(this);
}
playVideo();
Window {window: Window, self: Window, document: document, name: '', location: Location, …}

Ex­am­ple for Con­struc­tor Func­tions. Note the Con­struc­tor Func­tions are used with the new op­er­a­tor, that cre­ates a new emp­ty ob­ject and set this from the con­struc­tor func­tion to point to this new emp­ty ob­ject.

function Video(title) {
    this.title = title;
    console.log(this);
}
const video = new Video('b');
Video {title: 'b'}

Ex­am­ple for Call­back Func­tion. The Call­back Func­tions are reg­u­lar func­tions de­spite they can be called in­side of a method of an ob­ject. Ex­cep­tion of this rule are the Ar­row Func­tion and they will be de­scribed with­in the next sec­tion.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(function(tag) {
         console.log(this.title, tag);
      });
  }
};
video.showTags();
undefined 'a'
undefined 'b'
undefined 'c'

We can see this.title re­turns un­de­fined be­cause the call­back func­tion refers to the glob­al Win­dow Ob­ject.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(function(tag) {
         console.log(this);
      });
  }
};
video.showTags();
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Window {window: Window, self: Window, document: document, name: '', location: Location, …}

A so­lu­tion for this par­tic­u­lar case is to pass this as ar­gu­ment to the sec­ond pa­ra­me­ter to the .forE­ach(calback_​​​fn(){…}, this­Arg) method.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(function(tag) {
         console.log(this.title, tag);
      }, this);
  }
};
video.showTags();
Title a
Title b
Title c

With­in the .forE­ach() method this­Arg could be this that refers to the par­ent ob­ject which runs the .show­Tags() method, but it could be any oth­er ob­ject.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(function(tag) {
         console.log(this.title, tag);
      }, {title: 'SuperCoolMovie'});
  }
};
video.showTags();
SuperCoolMovie a
SuperCoolMovie b
SuperCoolMovie c

Chang­ing this

As we said: this ref­er­ences the Ob­ject that is ex­e­cut­ing the cur­rent Func­tion. Here are list­ed few dif­fer­ent so­lu­tions to change the val­ue of this (the ref­er­ence Ob­ject) in a func­tion. The first so­lu­tion is avail­able when is used a built-in method which pro­vides this func­tion­al­i­ty – as it is shown in the above sec­tion. Let's imag­ine the .forEach() method from the above ex­am­ple doesn't pro­vide this func­tion­al­i­ty.

Get ref­er­ence of this by a vari­able and use it lat­er. It is a valid but not rec­om­mend­ed ap­proach.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      const self = this;
      this.tags.forEach(function(tag) {
         console.log(self.title, tag);
      });
  }
};
video.showTags();
Title a
Title b
Title c

Use the built-in meth­ods .apply(), .call() or .bund() – re­mem­ber in JavaScript the func­tions are ob­jects and there are few built-in meth­ods for them. By de­fault the func­tions are at­tached to the Win­dow (or Glob­al) Ob­ject – as we saw above.

function playVideo() {
    console.log(this);
}
playVideo();
Window {window: Window, self: Window, document: document, name: '', location: Location, …}

Use the .call(thisArg, 'a', 'b') method – it is the sim­plest meth­ods.

playVideo.call({name: 'Spas'});
{name: 'Spas'}

Use the .apply(thisArg, ['a', 'b']) method – its us­age is sim­i­lar to the above, the dif­fer­ence is how the next ar­gu­ments are passed.

playVideo.apply({name: 'Spas'});
{name: 'Spas'}

The dif­fer­ence be­tween .call() and .apply() is on­ly about the way pass­ing ar­gu­ments. Let's as­sume the func­tion has mul­ti­ple pa­ra­me­ters like a and b, we can sup­ply them in the fol­low­ing way.

function playVideo(a, b) {
    console.log(this, a, b);
}
{name: 'Spas'} 1 2
{name: 'Spas'} 1 2

Use the .bind() method – it doesn't call the func­tion, in­stead it cre­ates (re­turns) a new func­tion and sets its this to point the new (passed) Ob­ject per­ma­nent­ly. We can store the re­sult in a con­stant and that con­stant can be used just like a reg­u­lar func­tion.

const fn = playVideo.bind({name: 'Spas'});
fn(1, 2);
{name: 'Spas'} 1 2

We can im­me­di­ate­ly call the func­tion that is re­turned from the .bind() method.

playVideo.bind({name: 'Spas'})(1, 2);
{name: 'Spas'} 1 2

Use the .bind() method with­in call­back func­tion – in or­der to solve the prob­lem from the be­gin­ning of the sec­tion.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(function(tag) {
         console.log(this.title, tag);
      }.bind(this));
  }
};
video.showTags();
Title a
Title b
Title c

Us­ing the Ar­row Func­tions in or­der to solve such prob­lems (with the call­back func­tions) is a new­er and bet­ter so­lu­tion. The Ar­row Func­tions in­her­it the this val­ue from the con­tain­ing func­tion.

const video = {
  title: 'Title',
  tags: ['a', 'b', 'c'],
  showTags() {
      this.tags.forEach(tag => console.log(this.title, tag));
  }
};
video.showTags();
Title a
Title b
Title c