This is the 22nd day of my participation in the August More Text Challenge
Typically, a function is a series of instructions or “subroutines” that can be called by code outside (or inside) the function. In essence, a function “encapsulates” a specific task.
Functions are one of the basic building blocks of JavaScript, and a real understanding of them can help solve some of JavaScript’s strange behavior.
Functions in JavaScript
It is important to note that functions in JavaScript are first-class objects. This basically means that functions in JavaScript can be treated like any other JavaScript object and can be referred to as other variables or passed to functions as arguments.
Functions can even have attributes and other methods, just like any other JavaScript object. The key difference between functions and other objects is that functions can be called (or called).
Every Function in JavaScript is a Function object. Try executing from the console:
function typeCheck() {}
typeCheck instanceof Function // true
Copy the code
Function objects have specific methods and properties, such as apply, call, bind, isGenerator, etc., that are not available in other objects.
There are several different ways you can define a function in JavaScript, and how you define a function affects its behavior.
Function declaration
This is probably the most common way to define a function. The function declaration contains a name preceded by the mandatory function keyword followed by a list of optional arguments within the required parentheses ().
function sum(param1, param2) {
return param1 + param2
}
Copy the code
There are two things to note about the function definition of this form:
- The variable holding the function object is created in the current scope with the same identifier as the provided function name — in our example
sum
. - The variable is promoted to the top of the current scope.
To better understand ascension, let’s look at an example:
console.log(sayHi()) // 'Hi'
function sayHi() {
return 'Hi'
}
Copy the code
We can call the sayHi function before defining it.
Functional expression
Function expressions are syntactically very similar to function declarations. The main difference is that function expressions do not require function names.
let sum = function(param1, param2) {
return param1 + param2
}
Copy the code
A function expression is part of another statement. In the example above, the function expression is part of the assignment to the sum variable.
Unlike function declarations, function expressions are not promoted.
console.log(sayHi()) // Uncaught ReferenceError: sayHi is not defined
let sayHi = function() {
return 'Hi'
}
Copy the code
An interesting use case for function expressions is their ability to create IIFE (execute function expressions immediately). In some cases, we might want to define a function and call it immediately after it is defined, but not again.
Of course, it can be done with function declarations, but to make it more readable and to ensure that our program doesn’t accidentally access it, we use IIFE. Consider this example:
function callImmediately(foo) {
console.log(foo)
}
Copy the code
We create a function called callImmediately that takes an argument and prints it, and then we call it immediately. The same result can be obtained by doing this:
(function(foo) {
console.log(foo)
})('foo') // 'foo'
Copy the code
The key difference is that in the first case, the function declaration pollutes the global namespace, and the named function callImmediately hangs long after it needs it. IIFE is anonymous and therefore cannot be called in the future.
Arrow function
Arrow functions are a complement to ES6 and a syntactically compact alternative to function expressions. Arrow functions are defined using a pair of parentheses containing a list of arguments, followed by a => and function statement with curly braces {}.
let sum = (param1, param2) = > {
return param1 + param2
}
Copy the code
Since the main purpose of the arrow function is syntax compactness, if the only statement in the arrow function is return, we can remove the curly braces and return keyword, as shown below:
let sum = (param1, param2) = > param1 + param2
Copy the code
Also, if we only have one argument to pass to the arrow function, we can eliminate the parentheses:
let double = param1= > param1 * 2
Copy the code
There are a few things to note about the function definition of this form:
- Arrow functions don’t have their own
this
, which uses closed lexical scopethis
Value.
let user = {
name: 'O.O'.getUserNameArrow: () = > {
console.log(11.this.name)
},
getUserNameExpression: function() {
console.log(this.name)
}
}
user.getUserNameArrow() / /"
user.getUserNameExpression() // O.O
Copy the code
In the example above, we have an arrow function and a function expression that prints user.name using the this function.
- Arrow function doesn’t have any
prototype
Properties.
let foo = () = > {}
console.log(foo.prototype) // 'undefined'
Copy the code
- It’s not in the arrow function
arguments
Object.
Function
The constructor
As mentioned earlier, every Function in JavaScript is a Function object, so to define a Function, we can also call the constructor of the Function object directly.
let sum = new Function('param1'.'param2'.'return param1 + param2')
Copy the code
Parameters are comma-delimited strings ‘param1’, ‘param2’… , ‘paramN’ is passed as a list, and the last argument is the body of the function passed as a string.
In terms of performance, this way of defining functions is not as efficient as function declarations or function expressions. Functions defined using the Function constructor are parsed each time the constructor is called, because the Function body strings need to be parsed each time, and the other Function body strings are parsed along with the rest of the code.
One use case for defining functions in this way is to access the Global object in Node or the Window object in a browser. These functions are always created in the global scope and do not have access to the current scope.
The Generator function
Generator is a complement to ES6. Generators are a special type of function that, unlike traditional functions, generates multiple values on a per-request basis while pausing execution between those requests.
function* idMaker() {
let index = 0
while(true)
yield index++
}
let gen = idMaker()
console.log(gen.next().value) / / 0
console.log(gen.next().value) / / 1
console.log(gen.next().value) / / 2
Copy the code
The function* and yield keywords are unique to generators. Generators are defined by adding * to the end of the function keyword. This enables us to use the yield keyword in the generator body to generate values based on the request.
conclusion
The choice of which definition type to use depends on the situation and the goal you are trying to achieve. A few basic points to remember:
- If you want to take advantage of function promotion, use function declarations. For example, for the sake of clarity, you might want to move the function implementation details to the bottom and only the abstract process to the top.
- Arrow functions are great for short callback functions and, more importantly, when needed
this
Is a closed function. - Avoid the use of
Function
Constructor to define a function. If the annoying syntax isn’t enough to keep you away, it’s very slow because the function is parsed every time it’s called.