闭包(closure)
大牛的讲解,点击
我们首先需要有作用域的概念,点击
那么什么是闭包?
官方的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
广义上的闭包就是指一个变量在它自身作用域的被使用了,就叫发生了闭包。粗鲁地理解:闭包就是能够读取其它函数内部变量的函数。 在js中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单粗暴地理解成“定义在一个函数内部的函数”,即一个函数嵌套了另一个函数。
闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:
作用域链,垃圾(内存)回收机制,函数嵌套......
由于作用域的关系,我们在函数外部是无法直接访问到函数内部的变量的,但是函数内部可以把这个变量传给全局变量或者返回出来,这样外部作用域就可以访问函数内部作用域的变量了;
简单的说,闭包就是有权限访问另一个函数内部作用域的变量的函数;
- javascript具有自动垃圾回收机制,函数运行完之后,其内部的变量和数据会被销毁;
- 但是闭包就是在外部可以访问此函数内部作用域的变量,所以闭包的一个特点就是
- script type="text/javascript">
- function outer(){
- var a = 1;
- function inner(){
- return a++; }
- return inner;
- }
- var abc = outer(); //outer()只要执行过,就有了引用函数内部变量的可能,然后就会被保存在内存中; //outer()如果没有执行过,由于作用域的关系,看不到内部作用域,更不会被保存在内存中了; console.log(abc());//1 console.log(abc());//2 //因为a已经在内存中了,所以再次执行abc()的时候,是在第一次的基础上累加的 var def = outer(); console.log(def());//1 console.log(def());//2 //再次把outer()函数赋给一个新的变量def,相当于绑定了一个新的outer实例; //console.log(a);//ReferenceError: a is not defined //console.log(inner);//ReferenceError: a is not defined //由于作用域的关系我们在外部还是无法直接访问内部作用域的变量名和函数名 abc = null; //由于闭包占用内存空间,所以要谨慎使用闭包。尽量在使用完闭包后,及时解除引用,释放内存;
- </script>

立即执行函数能配合闭包保存状态。

<script type="text/javascript">
for(var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i); //3 3 3
//在执行到这一行时,发现匿名函数里没有i,然后向往外部作用域找,然后找到的其实是for循环执行完了的i,也就是2++,3
},0);
}; for(var i = 0; i < 3; i++){
setTimeout((function(x){
console.log(x); //0 1 2
})(i),0);
//在立即执行函数内部i传给了x,并且锁在内存中,所以不会变
};
</script>
一道经典面试题:
下面的ul中,如何点击每一个 li 的时候弹出其下标?
<ul>
<li>index 00000</li>
<li>index 11111</li>
<li>index 22222</li>
</ul>方法一:用闭包

<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
(function(index){
oLi[index].onclick = function(){
console.log(index);
};
})(i);
}
}
</script>
方法二:闭包还有一种写法

<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
oLi[i].onclick = (function(index){
return function(){
console.log(index);
}
})(i);
}
}
</script>
方法三:将下标作为对象的一个属性,添加到每个数组元素中,(name: " i ", value: i 的值);

<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
oLi[i].i = i;
oLi[i].onclick = function(){
console.log(this.i);
};
}
}
</script>
变量的作用域
Js中变量的作用域分两种:全局变量和局部变量。
函数内部可以直接读取全局变量。
1 var n = 'RORO彦';
2 function f1() {
3 console.log(n);
4 }
5 f1();在函数外部自然无法读取函数内的局部变量。
1 function f1(){
2 var n= 'RORO彦';
3 }
4 console.log(n); // error此外,函数内部声明变量时,一定要用var关键字来命名变量。否则,就声明了一个全局变量。
当我们需要从外部读取局部变量,得到函数内的局部变量,可以在函数的内部再定义一个函数。
1 function f1(){
2 n = 'RORO彦';
3 function f2(){
4 console.log(n);
5 }
6 }如上,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2是可见的。反之,不行。这就是Javascript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。
现在f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量

