Function object, NFE

As we already know, functions in JavaScript are values. Every value in JavaScript has a type. What type is a function? In JavaScript, functions are objects. We can not only call them, but also treat them as objects: add/remove properties, pass by reference etc.

The “name” property

A function’s name is accessible as the “name” property:

function sayHi() {
  alert("Hi");
}

alert(sayHi.name); // sayHi

What’s more funny, the name-assigning logic is smart. It also assigns the correct name to functions that are used in assignments:

let sayHi = function() {
  alert("Hi");
}

alert(sayHi.name); // sayHi (works!)

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

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.

In most cases the length is equals to arguments.length. For example -

function check(a, b){
	console.log(arguments.length == check.length); // true
}

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 ${sayHi.counter} times` ); // Called 2 times

A property is not a variable.

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.

Function Closure Vs Function Property

Function properties can replace closures sometimes. For instance, we can rewrite the counter function example from the chapter 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 (Lexical Environment), 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
alert( counter() ); // 11

Named Function Expression(NFE)

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(who) {
  alert(`Hello, ${who}`);
};

And add a name to it:

let sayHi = function func(who) {
  alert(`Hello, ${who}`);
};

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, ${who}`);
};

sayHi("John"); // Hello, John

There are two special things about the name func:

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, ${who}`);
  } 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, ${who}`);
  } else {
    sayHi("Guest");
  }
};

The problem with that code is that the value of sayHi may change. The function may go to another variable, and the code will start to give errors:

let sayHi = function(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } 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 sayHi = function func(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } 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.

Function Name is only available for Function Expression

The “internal name” feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, there’s just no syntax possibility to add a one more “internal” name. Sometimes, when we need a reliable internal name, it’s the reason to rewrite a Function Declaration to Named Function Expression form.

Example : Sum with an arbitrary amount of brackets

Question:

WriteSolution function sum that would work like this:

sum(1)(2) == 3; // 1 + 2
sum(1)(2)(3) == 6; // 1 + 2 + 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15

Solution:

function sum(a) {

  let currentSum = a;

  function f(b) {
    currentSum += b;
    return f;
  }

  f.toString = function() {
    return currentSum;
  };

  return f;
}

alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
alert( sum(6)(-1)(-2)(-3) ); // 0
alert( sum(0)(1)(2)(3)(4)(5) ); // 15

new Function

There’s one more way to create a function. It’s rarely used, but sometimes there’s no alternative. The syntax for creating a function:

let func = new Function ([arg1[, arg2[, ...argN]],] functionBody)

In other words, function parameters (or, more precisely, names for them) go first, and the body is last. All arguments are strings. It’s easier to understand by looking at an example. Here’s a function with two arguments:

let sum = new Function('a', 'b', 'return a + b');

alert( sum(1, 2) ); // 3

If there are no arguments, then there’s only a single argument, the function body:

let sayHi = new Function('alert("Hello")');

sayHi(); // Hello

The major difference from other ways we’ve seen is that the function is created literally from a string, that is passed at run time. All previous declarations required us, programmers, to write the function code in the script. But new Function allows to turn any string into a function. For example, we can receive a new function from a server and then execute it:

let str = ... receive the code from a server dynamically ...

let func = new Function(str);
func();

It is used in very specific cases, like when we receive code from a server, or to dynamically compile a function from a template. The need for that usually arises at advanced stages of development.

Lexical Environment for new Function

Usually, a function remembers where it was born in the special property [[Environment]]. It references the Lexical Environment from where it’s created.

But when a function is created using new Function, its [[Environment]] references not the current Lexical Environment, but instead the global one.

function getFunc() {
  let value = "test";

  let func = new Function('alert(value)');

  return func;
}

getFunc()(); // error: value is not defined

Compare it with the regular behavior:

function getFunc() {
  let value = "test";

  let func = function() { alert(value); };

  return func;
}

getFunc()(); // "test", from the Lexical Environment of getFunc

This special feature of new Function looks strange, but appears very useful in practice. Imagine that we must create a function from a string. The code of that function is not known at the time of writing the script (that’s why we don’t use regular functions), but will be known in the process of execution. We may receive it from the server or from another source. Our new function needs to interact with the main script. Perhaps we want it to be able to access outer local variables. In this case the new Function feature saves us.