什么是闭包

        ----本文摘自programming in  lua

假设在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被觉得是闭包(closure)

当一个函数内部嵌套还有一个函数定义时,内部的函数体能够訪问外部的函数的局部变量,这样的特征我们称作词法定界。尽管这看起来非常清楚,事实并不是如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,非常少语言提供这样的支持。 

      以下看一个简单的样例,假定有一个学生姓名的列表和一个学生名和成绩相应的表,如今想依据学生的成绩从高到低对学生进行排序,能够这样做: 
names = {"Peter", "Paul", "Mary"} 
grades = {Mary = 10, Paul = 7, Peter = 8} 
table.sort(names, function (n1, n2) 
  return grades[n1] > grades[n2]    -- compare the grades 
end) 

假定创建一个函数实现此功能: 
function sortbygrade (names, grades) 
  table.sort(names, function (n1, n2) 
    return grades[n1] > grades[n2]    -- compare the grades 
  end) 
end 

样例中包括在 sortbygrade 函数内部的 sort 中的匿名函数能够訪问 sortbygrade 的參数grades,在匿名函数内部 grades 不是全局变量也不是局部变量,我们称作外部的局部变量(external local variable)或者 upvalue。(upvalue 意思有些误导,然而在 Lua 中他的存在有历史的根源,还有他比起 external local variable 简短)。 


看以下的代码: 

function newCounter() 
  local i = 0 
  return function()     -- anonymous function 
    i = i + 1 
    return i 
  end 
end 

c1 = newCounter() 
print(c1())  --> 1 
print(c1())  --> 2 


匿名函数使用 upvalue i 保存他的计数,当我们调用匿名函数的时候 i 已经超出了作用范围,由于创建 i 的函数 newCounter 已经返回了。然而 Lua 用闭包的思想正确处理了这样的情况。简单的说闭包是一个函数加上它能够正确訪问的 upvalues。假设我们再次调用 newCounter,将创建一个新的局部变量 i,因此我们得到了一个作用在新的变量 i 上的新闭包。 

c2 = newCounter() 
print(c2())  --> 1 
print(c1())  --> 3 
print(c2())  --> 2 

c1、c2 是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。 技术上来讲,闭包指值而不是指函数,函数不过闭包的一个原型声明;虽然如此,在不会导致混淆的情况下我们继续使用术语函数代指闭包。 闭包在上下文环境中提供非常实用的功能,如前面我们见到的能够作为高级函数(sort)的參数;作为函数嵌套的函数(newCounter)。这一机制使得我们能够在 Lua 的函数世界里组合出奇幻的编程技术。闭包也可用在回调函数中,比方在 GUI 环境中你须要创建一系列 button,但用户按下 button 时回调函数被调用,可能不同的button被按下时须要处理的任务有点差别。详细来讲,一个十进制计算器须要 10 个相似的button,每一个button相应一个数字,能够使用以下的函数创建他们: 

function digitButton (digit) 
  return Button{  label = digit, 
      action = function () 
        add_to_display(digit) 
      end 
  } 
end 


这个样例中我们假定 Button 是一个用来创建新button的工具,  label 是button的标签,action 是button被按下时调用的回调函数。(实际上是一个闭包,由于他訪问 upvalue digit)。digitButton 完毕任务返回后,局部变量 digit 超出范围,回调函数仍然能够被调用而且能够訪问局部变量 digit。 闭包在全然不同的上下文中也是非常实用途的。由于函数被存储在普通的变量内我们能够非常方便的重定义或者提前定义函数。通常当你须要原始函数有一个新的实现时能够重定义函数。比如你能够重定义 sin 使其接受一个度数而不是弧度作为參数: 
oldSin = math.sin 
math.sin = function (x) 
  return oldSin(x*math.pi/180) 
end 


更清楚的方式: 

do 
  local oldSin = math.sin 
  local k = math.pi/180 
  math.sin = function (x) 
    return oldSin(x*k) 
  end 
end 


这样我们把原始版本号放在一个局部变量内,訪问 sin 的唯一方式是通过新版本号的函数。 利用相同的特征我们能够创建一个安全的环境(也称作沙箱,和 java 里的沙箱一样),当我们执行一段不信任的代码(比方我们执行网络server上获取的代码)时安全的环境是须要的,比方我们能够使用闭包重定义 io 库的 open 函数来限制程序打开的文件。 
do 
  local oldOpen = io.open 
  io.open = function (filename, mode) 
    if access_OK(filename, mode) then 
      return oldOpen(filename, mode) 
    else 
      return nil, "access denied" 
    end 
  end 
end 

原文地址:https://www.cnblogs.com/blfshiye/p/4279865.html