在介绍闭包之前,我们先看看是什么全局变量和局部变量
局部变量:定义在函数内部的变量(只能在内部被访问) 形参也是一种局部变量
全局变量:不在函数内部定义的变量, 就称为全局变量,全局变量在任何函数内都可以被访问和修改
假如我们在函数内部 定义了一个和外部相同名字的变量, 那么在函数内部是用的哪个变量呢?
<script> var ccc = 5; function inner() { var ccc = 4; console.log(ccc); } inner(); </script>
以上结果会输出4 , 因为在执行过程中, 会由内向外寻找变量的定义,所以局部变量在函数内部比全局变量更有优先被使用的权力
再看一个例子, 假如我们打印外部的ccc变量会输出什么?
<script> var ccc = 5; function inner() { var ccc = 4; } inner(); console.log(ccc); </script>
上面的例子会输出 5, 因为函数内部定义的变量无法被外部看到
我们再来看一个比较奇葩的例子
<script> var ccc = 5; function inner() { ccc += 1; var ccc = 4; console.log(ccc); } inner(); </script>
这个例子第一次打印会输出 NaN , 第二次打印会输出 4
第二次打印好理解, 因为我们已经知道局部变量优先被使用。
第一次比较奇怪, 按道理讲, 在函数内部也应该能引用到外部的全局变量ccc,但是程序的运行结果和我们的推断相反,这是因为js语言有个比较坑的知识点;即在函数执行前,语法规定把变量的定义提升到 函数的前面。
上面的例子在运行的时候浏览器是这么理解的
<script> var ccc = 5; function inner() { var ccc = undefined; ccc += 1; ccc = 4; console.log(ccc); } inner(); </script>
这种特性就叫做函数变量提升, 把一个后面定义变量, 提升到前面,因为前面有这个地方的引用。
一个函数内部也可以定义另一个函数内部
<script> function outer() { function inner() { console.log("innser函数") } inner() } outer(); </script>
上面的inner() 函数只能再 outer() 函数内部调用
如果不加修饰词定义一个变量的话, 此变量将被视为全局变量,即使它定义在了函数内部
<script> function fa() { quanju = 10; } fa(); console.log(quanju); </script>
从概念上讲,闭包就是一个函数以及捆绑在此函数周围环境状态的引用组合, 每创建一个函数都会产生一个闭包
这个定义比较抽象,下面,我们举个栗子
<script> function outter(){ var cons = '常量小常' return function() { return cons; } } // 得到外部函数的内部函数 var consFun= outter(1,2); // 执行内部函数 consFun(); </script>
这个例子最终会输出 ‘常量小常’, 虽然内部函数是在外部执行的, 但它依然能够找到定义时候的一些常量值。这个就是一种闭包的表现形式
换句话说,就是函数在执行的时候,能够还原它定义时所处的环境,能够按照定义时候的样子去执行
很多语言都支持私有变量, 就是将一个变量或者方法声明为私有,只能被内部使用,外部要想使用只能通过定义的一些公共方法访问
Js原生不支持这种语法,但可以通过闭包模拟这种私有变量的特性
下面就是一个闭包模拟私有变量的例子 , privateCounter 变量不能直接被外部访问,只能通过特定的方法操作。这个例子来源于此:
用闭包模拟私有变量
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures#用闭包模拟私有方法
<script> var Counter = (function () { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function () { changeBy(1); }, decrement: function () { changeBy(-1); }, value: function () { return privateCounter; }, }; })(); console.log(Counter.value()); /* logs 0 */ Counter.increment(); Counter.increment(); console.log(Counter.value()); /* logs 2 */ Counter.decrement(); console.log(Counter.value()); /* logs 1 */ </script>