解释从 PowerShell 闭包调用的函数的作用域
Explain scoping for functions called from PowerShell closures
以下 PowerShell 代码显示了从闭包调用的函数的意外作用域行为。您能解释一下这是 "by design" 还是缺陷吗?
function runblock($block) {
$x = 4
& $block
}
function printx() {
" in printx: x=" + $x
}
"PSVersion $($PSVersionTable.PSVersion)"
$x = 1
$b = {"In block x=" + $x ; $x = 3 ; printx}
$x = 2
runblock $b
$x = 1
$b = {"In closure x=" + $x ; $x = 3 ; printx}.GetNewClosure()
$x = 2
runblock $b
上面的输出是
PSVersion 3.0
In block x=4
in printx: x=3
In closure x=1
in printx: x=4
大部分输出对我来说都有意义:
脚本块输出 In block x=4
因为它的父作用域是 runblock
函数。 printx
函数输出 x=3
因为它的父作用域是脚本块作用域。
闭包输出 In closure x=1
因为 $x
的值被 GetNewClosure
调用捕获。一切如预期。
但是:
从闭包中调用 printx
输出 in printx: x=4
。因此 printx
在其中执行的范围不受 $x = 3
.
闭包范围的影响
从普通脚本块调用的函数确实看到脚本块范围内的变量,但从闭包调用的函数看不到闭包中的变量,这对我来说似乎很奇怪。
考虑以下代码:
function Run {
param($ScriptBlock)
$a = 3
# Picture 4
& $ScriptBlock
}
function Print {
# Picture 6
"Print `$a=$a"
"Print `$b=$b"
}
$a = 1
$b = 1
# Picture 1
$SB = {
# Picture 5
"Closure `$a=$a"
"Closure `$b=$b"
Print
}.GetNewClosure()
# Picture 2
$a = 2
$b = 2
# Picture 3
Run $SB
它打印:
Closure $a=1
Closure $b=1
Print $a=3
Print $b=2
图1:你从全局作用域开始,定义了一些变量。
图2: GetNewClosure()
新建模块,复制变量。 (红色箭头表示父作用域关系)
图 3: 你改变了变量的值。模块作用域不受影响。
图片 4: 函数 Run
已调用。它创建局部变量 $a
。 (蓝色箭头表示调用方向)
图片 5: $SB
调用了脚本块。绑定到模块会话状态的脚本块,所以你转移到它。
图片 6: 函数 Print
已调用。绑定到全局会话状态的函数,因此您 return 它。
以下 PowerShell 代码显示了从闭包调用的函数的意外作用域行为。您能解释一下这是 "by design" 还是缺陷吗?
function runblock($block) {
$x = 4
& $block
}
function printx() {
" in printx: x=" + $x
}
"PSVersion $($PSVersionTable.PSVersion)"
$x = 1
$b = {"In block x=" + $x ; $x = 3 ; printx}
$x = 2
runblock $b
$x = 1
$b = {"In closure x=" + $x ; $x = 3 ; printx}.GetNewClosure()
$x = 2
runblock $b
上面的输出是
PSVersion 3.0
In block x=4
in printx: x=3
In closure x=1
in printx: x=4
大部分输出对我来说都有意义:
脚本块输出 In block x=4
因为它的父作用域是 runblock
函数。 printx
函数输出 x=3
因为它的父作用域是脚本块作用域。
闭包输出 In closure x=1
因为 $x
的值被 GetNewClosure
调用捕获。一切如预期。
但是:
从闭包中调用 printx
输出 in printx: x=4
。因此 printx
在其中执行的范围不受 $x = 3
.
从普通脚本块调用的函数确实看到脚本块范围内的变量,但从闭包调用的函数看不到闭包中的变量,这对我来说似乎很奇怪。
考虑以下代码:
function Run {
param($ScriptBlock)
$a = 3
# Picture 4
& $ScriptBlock
}
function Print {
# Picture 6
"Print `$a=$a"
"Print `$b=$b"
}
$a = 1
$b = 1
# Picture 1
$SB = {
# Picture 5
"Closure `$a=$a"
"Closure `$b=$b"
Print
}.GetNewClosure()
# Picture 2
$a = 2
$b = 2
# Picture 3
Run $SB
它打印:
Closure $a=1
Closure $b=1
Print $a=3
Print $b=2
图1:你从全局作用域开始,定义了一些变量。
图2: GetNewClosure()
新建模块,复制变量。 (红色箭头表示父作用域关系)
图 3: 你改变了变量的值。模块作用域不受影响。
图片 4: 函数 Run
已调用。它创建局部变量 $a
。 (蓝色箭头表示调用方向)
图片 5: $SB
调用了脚本块。绑定到模块会话状态的脚本块,所以你转移到它。
图片 6: 函数 Print
已调用。绑定到全局会话状态的函数,因此您 return 它。