Throughout the other sections we have seen functions being created and used, now its time to dive deeper, I will be looking at function expressions, declarations and arrow functions, looking at default parameters and spread operators and finally private variables using closures.
One point to remember is that functions are objects in Javascript, which means they have properties and methods just like other functions. Function names are really just a pointer to an object, there are a number of ways to create a function but the best two are below
Create basic function example (traditional) | let sum = function(num1, num2) { return num1 + num2; }; |
Using arrow function example | let sum = (num1, num2) => { // notice the arrow function, multiple arguments need brackets return num1 + num2; }; // For single arguments you don't need to use the brackets let display = d => { console.log(d); } // However if you pass zero arguments you need empty brackets let displayHello = () => { console.log("Hello World!"); } Note: you can sometimes don't have use the body curly braces but things do start to get confusing. |
Because functions are objects you can create multiple variables all pointing to the same function
Function name | let func1 = function() { console.log("Hello World!");} let func2 = func1; func2(); // "Hello World!" console.log(func2.name); // func1 |
Because functions are objects you can pointer any number of variables to the same function, also you can pass any number of arguments to a function it does not matter what you have declared. You cannot overload function in Javascript
Function name | let func1 = function() { console.log("Hello World!"); } let func2 = func1; func2(); // "Hello World!" console.log(func2.name); // func1 |
Function arguments | let func3 = (name, age) => { console.log(name + " " + age); } func3("Paul", 21); func3(); func3("Paul", 21, "Valle", 1999); ----------------------------------------------------------------------- function argumentTest () { console.log(arguments.length); // 2 console.log(arguments[0] + " " + arguments[1]); // "Paul Valle" } argumentTest("Paul", "Valle"); Note: you cannot use arguments with the arrow function |
You can apply default values to parameters in ES6
Default parameters | // ES5 function makeKing(name) { name = (typeof name !== 'undefined') ? name : 'Henry'; return `King ${name} VIII`; } // ES6 function makeKing(name = 'Henry') { return `King ${name} VIII`; } let makeKing = (name = 'Henry') => `King ${name}`; // using the arrow function |
Spread opeartor allows you to manage groups of arguments, the rest opeartor allows us to represent an indefinite number of arguments as an array
Spread operator example | function getSum() { let sum = 0; for (let i = 0; i < arguments.length; ++i) { sum += arguments[i]; } return sum; } let values = [1, 2, 3, 4, 5, 6, 7, 8, 9]; console.log(getSum.apply(null, values)); // use apply to flatten the array, results in 45 // spread operator examples console.log(getSum(...values)); // 45 console.log(getSum(...values, 5)); // 50 = the array elements (45) + 5 console.log(getSum(-1, ...values, 10)); // 59 = -1 + array elements (45) + 10 console.log(getSum(...values, ...[10, 11, 12])); // 78 |
Rest operator | function getSum(...values) { // rest parameter let sum = 0; for (let i = 0; i < arguments.length; ++i) { sum += arguments[i]; } return sum; } console.log(getSum(1,2,3)); // 6 console.log(getSum(1,2,3,4,5)); // 15 console.log(getSum(1,2,3,4,5,6,7,8,9)); // 45 ----------------------------------------------------------------------------------- // Same as above but more readable function getSum(...values) { // rest parameter let sum = 0; for (let arg of values) sum += arg; return sum; } console.log(getSum(1,2,3)); // 6 console.log(getSum(1,2,3,4,5)); // 15 console.log(getSum(1,2,3,4,5,6,7,8,9)); // 45 |
You can even pass a function as a argument to functions
Passing a function as a argument | function getSum(...values) { // rest parameter let sum = 0; for (let arg of values) sum += arg; return sum; } function funcTest(func, ...values) { return func(...values); } console.log(funcTest(getSum,1,2,3)); // 6 console.log(funcTest(getSum,1,2,3,4,5)); // 15 console.log(funcTest(getSum,1,2,3,4,5,6,7,8,9)); // 45 |
Functions have a number of special objects, callee, this, caller and a number of properties (length and prototype) and methods (toString(), valueof(), apply and call())
arguments.callee | function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); // will call the correct function even if renamed } } |
this | // when run inside a web page this is the window window.color = 'red'; function sayColor() { console.log(this.color); } sayColor(); |
caller | function outer() { inner(); } function inner() { // contains a reference to the function that called this function or null if the function was called from the global console.log(arguments.callee.caller); } outer(); |
length | // length = number of arguments function sum(num1, num2) { return num1 + num2; } console.log(sum.length); // 2 arguments |
apply | // apply() method accepts two arguments: the value of this inside the function and an array of arguments. // This second argument may be an instance of Array, but it can also be the arguments object. function sum(num1, num2) { return num1 + num2; } function callSum1(num1, num2) { return sum.apply(this, arguments); // passing in arguments object } function callSum2(num1, num2) { return sum.apply(this, [num1, num2]); // passing in array } console.log(callSum1(10, 10)); // 20 console.log(callSum2(10, 10)); // 20 |
call | // call() is same as apply() but arguments are passed to it differently. // The first argument is the this value, but the remaining arguments are passed directly into the function. function sum(num1, num2) { return num1 + num2; } function callSum(num1, num2) { return sum.call(this, num1, num2); } console.log(callSum(10, 10)); // 20 ------------------------------------------------------------------------------ color = 'red'; let o = { color: 'blue' }; function sayColor() { console.log(this.color); } sayColor(); // red sayColor.call(o); // blue (the object passed) |
Closures are functions that have access to variables from another functions scope. The closure has three scope chains:
Closure example | let a = 1; function outer() { let b = 2; function inner() { let c = 3; let d = a + b +c return d; // has access to global, outer and inner variables } return inner(); } console.log(outer()); Note: When a function is not defined using the arrow syntax, the this object is bound at runtime based on the context in which a function is executed, when used inside global functions, this is equal to window in nonstrict mode and undefined in strict mode, whereas this is equal to the object when called as an object method.Anonymous functions are not bound to an object in this context, meaning the this object points to window unless executing in strict mode (where this is undefined) |
Javascript does not really have private variables but any variable inside a function is not accessible
Private access example | function count() { let count = 0; // private access this.getCount = function() { return count; } this.increaseCount = function() { count++; } this.decreaseCount = function() { count--; } } let counter = new count(); console.log(counter.getCount()); // 0 counter.increaseCount(); counter.increaseCount(); console.log(counter.getCount()); // 2 counter.decreaseCount(); console.log(counter.getCount()); // 1 console.log(counter.count); // undefined no access |
Static variable example | function foo() { if( typeof foo.counter == 'undefined' ) { foo.counter = 0; } foo.counter++; console.log(foo.counter); } foo(); foo(); foo(); |