Javascript底层原理之作用域链和闭包

72 阅读1分钟

在Javascript中,有全局作用域和函数作用域,ES6中又引入了块级作用域。

作用域就是变量可以访问的范围。

块级作用域

function test() {
  let a = 'apple';
  {
    let b = 'pear';
    console.log('b:', b);
  }
  console.log('a:', a);
  console.log('b:', b);
}

test();

image.png

如图,let标识符和{} 创建了块级作用域,b变量只能在所属的块级作用域中访问。

作用域链

在查找变量的过程中,是从 当前的作用域 -> 父级作用域 -> 爷爷作用域...一步步向外查找的,查找过程中作用域像链条一样关联起来,这种隐性的作用域链接叫做作用域链

function grandFather () {
  let a = 'grandfather';
  function father () {
    let a = 'father';
    function son () {
      let a = 'son';
      console.log( a);
    }
    son()
  }
  father();
}

grandFather();// son
function grandFather () {
  let a = 'grandfather';
  function father () {
    let a = 'father';
    function son () {
      console.log( a);
    }
    son()
  }
  father();
}

grandFather();// father
function grandFather () {
  let a = 'grandfather';
  function father () {
    function son () {
      console.log( a);
    }
    son()
  }
  father();
}

grandFather();// grandfather

从上面的例子可以看到a变量的查找会是从son->father->grandFather。

闭包

在JavaScript中,在外部作用域访问内部作用域变量的方法叫做闭包

如下图,可以看到father函数建立了一个Closure,里面有变量a、变量b。

image.png
function father() {
  let a = 'apple';
  let b = 'pear';
  return function son () {
    debugger
    console.log(a);
    console.log(b);
  }
}

const fn = father(); 
fn();

闭包的本质是一组变量的集合,closure里面的变量、可以访问closure的函数,会存放到堆内存的老生代区域(对于老生代的内存设计可以看文章V8的垃圾回收机制)不会被垃圾回收机制主动回收,除非手动将其置为undefined或null, 即让其不再被引用。

闭包和全局变量一样,默认情况是在Tab页关闭的时候被回收。