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 = ;
function t2(){
alert(age);
}
return t2;
}
在大部分的语言中,t1 被调用执行,则申请内存,并把其局部变量 push 入栈,t1 函数执行完毕,内部的局部变量,随着函数的退出而销毁,导致 age = 20 的局部变量消失。
因此,t1(); 执行完毕之后,按 C 语言的理解,t1 内的局部变量都释放了。
function t1(){
var age = ;
function t2(){
alert(age);
}
return t2;
}
在 js 中,t1 执行过程中,又生成了 t2,
而从作用域上来说,t2 能访问到 age = 20,
于是 “ age = 20 ” 没有消失,而是被 t2 捕捉到了,
同时 “ age = 20 ” 与返回的 t1 函数形打了个包返回来了,成了一个 “ 环境包 ”,
这个包属于 t2,所以叫 “ 闭包 ”。
t2 把周围的环境打了包,也就是说 t2 是由自己的生态环境的,
即 即使t1 执行完毕,通过 t2 ,依然能访问该变量:这种情况,函数并非孤立的函数,甚至把其周围的变量环境,形成了一个封闭的环境包,共同返回。
一句话概括:函数的作用域取决于声明时,而不取决于调用时。
【例2】
function prev(){
var leg = "Alexis";
var arsenal_leg = function(){
return leg;
}
return arsenal_leg; //前半赛季阿森纳的大腿是桑切斯
} function after(){ //来到了后半赛季
var leg = "Giroud";
var arsenal_leg = prev(); //调用prev(),arsenal_leg 函数来到了后半赛季
alert(arsenal_leg());
}
after(); //谁是大腿 </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循环在异步函数被执行前,已经跑完了。"
Javascript 笔记与总结(1-5)闭包的更多相关文章
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- Jquery和Javascript 实际项目中写法基础-闭包 (2)
一.什么是闭包? 概念性的我就不去百度了,感兴趣的可以自己去搜下,我自己的理解,闭包就是一个封装的包,相当于类的概念,把乱七八糟的的东西封装到一起,然后统一使用一个对象来调用,实现代码部分对外开放,部 ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- 从头开始学JavaScript 笔记(一)——基础中的基础
原文:从头开始学JavaScript 笔记(一)--基础中的基础 概要:javascript的组成. 各个组成部分的作用 . 一.javascript的组成 javascript ECMASc ...
- 【原】javascript笔记之Array方法forEach&map&filter&some&every&reduce&reduceRight
做前端有多年了,看过不少技术文章,学了新的技术,但更新迭代快的大前端,庞大的知识库,很多学过就忘记了,特别在项目紧急的条件下,哪怕心中隐隐约约有学过一个方法,但会下意识的使用旧的方法去解决,多年前ES ...
- 【译】学习JavaScript中提升、作用域、闭包的终极指南
这似乎令人惊讶,但在我看来,理解JavaScript语言最重要和最基本的概念是理解执行上下文.通过正确学习它,你将很好地学习更多高级主题,如提升,作用域链和闭包.考虑到这一点,究竟什么是"执 ...
- JavaScript笔记目录
JavaScript笔记目录 一.JavaScript简介 二.在HTML中使用JavaScript ...持续更新中,敬请期待
随机推荐
- java单例,懒汉&饿汉
* 单例模式Singleton * 应用场合:有些对象只需要一个就足够了,如皇帝 * 作用: 保证整个应用程序中某个实例有且只有一个 * 区别: 饿汉模式的特点是加载类时比较慢,但运行是比较快 ...
- register
register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率.注意是尽可能,不是绝对.你想想,一个CPU 的寄存器也就那么几个或几十个,你要是定义了很 ...
- display : -webkit-box-inline 的理解
发现: 最近在做移动端的东西,说起移动端弹性盒子布局真是无往不利,用起来特别爽,我也是偶尔间发现的这个属性并且它的用法,在网上基本查不到这个属性的资料(个人看法).如果没有听说过(display:bo ...
- Jil序列化JSON
使用Jil序列化JSON提升Asp.net web api 性能 JSON序列化无疑是Asp.net web api 里面性能提升最重要的一环. 在Asp.net web api 里面我们可以插入 ...
- loj 1108(spfa判负环)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26823 思路:题目的意思是求出所有的能够到达负环的点.负环很好求, ...
- JNI,NDK
jni的调用过程 1)安装和下载Cygwin,下载Android NDK 2)在ndk项目中JNI接口的设计 3)使用C/C++实现本地方法 4)JNI生成动态链接库.so文件 5)将动态链接库复制到 ...
- 详细剖析电脑hosts文件的作用和修改
提到电脑系统中的hosts文件,如果不是太熟悉的话,还真是闻所未闻,一是由于系统的hosts文件为系统属性,在系统默认设置下,我们根本无法看到它的存在,而是由于身处系统深层文件夹内,我们一般也无法察觉 ...
- 2016.7.9 计算机网络复习要点第四章之网际控制报文协议ICMP
1.ICMP允许主机或路由器报告差错情况和提交有关异常情况的报告:为了更有效地转发IP数据报和提高交付成功的机会: 2.ICMP不是高层协议,因为ICMP报文是装在IP数据报中的,作为其中数据部分,所 ...
- 结合ItemsControl在Canvas中动态添加控件的最MVVM的方式
今天很开心的收获: ItemsControl 中 ItemsPanel的重定义和 ItemContainerStyle 以及 ItemTemplate 三者的巧妙结合,在后台代码不实例化任何控件的前提 ...
- C# 新技巧(一)
概述:所有代码均来自MVC源码的阅读.实际上,也是框架开发中常用的技巧. 1.使用Empty模式处理空对象 return Enumerable.Empty<ModelValidationResu ...