js闭包深入理解(Closure)
闭包的概念
- 闭包是指有权访问另一个函数作用域中的变量的函数
- 闭包是基于词法作用域书写代码时所产生的必然结果。
- 函数对象可以通过作用域关联起来,函数体内的变量都可以保存在函数作用域内,这在计算机科学文献中称为“闭包”,所有的javascirpt函数都是闭包
- 函数可以通过作用域链相互关联起来,函数内部的变量可以保存在其他函数作用域内,这种特性在计算机科学文献中称为闭包。
- 闭包不是一个函数,它是一种机制,用于访问自由变量
优点
- 不会造成全局变量的污染;
- 可以在函数的外部访问到函数内部的局部变量(实现所谓的变量‘公有化’)。
- 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。
缺点
- 闭包导致作用域链的不释放,会造成内存溢出,所以就会占用内存空间, IE容易造成内存泄露
解决方法——使用完变量后,手动将它赋值为null
- 闭包可能在父函数外部,改变父函数内部变量的值。
-
由于闭包涉及跨作用域的访问,所以会导致性能损失。
解决方法——通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响
代码片段一:
<p>1</p>
<p>2</p>
<p>3</p>
var ps = document.querySelectorAll('p');
for (var i = 0; i < ps.length; i++) {
ps[i].onclick = function () {
console.log(i);
}
}
console.log(i); //3
这是因为for循环已经加载完毕,可以看到每个p标签都有点击事件,而onclick是点击之后才执行的,属于同步和异步问题。当我们点击的时候,for循环已经完成,所以i的值恒为3。产生这样的问题在于这个i的值在初始化完成的时候就已经是3了
原因
- 函数作用域 function{ },块级作用域 {}。
- 一定要有 function 关键字,才会有函数作用域。
- js里面 var声明的变量只有函数作用域,没有块级作用域。(也就是说,函数可以隔离变量,for不能隔离变量)。
- 因此,可在全局(外部)通过console.log看到 i 的值。
解决方案
1.let/const 块级作用域(推荐)
var ps = document.querySelectorAll('p');
for (let i = 0; i < ps.length; i++) {
ps[i].onclick = function () {
console.log(i);
}
}
console.log(i);
2.闭包:用立即执行的匿名函数把它包装起来,这样子做的话,log(i)的值就取自闭包环境中的i
for (var i = 0; i < ps.length; i++) {
(function (i) {
ps[i].onclick = function () {
console.log(i);
}
})(i);
}
3.添加自定义属性
for (var i = 0; i < ps.length; i++) {
//自定义属性标签
ps[i].index = i;
ps[i].onclick = function () {
console.log(this.index);
}
}