闭包 作用域 作用域链

153 阅读4分钟

1.什么是闭包

  闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。                                   ——摘自百度百科

理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象,这就是闭包的重要概念。

  闭包的缺点:滥用闭包函数会造成内存泄露,因为闭包中引用到的包裹函数中定义的变量都
永远不会被释放,所以我们应该在必要的时候,及时释放这个闭包函数


一个闭包的例子

 function a(){
     var i=0;
     function b(){
         alert(++i);
     }     return b;
 }
 var c = a(); 
 c();  //弹出i的值 1

  1. 函数b嵌套在函数a内部
  2. 函数a返回函数b
  3. 外部再调用函数b,就可以获取函数a中的局部变量
 闭包就是一个函数引用另一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会增加内存消耗。

或者说闭包就是子函数可以使用父函数的局部变量,还有父函数的参数。

为什么会形成闭包

所有函数都能访问全局变量。 

实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。故子函数可以访问父函数中的局部变量,并在函数最后返回,这样他们彼此都被使用无法被销毁。因此形成闭包。

如何销毁闭包

释放对闭包的引用 使引用变量为空。

javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

2.作用域

在JavaScript中,作用域为可访问变量,对象,函数的集合。

javaScript局部作用域

变量在函数内部声明,只能在函数内部访问。

function demo() {
    var a = 6; //局部作用域
}

函数内声明需加var否则仍是全局变量。

JavaScript全局作用域

变量在函数外定义,网页中的所有脚本和函数均可使用。

var num = 1; //全局变量
//此处可调用num
function a() {
//此处可调用num
}

3.作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain,不简称sc)来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象

原理

只要搞清楚JavaScript作用域链的原理我们就很容易理解作用域链。

  • 函数在执行的过程中,先从自己内部寻找变量
  • 如果找不到,再从创建当前函数所在的作用域去找,从此往上,也就是向上一级找。

function foo() {
    let a = 1;
    function bar() {
        let a = 2;
        function baz() {
            let a = 3;
            console.log(a);
        }
        baz();
    }
    bar();
}
foo(); 

let是块级作用域,只可在自己所在的花括号内被调用。我们先从自己内部寻找变量,console.log(a)在函数baz中,而函数baz内部变量 a = 3, 所以输出a = 3;

let a = 1;
function foo(){
  let a = 2;
  function baz(){
        console.log(a);
  }
  bar(baz);
}
function bar(fn){
  let a = 3;
      fn();
}
foo(); 

我们先从自己内部寻找变量,函数baz内部并未定义任何变量,所以我们再从创建当前函数所在的作用域去找,也就是他的父级,发现定义了a = 2。