6 Ways to Declare JavaScript Functions

A function is a parameterized block of code that can be called more than once. In JavaScript, functions are made up of and influenced by many components:

  • The code for the function body
  • The list of parameters
  • Variables that can be accessed from lexical scope
  • The return value
  • The context in which the function is calledthis
  • Named or anonymous functions
  • Saves the variables of the function object
  • Arguments object (not available in arrow function)

This article covers six ways to declare JavaScript functions: syntax, examples, and common pitfalls. In addition, you’ll learn when to use a particular function type in a particular situation.

1. Function declaration

“Function declarations consist of the function keyword, the required function name, and a parenthesized argument list (para1,… , paramN) and a pair of curly braces {… } is wrapped around the body code.

An example of a function declaration:

/ / functionfunction isEven(num) {
  returnnum % 2 === 0; } isEven(24); / / = >trueisEven(11); / / = >false
Copy the code

The function isEven (num) {… } is a function declaration that defines the isEven function used to determine whether the number isEven.

A function declaration creates a variable in the current field whose identifier is the function name and whose value is the function object.

Function variables are promoted to the current top-level scope, which means they can be called before the function is declared (see this chapter for more details).

The function created is named, and the value of the function object’s name attribute is its name. Name is useful when looking at the call stack: when debugging or reading error messages.

Let’s look at these properties in an example:

// Promoted variable console.log(hello('Aliens')); / / = >'Hello Aliens! 'Console. log(hello.name) // =>'hello'// The variable holds the function object console.log(typeof hello); / / = >'function'
function hello(name) {
  return `Hello ${name}! `; }Copy the code

Function hello(name){… } creates a variable hello, which is promoted to the top of the current scope. The hello variable holds the function object, and hello.name contains the function name :’hello’.

1.1 General functions

Function declarations are appropriate when you need to use regular functions. General means declaring the function once and then calling it in many different places. Here’s the basic scenario:

function sum(a, b) {
  return a + b;
}
sum(5, 6);           // => 11
([3, 7]).reduce(sum) // => 10
Copy the code

Because a function declaration creates a variable in the current scope and also creates a regular function call, it is useful for recursive or detached event listeners. In contrast to function expressions or arrow functions, it does not create a binding by the name of a function variable.

For example, to recursively compute the factorial, we must access the function:

function factorial(n) {
  if (n === 0) {
    return 1;
  }
  returnn * factorial(n - 1); } factorial(4); / / = > 24Copy the code

Make a recursive call inside the factorial() function using the variable holding the function :factorial(n-1).

You can take a function expression and assign it to a regular variable, such as const factorial = function(n){… }. But the function declaration function factorial(n) is compact (does not require const and =).

An important feature of function declarations is their promotion mechanism, which allows the same domain to be used before the declaration.

Promotion can be useful in some situations. For example, when you want to know how to call a function at the beginning of a script without reading its implementation. Function implementations can be placed at the bottom of the file, so you don’t have to scroll to the bottom.

You can read more details about the function declaration enhancement here.

1.2 and function expressions

Function declarations and function expressions can easily be confused. They look very similar, but the resulting functions have different properties.

An easy to remember rule: a function declaration in a statement always begins with the keyword function, otherwise it is a function expression (see Section 2, p.

The following example is a function declaration that begins with the function keyword:

// Function declaration: to"function"startfunction isNil(value) {
  return value == null;
}
Copy the code

In cases where function expressions are used, JavaScript statements do not begin with the function keyword (which appears in the middle of the statement code):

// Function expression: to"const"Beginning const isTruthy =function(value) {
  return!!!!! value; }; Const numbers = ([1, const numbers = ([1,false, 5]).filter(function(item) {
  return typeof item === 'number'; }); // function expression (IIFE)"("At the beginning,function messageFunction(message) {
  return message + ' World! '; }) ('Hello');
Copy the code

1.3 Function declarations in conditional statements

