闭包的概念
概念:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
简单理解:声明函数过程中将环境信息和所属信息绑定在一起的数据结构。例如:
function test(){
const a = 10;
return function fn(){
console.log(a);
}
}
const myfn = test();
myfn(); // 10
上述fn函数执行后可以访问到test函数作用域里的a常量,这里就是典型的闭包。
作用域
闭包的出现是和js的作用域有关系的。js中作用域主要分两种:1、全局作用域。2、局部作用域。
- 全局作用域:就是变量在全局范围内都会起作用,js中全局变量都会挂在window全局对象上。例如:
<script>
var a = 10;
console.log(a); // 10
console.log(window.a); // 10
</script>
-
局部作用域:变量在局域范围内起作用的作用域。
-
函数作用域,可以通过函数来实现变量只在局部范围内起作用,例如:
function test(){ //函数作用域变量只在函数内部起作用 var a = 10; console.log(a); } test(); // 10 console.log(a); // a is not defined
-
作用域链:根据内部函数可以访问可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称为函数作用域链。作用域链查找原则是就近原则。
function fn1 (){ var num = 10; function fn2(){ var num = 20; function fn3(){ console.log(num); } fn3(); } fn2(); } fn1();
上述代码 num 会向上层作用域查找,如果没有找到会继续在上层作用域中查找num变量,这样就形成了作用域链。
-
块级作用域:在“{}”代码块中起作用的作用域。例如:
{ // 变量只在“{}”代码块内起作用 let a = 10; console.log(a); } console.log(a); // a is not defined
闭包
由于 声明函数过程中将环境信息和所属信息绑定在一起,也就是类似于上述作用域链中体现的,在函数作用域中仍然保存着对父级对象的引用就形成了闭包。利用闭包 可以实现js中一些高级功能。
-
区分作用域
-
利用函数作用域及闭包特性可以区分局部作用域防止变量污染
(function (){ var a = 10; function test(){ console.log(a); } test(); // 10 })() console.log(a); // a is not defined
-
-
让外部可以访问内部变量
function test(){ var a = 10; return function(){ console.log(a); } } let testFn = test(); testFn();
上述代码可以访问到test内部a变量,同样test函数也是一个高阶函数。高阶函数:接受另一个函数作为参数的函数被称为高阶函数(Higher-Order Function)。
-
缓存特性
- 可以利用闭包来缓存变量,例如:
const once = (fn)=>{ let done = false; return function(){ if(!done){ fn.apply(this,fn); }else{ console.log("this fn is already execute"); } done = true; } } function test(){ console.log("test..."); } let myfn = once(test); myfn(); // test... myfn(); // this fn is already execute
-
上述代码 通过闭包实现高阶函数once 来实现函数执行结果的缓存。
综上所述,js中闭包可以给我们带来诸多便利。但是在使用缓存特性时候需要注意缓存的局部变量会常驻内存,会造成内存泄露。所以在使用的过程中可以手动的将不需要的局部变量删除。
参考文献
developer.mozilla.org/zh-CN/docs/…