JavaScript 闭包

去看Ruby元编程这本书... 第三章讲到这个概念, 不仅是闭包,还有其他概念例如作用域、环境等等解释的都很不错,可入手。

 

1.词法作用域: 简单地说子集能访问父级的变量, 说人话就是变量拿来就用不用传入

2.函数局部变量: 在函数体中以var 声明变量的为局部变量 + 函数传入的参数, 直接写变量名声明的变量是全局变量

3.局部变量生存期: 局部变量在函数函数的执行期间可用,  一旦执行过后,局部变量将不再可用

4.延长局部变量生存期: 现在问题来了,我想要延长局部变量的生存期,怎么办。(因为调用函数不仅仅是为了return, 有时候还需要保存函数中的状态, 或者实现类等等)

5.使用全局变量不好吗: 不好。有时函数内部成员不想被外部直接访问, 全局变量做不到;一般而言函数内部成员与外界成员如果重名也没什么关系, 但如果使用全局变量就要小心命名问题(还有很多问题)

6.闭包: 函数返回内部定义函数(的句柄) or 包含若干内部定义函数(句柄)的集合。说人话就是函数 return function(param){//code} 或者 return 一个array/dict,集合的值都是function(param){//code}(本来想贴代码来着但这个编辑器实在是太...),这些内部定义的function包含/使用了函数的局部变量。

7.所以闭包就是返回一个内部定义函数(句柄)or一个集合?: 可以这么理解,但是返回这个内部定义的函数(句柄) or 包含大量内部定义的函数(句柄)的array/dict的结果是维持了局部变量的生存期,也就是给局部变量续命。这个意义十分重大。

8.使用闭包: 调用函数→得到内部函数(句柄) or array/dict(包含大量内部函数句柄)→使用内部函数为局部变量续命

  片面的总结一下:闭包就是两层看成一个整体。外层的function可以看做Java的class, 内层的function可以看做是类的函数成员。Java中实例化一个class用new, JavaScript调用外层函数可以看做实例化。Java中调用公开的方法是obj.func, JavaScript外层函数如果直接return function(param){}那直接拿着当函数用, 如果返回array/dict用法和java很相似obj.func。每调一次外层函数相当于不同的实例,不同实例相互独立。这个理解片面一点在于循环,在后面我有解释。

9.几个注意事项

  (1)闭包很像一个2层俄罗斯套娃,就是一个函数必须套一个函数,外层函数返回内层函数,内层函数给外层函数的局部变量续命。如果包含若干内层函数, 则共享外层函数的局部变量。(一个函数f1被f2包裹(不管f2有没有被其他函数包裹, 被多少函数包裹),则f1可以享用f2的局部变量状态)

  (2)先看代码

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    document.getElementById(item.id).onfocus = function() {
       document.getElementById('help').innerHTML = helpText[i].help;
    }
  }
}

setupHelp();

  大意是通过循环设置onfocus事件, 设置的手段就是赋予匿名function, 偏偏不巧的是匿名function内部包含了关于循环变量 i 的内容,不过也不奇怪, 如果用不到 i 我还用循环作甚。问题在于这个i不会很听话的0 1 2逐一赋予,当循环跑完再调用setupHelp函数的时候, i变成2, 所有匿名function中的item.help指向最后一项。怎么才能让 i 维持状态。

  先分析这段代码。循环产生三个匿名function,这三个匿名function被setupHelp函数包裹,所以这三个function共享i变量,共享的意思是都是一个值。所以怎么解决i维持状态这个事呢?答案是再加一层function。内层的匿名function不管外层包裹多少function, 就是看最近包裹我的函数, 最近包裹我的函数传进来是几,我用了就能维持这个数据, 反正就是内层函数用了外层函数的局部变量维持了变量的空间。改动如下。

document.getElementById(item.id).onfocus = (function makecallback(i){
    return function(){
        document.getElementById('help').innerHTML = helpText[i].help;
    }
})(i)

  可能会想直接赋予一个匿名function和return一个匿名function有区别吗,其他语言可能没什么区别(不会碰到为了维持内部状态、让循环变量立刻生效必须闭包), 但是JavaScript就是这么蛋疼, 没有办法因为你没有选择。

  吐槽时间:知乎上有个问题问JavaScript闭包的, 很多答案真是...有股论文风。不得不说搞前端的就是洋气, 解释起问题来就是一套一套的, 不过这也很职业有关。前端让人爽是目的之一, 所以会装逼是必修课。你看叶老师,龚大低调得很(虽然龚大最近开始炫富【大雾】)答案除了幽默一下就是比较干货了。当然知乎还是有很多前辈的, 所以我明明是个后端为什么开始学JavaScript, 明明我只想实现一个背景轮播图而已啊... ...

主要资料来源:https://developer.mozilla.org/cn/docs/Web/JavaScript/Closures

posted @ 2017-04-23 22:59  Air_L  阅读(187)  评论(0编辑  收藏  举报