var add_the_handlers = function(nodes){ var i; for (i = 0; i < nodes.length; i += 1){ nodes[i].onclick = function(e){ alert(i); } } };//糟糕的代码
我们先看这个糟糕的列子!这是javascript精粹上面的一个例子,一开始很是迷惑找了半天也找不到答案,最后自己啃书算是明白了一点(只自己明白了,不知道对否。)
先说几个名词:变量对象,作用域,作用域链,函数执行环境
再说几个和变量有关的知识:变量值分为基本类型值和引用类型值,而基本类型的值的复制是直接拷贝副本,而引用类型只是拷贝地址。但是在给函数传参时,全部是按值传递的,就像基本类型值变量的复制一样。
我们来说列子。
当调用这个add_the_handlers函数时,把节点数组传递到这个函数执行环境中,这时的执行环境中有两个变量一个是I,另一个则是nodes.length,而这两个变量当add_the_handlers这个函数执行完毕,而且也没有函数或者其他变量引用这个两个值,他就会消失(这里是作用域的概念)。
但是当进入for循环后,当I为0的时间给节点0绑定上这个事件函数,也就是function(e){ alert(i); }这段代码,这里你要注意哦!这段代码没有人去调用,所以他不会执行,直到你触发这个节点为0的事件后这段代码才调用。所以当I值为1的时间同样给节点为1的绑定上一样的函数。直到节点数组的值绑定完。
这时这个add_the_handlers函数执行环境中的变量I值就变成了节点数目的值。但当我们去触发所有这些节点事件时,开始调用绑定的事件函数,而这个事件函数的执行环境中压根没有这个I变量值,他该怎么办,他根据作用域链的关系只有往上一级的执行环境寻找这个I的值。所以对于上级的执行环境也就是
add_the_handlers这个环境中的I值,有人在引用着他所以这个函数执行完毕I值不能消失而节点数可以消失,但是不能消失他保存在哪里了呢?实际上市保存在变量对象中(这个东西看不到,也不能调用),而这时的I值就是循环后的结果了,也就是节点数,所以当你不管点击那个节点弹出来的都是总节点的数目(也就是nodes.length的值)
经过改良的列子
var add_the = function (nodes){ var helper = function (i){ return function (e){ alert(i); } } var i; for(i = 0; i < nodes.length; i +=1){ nodes[i].onclick = helper(i); } }
当调用函数时,初始I值,给第0个节点绑定上这个函数helper(i),而这个I值根据函数传参的原理,会有一个同样的I值进入到helper这个执行环境中,而这个环境中返回出来的一个函数,在引用着helper这个函数执行环境中的变量I(因为返回出来的函数他没有I那个变量,只能往上级执行环境查找这个值),所以这个I值不会消失。其他同理
总结:
当一个函数内部被返回出来的函数,可以访问到他外部的函数执行环境中的变量或者对象,也就是说可以访问到他被创建时间的上下文环境,这就被称为闭包!我们这个例子中第二个例子helper()被称为闭包
闭包的特性:
1,闭包外层是个函数.
2,闭包内部都有函数.
3,闭包会return内部函数.
4,闭包返回的函数内部不能有return.(因为这样就真的结束了)
5,执行闭包后,闭包内部变量会存在,而闭包内部函数的内部变量不会存在.
闭包的应用场景
1、保护函数内的变量安全。
2、在内存中维持一个变量。