Functions in Detail
Function Expressions
- A function can also be defined using an expression.
- Function expression ends with a semicolon because it is a part of an executable statement.
- Functions stored in variables do not need function names. They are always invoked using the variable name.
// Function Expression - Named
let runNamed = function run() {
console.log("run");
};
// Function Expression - Anonymous
let runAnonymous = function() {
console.log("run");
};
runNamed();
runAnonymous();
Hoisting
- The process whereby the interpreter moves the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.
walk();
function walk() {
console.log("walk");
}
Arguments
- Functions have a built-in object called the arguments object.
- The argument object contains an array of the arguments used when the function was called.
Function can be called with or without arguments. If the argument is not supplied, the parameter value will be undefined.
function sum(x, y) {
return x + y;
}
sum();
sum(1);
sum(1, 2);
sum(1, 2, 3, 4)
Function with varying number of parameters
function getSum() {
let sum = 0;
for(let value of arguments) {
sum += value;
}
return sum;
}
console.log(getSum(1, 2, 3, 4, 5));
Rest Operator
- Allows a function to accept an indefinite number of arguments as an array
- A function definition can only have one rest parameter, and the rest parameter must be the last parameter in the function definition.
- Rest parameter bundles all the extra parameters into a single array
function getSum(...args)
{
return args.reduce((a, b) => a + b);
}
console.log(getSum(1, 2, 3, 4, 5, 6))
Default Parameters
- Allow named parameters to be initialized with default values if no value or
undefined
is passed. - Whenever you want to give the function parameter a default value, make sure that parameter is the last parameter in the list, or give all the parameters after that a default value.
Old Way
function calculateInterest(principal, rate, years) {
rate = rate || 10;
years = typeof years !== "undefined" ? years : 1;
return principal * (rate / 100 ) * years;
}
function calculateInterest(principal, rate = 10, years = 1) {
return principal * (rate / 100 ) * years;
}
console.log(calculateInterest(10000, 5, 2));
console.log(calculateInterest(10000));
Getters and Setters
const person = {
firstName: "Ganesh",
lastName: "Kumar",
get fullName() {
return `using prop - ${this.firstName} ${this.lastName}`;
},
set fullName(value) {
const parts = value.split(" ");
this.firstName = parts[0];
this.lastName = parts[1];
}
}
// getter
console.log(person.fullName);
// setter
person.fullName = "Siva Kumar";
console.log(person.fullName);
Error Handling
- When executing JavaScript code, different errors can occur. Errors can be coding errors made by the programmer, errors due to wrong input, and other unforeseeable things.
Try Catch Finally
try
statement defines a code block to run.catch
statement defines a code block to handle any error.finally
statement defines a code block to run regardless of the result.
function getWords(text) {
return text.split(" ");
}
try {
console.log(getWords());
}
catch(error) {
console.log(error);
}
Throw Custom Error
- Sometimes you want to report an error in your application. To do this you need to throw an exception.
- Use the throw keyword and then create a new error object. This is how you throw an exception
- Difference between error and exception,
new Error("invalid data")
is just a plain object.throw new Error("invalid data")
the moment you throw the error it is referred as an exception. So this is an exceptional situation that should not have happened.- When you throw an exception, the lines after the throw statement are not executed. Will jump out of this method and the control will move to the catch block.
- You can throw string, number, boolean, object
function getWords(text) {
if(typeof text !== "string")
throw new Error("Invalid data");
return text.split(" ");
}
throw 100; // throw number
throw "too low"
Notes
- You should do error handling at the beginning of a function or a method, this is called defensive programming. So you want to make sure that the values coming in are valid, they're in the right shape, so we can execute our logic.
this
this
references the object that is executing the current function- If the function is part of an object, you call that function a method, this references that object itself
- If that function is a regular function, which means it's not part of an object,
this
means the global object, which is the window object in browsers and global in Node - When you use the new operator it creates a new empty object, and sets
this
in the constructor function to point to the empty object.
Changing this
- You can define a constant, called
self
, and setting it tothis
orthat
. - Function is technically, an object, so it has properties and methods that you can access using a dot notation. You have 3 methods
apply
,bind
andcall
, and with these you can change the value of this - The difference between call and apply is only about passing arguments. With the apply method we have to pass arguments as an array
bind
method does not call the function instead it returns a new function, and setsthis
to point to the object permanently. So bind returns the new function you can store the result in a constant and call the function just like a regular function.- Arrow functions inherit this from the containing function. In other words, this is not rebound to a new object.
Problem
const video = {
title: "Avatar",
tags: ["fiction", "graphics"],
play() {
console.log("video", this);
},
display: function() {
this.tags.forEach(function(element){
console.log(this.title, element);
});
}
}
Solution 1 - thisArg
const video = {
title: "Avatar",
tags: ["fiction", "graphics"],
play() {
console.log("video", this);
},
display: function() {
const self = this;
this.tags.forEach(function(element){
console.log(this.title, element);
}, this);
}
}
video.play();
video.display();
Solution 2 - Self
const video = {
title: "Avatar",
tags: ["fiction", "graphics"],
play() {
console.log("video", this);
},
display: function() {
const self = this;
this.tags.forEach(function(element){
console.log(self.title, element);
});
}
}
video.play();
video.display();
Solution 3 - bind
const video = {
title: "Avatar",
tags: ["fiction", "graphics"],
play() {
console.log("video", this);
},
display: function() {
const self = this;
this.tags.forEach(function(element){
console.log(this.title, element);
}.bind(this));
}
}
video.play();
video.display();
Solution 4 - arrow
const video = {
title: "Avatar",
tags: ["fiction", "graphics"],
play() {
console.log("video", this);
},
display: function() {
const self = this;
this.tags.forEach((element) => {
console.log(this.title, element);
});
}
}
video.play();
video.display();