作用域/变量回收/闭包

所有变量(包括基本类型和引用类型)都存在于一个执行环境(也称作作用域)当中。这个变量决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。

变量的执行环境有助于确定应该何时释放内存。

JS是一门具有自动垃圾收集机制的编程语言。离开作用域的值将被自动标记为可以回收,在垃圾收集期间被删除。

当函数内部定义了其它函数时,就创建了闭包。闭包的作用域链包含自己的作用域、包含函数(外部函数)的作用域和全局作用域。通常函数作用域及其所有变量都会在函数执行结束后被销毁,但是当函数返回一个闭包时,这个函数的作用域会一直在内存中保存到闭包不存在为止。

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

f2可以读取f1的局部变量,要把f2作为返回值,可以在f1外部读取它的内部变量。

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

add() //1
add() //2
add() //3

注意: 为什么上面这段代码没有直接写的 function add (){...} 而是把function赋值给了变量add呢?

我们通常会想当然的认为每次调用 add() 都会重走一遍add()中的代码块, 但其实不然。

注意add方法中的return, 它return的并不是1,2,3这样的数值,而是return了一个方法,并且把这个方法赋值给了add变量。

那么在这个function自运行一遍之后,其实最后赋值给add的是return counter += 1 这段代码。

所以后面每次调用add() 其实都是在调用return counter += 1。

再结合之前所说的, 闭包会持有父方法的局部变量并且不会随父方法销毁而销毁, 所以这个counter其实就是来自于第一次function执行时创建的变量。

使用闭包可以模仿块级作用域。

闭包还可以用于在对象中创建私有变量。

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25

创建闭包必须维护额外的作用域,过度使用它们可能会占用大量内存。

你可能感兴趣的:(作用域/变量回收/闭包)