函数闭包,变量作用域与闭包
分类:计算机编程

函数闭包

什么是闭包?

  • 内部函数外部函数作用域里对象的引用(非全局变量),则称内部函数为闭包
  • 一个闭包就是你调用了外部函数,外部函数返回内部函数,此时的内部函数就叫做闭包函数
  • 闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例

闭包简单示例:

1
2
3
4
5
6
7
8
def wai():
 a = 1
 def nei():#闭包
  print(a)
 return nei

func = wai()
func()

通过这个例子大家可以看到,在外部函数中,本应该在 style="color: #6495ed">wai函数执行完死掉的变量 style="color: #6495ed">a,由于此时有了 内部函数的引用,从而使得这个变量并不会死去,而是类似于继承一样,通过 style="color: #6495ed">nei函数又存活了下来


接着让我们愉快的看下一个例子,继续理解闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
num  =  1 #全局变量num
def func():
 a = 1
 def func1():
  global num #这里内部函数 使用全局变量num
  num  = a #并且在全局num函数的基础上 每次 a
  print(num)
 return func1

res = func() #返回值为闭包函数
res() #第一次调用闭包函数
res() #那么这肯定是第二次调用闭包函数
res() #我这里注释第三次,没人有歧义吧

三次闭包函数的执行结果,我们来看一下吧

1
2
3
2
3
4

分析一下,这里出现的结果
首先三次值都是在全局变量 style="color: #6495ed">num的基础上做累加  style="color: #6495ed">a 的操作
说明在闭包函数对象 style="color: #6495ed">res存活期间, style="color: #6495ed">a变量将会一直存活


最后我们以将一个可变数据对象作为闭包引用的变量为例

1
2
3
4
5
6
7
8
9
10
def func(obj): #接收可变数据对象作为参数
 def func1():
  obj[0]  = 1 #内部将可变数据对象的第一个位置值  = 1
  print(obj) #打印加完之后的可变数据对象
 return func1
mylist = [1,2,3,4,5]
res = func(mylist)
res()
res()
res()

执行的结果

1
2
3
4
[1, 2, 3, 4, 5]
[2, 2, 3, 4, 5]
[3, 2, 3, 4, 5]
[4, 2, 3, 4, 5]

闭包思考

  • 闭包私有化了变量,实现了类似于面向对象中实例的功能
  • 由于闭包引用了外部函数的局部变量,则外部函数中的局部变量没有及时释放,消耗内存
  • 在 style="color: #6495ed">python中,使用闭包的另一个场景就是装饰器,也叫语法糖  style="color: #6495ed">@

实现闭包

  • 函数嵌套
  • 内部函数对外部函数作用域里对象的引用
  • 外部函数返回内部函数对象

转自 http://www.cnblogs.com/BeginMan/p/3179040.html

什么是闭包?
百度的答案:

style="font-family: arial, 宋体, sans-serif; text-indent: 28px"> style="font-family: arial, 宋体, sans-serif; text-indent: 28px">闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。


 
     style="font-weight: bold; color: #333333; font-family: arial, 宋体, sans-serif; text-indent: 2em">什么是闭包

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

相信很少有人能直接看懂这句话,因为他描述的太学术。我想用如何在Javascript中创建一个闭包来告诉你什么是闭包,因为跳过闭包的创建过程直接理解闭包的定义是非常困难的。看下面这段

代码

1
2
3
4
5
6
7
8
9
function a(){
var i=0;
function b(){
alert( i);
}
return b;
}
var c=a();
c();

特点

这段代码有两个特点:

1、函数b嵌套在函数a内部;

2、函数a返回函数b。

这样在执行完var c=a( )后,变量c实际上是指向了函数b,再执行c( )后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:

当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

作用

简而言之,闭包的作用就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。

在上面的例子中,由于闭包的存在使得函数a返回后,a中的i始终存在,这样每次执行c(),i都是自加1后alert出i的值。

style="color: #333333; font-family: arial, 宋体, sans-serif; text-indent: 2em">那 么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引 用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC style="color: #333333; font-family: arial, 宋体, sans-serif; text-indent: 2em">回收。 

另一个例子

模拟私有变量

function Counter(start){

var count = start;
  return{
  increment:function(){
  count ;
  },
  get:function(){
  return count;
  }
  }
  }
  var foo =Counter(4);
  foo.increment();
  foo.get();// 5

结果

style="color: #333333; font-family: arial, 宋体, sans-serif; text-indent: 2em">这里,Counter 函数返回两个闭包,函数 increment 和函数 get。 这两个函数都维持着 对外部作用域 Counter 的引用,因此总可以访问此作用域内定义的变量 count. 