Some JavaScript environments call a code that appears in {… } in the if, for, or while statement throws an exception.

Let’s enable strict mode to see when a function is declared in a conditional statement:

(function() {
  'use strict';
  if (true) {
    function ok() {
      return 'true ok'; }}else {
    function ok() {
      return 'false ok';
    }
  }
  console.log(typeof ok === 'undefined'); / / = >true
  console.log(ok()); // Throws "ReferenceError: ok is not defined"}) ();Copy the code

When ok() is called, JavaScript throws ReferenceError: OK is not defined because the function is declared in a conditional block.

Function declarations in conditional statements are allowed in non-strict mode, but this confuses the code.

As a general rule in these cases, when a function should be created under certain conditions — use function expressions. Let’s see how to handle it:

(function() {
  'use strict';
  let ok;
  if (true) {
    ok = function() {
      return 'true ok';
    };
  } else {
    ok = function() {
      return 'false ok';
    };
  }
  console.log(typeof ok === 'function'); / / = >trueconsole.log(ok()); / / = >'true ok'}) ();Copy the code

Because a function is a regular object, it is assigned to a variable based on the condition. The call to OK () worked fine with no errors.

2. Function expressions

“Function expressions consist of the function keyword, optional function names, and a parenthesized argument list (para1,… , paramN) and a pair of curly braces {… } separate body code composition.

Some examples of function expressions:

const count = function(array) {// Function expressionreturn array.length;
}
const methods = {
  numbers: [1, 5, 8],
  sum: function() {// function expressionreturn this.numbers.reduce(function(acc, num) { // func. expression
      returnacc + num; }); } } count([5, 7, 8]); // => 3 methods.sum(); / / = > 14Copy the code

A function expression creates a function object that can be used in different situations:

  • Assign to a variable as an objectThe count = function (...). {... }
  • Create a method on the objectThe sum function () {... }
  • Use functions as callbacks.Reduce (function (...). {... })

Functional expressions are the most important part of JavaScript. Typically, you deal with this type of function declaration in addition to the arrow function (if you prefer a short syntactic and lexical context).

2.1 Named function expressions

Functions without names are anonymous (the name attribute is a null character “”):

(
  function(variable) {returntypeof variable; } ).name; / / = >' '
Copy the code

This is an anonymous function whose name is an empty string.

Sometimes function names can be inferred. For example, when an anonymous function is assigned to a variable:

const myFunctionVar = function(variable) { 
  returntypeof variable; }; myFunctionVar.name; / / = >'myFunctionVar'
Copy the code

The anonymous function name is ‘myFunctionVar’ because the myFunctionVar variable name is used as the function name.

When an expression specifies a name, it is a named function expression. It has some additional attributes compared to simple function expressions:

  • Create a named function, i.enameProperties are function names
  • Inside the function body, variables with the same name refer to the function object

Let’s use the example above, but set a name in the function expression:

const getType = function funName(variable) {
  console.log(typeof funName === 'function'); / / = >true
  returntypeof variable; } console.log(getType(3)); / / = >'number'console.log(getType.name); / / = >'funName'console.log(typeof funName); / / = >'undefined'
Copy the code

Function funName (variable) {… } is a named function expression. The funName variable can be accessed within the function domain, but not externally. Either way, the name attribute of the function object is the function name: funName.

2.2 Specifying function expressions

When a function expression const fun = function(){} is assigned to a variable, some engines infer the function name from the variable. However, a callback may be passed as an anonymous function expression and not stored in a variable: therefore, the engine cannot determine its name.

Supporting named functions and avoiding anonymous functions have the following benefits:

  • When the function name is used, the error message and call stack show more detail
  • Make debugging more comfortable by reducing the number of anonymous stack names
  • The function name tells you what the function does
  • You can access functions within the scope of recursive calls or detached event listeners

3. Definition of shorthand methods

