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 循环添加事件时闭包的影响有哪些解法?

时间: 2024-11-06 14:02:23

Javascript 笔记与总结(1-5)闭包的相关文章

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

JavaScript笔记之Function

一.函数定义 (1)使用function declaration 格式:function functionName(parameters) { function body } 注:此种方式声明的函数作用域是全局的,即在声明之前可以调用 (2)使用function expression 格式:var name = function (parameters) { function body }; 注:与(1)不同,在声明之前不可以调用 (3)使用function constructor() 格式:v

javascript笔记(二)

concat() 连接多个字符串,返回合并后的字符串. 1 var s1="a"; 2 var s2="b"; 3 var s3="c"; 4 5 console.log(s1.concat(s2,s3));//abc concat() 方法的结果等同于:result = s1 + s2 + ... + sN.如果有不是字符串的参数,则它们在连接之前将首先被转换为字符串. 数组中的concat():将参数添加为数组的元素,返回新的数组. 1 va

JavaScript中的作用域链和闭包

JavaScript中的作用域链和闭包 2012-06-29 11:41 1878人阅读 评论(46) 收藏 举报 JavaScript中出现了一个以前没学过的概念——闭包.何为闭包?从表面理解即封闭的包,与作用域有关.所以,说闭包以前先说说作用域. 作用域(scope) 通常来说一段程序代码中使用的变量和函数并不总是可用的,限定其可用性的范围即作用域,作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突. 全局作用域(Global Scope) 在代码中任何地方都能访问到的对象拥

[Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof "s";//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.t

【译】学习JavaScript中提升、作用域、闭包的终极指南

这似乎令人惊讶,但在我看来,理解JavaScript语言最重要和最基本的概念是理解执行上下文.通过正确学习它,你将很好地学习更多高级主题,如提升,作用域链和闭包.考虑到这一点,究竟什么是"执行上下文"?为了更好地理解它,我们首先来看看我们如何编写软件. 编写软件的一种策略是将代码分解为单独的部分.虽然这些"部分"有许多不同的名称(功能,模块,包等),但它们都是为了一个目的而存在 - 分解和管理应用程序的复杂性.现在,不要像编写代码的人那样思考,而是根据JavaScr

[Effective JavaScript 笔记] 第11条:熟练掌握闭包

理解闭包三个基本的事实 第一个事实:js允许你引用在当前函数以外定义的变量. function makeSandwich(){ var magicIngredient=”peanut butter”; function make(filling){ return magicIngredient+’and ’+filling; } return make(‘jelly’); } makeSandwich();//”peanut butter and jelly” 图上直接指出如下 第二个事实:即使

JavaScript笔记杂谈篇(啥都有)

二维码缩放比例以43PX的倍数缩放最为标准. NuGet相关管理http://www.cnblogs.com/dudu/archive/2011/07/15/nuget.html 学习笔记: http://kb.cnblogs.com/page/143190/ 动态创建标记,给标记添加样式,class,等等   vardtProductName=$("<dt></dt>",{style:"text-overflow: ellipsis;overflow