munificent / craftinginterpreters

Repository for the book "Crafting Interpreters"
http://www.craftinginterpreters.com/
Other
8.87k stars 1.04k forks source link

A question about Chapter 11 / closures #1075

Closed tianz closed 2 years ago

tianz commented 2 years ago

I have a question about closures. The chapter 11 of the book says

If you’re familiar with closures in other languages, you’ll expect it to print “global” twice.

about this example:

var a = "global";
{
  fun showA() {
    print a;
  }

  showA();
  var a = "block";
  showA();
}

I'm not familiar with the concepts of closures at all. My question is in what other languages would print global twice? From what I've read, JavaScript has closures, but I'm struggling with getting this example to print global twice in JavaScript. This is what I tried:

a = "global";
function foo() {
  showA = function() {
    console.log(a);
  }

  showA();
  a = "block";
  showA();
}

foo();

It printed global and block. If I add var to both assignment of a, it will print undefined and block. Am I missing anything?

Thank you!

chrisjbreisch commented 2 years ago

Yes, you are missing something. You did not declare a new a between the showA() calls. You re-assigned the value of the existing a. That's the whole point of this exercise. Both showA() calls refer to the same global variable, not the local one. They refer to the only a that is defined when showA() is defined.

As for what you got when you added the var declarations, I couldn't say. That doesn't make any sense to me. But JS is an odd beast. I'm not sure exactly what var is supposed to do in JS.

ChayimFriedman2 commented 2 years ago

@chrisjbreisch This is because JavaScript hoists var to the start of the block, so a in the closure actually captures the inner a because it is declared before (hoisted), but before its actual declaration its value is undefined.

chrisjbreisch commented 2 years ago

@ChayimFriedman2 - thanks for the explanation. That makes sense. Well, actually it doesn't. Your explanation makes sense. But that behavior is utterly bizarre, IMO. Still, as I said, JS is an odd beast.

tianz commented 2 years ago

@chrisjbreisch @ChayimFriedman2 Thank you both for the explanation! Now I understand because how JS handles var it works differently from the book example.

tianz commented 2 years ago

I'm able to produce the same result as the book example with go:

package main

import "fmt"

var a = "global"

func main() {
    showA := func() {
        fmt.Println(a)
    }
    showA()
    var a = "block"
    _ = a
    showA()
}