- Function object, NFE
- The “name” property
- The “length” property
- Custom properties
- Named Function Expression
- Summary
- Tasks
- Set and decrease for counter
- Function
- Constructor
- Instance properties
- Instance methods
- Examples
- Difference between Function constructor and function declaration
- Specifications
- Browser compatibility
- See also
- Found a content problem with this page?
Function object, NFE
As we already know, a function in JavaScript is a value.
Every value in JavaScript has a type. What type is a function?
In JavaScript, functions are objects.
A good way to imagine functions is as callable “action objects”. We can not only call them, but also treat them as objects: add/remove properties, pass by reference etc.
The “name” property
Function objects contain some useable properties.
For instance, a function’s name is accessible as the “name” property:
function sayHi() < alert("Hi"); >alert(sayHi.name); // sayHi
What’s kind of funny, the name-assigning logic is smart. It also assigns the correct name to a function even if it’s created without one, and then immediately assigned:
let sayHi = function() < alert("Hi"); >; alert(sayHi.name); // sayHi (there's a name!)
It also works if the assignment is done via a default value:
function f(sayHi = function() <>) < alert(sayHi.name); // sayHi (works!) >f();
In the specification, this feature is called a “contextual name”. If the function does not provide one, then in an assignment it is figured out from the context.
Object methods have names too:
let user = < sayHi() < // . >, sayBye: function() < // . >> alert(user.sayHi.name); // sayHi alert(user.sayBye.name); // sayBye
There’s no magic though. There are cases when there’s no way to figure out the right name. In that case, the name property is empty, like here:
// function created inside array let arr = [function() <>]; alert( arr[0].name ); // // the engine has no way to set up the right name, so there is none
In practice, however, most functions do have a name.
The “length” property
There is another built-in property “length” that returns the number of function parameters, for instance:
function f1(a) <> function f2(a, b) <> function many(a, b, . more) <> alert(f1.length); // 1 alert(f2.length); // 2 alert(many.length); // 2
Here we can see that rest parameters are not counted.
The length property is sometimes used for introspection in functions that operate on other functions.
For instance, in the code below the ask function accepts a question to ask and an arbitrary number of handler functions to call.
Once a user provides their answer, the function calls the handlers. We can pass two kinds of handlers:
- A zero-argument function, which is only called when the user gives a positive answer.
- A function with arguments, which is called in either case and returns an answer.
To call handler the right way, we examine the handler.length property.
The idea is that we have a simple, no-arguments handler syntax for positive cases (most frequent variant), but are able to support universal handlers as well:
function ask(question, . handlers) < let isYes = confirm(question); for(let handler of handlers) < if (handler.length == 0) < if (isYes) handler(); >else < handler(isYes); >> > // for positive answer, both handlers are called // for negative answer, only the second one ask("Question?", () => alert('You said yes'), result => alert(result));
This is a particular case of so-called polymorphism – treating arguments differently depending on their type or, in our case depending on the length . The idea does have a use in JavaScript libraries.
Custom properties
We can also add properties of our own.
Here we add the counter property to track the total calls count:
function sayHi() < alert("Hi"); // let's count how many times we run sayHi.counter++; >sayHi.counter = 0; // initial value sayHi(); // Hi sayHi(); // Hi alert( `Called $ times` ); // Called 2 times
A property assigned to a function like sayHi.counter = 0 does not define a local variable counter inside it. In other words, a property counter and a variable let counter are two unrelated things.
We can treat a function as an object, store properties in it, but that has no effect on its execution. Variables are not function properties and vice versa. These are just parallel worlds.
Function properties can replace closures sometimes. For instance, we can rewrite the counter function example from the chapter Variable scope, closure to use a function property:
function makeCounter() < // instead of: // let count = 0 function counter() < return counter.count++; >; counter.count = 0; return counter; > let counter = makeCounter(); alert( counter() ); // 0 alert( counter() ); // 1
The count is now stored in the function directly, not in its outer Lexical Environment.
Is it better or worse than using a closure?
The main difference is that if the value of count lives in an outer variable, then external code is unable to access it. Only nested functions may modify it. And if it’s bound to a function, then such a thing is possible:
function makeCounter() < function counter() < return counter.count++; >; counter.count = 0; return counter; > let counter = makeCounter(); counter.count = 10; alert( counter() ); // 10
So the choice of implementation depends on our aims.
Named Function Expression
Named Function Expression, or NFE, is a term for Function Expressions that have a name.
For instance, let’s take an ordinary Function Expression:
let sayHi = function func(who) < alert(`Hello, $`); >;
Did we achieve anything here? What’s the purpose of that additional «func» name?
First let’s note, that we still have a Function Expression. Adding the name «func» after function did not make it a Function Declaration, because it is still created as a part of an assignment expression.
Adding such a name also did not break anything.
The function is still available as sayHi() :
let sayHi = function func(who) < alert(`Hello, $`); >; sayHi("John"); // Hello, John
There are two special things about the name func , that are the reasons for it:
- It allows the function to reference itself internally.
- It is not visible outside of the function.
For instance, the function sayHi below calls itself again with «Guest» if no who is provided:
let sayHi = function func(who) < if (who) < alert(`Hello, $`); > else < func("Guest"); // use func to re-call itself >>; sayHi(); // Hello, Guest // But this won't work: func(); // Error, func is not defined (not visible outside of the function)
Why do we use func ? Maybe just use sayHi for the nested call?
Actually, in most cases we can:
let sayHi = function(who) < if (who) < alert(`Hello, $`); > else < sayHi("Guest"); >>;
The problem with that code is that sayHi may change in the outer code. If the function gets assigned to another variable instead, the code will start to give errors:
let sayHi = function(who) < if (who) < alert(`Hello, $`); > else < sayHi("Guest"); // Error: sayHi is not a function >>; let welcome = sayHi; sayHi = null; welcome(); // Error, the nested sayHi call doesn't work any more!
That happens because the function takes sayHi from its outer lexical environment. There’s no local sayHi , so the outer variable is used. And at the moment of the call that outer sayHi is null .
The optional name which we can put into the Function Expression is meant to solve exactly these kinds of problems.
Let’s use it to fix our code:
let sayHi = function func(who) < if (who) < alert(`Hello, $`); > else < func("Guest"); // Now all fine >>; let welcome = sayHi; sayHi = null; welcome(); // Hello, Guest (nested call works)
Now it works, because the name «func» is function-local. It is not taken from outside (and not visible there). The specification guarantees that it will always reference the current function.
The outer code still has its variable sayHi or welcome . And func is an “internal function name”, the way for the function to can call itself reliably.
The “internal name” feature described here is only available for Function Expressions, not for Function Declarations. For Function Declarations, there is no syntax for adding an “internal” name.
Sometimes, when we need a reliable internal name, it’s the reason to rewrite a Function Declaration to Named Function Expression form.
Summary
Here we covered their properties:
- name – the function name. Usually taken from the function definition, but if there’s none, JavaScript tries to guess it from the context (e.g. an assignment).
- length – the number of arguments in the function definition. Rest parameters are not counted.
If the function is declared as a Function Expression (not in the main code flow), and it carries the name, then it is called a Named Function Expression. The name can be used inside to reference itself, for recursive calls or such.
Also, functions may carry additional properties. Many well-known JavaScript libraries make great use of this feature.
They create a “main” function and attach many other “helper” functions to it. For instance, the jQuery library creates a function named $ . The lodash library creates a function _ , and then adds _.clone , _.keyBy and other properties to it (see the docs when you want to learn more about them). Actually, they do it to lessen their pollution of the global space, so that a single library gives only one global variable. That reduces the possibility of naming conflicts.
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.
Tasks
Set and decrease for counter
Modify the code of makeCounter() so that the counter can also decrease and set the number:
- counter() should return the next number (as before).
- counter.set(value) should set the counter to value .
- counter.decrease() should decrease the counter by 1.
See the sandbox code for the complete usage example.
P.S. You can use either a closure or the function property to keep the current count. Or write both variants.
The solution uses count in the local variable, but addition methods are written right into the counter . They share the same outer lexical environment and also can access the current count .
function makeCounter() < let count = 0; function counter() < return count++; >counter.set = value => count = value; counter.decrease = () => count--; return counter; >
Function
The Function object provides methods for functions. In JavaScript, every function is actually a Function object.
Constructor
Creates a new Function object. Calling the constructor directly can create functions dynamically but suffers from security and similar (but far less significant) performance issues to eval() . However, unlike eval() , the Function constructor creates functions that execute in the global scope only.
Instance properties
These properties are defined on Function.prototype and shared by all Function instances.
Represents the arguments passed to this function. For strict, arrow, async, and generator functions, accessing the arguments property throws a TypeError . Use the arguments object inside function closures instead.
Represents the function that invoked this function. For strict, arrow, async, and generator functions, accessing the caller property throws a TypeError .
The constructor function that created the instance object. For Function instances, the initial value is the Function constructor.
These properties are own properties of each Function instance.
The display name of the function.
Specifies the number of arguments expected by the function.
Used when the function is used as a constructor with the new operator. It will become the new object’s prototype.
Instance methods
Calls a function with a given this value and optional arguments provided as an array (or an array-like object).
Creates a new function that, when called, has its this keyword set to a provided value, optionally with a given sequence of arguments preceding any provided when the new function is called.
Calls a function with a given this value and optional arguments.
Returns a string representing the source code of the function. Overrides the Object.prototype.toString method.
Specifies the default procedure for determining if a constructor function recognizes an object as one of the constructor’s instances. Called by the instanceof operator.
Examples
Difference between Function constructor and function declaration
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 created. This is different from using eval() with code for a function expression.
// Create a global property with `var` var x = 10; function createFunction1() const x = 20; return new Function("return x;"); // this `x` refers to global `x` > function createFunction2() const x = 20; function f() return x; // this `x` refers to the local `x` above > return f; > const f1 = createFunction1(); console.log(f1()); // 10 const f2 = createFunction2(); console.log(f2()); // 20
While this code works in web browsers, f1() will produce a ReferenceError in Node.js, as x will not be found. This is because the top-level scope in Node is not the global scope, and x will be local to the module.
Specifications
Browser compatibility
BCD tables only load in the browser
See also
Found a content problem with this page?
This page was last modified on Apr 18, 2023 by MDN contributors.
Your blueprint for a better internet.