Js中闭包的定义与理解

了解闭包之前,应该先了解js的作用域

作用域

1. 全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域,所有为定义但直接复制的变量也拥有全局作用域

2.函数作用域(局部)
Js中没有块及作用域,变量在当前函数体或当前函数嵌套的函数中都是有定义的


作用域链

先看段网上的代码和作者的解释:

name=”lwy”;
function t(){
var name=”tlwy”;
function s(){
var name=”slwy”;
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();

当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是”slwy”。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy”

这里引申出一个优化的问题,因为每次的活动对象都被推到链表的开头,而全局变量链在了最后面,每次调用全局变量都要从新创建并且从链表最上面检索,这就导致了每次使用全局变量,都要从头检索到最后面,大量的这种操作就会使程序效率低下。

1.使用局部变量
当一个函数中重复的使用了两次或两次以上的全局变量,将全局变量定义为局部变量
例如:

var doc = document;

2.推荐一片关于作用域和作用域链的文章
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

闭包

1.定义

官方对闭包的定义比较晦涩,在网上找了找有如下几种解释:

  • 闭包就是能够读取其他函数内部变量的函数。

    • 来自:
    • http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
  • 闭包就是一个函数把外部的那些不属于自己的对象也包含(闭合)进来了

    • 来自:
    • https://www.zhihu.com/question/34547104
  • 因此,无论通过何种手段,只要将内部函数传递到所在的词法作用域以外,它都会持有对原始作用域的引用,无论在何处执行这个函数都会使用闭包
    • 来自:
    • http://www.cnblogs.com/xiaohuochai/p/5728577.html

定义总结一下:闭包的定义很多,在一个嵌套函数中,函数内部可以引用外部的参数和变量,并且不被GC回收

2.实例

//外部function
function withoutA(x){
    var a = 3;
    return function(y){  // 内部返回的function
        a++;
        alert(x+y+a);
    }
}
var insideA = withoutA(2);  //被insideA接收
insideA(10);        //输出为16 
insideA(10);        //输出为17

调用withoutA(2)返回的function赋值给insideA

第一次执行insideA(10),x=2,y=10,a++后变为4,输出值为16

第二次执行时因为a还存在于insideA闭包的内部,并且经过第一次执行的++,值为4,执行第二次++后变为a=5,x=2 , y=10,输出值为17

Js里处理object是引用传递,调用widthout时函数return的闭包也会引用之前的object

// without函数return的闭包也会引用最初那个object
function without(x) {
    var tmp = 3;
    return function (y) {
        alert(x + y + tmp);
        x.xt= x.xt? x.xt+ 1 : 1;
        alert(x.xt);
    }
}
var tt= new Number(2);
var insideA= without(tt); // bar 现在是一个引用了tt的闭包
insideA(10);    //x.xt= undefind = 1
insideA(10);    //x.xt= xt+ 1 = 2  引用了同一个number对象

结束引用,避免大量累加后性能下降

function box(){
    var age = 100;
    return function(){

        age++;

        return age;
    };
}
var b = box();
alert(b());         //101
alert(b());         //102
alert(box()());     //101
alert(box()());     //101
b = null;       //解除引用

需要注意b()与box()(),虽然执行的都是return的function,但box()()并不能累加其中的变量

应用

一个例子:

<html>
<head>
    <script type="text/javascript">
        function buttonInit(){
            for(var i=1;i<4;i++){
                var b=document.getElementById("button"+i);
                b.addEventListener("click",function(){ alert("Button"+i);},false);
            }
        }
        window.onload=buttonInit;
    script>
head>
<body>
<button id="button1">Button1button>
<button id="button2">Button2button>
<button id="button3">Button3button>
body>
html>

Js中闭包的定义与理解_第1张图片

可以看到,因为循环后的 i 变成了4,所以不论点哪个都会提示Botton4,用闭包改造一下

<html>
<head>
    <script type="text/javascript">
            window.onload = function(){
                var a = document.getElementsByTagName('button');
                for (var i = 0; i < a.length; i++) {
                    (function (i) {
                        a[i].addEventListener("click"
                            ,function(){alert("Button" + (i+1))}
                            ,false)
                    })(i);
                }
            };
    script>
head>
<body>
<button id="button1">Button1button>
<button id="button2">Button2button>
<button id="button3">Button3button>
body>
html>

这样每次调用,i 的值都是当初的 i 值

Js中闭包的定义与理解_第2张图片

总结

简单的闭包并不难实现,但了解实现原理并不轻松,闭包的实际应用也要谨慎,如果运用不当很多地方都会出现莫名其妙的错误,大量的累加没有接触引用也会导致性能问题

附上个人觉得写得很好的博客地址:

http://kb.cnblogs.com/page/110782/

你可能感兴趣的:(javascript)