1 function f1(){
2 n = 'RORO彦';
3 function f2(){
4 console.log(n);
5 }
6 return f2;
7 }
8 var result=f1();
9 result(); // RORO彦
作用
1.读取函数内部的变量
2.令这些变量的值始终保持在垃圾(内存)回收机制中。

1 function f1(){
2 var n = 'RORO彦';
3 add=function(){n = 'RORO';};
4 function f2(){
5 console.log(n);
6 }
7 return f2;
8 }
9 var result=f1();
10 result(); // RORO彦
11 add();
12 result(); // RORO
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次是RORO彦,第二次是RORO。函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除,而是保存在内存堆(heap)里, 原因是它被包装或封装在一个函数体内,这些构造器都被称为闭包。它返回调用函数的运行结果,是函数本身。此外,add的值是一个匿名函数,它本身也是一个闭包,所以add相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
注意事项
函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。闭包会在父函数外部改变父函数内部变量的值。所以,当你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,不要随便改变父函数内部变量的值。
闭包与抽象数据类型
我们通过闭包能简单地引入抽象数据类型。
例如,通过闭包实现一个 堆栈。

function createStack() { var elements = []; return { push: function(el) { elements.unshift(el); }, pop: function() { return elements.shift(); } }; } var stack = createStack(); stack.push(3); stack.push(4); stack.pop(); //
闭包与面向对象编程
在 JavaScript 中,闭包不是堆栈数据类型的最佳实现方式。用原型 Prototype 实现对内存更友好,在当前对象实例找不到相应属性或方法时,会到相应实例共同引用的 Prototype 属性寻找相应属性或方法。如果在当前Prototype属性找不到时,会沿着当前原型链向上查找,而Prototype 上的属性或方法是公用的,而不像实例的属性或方法那样,各自单独创建属性或方法,从而节省更多的内存。
上述构造器看起来非常像类、对象、实例值和私有/公有方法。闭包与类相似,都会将一些能操作内部数据的函数联系在一起。于是,我们可以像使用对象一样使用闭包。当我们想在 JavaScript 创建“真正的”隐藏域,或者需要创建简单的构造器时,我们可以优先使用闭包。不过对于一般的类来说,闭包可能还是有点太繁重了。
闭包的基本结构
- 通过函数: 写一个函数, 函数内定义一个新函数, 返回新函数, 使用新函数得到函数内的数据
- 通过对象: 写一个函数, 函数内顶一个对象, 对象中绑定多个方法, 返回对象, 利用对象内的方法访问函数内中的数据
闭包的基本用法
带有私有访问数据的对象
function Person(){
this.name = 'Peter';
// setName('')
}
// 私有数据指的是只有函数内部可以访问的数据,或者对象内部的方法 // 简单的例子,判断孩子是不是老张家的
function createPerson(){
var name = '张全蛋';
return {
getName : function(){
return name;
},
setName : function(value){
// 如果value的第一个字符串是 张 ,就设置
if(value.charAt(0) === '张'){
name = value;
} esle {
//不姓 张 ,就将错误抛出
throw new Error('你不姓张,难道还能姓王?');
}
}
}
}带有私有数据的函数
// 不具有私有数据
var fn = function(){};
function fn(){} // 具有私有数据
var foo = (function(){
// 私有数据
return function(){
// 可以访问私有数据
}
})();
闭包的基本模型
函数模型
function foo(){
// 私有数据
return function(){
// 可以访问上面的私有数据
}
}对象模型
function foo(){
// 私有数据
return {
method : function(){
// 可以访问的私有数据
}
}
}闭包的性能问题
- 函数的执行需要内存, 函数中定义的变量会在函数执行后自动回收
- 因为是闭包结构, 如果还有变量引用这些数据的话, 这些数据不会被回收
因此在使用闭包的时候,如果不使用这些数据了, 需要将函数赋值为 null
var foo = (function(){
var num = 123;
return function(){
return num;
}
})();
// 如果不需要foo中的数据, 赋值为 null
foo = null;
闭包(closure)的更多相关文章
- JavaScript闭包(Closure)
JavaScript闭包(Closure) 本文收集了多本书里对JavaScript闭包(Closure)的解释,或许会对理解闭包有一定帮助. <你不知道的JavsScript> Java ...
- 聊一下JS中的作用域scope和闭包closure
聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...
- python 函数对象(函数式编程 lambda、map、filter、reduce)、闭包(closure)
1.函数对象 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 秉承着一切皆对象的理念,我们再次回头来看函数(function).函 ...
- 深入理解JavaScript闭包(closure)
最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...
- [转] Java内部类之闭包(closure)与回调(callback)
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域.通过这个定义,可以看出内部类是面向对象的闭包,因为它 不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥 ...
- JavaScript 进阶(四)解密闭包closure
闭包(closure)是什么东西 我面试前端基本都会问一个问题"请描述一下闭包".相当多的应聘者的反应都是断断续续的词,“子函数”“父函数”“变量”,支支吾吾的说不清楚.我提示说如 ...
- [转载]学习Javascript闭包(Closure)
学习Javascript闭包(Closure) 源地址: http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures ...
- Swift语言精要-闭包(Closure)
闭包(Closure)这个概念如果没学过Swift的人应该也不会陌生. 学过Javascript的朋友应该知道,在Javascript中我们经常会讨论闭包,很多前端工程师的面试题也会问到什么是闭包. ...
- 【Python】闭包Closure
原来这就是闭包啊... 还是上次面试,被问只不知掉js里面的闭包 闭包,没听过啊...什么是闭包 回来查了下,原来这货叫闭包啊...... —————————————————————————————— ...
- 闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别
闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ con ...
随机推荐
- zabbix的各种键值
zabbix服务器端通过与zabbix agent通信来获取客户端服务器的数据,agent分为两个版本,在配置主机我们可以看到一个是agent,另一个是agent(active). agent:zab ...
- 数据库 --> sqlite3总结
Sqlite3总结 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中. sqlite语句 #sqlite3 test.db //设置宽度为2sqlit ...
- Divisor counting [线性筛积性函数]
Divisor counting 题目大意:定义f(n)表示整数n的约数个数.给出正整数n,求f(1)+f(2)+...+f(n)的值. 注释:1<=n<=1000,000 想法:我们再次 ...
- .Net的垃圾回收机制(GC)之拙见——托管类型的垃圾回收
各种语言的垃圾回收在IT界噪的沸沸扬扬,有极大的优化同时也有瓶颈. 而在.Net中的垃圾回收机制又是怎样的呢? 众所知周,.Net中的垃圾回收机制是由.Net Framework托管的,带给开发者最大 ...
- Beta冲刺 第六天
Beta冲刺 第六天 1. 昨天的困难 1.对于设计模式的应用不熟悉,所以在应用上出现了很大的困难. 2.SSH中数据库的管理是用HQL语句实现的,所以在多表查询时出现了很大的问题. 3.页面结构太凌 ...
- 敏捷冲刺每日报告——Day3
1.情况简述 Alpha阶段第一次Scrum Meeting 敏捷开发起止时间 2017.10.27 00:00 -- 2017.10.28 00:00 讨论时间地点 2017.10.27晚9:30, ...
- Week02-Java基本语法与类库
1. 本周学习总结 本周主要学习了包装类,明白了包装类比基本数据类型的优点(见书面作业4.2),了解了自动装箱,自动拆箱的概念和区别(见书面作业4.1),知道了java中的引用类似C语言中的指针,明白 ...
- Mongodb 3 查询优化(语句优化、建索引)
一.explain(),语句分析工具 MongoDB 3.0之后,explain的返回与使用方法与之前版本有了很大的变化,介于3.0之后的优秀特色和我们目前所使用给的是3.0.7版本,本文仅针对Mon ...
- idea导入本地maven项目
首先把项目关闭File->Close Project 否则会将项目导入到当前项目中 回到主界面,点击Import Project 一定要选择项目的pom文件 默认设置,继续往下走 默认配置,下一 ...
- KNN算法的代码实现
# -*- coding: utf-8 -*-"""Created on Wed Mar 7 09:17:17 2018 @author: admin"&quo ...