Ian*_*vis 11 powershell lambda closures scope function
PowerShell ScriptBlock不是词法闭包,因为它不会关闭其声明环境中引用的变量.相反,似乎利用动态范围和自由变量,这些变量在运行时绑定在lambda表达式中.
function Get-Block {
$b = "PowerShell"
$value = {"Hello $b"}
return $value
}
$block = Get-Block
& $block
# Hello
# PowerShell is not written as it is not defined in the scope
# in which the block was executed.
function foo {
$value = 5
function bar {
return $value
}
return bar
}
foo
# 5
# 5 is written $value existed during the evaluation of the bar function
# it is my understanding that a function is a named scriptblock
# which is also registered to function:
Run Code Online (Sandbox Code Playgroud)
在ScriptBlock上调用GetNewClosure()会返回一个新的ScriptBlock,它会关闭引用的变量.但这在范围和能力上非常有限.
什么是ScriptBlock的分类?
根据文档,scriptblock是"预编译的脚本文本块".因此,默认情况下,您只需要预先解析的脚本块,不多也不少.执行它会创建一个子范围,但除此之外,就像您将代码内联粘贴一样.所以最合适的术语就是"只读源代码".
GetNewClosure在动态生成的模块上调用螺栓,该模块基本上在调用时携带调用者范围内所有变量的快照GetNewClosure.它不是真正的闭包,只是变量的快照副本.scriptblock本身仍然只是源代码,并且在调用之前不会发生变量绑定.您可以根据需要在附加的模块中添加/删除/编辑变量.
function GetSB
{
$funcVar = 'initial copy'
{"FuncVar is $funcVar"}.GetNewClosure()
$funcVar = 'updated value' # no effect, snapshot is taken when GetNewClosure is called
}
$sb = GetSB
& $sb # FuncVar is initial copy
$funcVar = 'outside'
& $sb # FuncVar is initial copy
$sb.Module.SessionState.PSVariable.Remove('funcVar')
& $sb # FuncVar is outside
Run Code Online (Sandbox Code Playgroud)
一个PowerShell脚本块相当于一个一流的,匿名函数.我见过的大多数混淆不是使用ScriptBlocks,而是使用function关键字.
例子
功能:
PS> function Hello {
>> param ([string] $thing)
>>
>> return ("Hello " + $thing)
>> }
PS> Hello "World"
"Hello World"
Run Code Online (Sandbox Code Playgroud)
脚本块:
PS> $HelloSB = {
>> param ([string] $thing)
>>
>> return ("Hello " + $thing)
>> }
PS> & $HelloSB "World"
"Hello World"
PS> $HelloRef = $HelloSB
PS> & $HelloRef "Universe"
"Hello Universe"
Run Code Online (Sandbox Code Playgroud)
关闭:
PS> $Greeter = {
>> param ([string] $Greeting)
>>
>> return ( {
>> param ([string] $thing)
>>
>> return ($Greeting + " " + $thing)
>> }.GetNewClosure() )
>> }
PS> $Ahoy = (& $Greeter "Ahoy")
PS> & $Ahoy "World"
"Ahoy World"
PS> $Hola = (& $Greeter "Hola")
PS> & $Hola "Mundo"
"Hola Mundo"
Run Code Online (Sandbox Code Playgroud)
虽然您可以使用"Set-Item"cmdlet 解决function关键字的限制:
PS> function Greeter = { ... } # ? Error
PS> function Greeter { ... }.GetNewClosure() # ? Error
PS> Set-Item -Path "Function:Greeter" -Value $Greeter # (defined above) ? OK
PS> $Hola = Greeter "Hola"
PS> & $Hola "Mundo"
"Hola Mundo"
Run Code Online (Sandbox Code Playgroud)
"Set-Item"cmdlet 的Value参数可以是任何ScriptBlock,甚至是另一个函数返回的ScriptBlock.(例如,"Greeter"函数返回一个闭包,如上所示.)
PS> Set-Item -Path "Function:Aloha" -Value (Greeter "Aloha")
PS> Aloha "World"
"Aloha World"
Run Code Online (Sandbox Code Playgroud)
另外两个要点:
PowerShell使用动态范围,而不是词法范围.
甲词法闭合锁合在其源代码的环境中,而一个动态闭合是基于当GetNewClosure()被调用时存在的活性/动态环境封闭.(哪种更适合脚本语言.)
PowerShell可能有"函数"和"返回"语句,但实际上它的输入/输出基于流和管道.将返回使用"写入输出"或"写入"cmdlet从ScriptBlock写出的任何内容.