Author Topic: JavaScript Closures - Part 1  (Read 108 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4034
JavaScript Closures - Part 1
« on: October 24, 2017, 06:38:40 PM »
Since someone was curious ;)

Closures require two things:
  • Nested Functions - which affects the scope of identifiers
  • First-Class Functions - functions can be passed around and returned like data values

Note: I'm using /* */ style comments here, since the syntax highlighter makes a mess of // comments.

This first example demonstrates only nested functions:
Code: Javascript [Select]

/* Nested functions
/  The inner function has access to the local variables of the outer function
/  The inner function is only in scope for the outer function. It can not be seen globally
*/
function a() {
  var x = 0;
 
  function b() {
    console.log("x =", x);
  }
 
  x++;
  b();  /* x = 1 */
  x++;
  b();  /* x = 2 */
}

/* Call a, which will call b, which will output x (from a) */
a();

/* The inner function b is not in scope here */
//b();  // Reference error



This example returns a function object, creating a closure. This exposes the inner function to the outer world in a controlled manner:

Code: Javascript [Select]

/* JavaScript closure
/  Function c returns its inner function d
/  The function d still has access to c's variables even after c has returned
*/
function c() {
  var y = 3;
 
  function d() {
    return y;
  }
 
  return d;  /* Return function as value, as opposed to result of calling the function */
}

/* Call c, which will return the function d, which when called returns y (from c) */
var e = c();  /* e = d */

console.log("Function returns:", e());  /* 3 */

/* Can not access d directly. We must be given access by c. */
//d();  // Reference error



This example uses a closure to create a counting object. Each call to the outer function creates a unique context, which is attached to the inner function that is returned:

Code: Javascript [Select]

/* A counter object constructed from a closure */
function generateCounter() {
  var count = 0;
 
  function next() {
    return count++;
  }
 
  return next;  /* Return function 'next' along with it's enclosing scope */
}

/* Create a counter object */
var counter1 = generateCounter();
console.log(counter1(), counter1(), counter1());  /* 0 1 2 */

/* Create a new independent counter */
var counter2 = generateCounter();
console.log(counter2(), counter2(), counter2());  /* 0 1 2 */

console.log();

/* Counters proceed independently */
console.log(counter1(), counter1());  /* 3 4 */
console.log(counter2(), counter2());  /* 3 4 */


In this last example, the local variable "count" has limited scope. This makes it similar to a private variable of a class in a language like C++. The function "next" is somewhat like a member function of the class.

In this simple example, only a single member function is visible, which the class-like variables are bound to implicitly. There is no syntactic distinction of "object.memberFunction" here, only "memberFunction".
« Last Edit: October 25, 2017, 04:58:22 AM by Hooman »

Offline Vagabond

  • Sr. Member
  • ****
  • Posts: 492
Re: JavaScript Closures - Part 1
« Reply #1 on: October 25, 2017, 10:07:46 AM »
This is cool, I've never read up on closures.

Both C# and C++ support Lamda expressions. Would this qualify as supporting first class functions? (Don't feel like you have to research that if you don't know.)

Sadly the Wikipedia article on first class functions listed JavaScript as a scripting language and not a higher level programming language, which is just not true despite the language's poor choice on names.

Brett

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4034
Re: JavaScript Closures - Part 1
« Reply #2 on: October 25, 2017, 02:06:31 PM »
Quote
Both C# and C++ support Lamda expressions. Would this qualify as supporting first class functions?

Ahh yes. I've never really used C# nor many new features in C++, so take this with a grain of salt, but yes, lambda expressions are very much about having first order functions. The question I'm unclear about, is if they capture the variables in their surrounding scope to form closures. I've heard mention of "delegates" in these languages, which do seem to form closures. I've also seen implementations in other languages which don't capture variables in the surrounding scope to form closures.

Just came across this article which states that C# lambdas do capture variables from the surrounding scope. Near the end of the article is a section titled "Variable Scope in Lambda Expressions":
Lambda Expressions


I just checked out the Wikipedia article on First-class functions. Indeed, they had three main sections in their table of support. They had first-order functions, nested functions, and then closures. They're explicitly discussing the capture of variables from the surrounding environment (the closures), separately from the other two supporting features. That's something I kind of ignored in my first post. I like how they presented that.

Quote
Sadly the Wikipedia article on first class functions listed JavaScript as a scripting language and not a higher level programming language, which is just not true despite the language's poor choice on names.

Sounds like you view scripting languages as somehow inferior. Though oddly, it seems to go the other way. The only low level languages I can think of, such as assembler, are definitely not scripting languages. Even languages like C/C++ are sometimes viewed as medium-level languages, due to all the extra concerns they burden the programmer with, such as memory management. Actually, from the Wikipedia article on Scripting Languages:
Quote
Scripting languages are also sometimes referred to as very high-level programming languages, as they operate at a high level of abstraction, or as control languages, particularly for job control languages on mainframes.

I suppose you mean they have a reputation of being used for short small programs, particularly of the nature of batch files/shell scripts. In which case, JavaScript did sort of fit that bill for browser scripting in the early days. Though you're right, now it's definitely a full on language, with support from standalone JavaScript engines, such as NodeJS.
« Last Edit: October 25, 2017, 02:26:47 PM by Hooman »

Offline Vagabond

  • Sr. Member
  • ****
  • Posts: 492
Re: JavaScript Closures - Part 1
« Reply #3 on: October 26, 2017, 02:56:18 PM »
Yeah, I use delegates in C# both for using events and for passing a specific method to run.

You can strongly define methods when using delegates. Saying things like the function must return an integer and take a string as an input.

I could try to isolate an example of a delegate from some of my work and post it if you are interested (kind of off topic from Closures though.)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4034
Re: JavaScript Closures - Part 1
« Reply #4 on: October 26, 2017, 05:09:38 PM »
Start a new topic =)

I would actually like to learn some C#

Online Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1136
Re: JavaScript Closures - Part 1
« Reply #5 on: October 26, 2017, 05:19:29 PM »
Ahh yes. I've never really used C# nor many new features in C++, so take this with a grain of salt, but yes, lambda expressions are very much about having first order functions. The question I'm unclear about, is if they capture the variables in their surrounding scope to form closures. I've heard mention of "delegates" in these languages, which do seem to form closures. I've also seen implementations in other languages which don't capture variables in the surrounding scope to form closures.
C++ lambdas do. http://en.cppreference.com/w/cpp/language/lambda That's what the [] part of the syntax is for. Nothing is captured by default, you have to explicitly capture by using [&], or you can capture only specific variables like [&myVar, &myOtherVar], etc.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4034
Re: JavaScript Closures - Part 1
« Reply #6 on: October 27, 2017, 12:48:14 PM »
Ahh, interesting take on it. The added complication makes sense considering the runtime environment in C++, and its concern for efficiency.