Any function, when it's called, creates its own execution "context," which means it has its own memory space only for the life of that function. As soon as the function ends and returns something, that memory space is packed up and re-purposed. Any variables defined in the function are gone.
Well, usually. If a function returns another function, which is possible in JavaScript and other functional programming languages, that execution context is not closed. It remains available, but ONLY to the returned, "inner" function. In your example, the IIFE returns an inner function, which is assigned as the value of foo. foo, then, is a function, and can be called with foo().
x stays in memory, but in a completely different execution context from foo, which is weird and a bit of a mind bender. It's like foo is a portal into another dimension or something. Whenever foo() is called, the value of x is returned, AND x is incremented. So the function foo() becomes a counter. Each time you call it you'll get the next integer in the sequence 0, 1, 2, 3, ... etc.
So, specifically, the var x = 0; only runs once because the IIFE only runs once. It just never stops running. (Well, until foo is disposed of.)
Higher-order functions are a challenge to understand. Please let me know if you need more help with this subject.
Ian