A "Tab" container is equated to a runspace (or powershell execution environment) in the ISE. Since you are creating a new Tab (i.e. powershell execution environment) the variable v is undefined in that execution environment. The scriptblock is evaluated in the new execution environment and outputs the value of v (nothing).
It's easy to see how variable resolutions differs in the case of Test-Scriptblock from the case of Start-NewTab if you try to get the variable in the scriptblock by explicitly mentioning the scope where the variable should be found.
PS>Test-ScriptBlock { get-variable v -scope 0}
Get-Variable : Cannot find a variable with name 'v'.
PS>Test-ScriptBlock { get-variable v -scope 1}
Get-Variable : Cannot find a variable with name 'v'.
PS>Test-ScriptBlock { get-variable v -scope 2} # Variable found in grandparent scope (global in the same execution environment)
Name Value
---- -----
v hello world
PS>Start-NewTab "Test" { get-variable v -scope 0 } # global scope of new execution environment
Get-Variable : Cannot find a variable with name 'v'.
PS>Start-NewTab "Test" { get-variable v -scope 1 } # proof that scope 0 = global scope
Get-Variable : The scope number '1' exceeds the number of active scopes.
One workaround for your problem is to define your variable in the scriptblock:
Start-NewTab "Test" { $v = "hello world";$v }
Edit: One more thing, your title mentions 'closure'. Scriptblocks in Powershell are not closures, however you can create a closure from a scriptblock. This won't help you with the problem you describe, though.
Edit2: Another workaround:
$v = "hello world"
Invoke-Expression "`$script = { '$v' }"
Start-NewTab "test" $script