style="font-family: arial, 宋体, sans-serif; text-indent: 28px"> style="font-family: arial, 宋体, sans-serif; text-indent: 28px"> style="font-family: arial, 宋体, sans-serif; text-indent: 28px"> 

 

下面是论坛里的探讨: 

一、全局变量与局部变量

  当function里嵌套function时,内部的function可以访问外部function里的变量。

一个模块中,最高级别的变量有全局作用域。

function foo(x) {
    var tmp = 3;
    function bar(y) {
        alert(x   y   ( tmp));
    }
    bar(10);
}
foo(2)

全局变量一个特征就是:除非被删除,否则他们存活到脚本运行结束,且对于所有的函数都可访问。

  不管执行多少次,都会alert 16,因为bar能访问foo的参数x,也能访问foo的变量tmp。

当搜索一个标识符(也称变量、名字等),Python是先从局部作用域开始搜索,如果局部作用域内没有找到那个名字,那么就在全局域找,如果还没有则抛出一个NameError异常。

  但,这还不是闭包。当你return的是内部function时,就是一个闭包。内部function会close-over外部function的变量直到内部function结束。

一个变量的作用域和它寄存的名称空间相关,注意,局部变量会覆盖全局变量。如:

function foo(x) {

bar = 10
def foo():
    bar = 100
    return bar

print foo() #100

    var tmp = 3;
    return function (y) {
        alert(x   y   ( tmp));
    }
}
var bar = foo(2); // bar 现在是一个闭包
bar(10);

global语句:

  上面的脚本最终也会alert 16,因为虽然bar不直接处于foo的内部作用域,但bar还是能访问x和tmp。

为了明确地引用一个已命名的全局变量,必须使用global语句,语法如下:

  但是,由于tmp仍存在于bar闭包的内部,所以它还是会自加1,而且你每次调用bar时它都会自加1.

global var1[,val2...]

  (我们其实可以建立不止一个闭包方法,比如return它们的数组,也可以把它们设置为全局变量。它们全都指向相同的x和相同的tmp,而不是各自有一份副本。)

bar = 10
def foo():
    global bar
    return bar

bar = 10000
print foo() #10000

    

下面的例子更为详细:

  上面的x是一个字面值(值传递),和JS里其他的字面值一样,当调用foo时,实参x的值被复制了一份,复制的那一份作为了foo的参数x。

bar = 10            #声明一个全局变量bar
def foo():
    global bar      #指定bar是全局变量,谁都不能替换!但是能修改原值!
    bar2 = 0.1      #声明一个局部变量bar2
    bar = 0.0001    #是对全局变量bar的修改,而不是在又声明一个局部变量将全局变量覆盖。
    return bar bar2

bar = 10000         #又对全局变量bar进行修改,但是为时已晚了,因为在foo函数里已经 return bar bar2了
print foo()         #综上,输出:0.1001

  那么问题来了,JS里处理object时是用到引用传递的,那么,你调用foo时传递一个object,foo函数return的闭包也会引用最初那个object!

嵌套函数作用域:

function foo(x) {
var tmp = 3;
return function (y) {
    alert(x   y   tmp);
    x.memb = x.memb ? x.memb   1 : 1;  //这里三元运算符?优先等级大于赋值运算符=
    alert(x.memb);
    }
}
var age = new Number(2);
var bar = foo(age); // bar 现在是一个引用了age的闭包
bar(10);

def foo():
    m = 1
    def bar():
        n = 2
        return m n
#    m  =n   #NameError: global name 'n' is not defined
    return bar()

print foo() #3

  不出我们意料,每次运行bar(10),x.memb都会自加1。但需要注意的是x每次都指向同一个object变量——age,运行两次bar(10)后,age.memb会变成2.

二、闭包

  这和HTML对象的内存泄漏有关,呃,不过貌似超出了答题的范围。

如果在一个内部函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

  JohnMerlino 对Ali说:

定义在外部函数内但由内部函数引用或者使用的变量称为自由变量

  这里有一个不用return关键字的闭包例子:

style="color: #ff0000">闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式 style="color: #ff0000">组织代码,并提高代码的 style="color: #ff0000">可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。

style="font-family: courier new, courier">不同的语言实现闭包的方式不同。Python以 style="color: #ff0000">函数对象为基础,为闭包这一语法结构提供支持的 (我们在特殊方法与多范式 style="font-family: courier new, courier">中,已经多次看到Python使用对象来实现一些特殊的语法)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象 style="font-family: courier new, courier">中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。

style="font-family: courier new, courier">参考:

