闭包是js开发惯用的技巧,什么是闭包?
闭包指的是:能够访问另一个函数作用域的变量的函数
简单说:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
匿名函数与闭包的关系,闭包是站在<b>作用域</b>的角度上来定义的 因为 inner 访问到 outer 作用域的变量,所以inner就是一个闭包函数。 虽然定义很简单,但是有很多坑点,比如this指向、变量的作用域,不注意的话可能就造成 <b>内存泄露</b>。
为什么闭包函数能够访问其他函数的作用域 ?
从堆与栈的角度看待JS函数
栈内存:基本数据类型( Number 、Boolean、Undefined、String、Null)
堆内存:对象类型的变量
var num = 10 //num是基本数据类型
Var num2 = {name:'Jack'} //num2是对象
>
>
>
>当num2 = {name:'Tom'} 时,堆 里边就有一个新的对象{name:'Tom'},原来的{name:'Jack'} 就会被程序垃圾引擎回收掉,节约内存空间
>
>######JS函数
function fn() {
var a = 1
var b = 2
console.log(a)
var p = function () {
console.log(b)
}
return p
}
fn()
var p2 = new fn()
p2()
><b>当程序在调用某个函数时,做了以下的工作:准备执行环境,初始函数作用域链和arguments参数对象</b>
>>注:arguments参数对象:是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。
>> 此对象包含传递给函数的每个参数,第一个参数在索引0处
>
>当程序执行完var p2 = new fn(),其实fn的执行环境并没有被销毁,因为他里面的变量a仍然被被inner的函数作用域链所引用
>
>当程序执行完p(), 这时候,fn和p的执行环境才会被销毁调;
>
>由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多
>
####注意的是闭包看似简单,但是又有很多的细节与坑点
#####坑点一:引用的变量可能发生变化
function fn() {
var arr= [];
for (var i = 0; i<10; i++){
arr.[i] = function () {
console.log(i)
}
}
return arr
}
>我们认为误认为 输出的结果为0,1,2...,10,但并不是
>因为每个闭包函数访问变量i是fn执行环境下的变量i,
>随着循环的结束,i已经变成10了,所以执行每个闭包函数,结果打印10, 10, ..., 10
#####解决办法:
function fn() {
var arr= [];
for (var i = 0; i<10; i++){
arr.[i] = function (num) {
return function(){
console.log(i)
}
}(i)
}
return arr
}
#####坑点二:this指向问题
var object = {
name: ''object",
getName: function() {
return function() {
console.info(this.name)
}
}
}
object.getName()() // underfined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
#####坑点三:内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
el = null // 主动释放el
}