对于JS语言来说,我们在写项目的时候,很容易在不经意之间产生闭包,可以访问到另一函数作用域的变量。那么究竟闭包是什么,他是怎么产生的?闭包的用途是什么以及闭包的缺点又是什么,我们都要搞搞清楚,才能有利于对代码的深度理解。
- 我们先从JavaScript三本书上获取对于闭包的概念 :
1.犀牛书:
函数变量可以保存在函数作用域内(概念太过宽泛了 )
2.高级程序设计:
闭包是指有权访问另一个函数作用域中变量的函数(函数没导出)个人更偏向于高程的概念,有些闭包是产生的但是并没有保持住而已
function foo(){
var n = 0;
function bar(){
console.log(n);//产生了闭包,但并没有保持住
};
bar();
}
foo();
3. 你不知道的JavaScript
当函数 可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。(有一些强调要导出函数的意思在里面)
function foo(){
var n = 0;
return function bar(){
console.log(n);//产生了闭包,并将函数抛出
};
}
foo()();
那么我们可以进行总结一下:闭包的概念:
当函数的执行,导致函数被定义,并抛出?(两本书的概念有些许差别,我个人倾向,如果不return出函数,也是会产生闭包的,只是没有保持住而已)
来看一道例题 :
function foo(fn){
var n = 0;
fn();
}
foo();
function test(){
console.log(n);
}
foo(test);
问?n打印出来是多少呢,会不会产生闭包?那么如果不会产生闭包,应该怎样修改才会产生闭包呢?
根据我们的概念,foo函数执行之后,fn()是函数执行过程,并不是函数定义的过程,所以并没有函数被定义,所以不会产生闭包,n打印报错undefined;
所以,闭包和函数的定义有关
联想一下:this指向和函数的执行方式有关
- 闭包的几种常见形式
1.函数的返回值是函数
function foo(){
var n = 0;
return function(){
};
}
foo();
2.函数返回的变量是函数
function foo(){
var n = function(){
};
return n;
}
foo()();
3.通过全局变量来定义闭包;核心依然是函数执行导致函数被定义
var outter;
function foo(){
var a = 10;
outter = function(){
console.log(a)
};
}
foo();
outter();
4.函数的参数的方式
var inner = funtion(fn){
console.log(fn());
}
function foo(){
var b ="local";
var n = function(){
return b;
};
inner(n);
}
foo();//可以正常打印出local
换一种写法(立即执行函数)也要分辨出来
var inner = funtion(fn){
console.log(fn());
}
(function(){
var b ="local";
var n = function(){
return b;
};
inner(n);
})();
//可以正常打印出local
5.循环赋值的问题;
function foo() {
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i);
}
}
return arr;
}
var bar = foo();
// for (var j = 0; j < 10; j++) {
// bar[j]();
// }
bar[0]();//打印出10
在执行最后一行bar[j]()的时候,里面定义的函数并没有被执行,只是被保存了起来,所以当执行的时候,i已经循环完毕,此时i为10,所以打印出10;
那么如果我们想打印出0-9,应该在代码上(利用闭包)做出什么样的改变呢?
function foo() {
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = (function(j) {
return function(){
console.log(j);//此时的闭包j拿到的就是立即执行函数每次的i的值,就是0-9
};
})(i)
}
return arr;
}
var bar = foo();
for (var j = 0; j < 10; j++) {
bar[j]();
}
//bar[0]();//0
***写一个迭代器(累加器)(闭包)
var add = (function() {
var count = 0;
return function() {
return ++count;
};
})();
console.log(add());
console.log(add());
console.log(add());
console.log(add());
console.log(add());
console.log(add());
- 趁热打铁,来两道闭包面试题练一练
//闭包面试题1
function fun(n, o) {
console.log(o);
return { //返回一个对象
fun: function(m) { //对象里有fun属性是一个函数
return fun(m, n); //返回一个fun函数,因为当前作用域没有fun函数,
//所以依次向上级作用域中查找,对象里的这个fun属性名的函数调用方式应该是obj.fun
//所以这里返回的fun应该是全局中的fun,传入m=1,n向父函数中找n=0
}
};
}
var a = fun(0); //undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
var b = fun(0) // undefined
.fun(1) // 0
.fun(2) // 1
.fun(3); // 2
var c = fun(0).fun(1); //undefined 0
c.fun(2); //1
c.fun(3); //1
//闭包面试题2
let a = 0,
b = 0,
function A(a) {
A = function(b) {
console.log(a + b ++);
};
console.log(a++)
}
A(1);//1
A(2);//4