目录
闭包(面试必问)
闭包的概念
什么是闭包?
- 闭包就是函数嵌套函数。
为什么要用闭包?
- 在外面定义全局变量会造成污染,命名空间冲突。
- 主要是为了在函数外面可以访问函数内部的局部变量。
- 函数:调用时,开辟空间,调用结束后,立即释放空间
闭包的坏处
大量使用闭包的话,会产生内存泄漏(IE)
如何解决内存泄漏?
- 将使用完毕的变量赋值为null。
闭包的原理
利用了js的垃圾回收机制,如果在正常情况下,当一个函数调用完就会立即释放空间,包括函数里面声明的变量。但是在回收垃圾的过程中,发现了被回收的这个变量正在被另一个函数使用,那么这个变量将永远不再被回收。
闭包的基本语法
- 在函数中返回一个匿名函数,匿名函数中返回要返回的变量
- 使用这个变量的方法:通过匿名函数自运行
- 闭包返回的是一个函数
function fn(){
var a = 3;
return function(){ //闭火罩(闭包函数) 保护变量a
return a; //常驻内存 相当于把var a = 3 变成了全局变量
}
}
alert(fn()()); //3
闭包的案例
使用全局变量进行累加和
- 案例一:
// 使用全局变量进行累加和 这里没有使用闭包
var a = 3;
function fn(){
return ++ a;
}
fn();
console.log(a); //4
fn();
console.log(a); //5
fn();
console.log(a); //6
// 分析:调用一次函数,输出次变量,由于函数用没有声明变量,所以会在全局变量中寻找a的值
- 案例二:
//使用局部变量进行累加和 使用闭包的错误方法
function fn(){
var a = 3;
return function(){
return ++ a;
}
}
console.log(fn()()); //4
/*
var a = 3;
function(){ return ++ a}(); //a = 4
*/
console.log(fn()()); //4
/*
var a = 3;
function(){ return ++a}; //a = 4
*/
console.log(fn()()); //4
/*
var a = 3;
function(){ return ++a}; //a = 4
*/
// 分析:这里用到了闭包,闭包返回一个++a的值,也就是4,因为每次输出都调用了闭包 所以每次去到的值都是4
- 案例三:
//使用局部变量进行累加和 闭包的正确使用方法
function fn(){
var a = 3;
return function(){
return ++ a;
}
}
let fun = fn(); //调用一次fn,返回 function(){}
/*
var a = 3;
return function(){return ++ a}()
*/
console.log(fun()); //4
console.log(fun()); //5
console.log(fun()); //6
/*
分析:通过变量fun 接收回来fn的返回值,这个返回值是一个函数,
所以现在fun就是一个函数,这个函数的功能是 返回++a的值,也取到
了变量a的值=3,接下来调用三次fun,结果就是4 5 6
*/
循环里的匿名函数的取值问题
- 案例一:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = i;
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); //01234
}
/*
分析:函数fn返回值是数组[0,1,2,3,4]
把函数的返回值赋值给了变量list
遍历数组输出结果
*/
- 案例二:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
};
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); // 5 * function(){return i;}
}
/*
分析:函数fn中arr是一个数组,循环添加了函数块,这个函数块没有调用所以就不会执行,只是在声明函数
函数fn的返回值是这个数组
变量list拿到了函数fn的返回值 5个函数块
遍历输出list的没一个函数块,没有调用 结果就是5个函数块
*/
- 案例三:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
};
}
return arr;
}
/*
i = 5;
arr[0] = function(){return i;}
arr[1] = function(){return i;}
arr[2] = function(){return i;}
arr[3] = function(){return i;}
arr[4] = function(){return i;}
*/
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]()); // 55555
}
/*
分析:函数fn返回了五个函数块 此时i=5退出循环
五个函数块的功能是返回i
变量list拿到了这五个函数块 此时fn已经执行完毕 i的值是5(代码是从上到下执行的)
遍历list依次调用这5个函数块 返回55555
*/
- 案例4:
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
}();
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]()); // 报错
}
/*
分析:函数fn中 里层函数自调用 返回值是01234
此时arr[i]的值并不是函数块
函数fn把arr反出去,赋值给了list
下方输出的时候,调用函数,因为list中没有函数,所以报错!
*/
- 案例5:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
}();
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); // 01234
}
/*
分析:函数fn中 里层函数自调用 返回值是01234(向父级查找i)
此时arr[i]的值为0,1,2,3,4
函数fn把arr反出去,赋值给了list
下方遍历输出list 结果为0,1,2,3,4
*/
- 案例6:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(i){
return i;
}();
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); // 5 * undefined
}
/*
分析:函数fn中 里层函数有型材 形参是i
由于自调用的时候没有传参
所以返回结果是undefined
函数fn的返回值是arr 这里赋值给了list
此时list的值就是五个undefined
遍历结果,输出5个undefined
*/
- 案例7:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(i){
return function(){
return i;
};
}(i);
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); // 5 * function(){retunr i}
}
/*
分析:中间层函数自调用 函数的功能是返回五个函数块
最里层的函数并没有执行
此时arr拿到的值是中间层函数的返回值 五个函数块
函数fn的返回值是arr 这里赋值给了list
遍历输出list 结果就是五个函数块
*/
- 案例8:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
}(i);
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]); // 01234
}
/*
分析:里层函数自调用 调用时传了一个实参
由于该函数没有形参,所以实参忽略
实参多余形参,多余的值忽略不计
实参少于形参,多余的形参值为undefined
函数功能是返回0,1,2,3,4;赋值给了arr
函数fn的返回值是arr,这里赋值给了list
遍历list 结果输出01234
*/
- 案例9:
//循环里的匿名函数的取值问题
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(i){ // 0 1 2 3 4
return function(){
return i;
};
}(i);
}
return arr;
}
let list = fn(); //返回arr-数组
for(let i = 0,len = list.length;i < len;i ++){
console.log(list[i]()); // 01234
}
/*
分析:中间层函数自调用 传参 0 1 2 3 4
中间层函数的功能是返回一个函数块 也就是里层函数块
由于里层函数没有调用,不会执行
里层函数的功能是返回i
由于里层没有声明i所以向上查找 找到了0 1 2 3 4
这时,里层函数的功能就是返回 0 1 2 3 4 由于没有调用所以不会执行
中间函数的功能是返回里层函数块
所以arr的值就是
arr[0] = function(){return 0};
arr[1] = function(){return 1};
....
函数fn的返回值是arr;遍历list接收到了arr
遍历list 依次调用list里的每一个函数
结果就是01234
*/
总结
什么是闭包?
答:闭包就是函数嵌套函数
为什么要用闭包?
答:主要是为了在函数外面使用函数内部定义的局部变量
闭包的坏处?
答:大量使用闭包的话,会产生内存泄漏(IE)
如何解决?
答:将使用完的变量赋值为null。
闭包的原理?
答:利用了js的垃圾回收机制
- 正常情况下,一个函数调用完,就会立即释放存储空间,包括函数里面声明的变量
- 但是在回收垃圾的过程中,发现了这个变量正在被另外一个函数使用,那么这个变量就永远不会被回收。
闭包的基本语法?
- 在函数中返回一个匿名函数,匿名函数中返回要返回的变量
- 使用这个变量的方法?通过匿名函数自运行
- 闭包返回的是一个函数