3

I am experimenting with closures but rather than enclosing a function within a function, I enclosed a function within a block. Since functions are not blocked-scoped and will be hoisted outside of the block I assumed that it wouldn't have access to the scope within the block. Yet, in this case, the function returns the block-scoped variable. Does this mean that the function is a closure?

{
   let a = 'hi'
   function test() {
      return a
   }
}
test() // hi
Andy Tran
  • 135
  • 2
  • 9
  • 3
    For legacy code compatibility we have [`blocked scoped functions in JS`](https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics), Some related ones [`How does JS declare variables and functions without lexical scope in a statement block?`](https://stackoverflow.com/questions/58151953/how-does-js-declare-variables-and-functions-without-lexical-scope-in-a-statement) and [`What are the precise semantics of block-level functions in ES6?`](https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6) – Code Maniac Oct 17 '19 at 05:03
  • These links are awesome, thanks for pointing them out. – Ray Toal Oct 17 '19 at 05:17
  • Your editor should have told you that you shouldn't declare a functions in blocks. – StackSlave Oct 17 '19 at 05:34
  • Technically every function is a closure in JS, since every function has a reference to the environment it was created in. – Felix Kling Oct 17 '19 at 05:54
  • @StackSlave There is absolutely nothing wrong with declaring functions in blocks. (There is a problem with not using strict mode though) – Bergi Oct 17 '19 at 07:15

1 Answers1

1

I'd be happy to call it a closure, at least according to Wikpedia's definition:

Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

In your function test, the variable a is a free variable, used by the function but not defined inside the function. When you called the function outside the block, it retained the value of a. So, you've met the essential points of the definition of closure (according to Wikipedia).

Of course, you asked the question because it's kind of tricky. Normally with closures, we define a function inside an environment and then "export" it out by binding the function object to a name that has a wider scope. Because of the way JavaScript treats function declarations defined in a block (see Code Maniac's link to the ECMAScript specification on handling block-scoped function declarations) you do get this effect! So it's kind of a closure, even though you never explicitly exported the function.

Of course if you had written

{
   let a = 'hi'
   let test = () => a
}
test()

you get an error.

But this works:

let b;
{
   let a = 'hi'
   let test = () => a
   b = test
}
b() // "hi"

so yes, the block is acting as the non-local environment from which variables can be captured. I'm thinking yes, it's okay to speak of this as being a closure, because it acts like one (even if this behavior comes from a pre-ECMAScript 2015, "optional", and non-strict treatment of function declarations inside of blocks). If it walks like a duck, and all that.

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • 1
    Technically every function is a closure in JS, because every function has a reference to the environment it was created in. It doesn‘t matter where/when the function is executed. Even the spec uses the term “closure” in the context of function *definitions*. I agree that people seem to associate different meanings with that term, but I’ve also never seen an authoritative text that says a function needs be invoked outside the creating environment for it to be called a closure *shrug*. – Felix Kling Oct 17 '19 at 06:06
  • Fair. I'll agree with the shrug. At least Swift is more honest (and explicit) in the use of the term closure, defining the term as something much broader that a function (_any_ " self-contained blocks of functionality that can be passed around and used), and yes, even saying all functions are closures. Colloquially (and certainly not authoritatively) many people do reserve the term for functions with free variables intended to be used outside the environment they are defined in, and I think that's the spirit in which the OP asked, but yeah, all JS functions are closures too. Cheers. – Ray Toal Oct 17 '19 at 06:24
  • "this behavior comes from a pre-ECMAScript 2015, 'optional', and non-strict treatment of function declarations inside of blocks." So with this in mind, would a closure of this type be advised against? – Andy Tran Oct 17 '19 at 09:31
  • I would say yes. The fact that there are questions surrounding block-level function declarations, and that their semantics are not obvious, are good reasons, I think, to avoid them. – Ray Toal Oct 18 '19 at 05:41