何为闭包
闭包就是在一个函数内部创建另一个函数,让你可以在一个内层函数中访问到其外层函数的作用域。又或者说,闭包就是使一个函数能够读取其他函数内部变量。
在了解闭包之前,我们要先理解JavaScript的作用域——全局作用域和局部作用域(先不考虑块级作用域)。
直接编写在 script 标签之中的JS代码,都是全局作用域;
在函数内部就是局部作用域,这个代码的名字只在函数的内部起作用
全局作用域在页面打开时创建,页面关闭时销毁;
调用函数时创建局部作用域,函数执行完毕之后,函数作用域销毁;
- 在全局作用域下声明的变量就是全局变量
- 在局部作用域下声明的变量就是局部变量。
用闭包的原因
因为作用域链的存在 函数内部可以直接读取全局变量。而函数内部无法读取其他函数内部的局部变量 这也是为什么用闭包的原因。
闭包就是在一个函数内部创建另一个函数,让你可以在一个内层函数中访问到其外层函数的作用域。又或者说,闭包就是使一个函数能够读取其他函数内部变量。
关于作用域链
<script>
var a = 1;
var b = 2;
function fn(x) {
var a = 10;
function bar(x) {
var a = 100;
b = x + a;
return b;
}
bar(20);
bar(200);
}
fn(0);
</script>
各个作用域的嵌套关系组成一条作用域链。
例子中 bar 函数的作用域链式 bar -> fn -> 全局, fn函数保存在作用域链式 fn -> 全局
作用域链主要是进行标识符(变量和函数)的查询,
标识符解析就是沿着作用域链一级一级的搜索标识符的过程,而作用域链就是保证对变量和函数的有序访问。
(1)如果自身作用域中声明该变量,则无需使用作用域链
在上面的例子中,如果要在 bar 函数中查询变量 a ,则直接使用 LHS 查询,赋值为 100 即可。
(2)如果自身作用域中未声明该变量,则需要使用作用域链进行查找
何时产生闭包
- 当一个嵌套的内部(子)函数,引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
以下代码,可以解释闭包
// function f1(){
// var m=0;
// }
// function f2(){
// var n=1;
// console.log(m+n);//报错 Uncaught ReferenceError: m is not defined
// }
// f2();
// 如何让一个函数访问另一个函数的内部变量?
//答案是采用闭包
function fn1(){
var m=6;
function fn2(){
var n=1;
console.log(m+n);//输出7 可以正常访问fn1函数中的m啦;
}
return fn2();//return 去执行fn2函数
}
fn1();//输出7
闭包的特点
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
使用闭包有一个优点,也是它的缺点,就是可以把局部变量驻留在内存中,可以避免使用全局变量。全局变量在每个模块都可调用,这势必将是灾难性的。
全局变量的累加
var a = 1;
function abc(){
a++;
alert(a);
}
abc(); //2
abc(); //3
局部变量
function abc(){
var a = 1;
a++;
alert(a);
}
abc(); //2
abc(); //2
那么怎么才能做到变量a既是局部变量又可以累加呢?
三、局部变量的累加(闭包所能做到的)
function outer(){
var x=10;
return function(){ //函数嵌套函数
x++;
alert(x);
}
}
var y = outer(); //外部函数赋给变量y;
y(); //y函数调用一次,结果为11
y();
立即执行函数
定义:函数定义之后立即执行
函数表达式方式创建:
函数表达式 声明一个匿名函数并赋值给一个变量,该变量称为函数名
var functionName=function(arg0,arg1,arg2){
//函数体
}
函数表达式后面加括号()即可立即执行函数。
var test=function(n){
alert(n)
}(22); //弹出 22
匿名函数方式
匿名函数,即没有名称的函数
如果单独只写一个匿名函数,此时是不符合语法要求的 会报错。需要给 匿名函数包裹一个括号,使之成为表达式。
(function(n){
alert(n)
})(666); //弹出 666
//函数体不加括号,直接执行报错,
//Uncaught SyntaxError: Function statements require a function name
function(n){
alert(n)
}(22);
所以最简立即执行函数的形式是,小括号包裹的函数里+后面一个小括号。
let add=function(){
function fn(x,y){
console.log(x+y);
}
function sub(x,y){
console.log(x-y);
}
let xmlobj={};
xmlobj.fn=fn;
return xmlobj;
}();
console.log(add.fn(1,2));//3 ,
//这样写,只能通过add访问fn ,因为fn被暴露出去了,只能通过add去访问,就算和全局函数同名也没关系,
//而sub没有
//xmlobj.fn有点像export
参考:https://blog.csdn.net/mengfanshaoxia/article/details/123052297