“Shorthand methods define method declarations that can be used for object constants and ES2015 classes. You can define them using function names, followed by a list of arguments in parentheses (para1,… , paramN) and a pair of curly braces {… } separate the body statement.

The following example uses a shorthand method definition in an object constant:

const collection = { items: [], add(... items) { this.items.push(... items); }, get(index) {returnthis.items[index]; }}; collection.add('C'.'Java'.'PHP');
collection.get(1) // => 'Java'
Copy the code

The Add () and get() methods in the Collection object are defined using short method definitions. These methods are called like regular methods :collection.add(…) And the collection. The get (…). .

Use the name, colon, and function expression add: function(…) in contrast to the traditional attribute definition approach. {… } This short method-defined approach has several advantages:

  • Shorter grammars are easier to understand
  • In contrast to a function expression, a shorthand method definition creates a specified function. It is useful for debugging.

Class syntax requires short method declarations:

class Star {
  constructor(name) {
    this.name = name;
  }
  getMessage(message) {
    return this.name + message;
  }
}
const sun = new Star('Sun');
sun.getMessage(' is shining') / / = >'Sun is shining'
Copy the code

3.1 The calculated attribute name and method

ECMAScript 2015 adds a nice feature: evaluating property names in object constants and classes.

Evaluate attributes using slightly different syntax [methodName](){… }, then the method is defined as follows:

const addMethod = 'add',
  getMethod = 'get'; const collection = { items: [], [addMethod](... items) { this.items.push(... items); }, [getMethod](index) {returnthis.items[index]; }}; collection[addMethod]('C'.'Java'.'PHP');
collection[getMethod](1) // => 'Java'
Copy the code

[addMethod] (…) {… } and [getMethod] (…). {… } is a shorthand method declaration with the name of the computed property.

4. The arrow

“The arrow function uses a pair of arguments that contain a list of arguments (param1, param2… , paramN) followed by a fat arrow => and a pair of curly braces {… } separate the body statement for definition.

When the arrow function has only one argument, the parentheses can be omitted. When it contains a statement, the curly braces can also be omitted.

Basic usage:

const absValue = (number) => {
  if (number < 0) {
    return -number;
  }
  returnnumber; } absValue(-10); // => 10 absValue(5); / / = > 5Copy the code

AbsValue is an arrow function that calculates the absolute value of a number.

Functions declared with fat arrows have the following properties:

  • The arrow function does not create its execution context, but handles it lexically (as opposed to function expressions or function declarations, which create their own based on the callthis)
  • Arrow functions are anonymous. However, the engine can infer its name from a variable pointing to a function.
  • argumentsObject is not available in arrow functions (and providedargumentsObject of the opposite declared type). However, you are free to use itrestparameter(... params).

4.1 Context Transparency

The this keyword is a confusing aspect of JavaScript (see this article for a full explanation of this).

Because functions create their own execution context, it is often difficult to detect the value of this.

ECMAScript 2015 improves the use of this by introducing the arrow function, which takes context lexically (or directly uses this in an external field). This is good because you don’t have to use.bind(This) or store context var self = This when a function needs a closed context.

Let’s see how to inherit this from an external function:

class Numbers {
  constructor(array) {
    this.array = array;
  }
  addNumber(number) {
    if(number ! == undefined) { this.array.push(number); }return(number) => { console.log(this === numbersObject); / / = >truethis.array.push(number); }; } } const numbersObject = new Numbers([]); const addMethod = numbersObject.addNumber(); addMethod(1); addMethod(5); console.log(numbersObject.array); / / = > [1, 5]Copy the code

The Numbers class has an array of Numbers and provides the addNumber() method to insert new Numbers.

When addNumber() is called with no arguments, returns a closure that allows the insertion of numbers. The closure is an arrow function for an instance of this equals numbersObject, because the context is lexically obtained from the addNumbers() method.

If there is no arrow function, you must manually fix the context. It means workarounds like the.bind() method:

