JavaScript Course 6: Functions: Difference between revisions
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=" | <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=" | <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=" | 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=" | <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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
let run = function() { | let run = function() { | ||
console.log('Run'); | console.log('Run'); | ||
}; | }; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </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=" | </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=" | </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=" | <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=" | </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=" | 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=" | </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=" | </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=" | </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=" | </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=" | </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=" | 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=" | </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=" | </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=" | </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=" | </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=" | </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=" | 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=" | </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=" | 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=" | </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=" | </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=" | </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=" | </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=" | </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=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(person); | console.log(person); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 291: | Line 291: | ||
} | } | ||
}; | }; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </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=" | <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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
{ | { | ||
const message = 'hi'; | const message = 'hi'; | ||
Line 331: | Line 331: | ||
hi | hi | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <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=" | <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=" | <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=" | <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=" | <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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const color = 'red'; | const color = 'red'; | ||
Line 400: | Line 400: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const color = 'red'; | const color = 'red'; | ||
Line 416: | Line 416: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <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=" | 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=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
start(); | start(); | ||
</syntaxhighlight><syntaxhighlight lang="shell-session" class=" | </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=" | </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=" | </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=" | </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=" | </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=" | 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=" | </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=" | 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=" | </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=" | '''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=" | </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=" | </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=" | '''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=" | </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=" | </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=" | </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=" | </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=" | </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=" | 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=" | </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=" | 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=" | </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=" | 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=" | </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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const video = { | const video = { | ||
title: 'Title', | title: 'Title', | ||
Line 620: | Line 620: | ||
} | } | ||
}; | }; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </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=" | </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=" | </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=" | </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=" | </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=" | <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=" | </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=" | </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=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const video = { | const video = { | ||
title: 'Title', | title: 'Title', | ||
Line 674: | Line 674: | ||
} | } | ||
}; | }; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </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=" | </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=" | </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 07:28, 26 September 2022
References
- Code with Mosh: The Ultimate JavaScript Mastery Series – Part 1
- W3School: JavaScript Tutorial
- W3School: JavaScript Functions
- W3School: JavaScript Hoisting
- W3School: JavaScript Function Closures
- W3School: JavaScript Object Accessors – Getters and Setters
- Geeks for Geeks: JavaScript | Immediately Invoked Function Expressions (IIFE)
- LinkedIn: JavaScript Named vs Anonymous Functions
Read also:
- JavaScript Basics: Functions
- JavaScript Arrays: Arrow Functions
- JavaScript Objects: Constructor Functions
- JavaScript Objects: Factory functions
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.
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
Function Declarations vs Expressions
Functions are one of the building blocks of any programming language and JavaScript has taken the Functions to a whole new level. Functions are said to be a collection of statements to be executed in a proper sequence in a particular context.
JavaScript provides a variety of methods to define and execute Functions, there are Named Functions, Anonymous Functions and Functions that are executed as soon as they are mounted, these functions are known as Immediately Invoked Function Expressions or IIFEs.
Function declaration
The first thing you should know about function declarations is that they are hoisted. The function name cannot be changed once declared since it is loaded into the memory. In ES2015 and later, functions inside blocks are scoped to that block.
function walk() {
console.log('Walk');
}
Function expression
JavaScript's the functions are objects. So setting a variable to a function is similar to set it to an Object. Note, with function expression we need to put semicolon ;
at the end of the statement, in contrast with function declaration, by a convention, we do need do that.
Function expressions deal with the act of defining a function inside an expression and they are not hoisted. It is essentially while creating a function directly in function arguments like a callback or assigning it to a variable.
The function expressions can be named or anonymous. Named functions are useful for a good debugging experience, while anonymous functions provides context scoping for easier development.
? Arrow functions should only be used when functions act as data.
Function Expressions also do not have access to their constructor's name since it is anonymous, it will return the string ‘anonymous’ instead.
The arrow functions – () => {}
– are an ES2015 only syntax that lexically binds its this
value.
Named Function expression
let run = function walk() {
console.log('Run');
};
Anonymous Function expression
let run = function() {
console.log('Run');
};
run(); // just how we call a function in JavaScript
Run
We can declare another variable (called move
in the example below) which refers to the same function (object in the memory).
let move = run;
move();
Run
Immediately Invokable Function Expression (IIFEs)
Also called Self-invoking Functions or Function Closures: JavaScript variables can belong to the local or global scope. Global variables can be made local (private) with closures.
The Immediately Invokable Functions can be named and anonymous, but even if an IIFE does have a name it is impossible to refer/invoke it, so this is useful only for debugging. They could be written as declaration or as expression.
// IIFE (Immediately Invokable Function Expression)
(function() {
console.log('lumos'); // lumos
})();
IIFEs can also have parameters.
// Declaring the parameter required.
(function(dt) {
console.log(dt.toLocaleTimeString());
// Passing the Parameter.
})(new Date());
4:30:12 PM
Nested Functions and Closures
const add = (function() {
let counter = 0;
return function() {counter += 1; return counter};
})();
add(); add(); add(); // the counter is now 3
Example Explained:
- The variable
add
is assigned to the return value of a self-invoking function. - The self-invoking function only runs once. It sets the counter to zero (0), and returns a function expression.
- This way add becomes a function. The "wonderful" part is that it can access the counter in the parent scope.
- This is called a JavaScript closure. It makes it possible for a function to have "private" variables.
- The counter is protected by the scope of the anonymous function, and can only be changed using the add function.
Arguments and Parameters
Within the function's declaration or expression a
and b
(of the following example) are called Parameters.
function sum(a, b) {
return a + b;
}
When we call the function and pass values to its parameters, these values are called Arguments.
console.log(sum(1, 2));
3
In JavaScript have a special object, called arguments
, that holds all arguments pass to the function.
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 Operator
In modern JS, if you want to have a function with varying number of parameters, you can use the Rest operator: ...args
. 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.
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 contrast if we do not use the Rest operator the above function will return only the first argument.
function sum(args) {
console.log(args);
}
sum(1, 2, 3, 4, 5, 10);
1
Another example where we use Named Parameters and the Rest operator. Note, it is not possible to have more parameters after the Rest operator.
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 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 .reduce()
method and the Rest operator, instead of looping over the arguments
object as it was done in the above section.
function sumRest(...args) {
return args.reduce((a, b) => a + b );
}
console.log(sumRest(1, 2, 3, 4, 5, 10));
25
So you see, in modern JavaScript we can achieve the same functionality with less code. Instead of defining a total variable, setting it to zero and then looping over the arguments object… we can have one line of code that gives us the same result, and this is more elegant and more professional.
Default Parameters
In order to define default values for a function's Parameters we can use the logical or operator in the following 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 elegant and clear way to define default values.
function interest(principal, rate = 3.5, years = 5) {
return principal * rate / 100 * years;
}
console.log(interest(1000));
175
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.
console.log(interest(1000, undefined, 10)); // the 'rate' will be used with its default value '3.5'
350
Another example for the same trick., where the years
parameter even doesn't have a default value. Note, in such cases case is more correct to move the parameters with default values (in this case rate
parameter) at the end of the list in order to avoid such confusing solution.
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
Getter and Setters
Getters and setters allow you to define Object Accessors (Computed Properties). These are special methods of JavaScript Objects, which allows to threat methods as propertioes. We use getters to access the properties of an an Object, and use setters to change or mutate 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
Error handling – Try and Catch
In the above examle we assume the value
that will be passed is a valid string of two parts, separated by a whitespase, but what will happen if we pass a boolean, or somethin other?
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
Local vs Global Scope
Read also:
Scope of a variable or constant determinates where that variable or constant is accessible. When we declare variables or constants with let
or const
their scope is limited to the block in which they are defined.
{
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 different variables with the same name within different scope – different function as it is in the following example.
function start() {
const message = 'hi';
}
function stop() {
const message = 'bye';
}
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.
const color = 'red';
function paint() {
console.log(color);
}
paint();
red
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.
const color = 'red';
function paint() {
const color = 'blue';
console.log(color);
}
paint();
blue
Defining global variables or constants is considered bad practice! We should avoid that when it is possible.
Let vs Var
Read also:
- JavaScript Basics: Variables and Constants
- JavaScript Basics: Var vs Let and Const (issues with Var)
Issues with Var #1: Var Creates Function Scope
The first issue with var
is that, it creates function scope, while let
(and const
) creates block scope. So the variables created by var
can be accessed outside of the scope where they are defined.
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
Another example how var
is accessible from outside the block where it is defined.
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 error, but when we use var color = 'red'
we can access the variable inside the whole function not only within the if(…){…}
scope.
Issues with Var #2: Variables are Attached to the Window
The second issue with var
is related to the Global and the Local Scope – the variables, defined by var
are attached to the window
object (used by the browsers at the frontend).
var firstName = 'John';
let lastName = 'Smith';
console.log(window.firstName, window.lastName);
John undefined
Local vs Global Scope of Functions
The functions defined by the function
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.
function sayHi() {
console.log('hi');
}
window.sayHi();
hi
This Keyword in JavaScript
The this
keyword references the Object that is executing the current Function. For example if a function is method of an Object this
references to that Object itself. Otherwise if that function is a regular function
(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.
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: ƒ}
Examples for Regular Functions. Which references to the global object which is the Window Object in the bowsers and Global Object in Nod.js.
function playVideo() {
console.log(this);
}
playVideo();
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
Example for Constructor Functions. Note the Constructor Functions are used with the new
operator, that creates a new empty object and set this
from the constructor function to point to this new empty object.
function Video(title) {
this.title = title;
console.log(this);
}
const video = new Video('b');
Video {title: 'b'}
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.
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
returns undefined
because the callback function refers to the global Window Object.
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 solution for this particular case is to pass this as argument to the second parameter to the .forEach(calback_fn(){…}, thisArg)
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
Within the .forEach()
method thisArg
could be this
that refers to the parent object which runs the .showTags()
method, but it could be any other object.
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
Changing this
As we said: this
references the Object that is executing the current Function. Here are listed few different solutions to change the value of this
(the reference Object) in a function. The first solution is available when is used a built-in method which provides this functionality – as it is shown in the above section. Let's imagine the .forEach()
method from the above example doesn't provide this functionality.
Get reference of this
by a variable and use it later. It is a valid but not recommended approach.
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 methods .apply()
, .call()
or .bund()
– 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.
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 simplest methods.
playVideo.call({name: 'Spas'});
{name: 'Spas'}
Use the .apply(thisArg
, ['a', 'b'])
method – its usage is similar to the above, the difference is how the next arguments are passed.
playVideo.apply({name: 'Spas'});
{name: 'Spas'}
The difference between .call()
and .apply()
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.
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 function, instead it creates (returns) a new function and sets its this
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.
const fn = playVideo.bind({name: 'Spas'});
fn(1, 2);
{name: 'Spas'} 1 2
We can immediately call the function that is returned from the .bind()
method.
playVideo.bind({name: 'Spas'})(1, 2);
{name: 'Spas'} 1 2
Use the .bind()
method within callback function – in order to solve the problem from the beginning of the section.
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
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 this
value from the containing function.
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