文章后有彩蛋
闭包概念:是指在JavaScript中,内部函数总是可以访问-其所在局部作用域中声明的参数和变量,即使在其所在局部作用域被销毁后,依然可以访问.
闭包的原理:借助函数的立即执行、参数以及函数的return返回值,多创建了一层作用域。从而实现外部函数,持续性被引用而不能释放内存空间,将值存储下来。
闭包函数:定义在局部作用域中的函数.
闭包本质:将闭包函数外的局部变量常驻内存. (在程序中,避免使用全局变量,推荐使用私有,封装的局部变量)
缺点:内存泄露(任何对象在你不在需要他之后仍然存在).
解决方案:在退出函数之前,将不使用的局部变量全部删除。(面试的时候会问到哦)
window.onload = function(){ var el = document.getElementById("id"); el.onclick = function(){ alert(el.id); } }
window.onload = function(){ var el = document.getElementById("id"); var id = el.id; //解除循环引用 el.onclick = function(){ alert(id); } el = null; // 将闭包引用的外部函数中活动对象清除 }
闭包的特性:1.函数内再嵌套函数; 2.内部函数可以引用外层的参数和变量;3.参数和变量不会被垃圾回收机制回收。
以下1,2例题 是对概念的理解,其余都是一些练习,配合下一章的匿名函数自执行,希望你对闭包会有更深的理解:
1.函数内部访问函数外部的数据
var a = 1; //全局变量
function fn_01(){
alert(a);
}
fn_01(); //1
function fn_02(){
var num = 10;
}
fn_02();
alert(num); //num is not defined
//外部想访问函数内部局部的私有变量,是不能直接访问到的
//因为局部变量会在函数运行以后被销毁,这就用到了闭包。(闭包函数是可以访问到的)
2.通过闭包访问局部变量
function fn_01(){
var num = 3; //变量
return function(){ //闭包函数
return num;
}
}
alert( fn_01()() ); //闭包函数访问局部作用域中的变量
function fn_02(num){ //参数 var num 形式参数(形参):定义函数时接收数据的参数。 实际参数(实参):调用函数时传递的参数。
return function(){ //闭包函数
return num;
}
}
alert( fn_02(4)() ); //闭包函数访问局部作用域中的参数
3.循环里的匿名函数的取值问题
function fn_01(){
var arr = [];
for(var i = 0;i < 5;i++){
arr[i] = function(){ //函数没有执行,没有取外面i的值,只将函数块赋值给了数组arr
return i;
}
}
return arr;
}
alert( fn_01() );
/*解释: 数组arr 循环5次都将function匿名函数存放在了数组中,函数并没有执行
function(){
return i;
},function(){
return i;
},function(){
return i;
},function(){
return i;
},function(){
return i;
}
*/
//自己执行
function fn_02(){
var arr = [];
for(var i = 0;i < 5;i++){
arr[i] = (function(){
return i;
})(); //让闭包函数自执行,每次执行,都将i存放在数组arr中
}
return arr;
}
alert( fn_02() ); //arr = [0,1,2,3,4];
//传参数
function fn_03(){
var arr = [];
for(var i = 0;i<5;i++){
arr[i] = (function(i){
return i;
})(i) //函数执行,返回循环每一次i的值,作为参数,传递给闭包函数,返回值i,传递给数组
}
return arr;
}
//alert(fn_04());
var list = fn_04();
for(var j = 0,len = list.length;j < len;j++){
alert(list[j]); //返回数组是[0,1,2,3,4]
}
//传参数加 闭包函数
function fn_04(){
var arr = [];
for(var i = 0;i<5;i++){
arr[i] = (function(i){ //闭包函数
return function(){ //闭包函数
return i;
};
})(i) //函数执行,返回循环每一次i的值,作为参数,传递给闭包函数,返回值i,传递给数组
}
return arr;
//此时arr数组存放的是function(){return i;}这个函数块 取值的时候,向上面的父级函数取i,这里的i传递的参数是循环变量每次的i
}
//alert(fn_04());
var list = fn_04();
for(var j = 0,len = list.length;j < len;j++){
alert(list[j]());
}
练习题 1. fn(1)(2)(3) 使用闭包得到6
function fn(x){
return function(y){
return function(z){
alert(x+y+z);
}
}
}
fn(1)(2)(3);
练习题 2.
var name = "jack";
var obj = {
name: "Lisa",
getName : function(){
return function(){ //闭包
return this.name;
}
}
}
alert( obj.getName()() );
var name = "jack";
var obj = {
name: "Lisa",
getName :function(){
var that = this; //this 指向obj
return function(){
return that.name;
}
}
}
alert( obj.getName()() );
练习题 3.三个数的累加,要求:同时支持下面两种写法
//sum(1)(2)(3) 使用闭包得到6
//sum(1,2,3);
var sum = (...a)=>{ //a表示所有的参数
if(a.length == 3;){
return a[0] + a[1] + a[2];
}else{
var x = a[0];
return function(y){
return function(z){
return x + y + z;
}
}
}
}
alert( sum(1,2,3) );
alert( sum(1)(2)(3) );
练习题 4.点击按钮,让按钮里面的值++
<style type="text/css">
#btn{
80px;
height:30px;
font-size: 16px;
}
</style>
<input type="button" value="0" id="btn"/>
var oBtn = document.getElementById("btn")
oBtn.onclick = function(){
this.value = Number(this.value) + 1;
}
//闭包写法实现
(function(){
var i = Number(oBtn.value);
oBtn.onclick = function(){
this.value = ++ i;
}
})() //减少全局变量
function fn_06(){
var i = 0;
return function(){
return ++ i;
}
}
var n = fn_06();
oBtn.onclick = function(){
this.value = n();
}
//循环加计时器--闭包解决
for (var i=1; i<=9; i++) {
setTimeout( function timer(){
console.log( i );
},1000 );
}
/*解释 i = 0 ...覆盖 1,2,3,4
setTimeout(function(){console.log(i);},0) //计时器,是延时操作,即使事件是0,也排在后面执行(异步)
setTimeout(function(){console.log(i);},0)
setTimeout(function(){console.log(i);},0)
setTimeout(function(){console.log(i);},0)
setTimeout(function(){console.log(i);},0)
最后 i = 5 不执行下面了, 所以输出5个5 */
//闭包解决
for (var i=1; i<=9; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, 1000 );
})( i );
}
//let 也可以解决(详解可以看前几章关于for循环中有关let的解释)
for(let i = 0;i < 5;i ++){
setTimeout(function(){
console.log(i);
},0)
}
/* i=0
setTimeout(function(){console.log(i);},0)
i=1
setTimeout(function(){console.log(i);},0) .....i=2 i=3 i=4 */
这是 一道面试题,如何更改下面的代码,将a[6] 输出的结果得到6 ?
var a=[];
for(var i=0;i<10;i++){
a.push(
function(){console.log(i)}
);
}
a[6](); //10
var a=[];
for(let i=0;i<10;i++){
a.push(
function(){console.log(i)}
);
}
a[6](); // 6
var a=[];
for(var i=0;i<10;i++){
a.push(
function(i){
return function(){
console.log(i);
}
}(i)
);
}
a[6](); //6
彩蛋来啦!!!