/ /...return function(number) { console.log(this === numbersObject); / / = >truethis.array.push(number); }.bind(this); / /...Copy the code

Or store the context in a separate variable var self = this:

/ /... const self = this;return function(number) { console.log(self === numbersObject); / / = >trueself.array.push(number); }; / /...Copy the code

Context transparency can be used when you want this from a closed context to remain as it is.

4.2 short callback

When creating an arrow function, parenthesis pairs and curly braces are optional for a single argument and a single body statement. This helps to create very short callback functions.

Let’s build a function to find an array containing 0:

const numbers = [1, 5, 10, 0]; numbers.some(item => item === 0); / / = >true
Copy the code

Item => item === 0 is a simple arrow function.

Note that nested short arrow functions are difficult to understand, and the convenient way to use the shortest arrow function approach is a single callback (not nested).

Use the extended syntax of arrow functions when writing nested arrow functions, if necessary. It’s just easier to read.

5. Generator functions

A generator function in JavaScript returns a generator object. Its syntax is similar to a function expression, function declaration, or method declaration, except that it requires an asterisk *.

Function *

(): function*

():

function* indexGenerator(){
  var index = 0;
  while(true) { yield index++; } } const g = indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); / / = > 1Copy the code

Function * ():

const indexGenerator = function* () {
  let index = 0;
  while(true) { yield index++; }}; const g = indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); / / = > 1Copy the code

*

():

const obj = {
  *indexGenerator() {
    var index = 0;
    while(true) { yield index++; } } } const g = obj.indexGenerator(); console.log(g.next().value); // => 0 console.log(g.next().value); / / = > 1Copy the code

All three generator functions return the generator object G, which is then used to generate a series of incremented numbers.

6. One more thing: New Function

In JavaScript, functions are a class of objects — functions are regular objects of type function.

The declaration method described above creates the same function object type. Let’s look at an example:

function sum1(a, b) {
  return a + b;
}
const sum2 = function(a, b) {
  return a + b;
}
const sum3 = (a, b) => a + b;
console.log(typeof sum1 === 'function'); / / = >true
console.log(typeof sum2 === 'function'); / / = >true
console.log(typeof sum3 === 'function'); / / = >true
Copy the code

Function object types have one constructor :Function.

When Function is called as a constructor, new Function(arg1, arg2… , argN,bodyString), will create a new function. Arguments arg1, args2… ArgN is passed to the constructor as the parameter name for the new function, and the last parameter bodyString is used as the body code.

Let’s create a function, the sum of two numbers:

const numberA = 'numberA', numberB = 'numberB';
const sumFunction = new Function(numberA, numberB, 
   'return numberA + numberB'
);
sumFunction(10, 15) // => 25
Copy the code

The sumFunction created using the Function constructor call has parameters numberA and numberB, and the body returns numberA + numberB.

Functions created in this way do not have access to the current scope and therefore cannot create closures, so they are always created globally.

A better way to access global objects in a browser or NodeJS script is to apply new Function:

(function() {
   'use strict';
   const global = new Function('return this') (); console.log(global === window); / / = >trueconsole.log(this === window); / / = >false}) ();Copy the code

Keep in mind that new Function() should almost never be used to declare functions. Because the function body is executed at runtime, this approach inherits many eval() usage problems: security risks, difficulty in debugging, inability to apply engine optimization, and no editor to automate.

7. Finally, which approach is better?

There are no winners and no losers. Which declaration type you choose depends on the situation.

However, there are some rules you can follow in common situations.

If the function uses this from a closed function, the arrow function is a good solution. The arrow function is also a good choice when the callback function has a phrase, because it creates short and light code.

Use a shorter syntax when declaring methods on object constants, and shorthand method declarations are preferred.

The new Function method should not normally be used to declare functions. Mainly because it opens up potential security risks, does not allow code completion in the editor and does not allow engine optimization.