Javascript 笔记与总结(1-5)闭包

【例1】

<script>
function t1(){
    var age = 20;
    function t2(){
        alert(age);
    }
    return t2;
}
var tmp = t1();
var age = 99;
tmp();
</script>

弹出 20

【分析】

function t1(){
    var age = 20;
    function t2(){
        alert(age);
    }
    return t2;
}

在大部分的语言中,t1 被调用执行,则申请内存,并把其局部变量 push 入栈,t1 函数执行完毕,内部的局部变量,随着函数的退出而销毁,导致 age = 20 的局部变量消失。

因此,t1(); 执行完毕之后,按 C 语言的理解,t1 内的局部变量都释放了。

function t1(){
    var age = 20;
    function t2(){
        alert(age);
    }
return t2;
}

在 js 中,t1 执行过程中,又生成了 t2,

而从作用域上来说,t2 能访问到 age = 20,

于是 “ age = 20 ” 没有消失,而是被 t2 捕捉到了,

同时 “ age = 20 ” 与返回的 t1 函数形打了个包返回来了,成了一个 “ 环境包 ”,

这个包属于 t2,所以叫 “ 闭包 ”。

t2 把周围的环境打了包,也就是说 t2 是由自己的生态环境的,

即 即使t1 执行完毕,通过 t2 ,依然能访问该变量:这种情况,函数并非孤立的函数,甚至把其周围的变量环境,形成了一个封闭的环境包,共同返回

一句话概括:函数的作用域取决于声明时,而不取决于调用时

 

【例2】

 1 function prev(){
 2     var leg = "Alexis";
 3     var arsenal_leg = function(){
 4         return leg;
 5     } 
 6     return arsenal_leg; //前半赛季阿森纳的大腿是桑切斯
 7 }
 8 
 9 function after(){    //来到了后半赛季
10     var leg = "Giroud";
11     var arsenal_leg = prev();    //调用prev(),arsenal_leg 函数来到了后半赛季
12     alert(arsenal_leg());
13 }
14 after();    //谁是大腿
15 
16 </script>

弹出:Alexis

【分析】:

line:14 执行 after();

line:12 alert arsenal_leg();

line:11 aesenal_leg() = prev();

line:6  return  arsenal_leg();

line:3  函数声明 return leg;【真正执行的函数】

line:2  var leg = "Alexis";【作用域要从 函数声明之时寻找】

 

例3闭包计数器,多人开发 js 程序,需要一个全局的计数器

解决方案① 设立一个全局变量

window.cnt = 0;

调用 ++window.cnt

这个方案可行但是,污染了全局变量;其次比人的程序中,也可能会含有 window.cnt = ***; 则该计数器就会被损坏(所以要避免使用全局变量)

方案② 使用闭包维护一个别人污染不到的变量做技术器

<script>

function counter(){
    var cnt = 0;
    var cnter = function (){
        return ++cnt;
    }
    return cnter;
}

var inc = counter();
alert(inc());
alert(inc());
alert(inc());

</script>

弹出 3 次,分别是:1,2,3

简化上面的程序:

<script>

var inc = (function counter(){
    var cnt = 0;
    return function (){
        return ++cnt;
    }
})();

alert(inc());
alert(inc());
alert(inc());

</script>

再改版上面的程序(inc 依然是全局变量。在工作中,一般避免全局污染或冲突):

方案 ① 统一放在一个全局对象上,如 jQuery -> $

<script>

$ = {}; 
//模拟jQuery
$.cnt = (function(){
    var cnt = 0;
    return function cnter(){
        return ++cnt;
    }
})();

alert($.cnt());
alert($.cnt());
alert($.cnt());

</script>

方案 ② 每个人使用自己的命名空间(其实就是把自己的变量、函数都放在一个对象里)

<script>

var Dee = {}; //一般用姓名做命名空间
Dee.cnt = (function(){
    var cnt = 0;
    return function cnter(){
        return ++cnt;
    }
})();

alert(Dee.cnt());
alert(Dee.cnt());
alert(Dee.cnt());

</script>

 

【例4】要求:点击 4 个 li,分别弹出 0,1,2,3

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <ul>
        <li>阿森纳</li>
        <li>切尔西</li>
        <li>曼城</li>
        <li>利物浦</li>
    </ul>
</body>
<script>

for(var i = 0, lis = document.getElementsByTagName("li"), len = lis.length; i < len; i++){
    lis[i].onclick = (function(i){
        return function(){
            alert(i);
        }
    })(i);
}

</script>
</html>      

 【错误的写法】(会弹出 4,4,4,4):

<script>

for(var i = 0, lis = document.getElementsByTagName("li"), len = lis.length; i < len; i++){
    lis[i].onclick = function(){
        alert(i);
    }
}

</script>

【错误的写法分析】:

(回答者杨志):

" 这个for循环会立即执行完毕,那么当onclick触发时,inner function查找变量 i 时,会在AO+scope中找,AO中没有,scope中的变量i已经成为 link.length. ",

在运用了闭包之后,

" 这时,如果inner function被触发,他会从自己的AO以及scope(outer function的AO 和 window scope)中找寻变量i. 可以看到outer function的AO中已经包

含了i,而且对于这个for循环,会有对应有N个(function(){})() 被创建执行。所以每个inner function都有一个特定的包含了变量 i 的outer function。这样

就可以顺利输出0,1,2,3 。

结论: 我们可以看到,闭包其实就是因为Scope产生的,所以,广义上来讲,所有函数都是闭包。"

" 所谓的异步函数,是包括ajax,事件触发,setTimeout , setInterval等回调的函数。

当解析文档流时如果遇到js代码,会立即执行。如果这期间有异步函数被触发,会被存放在队列结构中。当之前的代码执行完毕后,浏览器查看此队列,如有待执行

函数,则执行;没有则等待触发。

所以,那个i 值会成为最大的那个。因为for循环在异步函数被执行前,已经跑完了。"

 

参考:关于 Js 循环添加事件时闭包的影响有哪些解法?

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。