Bodies to PowerShell keywords (for, foreach, if, switch, etc) execute out in the scope of wherever that keyword is used, as you've discovered. However, ScriptBlocks have their own scope unless they're dot-sourced. If you wanted to force a foreach loop to have its own scope for variable assignment, you could do something like this:
Sigh… forum software is being a bit of a pain due to ampersand characters being treated specially. See attachment instead.
(Alternatively, you could just put the body of the loop into a function and give it a descriptive name. That might be more readable anyway, and functions already have their own script blocks / scopes).