网站首页 > 基础教程 正文
闭包是functional language里面的核心概念。也是常见的JS面试题,是否理解闭包是一个简单的区分JS初级和高级程序员的判例。
几乎每个JS程序员都在使用闭包,有意或无意间。比如编写一个jQuery鼠标点击处理函数:
$(function() {
var option;
$(".scssbox").click(function() { // 闭包,该闭包同时也是一个匿名函数
option = scss; // 闭包可以访问和改变其外部函数(包含函数)中的变量
});
}
什么是闭包
“闭包”实际上就是一个函数,该函数被定义在一个包容(外部)函数的内部,并能够访问外部函数的变量,即使外部函数已返回。
这种外部变量访问能力不是通过数值拷贝传递,而是通过引用(Reference)传递的,因此闭包可以读取并改变外部变量的取值。
function Name (firstName, lastName) {
var nameIntro = "Your name is ";
// 该内部函数能访问外部函数中定义的变量,以及参数
function makeFullName () {
return nameIntro + firstName + " " + lastName;
}
return makeFullName ();
}
console.log( Name("Michael", "Jordan") ); // Your name is Michael Jordan
注意:上述代码中的闭包不能被公开函数(public function)所调用,另外闭包不能访问外部函数的arguments变量。所谓公开函数指的是通过原型(prototype)扩展来定义的对象函数。
为什么需要闭包
闭包的好处是可以方便的完成外部函数对象的某些特定功能。我们可以类比C++等其他面向对象的语言,访问控制是面向对象语言的一个基本特征,而通过闭包,我们可以在JS语言中实现对象私有成员函数(private member function)和授权函数的功能。
闭包的陷阱
由于闭包能够修改外部函数变量,如果不小心,可能会产生一些比较隐蔽的问题:
function Member(users) {
var i;
var uniqueID = 100;
for (i = 0; i < users.length; i++) {
users[i]["id"] = function() { return uniqueID + i;}
}
return users;
}
var users = [{name: "Ryan",id: 0},
{ name: "Mike",id: 0},
{name: "Mark",id: 0}];
var members = Member(users);
var rid = members[0];
console.log(rid.id()); // 103
上面的代码本来是想给3个用户分别分配唯一成员编号100、101、102,但实际上都是103。
原因就是在调用Member函数并返回后,i已经被修改为3,那么当再次调用闭包函数获取唯一编号时就已经是103。
实例分析
看下面代码:
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){alert(n);}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result()它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。为什么会这样呢?尤其是第二次,为何输出的不是999呢? nAdd=function(){n+=1}又起到了什么作用呢?
首先要说的是,闭包是functional language里面的核心概念。当出现高阶嵌套函数的时候,编译器会做closure convention闭包变换,核心就是变量不在分配在stack上,而是分配在heap上。这就是为什么f1已经返回,但是n还能被+1的原因。上面这段代码,实际上就是一个高阶嵌套函数。
因为在函数里面有定义的函数,这是嵌套。pascal也是允许嵌套函数。
高阶的原因是,函数可以所谓参数传递和返回,像我们熟悉的C语言。
但是当高阶和嵌套同时出现,就会造成麻烦,所以pascal和C都只能支持其中的一个。
我来分析一下这个程序的执行流。
var result=f1(); 返回了一个函数f2, 因此result为f2。这个高阶函数特性,参考C语言函数指针。
result(); 调用f2,显然输出999.
nAdd(); 这里需要注意,这个nAdd实际上在定义的时候是一个lambda,是一个匿名函数,功能是n+=1。定义时将这个函数赋值给nAdd。所以在此时,实际上是调用了n+=1.为什么能找到n?因为n在堆里面。
result(); 调用f2,显然输出1000.
最后一点,n在堆上如何被销毁,这个工作是垃圾收集器负责。当n不在被任何闭包的env引用的时候,会被回收。
猜你喜欢
- 2024-11-06 jQuery VS AngularJS 你更钟爱哪个?
- 2024-11-06 Jquery实现文本框得到焦点的时候,文本框的焦点出现在最后!
- 2024-11-06 bootstrap基础快速入门-14 分页pagination组件
- 2024-11-06 网站前端横幅滑动选项效果 网页出现横向滚动条
- 2024-11-06 JavaScript学习笔记(三十三)—jQuery(下)
- 2024-11-06 jQuery UI 下载 jquery怎样下载才能使用
- 2024-11-06 Python工程师带你分析必会框架Django之关于Ajax(附步骤和例子)
- 2024-11-06 JavaScript基础知识系列:判断类型(上)
- 2024-11-06 jQuery Mobile 按钮 jquery 按钮不可用
- 2024-11-06 前端排序算法总结;前端面试题2.0;JavaScript异步编程
- 最近发表
- 标签列表
-
- jsp (69)
- pythonlist (60)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (68)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- console.table (62)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)