1.什么是闭包?
就是函数内部可以直接读取全局变量,但在函数外部无法读取函数内部的局部变量。
能够读取其他函数内部变量的函数,可以理解为定义在一个函数内部的函数
2.闭包的用途
两大用处:可以读取函数内部的变量;也可以让这些变量始终保持在内存中,不会在f1调用后被自动清除
3.缺点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包
一般来讲,当函数执行完毕后,局部活动对象就会被销毁内存中仅保存全局作用域(全局执行环境的变量对象),但闭包又有所不同,
在另一个函数内部定义的函数会将包含函数(外部函数)的活动对象添加到它的作用域链中。当外部函数执行完毕后,其活动对象不会被销毁
因为内部函数的作用域链仍然在引用这个活动对象,。换句话说,当外部函数的执行完后,其执行环境的作用域链会被销毁,但他的活动对象
仍然会留在内存中,直到内部幻术被销毁后,外部的函数对象才会被销毁。
闭包域变量
闭包不属于任何对象,它不是一个对象的方法(就不能使用点运算符来调用)
闭包只能取得包含函数中任何变量的最后一个值,闭包所保存的是整个变量对象,而不是某个特殊的变量
function createFunction(){ var result = new Array();
console.log('a') for(var i = 0;i< 10;i++){ console.log(i)//0,1,2,3,4,5,6,7,8,9 result[i] = function(){ console.log(this,390)//window;;匿名函数中的this指向的是windows,和全局变量一样 console.log(i)//i=10 return i; } }
console.log('b') console.log(result)//打印出数组函数,打印出10个匿名函数function(){} return result; } createFunction();
可以这样理解:
运行结果:
当执行到第五步的时候:result返回的是一个闭包函数,闭包函数没有执行,如果想执行闭包函数的话,可以:createFunction()(); 就可以执行闭包函数了,但是,要注意的是:在调用createFunction()的时候,for循环就已经执行了,当我们再调用createFunction()()的时候,for循环已经结束了,此时全局的i = 10;
而function是闭包函数,所以function可以通过作用域链访问保存在createFunction()函数的活动对象i,所用function()函数中访问到的i都是10。这也是为什么闭包只能取得包含函数中任何变量的最后一个值的原因
解决办法:
function createFunction(){ var result = new Array(); for(var i = 0;i< 10;i++){ //立即执行函数:和普通函数传参一样,立即执行函数也可以传递参数。如果在函数内部定一个函数,而里面的那个函数能引用外部的变量和参数(闭包),我们就能用立即执行函数锁定变量保存状态。 //num参数是从外边传进来的,该形参相当于一个变量会存在于里面的那个闭包函数中,所以即使外面的i改变了,里边的i也不会改变 //每次循环的时候,生成一个新的自执行函数,把i 传递进去,这样每个函数都有自己的作用域。 //由于函数参数是按值传递的,所以就会将变量i的当前值赋值给参数num。而在这是匿名函数内部,又创建并返回了一个访问num的闭包,result数组中每个函数都有自己的num变量的一个副本 result[i] = function(num){ console.log(num,21)//0,1,2,3,4,5,6,7,8,9 return function(){ console.log(num,23) return num;//访问传进来的num } }(i); } return result; } var a = createFunction(); console.log(a)
//用(function(){xxxx})()是利用匿名函数和闭包函数来执行xxx里面的代码,同时所有的定义比如变量的作用域都在闭包里,不会污染到外部命名空间
还有一种方法,就是es6中的let命令,只有在该变量所在的代码块内生效
/*
for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域。
for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次
*/
function createFunction(){ var result = new Array(); //因为let会以for后面的两个{}形成作用域,for循环执行到{}里面时i就是第一次执行的i=0,第二次。。。每次循环都会在{}形成作用域,不会去外部访问i //代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,
for(let i = 0;i< 10;i++){ console.log(i,45)//0,1,2,3,4,5,6,7,8,9 setTimeout(result[i] = function(){ // console.log(this,390)//window console.log(i,46)//0,1,2,3,4,5,6,7,8,9 return i; }) } console.log(result,47)//打印出数组函数,打印出10个匿名函数function(){} return result; } createFunction();
用(function(){xxx})()是利用匿名函数和闭包来执行xxx里面的代码,同时所有的定义比如变量的作用域都在闭包里,不会污染到外部环境