function closureExample(objID, text, timedelay) { 
    setTimeout(function() { 
        document.getElementById(objID).innerHTML = text; 
    }, timedelay); 

closureExample(‘myDiv’, ‘Closure is created’, 500);

如实现一个计数的闭包例子:

John Pick这样回答:

 

  JS里的function能访问它们的:

def counter(start_at=0):
    count = [start_at]
    #内部函数incr()实现计数
    def incr():         #定义一个内部函数,在内部使用变量count,创建一个闭包
        count[0] =1
        return count[0]
    return incr         #返回一个可调用的函数对象

#看起来和实例化一个counter对象并执行这个实例是多么相似
count = counter(5)
for i in range(10):
    print count(),  #6 7 8 9 10 11 12 13 14 15

count = counter(100)
print count()       #101

  1. 参数

 

  2. 局部变量或函数

闭包的作用

  3. 外部变量(环境变量?),包括

图片 1

3.1 全局变量,包括DOM。

3.2 外部函数的变量或函数。

推荐阅读:Python深入04 闭包

  如果一个函数访问了它的外部变量,那么它就是一个闭包。

  注意,外部函数不是必需的。通过访问外部变量,一个闭包可以维持(keep alive)这些变量。在内部函数和外部函数的例子中,外部函数可以创建局部变量,并且最终退出;但是,如果任何一个或多个内部函数在它退出后却没有退出,那么内部函数就维持了外部函数的局部数据。

  一个典型的例子就是全局变量的使用。

  mykhal这样回答:

  Wikipedia对闭包的定义是这样的:

In computer science, a closure is a function together with a referencing environment for the nonlocal names (free variables) of that function.

  从技术上来讲,在JS中,每个function都是闭包,因为它总是能访问在它外部定义的数据。

  Since scope-defining construction in Javascript is a function, not a code block like in many other languages, what we usually mean by closure in Javascript is a fuction working with nonlocal variables defined in already executed surrounding function.

  闭包经常用于创建含有隐藏数据的函数(但并不总是这样)。

var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
    if (val === undefined) { return data[key] } // get
    else { return data[key] = val } // set
    }
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();

db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员

  看了这么多外国大牛的解答,不知道你懂还是不懂,反正我是懂了。

  P.S. 发布文章之后看到@xiaotie的一篇文章,觉得有必要推荐一下,因为其剖析得更为深入。有人说应该在文章结尾对闭包进行总结,可惜小弟才疏学浅,不能给出一个精辟的总结。

  @xiaotie对闭包的总结如下:

style="margin: 0px; padding: 0px; font-family: 微软雅黑">(1)闭包是一种设计原则,它通过分析上下文,来简化用户的调用,让用户在不知晓的情况下,达到他的目的;

style="margin: 0px; padding: 0px; font-family: 微软雅黑">(2)网上主流的对闭包剖析的文章实际上是和闭包原则反向而驰的,如果需要知道闭包细节才能用好的话,这个闭包是设计失败的;

style="margin: 0px; padding: 0px; font-family: 微软雅黑">(3)尽量少学习。

本文由pc28.am发布于计算机编程,转载请注明出处:函数闭包,变量作用域与闭包

上一篇:Ajax结果提取,python爬虫入门 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • Python面向对象编程思想
    Python面向对象编程思想
    Python中的类(一) 1.面向过程编程:计算机通过一系列指令来一步一步完成任务。 面向对象编程——Object OrientedProgramming,简称OOP,是一种程序设计思想。
  • Pycharm的安装和使用,Adelaide装修网深入分析厨房
    Pycharm的安装和使用,Adelaide装修网深入分析厨房
      MapServer linux上服务安装 关于厨房中水管的安装常见的就是下水管的安装,对于下水管的安装可能很多人都不知道该如何安装,青岛装修网资深装修达人说
  • 电子商务货品库的成品设计,PHP数组内容不重复
    电子商务货品库的成品设计,PHP数组内容不重复
    多年来在做ecshop的货物仓库储存模块,分别给黄金年代款商品的两性情格组合设置仓库储存,如下图: # 手艺文书档案 每一天逛天猫和京东的时候,映着重
  • 九彩拼盘的前端技能,LayUI框架的应用
    九彩拼盘的前端技能,LayUI框架的应用
    内容: HTML 普及标签和总体性 文书档案类型申明 转义字符 网页访问无障碍(只是掌握卡塔 尔(阿拉伯语:قطر‎ CSS 常用采取器 体制生效准绳(浏览器的
  • 编制程序总计,动态目的
    编制程序总计,动态目的
    dynamic是FrameWork4.0的新特色。dynamic的现身让C#具备了弱语言类型的风味。编写翻译器在编写翻译的时候不再对项目举行检查,编译期暗中同意dynamic对象扶植