前端重点知识整理(JavaScript)三:闭包
来源:互联网 发布:穆勒plc手动编程 编辑:程序博客网 时间:2024/06/04 17:44
作用域相关定义
在说闭包之前,我们首先说一下作用域。
JavaScript中有全局作用域,函数作用域。看下面的代码
var a = 10;//全局作用域中定义变量(function(){ var b = 20;//函数作用域中定义变量})();console.log(a);//可以访问全局变量console.log(b);//error, b is not defined
JavaScript是没有块级作用域的。看下面的代码
for(var item in {a:1,b:2}){ console.log(item);}console.log(item); //没有块级作用域,可以访问item
这里顺带一提,ES6提出了块级作用域及新变量声明(let)。
JS使用var声明变量,以function来划分作用域,大括号“{}” 却限定不了var的作用域。用var声明的变量具有变量提升(declaration hoisting,即先试用后声明不报错)的效果。
ES6里增加了一个let,可以在{}, if, for里声明。用法同var,但作用域限定在块级,let声明的变量不存在变量提升。
什么是闭包
维基百科是这样说的:
在计算机科学中,闭包(也称词法闭包或函数闭包)是指一个函数或函数的引用,与一个引用环境绑定在一起。这个引用环境是一个存储该函数每个非局部变量(也叫自由变量)的表。
闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量。
JavaScript之所以有闭包,是因为它是一个第一类函数特性的语言,即可用把函数当做对象去传递作为返回值。
看下面的函数,对于这种函数,当调用函数outer()之后,局部变量localVal就可以被释放了。
function outer(){ var localVal = 30; return localVal;}outer();//30
在JavaScript中,函数也是对象,并且函数也可以作为返回值也可以传参,函数里也可以套用别的函数。
对于下面的函数,调用函数outer()时,返回的是匿名函数,这个匿名函数里面依然可以访问外面的局部变量localVal。在调用outer()之后,调用func(),依然可以访问外部函数的局部变量localVal,localVal不会被释放。
这就是闭包。
function outer(){ var localVal = 30; return function(){ return localVal; }}var func = outer();func();//30
闭包无处不在
如下面的函数,点击事件中依然可以访问外部函数的局部变量
!function(){ var localData = "localData here"; document.addEvenetListener("click",function(){ console.log(localData); });}
再如下面的异步请求,jquery的$.ajax方法,在整个函数调用结束之后,回调函数依然可以访问到url,localData这些局部变量
!function(){ var localData = "localData here"; var url = "http://www.qq.com/"; $.ajax({ url: url, success: function(){ console.log(localData); } })}
闭包的坑
看下面的函数,输出结果是什么?
document.body.innerHTML = "<div id='div1'>aaa</div>" + " <div id='div2'>bbb</div>" + "<div id='div3'>ccc</div>";for(var i = 0; i < 4; i++){ document.getElementById('div" + i) .addEventListener("click", function(){ alert(i);//all are 4! } );}
实际上,点击任何一个div输出结果都是4。
在点击某个div的时候,执行回调函数,这个时候函数才会动态地拿到i的值。这一切是在整个过程初始化之后的,在初始化之后i的值就已经是4了。
那怎么样才能达到想要的效果呢?看下面的代码
document.body.innerHTML = "<div id='div1'>aaa</div>" + " <div id='div2'>bbb</div>" + "<div id='div3'>ccc</div>";for(var i = 0; i < 4; i++){ !function(i){ document.getElementById('div" + i) .addEventListener("click", function(){ alert(i);//1,2,3 } ); }(i);}
这里每一层循环的时候,用了立即执行的匿名函数把点击事件函数包装起来,并传参i,即1,2,3。那么每一次点击div,alert(i)里面的i就会取每一个闭包环境下的i,而这个i来自于每一次循环的i,所以点击每一个div的时候就会弹出相应的值了
闭包的作用
闭包可以用于封装一些变量,看下面的代码
(function){ var _userId = 23492; var _typeId = 'item'; var export = {}; function converter(userId){ return +userId; } export.getUserId = function(){ return converter(_userId); } export.getTypeId = function(){ return converter(_typeId); } window.export = export;}export.getUserId(); //23492export.getTypeId(); //itemexport._UserId; //undefinedexport._TypeId; //undefinedexport.converter; //undefined
这里定义了_userId等一些外部无法直接访问的局部变量,并通过window.export = export把对象export输出出去。在外部使用export的时候,只能通过export的一些方法来访问到函数的局部变量,而无法直接访问这些变量和方法。
这利用了闭包的特性,比如export.getUserId()函数,在整个匿名函数初始化之后,它依然能够访问到局部变量_userId。
同时,闭包也会带来一些问题,比如局部变量没有被释放掉造成空间浪费;内存泄露;性能消耗等。
- 前端重点知识整理(JavaScript)三:闭包
- 前端重点知识整理(JavaScript)一:数组与字符串
- 前端重点知识整理(JavaScript)二:事件
- 前端重点知识整理(JavaScript)四:对象及继承
- 前端重点知识整理(JavaScript)五:ajax
- 前端重点知识整理(CSS)
- 前端重点知识整理(算法等基础知识)
- 【前端知识点】CSS知识部分重点整理
- Javaweb重点知识整理
- C++重点知识整理
- Linux重点知识整理
- 闭包——Javascript 进阶知识整理
- c#重点知识详解(三)
- c#重点知识详解(三)
- c#重点知识详解(三)
- c#重点知识详细解说(三)
- JavaScript 脚本语言 重点整理
- JavaScript闭包整理
- http头文件
- http检测调试工具
- 连接oracle
- GSON解析json数据总结
- Android shape的属性介绍
- 前端重点知识整理(JavaScript)三:闭包
- RedHat下安装Python3步骤
- 读入优化
- Dynamics CRM 2015 通过JS控制下拉框实现国家省市级联
- How To Use MCI to Play AVI/WAVE Files from Memory
- 前端本地客户端压缩图片,兼容IOS,Android,PC、自动按需加载文件
- 图结构练习——最短路径
- telnet连接redis和redis-cli连接redis
- 以通配符(%)开始的like字符串,走索引