ref – https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/
https://www.davidbcalhoun.com/2011/different-ways-of-defining-functions-in-javascript-this-is-madness/
A function is a parametric block of code defined one time and called any number of times later.
1) function declaration
A function declaration is made of function keyword. It is followed by an obligatory function name, a list of parameters in a pair of parenthesis (para1, …, paramN) and a pair of curly braces {…} that delimits the body code.
|
// function declaration function isEven(num) { return num % 2 === 0; } isEven(24); // => true isEven(11); // => false |
The function declaration creates a variable in the current scope with the identifier equal to function name. This variable holds the function object.
The function variable is hoisted up to the top of the current scope, which means that the function can be invoked before the declaration
|
A(); // foo function A(){ console.log('foo'); }; |
Which practically means that, yes, you can call the functions before they’re written in your code. It won’t matter, because the entire function gets hoisted to the top of its containing scope. (This is contrasted with variables, which only have their declaration hoisted, not their contents, as we’ll see in the next section).
|
console.log(a); // runs ok, but is undefined var a = "hehe"; |
Function Expression
A function expression is determined by a function keyword, followed by an optional function name, a list of parameters in a pair of parenthesis (para1, …, paramN) and a pair of curly braces { … } that delimits the body code.
This function object can:
– be referenced by a local variable you declare.
– assigned to a method name on an object
– be used as a callback function
2) unnamed function expression
|
var count = function(array) { // Function expression return array.length; } count([5, 7, 8]); // => 3 |
function expression used as a method name on an object:
|
var methods = { numbers: [1, 5, 8], sum: function() { // Function expression return this.numbers.reduce(function(acc, num) { // func. expression return acc + num; }); } } |
|
var A = function(){}; var B = function(){}; var C = function(){}; |
Will be executed as this:
|
var A, B, C; // variable declarations are hoisted A = function(){}; B = function(){}; C = function(){}; |
Therefore, with functions, the order of setting and calling is important:
|
// this works var B = function(){}; B(); // executes function // this doesn't work B2(); // B2 is declared, but is undefined var B2 = function(){}; |
function expressions with parenthesis
|
var foo = (function(){ console.log("foo"); }); // resulting function assigned to foo var bar = function(){ console.log("bar"); }; // resulting function assigned to bar console.log(foo) // [Function: foo] console.log(bar) // [Function: bar] foo(); // foo bar(); // bar |
Here again, we’ll see that if we declare an anonymous function, the function’s name property will be valid to use inside the function. In other words, the function name is only accessible within the function. However, globally, we’ll have to use the reference name.
In the case below, reference name D must be used globally. The function name foo can be used inside the function.
|
var D = function foo(){ console.log(typeof foo); }; D(); // function console.log(typeof foo); // undefined console.log(typeof D); // function |
Useful for Recursion
Because the function’s name is accessible in the function itself, this turns out to be useful for recursive functions, much more useful than the plain old anonymous function.
Here’s a trivial recursive function to illustrate calling itself from within the named function expression:
|
var countdown = function a(count){ if(count > 0) { count--; return a(count); // we can also do this: a(--count), which is less clear } console.log('end of recursive function'); } countdown(5); |
IIFE
first, lets look at a standard example:
|
var foo = function(){ return 'bar'; }; var output = foo(); console.log(output); // 'bar' |
|
var foo = function(){ return 'bar'; }; var output = (foo)(); // note the extra grouping operators console.log(output); // 'bar' |
Useful for debugging
As a few have pointed out, giving previously anonymous functions names helps in debugging, since the function name shows up on the call stack.
3) Named function Expression
the function object has a property name, which displays the name of the function. It is assigned to a variable. The variable is used in the current scope. But inside the function body, the function name is used.
Also, a named function is created, the “name” property holds the function name.
|
var getType = function funName(variable) { console.log(typeof funName === 'function'); // => true return (typeof variable); } console.log(getType(3)); // => 'number' console.log(getType("hoho")); // => 'string' console.log(getType.name); // => 'funName' console.log(typeof funName === 'function'); // => false console.log(typeof getType === 'function'); // => true |
Inside the function body a variable with the same name holds the function object. The function name (funName) is available inside the function. However, outside, it is not. Outside, it recognizes the variable name which the function was assigned (getType).
4) Shorthand Method definitions
short hand method definitions are used in object literal construction like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
var collection = { items: [], // shorthand method definitons add(...items) { this.items.push(...items); }, get(index) { return this.items[index]; } }; // then, you can call these methods like so: collection.add('C', 'Java', 'PHP'); collection.get(1) // => 'Java' |
add() and get() methods in collection object are defined using short method definition. These methods are called as usual: collection.add(…) and collection.get(…).
but why?
The short approach of method definition has several benefits over traditional property definition with a name, colon : and a function expression like so:
– A shorter syntax is easier to read and write
– Shorthand method definition creates named functions, contrary to a function expression. It is useful for debugging.
5) Arrow Functions
An arrow function is defined using a pair of parenthesis that contains the list of parameters (param1, param2, …, paramN), followed by a fat arrow => and a pair of curly braces {…} that delimits the body statements.
note: When the arrow function has only one parameter, the pair of parenthesis can be omitted. When it contains a single statement, the curly braces can be omitted too.
|
var absValue = (number) => { if (number < 0) { return -number; } return number; } absValue(-10); // => 10 absValue(5); // => 5 |
The function declared using a fat arrow has the following properties:
– The arrow function does not create its own execution context, but takes it lexically (contrary to function expression or function declaration, which create own this depending on invocation)
– The arrow function is anonymous: name is an empty string (contrary to function declaration which have a name)
– arguments object is not available in the arrow function (contrary to other declaration types that provide arguments object)
this keyword is one of the most confusing aspects of JavaScript (check this article for a detailed explanation on this).
Because functions create their own execution context, often it is hard to catch the flying around this.
ECMAScript 6 improves this usage by introducing the arrow function, which takes the context lexically. This is nice, because from now on is not necessary to use .bind(this) or store the context var self = this when a function needs the enclosing context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
// hoisting. numbersObject available here class Numbers { constructor(array) { console.log(`constructring Numbers object`) this.array = array; } addNumber(number) { console.log(`--> addNumber ${number}`); // numbersObject available globally at line 1. It was undefined // but before this function was called, it was initialized // to a new Numbers object at * console.log(numbersObject); if (number !== undefined) { console.log(`number is valid!`) this.array.push(number); return; } // this points to the object referred to by addNumber. console.log(`<-- returning another function from addNumber`) return (number) => { console.log(`trying to push ${number}`) if (this === numbersObject) { console.log(`this object refers to our instance`) this.array.push(number); } }; } } // * var numbersObject = new Numbers([]); numbersObject.addNumber(1); var addMethod = numbersObject.addNumber(); addMethod(5); console.log(numbersObject.array); // => [1, 5] |
short callbacks
The parenthesis pairs and curly braces are optional for a single parameter and single body statement. This helps creating very short callback functions.
Originally, given an array, we use Array’s prototype function some to see if there exist a zero in the array. The some() method tests whether at least one element in the array passes the test implemented by the provided function.
If it exists, return 0, if it does not, return false.
|
var numbers = [1, 5, 10, 0]; numbers.some(function(item){ return item === 0; }); |
However, since there’s only a single parameter, and a single body statement, we can do it shorthand like so:
|
numbers.some(item => item === 0); // => true |
5) Generator functions
Function declaration
standard declaration of a generator function:
|
function* indexGenerator(){ var index = 0; while(true) { yield index++; } } var g = indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); // => 1 |
Function expressions
now we assign an anonymous function generator definition to an expression variable. Execute the expression, and call the methods on it.
|
var indexGenerator = function* () { var index = 0; while(true) { yield index++; } }; var g = indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); // => 1 |
shorthand
|
var obj = { *indexGenerator() { var index = 0; while(true) { yield index++; } } } var g = obj.indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); // => 1 |
In all 3 cases the generator function returns the generator object g. Later g is used to generated series of incremented numbers.
6 – new Function
Function objects created with the Function constructor are parsed when the function is created. This is less efficient than declaring a function with a function expression or function statement and calling it within your code because such functions are parsed with the rest of the code.
All arguments passed to the function are treated as the names of the identifiers of the parameters in the function to be created, in the order in which they are passed.
|
// Example can be run directly in your JavaScript console // Create a function that takes two arguments and returns the sum of those arguments var adder = new Function('a', 'b', 'return a + b'); // Call the function adder(2, 6); // > 8 |
Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
var x = 10; function createFunction1() { var x = 20; // always created in global scope return new Function('return x;'); // this |x| refers global |x| } function createFunction2() { var x = 20; // creates closure. uses scope to access x declared in parent scope. function f() { return x; // this |x| refers local |x| above } return f; } var f1 = createFunction1(); console.log(f1()); // 10 |